標籤:android 儲存 緩衝 應用資訊 計算
進行如下操作:
設定->應用->選擇一個應用->應用資訊
會到達如下介面:
可以看到這個應用佔用的磁碟空間。
先說結果,這幾項會計算哪些檔案(夾)。
1.應用,由三項相加組成
- /data/data/com.myapp/lib,即so庫檔案夾大小
- /data/app/com.myapp-1.apk,原始apk檔案的複製
- /data/dalvik-cache/[email protected]@[email protected],dalvik虛擬機器對dex檔案的緩衝
2.USB儲存(應用)。把程式裝到(或移到)sd卡後的大小。/sdcard/Android/...,參考“應用”項。
3.資料 = /data/data/com.myapp/目錄大小 - cache子目錄大小 - lib子目錄大小
4.SD 記憶卡 = /sdcard/Android/data/com.myapp/目錄大小 - /sdcard/Android/data/com.myapp/cache目錄大小
(總計是以上4個之和)
5.緩衝 = /data/data/com.myapp/cache目錄大小 + /sdcard/Android/data/com.myapp/cache目錄大小
下面是分析Android4.2源碼怎麼做的。比較詳細說分析過程,會累贅些,授之以漁吧。
根據應用資訊的介面Settings這個名字,猜測到源碼
android4.2/packages/apps/Settings/src/com/android/settings/applications/InstalledAppDetails.java
負責,對應的xml是
android4.2/packages/apps/Settings/res/layout/installed_app_details.xml
重新整理磁碟佔用大小的函數是
InstalledAppDetaileds.refreshSizeInfo()
根據代碼線索反查
mDataSize.setText(getSizeStr(dataSize));
->
long dataSize = mAppEntry.dataSize;
->
mAppEntry = mState.getEntry(packageName);
->
mState.requestSize(mAppEntry.info.packageName);
ApplicationsState mState
->
android4.2/packages/apps/Settings/src/com/android/settings/applications/ApplicationState.java
mPm.getPackageSizeInfo(packageName, mBackgroundHandler.mStatsObserver);
final PackageManager mPm
->
android4.2/frameworks/base/core/java/android/app/ApplicationPackageManager.java
public void getPackageSizeInfo(String packageName, int userHandle,
IPackageStatsObserver observer) {
mPM.getPackageSizeInfo(packageName, userHandle, observer);
private final IPackageManager mPM;
->
IPackageManager是個介面,用尋找檔案內容的方式搜尋它的實現,根據java文法,可用關鍵字“extends IPackageManager”
用shell命令列可得到這樣的結果:
[email protected] ~/Desktop/android4.2 $ grep -rnsw "extends IPackageManager" *
frameworks/base/services/java/com/android/server/pm/PackageManagerService.java:172:public class PackageManagerService extends IPackageManager.Stub {
->
android4.2/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
public void getPackageSizeInfo(
Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new MeasureParams(stats, observer);
mHandler.sendMessage(msg);
->
內部類class MeasureParams
void handleStartCopy() throws RemoteException {
mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);
->
getPackageSizeInfoLI函數
int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, publicSrcDir,
asecPath, pStats);
final Installer mInstaller
->
frameworks/base/services/java/com/android/server/pm/Installer.java
public int getSizeInfo(
String s = transaction(builder.toString());
->
private synchronized String transaction(String cmd) {
if (!writeCommand(cmd)) {
->
private boolean writeCommand(String _cmd)
mOut.write(buf, 0, 2);
mOut.write(cmd, 0, len);
->
OutputStream mOut;
LocalSocketAddress address = new LocalSocketAddress("installd",
LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mOut = mSocket.getOutputStream();
到此很無語,用socket的方式來通訊。
->
尋找檔案內容,關鍵字為 "installd", 注意包含雙引號
得到結果:
[email protected] ~/Desktop/android4.2 $ grep -rnsw "\"installd\"" *
cts/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/RootProcessScanner.java:33: "installd",
frameworks/base/services/java/com/android/server/pm/Installer.java:51: LocalSocketAddress address = new LocalSocketAddress("installd",
frameworks/base/cmds/installd/installd.h:18:#define LOG_TAG "installd"
frameworks/base/cmds/installd/installd.h:49:#define SOCKET_PATH "installd"
sdk/files/ant/build.xml:1355: <condition property="tested.project.install.target" value="installi" else="installd">
sdk/files/ant/build.xml:1392: <target name="installd" depends="-set-debug-files, install"
->
frameworks/base/cmds/installd/installd.h
int get_size(const char *pkgname, int persona, const char *apkpath, const char *fwdlock_apkpath,
const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
int64_t *asecsize);
或
frameworks/base/cmds/installd/installd.c
static int do_get_size(char **arg, char reply[REPLY_MAX])
res = get_size(arg[0], atoi(arg[1]), arg[2], arg[3], arg[4],
&codesize, &datasize, &cachesize, &asecsize);
->
看到在installd.c沒有實現get_size函數。可以根據函式宣告搜尋檔案內容,不過既然是c語言,那可能是把函數分離實現。
所以在installd.c的目錄下,看到另一個代碼檔案裡實現了。轉載請註明出處:http://blog.csdn.net/hursing
->
frameworks/base/cmds/installd/commands.c
這裡就是計算的源碼了。
int get_size(const char *pkgname, int persona, const char *apkpath, const char *fwdlock_apkpath, const char *asecpath, int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int64_t* _asecsize){ DIR *d; int dfd; struct dirent *de; struct stat s; char path[PKG_PATH_MAX]; int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; int64_t asecsize = 0; /* count the source apk as code -- but only if it's not * on the /system partition and its not on the sdcard. */ if (validate_system_app_path(apkpath) && strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) { if (stat(apkpath, &s) == 0) { codesize += stat_size(&s); } } /* count the forward locked apk as code if it is given */ if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') { if (stat(fwdlock_apkpath, &s) == 0) { codesize += stat_size(&s); } } /* count the cached dexfile as code */ if (!create_cache_path(path, apkpath)) { if (stat(path, &s) == 0) { codesize += stat_size(&s); } } /* add in size of any libraries */ if (!create_pkg_path_in_dir(path, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) { d = opendir(path); if (d != NULL) { dfd = dirfd(d); codesize += calculate_dir_size(dfd); closedir(d); } } /* compute asec size if it is given */ if (asecpath != NULL && asecpath[0] != '!') { if (stat(asecpath, &s) == 0) { asecsize += stat_size(&s); } } if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, persona)) { goto done; } d = opendir(path); if (d == NULL) { goto done; } dfd = dirfd(d); /* most stuff in the pkgdir is data, except for the "cache" * directory and below, which is cache, and the "lib" directory * and below, which is code... */ while ((de = readdir(d))) { const char *name = de->d_name; if (de->d_type == DT_DIR) { int subfd; int64_t statsize = 0; int64_t dirsize = 0; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { statsize = stat_size(&s); } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (subfd >= 0) { dirsize = calculate_dir_size(subfd); } if(!strcmp(name,"lib")) { codesize += dirsize + statsize; } else if(!strcmp(name,"cache")) { cachesize += dirsize + statsize; } else { datasize += dirsize + statsize; } } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) { // This is the symbolic link to the application's library // code. We'll count this as code instead of data, since // it is not something that the app creates. if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { codesize += stat_size(&s); } } else { if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { datasize += stat_size(&s); } } } closedir(d);done: *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; *_asecsize = asecsize; return 0;}
以上是計算了內部磁碟的佔用,還不齊,還有線索:
PackageManagerService.java的內部類MeasureParams.handleStartCopy()函數:
void handleStartCopy() throws RemoteException { synchronized (mInstallLock) { mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats); } final boolean mounted; if (Environment.isExternalStorageEmulated()) { mounted = true; } else { final String status = Environment.getExternalStorageState(); mounted = (Environment.MEDIA_MOUNTED.equals(status) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)); } if (mounted) { final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle); final File externalCacheDir = userEnv .getExternalStorageAppCacheDirectory(mStats.packageName); final long externalCacheSize = mContainerService .calculateDirectorySize(externalCacheDir.getPath()); mStats.externalCacheSize = externalCacheSize; final File externalDataDir = userEnv .getExternalStorageAppDataDirectory(mStats.packageName); long externalDataSize = mContainerService.calculateDirectorySize(externalDataDir .getPath()); if (externalCacheDir.getParentFile().equals(externalDataDir)) { externalDataSize -= externalCacheSize; } mStats.externalDataSize = externalDataSize; final File externalMediaDir = userEnv .getExternalStorageAppMediaDirectory(mStats.packageName); mStats.externalMediaSize = mContainerService .calculateDirectorySize(externalMediaDir.getPath()); final File externalObbDir = userEnv .getExternalStorageAppObbDirectory(mStats.packageName); mStats.externalObbSize = mContainerService.calculateDirectorySize(externalObbDir .getPath()); } }
這裡計算了sd卡上的磁碟佔用。
內部磁碟的計算,是用linux的stat函數。
一些目錄和檔案的常量,定義在installd.h
認真看代碼,則會得到文章開頭的結果。
代碼計算的結果得到的是佔用磁碟的大小,但由於儲存空間有"4K對齊”的概念,所以佔用磁碟大小不等於檔案大小。例如一個實際為2.98K的檔案,會在應用資訊裡顯示為4K。
轉載請註明出處:http://blog.csdn.net/hursing