Android系统应用信息中存储和缓存的计算方法

来源:互联网 发布:淘宝妈妈装模特安天天 编辑:程序博客网 时间:2024/05/03 19:52

转自:http://blog.csdn.net/hursing/article/details/26587451

进行如下操作:

设置->应用->选择一个应用->应用信息

会到达如下界面:

可以看到这个应用占用的磁盘空间。

先说结果,这几项会计算哪些文件(夹)。

1.应用,由三项相加组成

  • /data/data/com.myapp/lib,即so库文件夹大小
  • /data/app/com.myapp-1.apk,原始apk文件的复制
  • /data/dalvik-cache/data@app@com.myapp-1.apk@classes.dex,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命令行可得到这样的结果:

liuhx@uc ~/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", 注意包含双引号

得到结果:

liuhx@uc ~/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

这里就是计算的源码了。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int get_size(const char *pkgname, int persona, const char *apkpath,  
  2.              const char *fwdlock_apkpath, const char *asecpath,  
  3.              int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize,  
  4.              int64_t* _asecsize)  
  5. {  
  6.     DIR *d;  
  7.     int dfd;  
  8.     struct dirent *de;  
  9.     struct stat s;  
  10.     char path[PKG_PATH_MAX];  
  11.   
  12.     int64_t codesize = 0;  
  13.     int64_t datasize = 0;  
  14.     int64_t cachesize = 0;  
  15.     int64_t asecsize = 0;  
  16.   
  17.         /* count the source apk as code -- but only if it's not 
  18.          * on the /system partition and its not on the sdcard. 
  19.          */  
  20.     if (validate_system_app_path(apkpath) &&  
  21.             strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) {  
  22.         if (stat(apkpath, &s) == 0) {  
  23.             codesize += stat_size(&s);  
  24.         }  
  25.     }  
  26.         /* count the forward locked apk as code if it is given 
  27.          */  
  28.     if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') {  
  29.         if (stat(fwdlock_apkpath, &s) == 0) {  
  30.             codesize += stat_size(&s);  
  31.         }  
  32.     }  
  33.         /* count the cached dexfile as code */  
  34.     if (!create_cache_path(path, apkpath)) {  
  35.         if (stat(path, &s) == 0) {  
  36.             codesize += stat_size(&s);  
  37.         }  
  38.     }  
  39.   
  40.         /* add in size of any libraries */  
  41.     if (!create_pkg_path_in_dir(path, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {  
  42.         d = opendir(path);  
  43.         if (d != NULL) {  
  44.             dfd = dirfd(d);  
  45.             codesize += calculate_dir_size(dfd);  
  46.             closedir(d);  
  47.         }  
  48.     }  
  49.   
  50.         /* compute asec size if it is given 
  51.          */  
  52.     if (asecpath != NULL && asecpath[0] != '!') {  
  53.         if (stat(asecpath, &s) == 0) {  
  54.             asecsize += stat_size(&s);  
  55.         }  
  56.     }  
  57.   
  58.     if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, persona)) {  
  59.         goto done;  
  60.     }  
  61.   
  62.     d = opendir(path);  
  63.     if (d == NULL) {  
  64.         goto done;  
  65.     }  
  66.     dfd = dirfd(d);  
  67.   
  68.     /* most stuff in the pkgdir is data, except for the "cache" 
  69.      * directory and below, which is cache, and the "lib" directory 
  70.      * and below, which is code... 
  71.      */  
  72.     while ((de = readdir(d))) {  
  73.         const char *name = de->d_name;  
  74.   
  75.         if (de->d_type == DT_DIR) {  
  76.             int subfd;  
  77.             int64_t statsize = 0;  
  78.             int64_t dirsize = 0;  
  79.                 /* always skip "." and ".." */  
  80.             if (name[0] == '.') {  
  81.                 if (name[1] == 0) continue;  
  82.                 if ((name[1] == '.') && (name[2] == 0)) continue;  
  83.             }  
  84.             if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {  
  85.                 statsize = stat_size(&s);  
  86.             }  
  87.             subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);  
  88.             if (subfd >= 0) {  
  89.                 dirsize = calculate_dir_size(subfd);  
  90.             }  
  91.             if(!strcmp(name,"lib")) {  
  92.                 codesize += dirsize + statsize;  
  93.             } else if(!strcmp(name,"cache")) {  
  94.                 cachesize += dirsize + statsize;  
  95.             } else {  
  96.                 datasize += dirsize + statsize;  
  97.             }  
  98.         } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) {  
  99.             // This is the symbolic link to the application's library  
  100.             // code.  We'll count this as code instead of data, since  
  101.             // it is not something that the app creates.  
  102.             if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {  
  103.                 codesize += stat_size(&s);  
  104.             }  
  105.         } else {  
  106.             if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {  
  107.                 datasize += stat_size(&s);  
  108.             }  
  109.         }  
  110.     }  
  111.     closedir(d);  
  112. done:  
  113.     *_codesize = codesize;  
  114.     *_datasize = datasize;  
  115.     *_cachesize = cachesize;  
  116.     *_asecsize = asecsize;  
  117.     return 0;  
  118. }  
以上是计算了内部磁盘的占用,还不齐,还有线索:

PackageManagerService.java的内部类MeasureParams.handleStartCopy()函数:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void handleStartCopy() throws RemoteException {  
  2.             synchronized (mInstallLock) {  
  3.                 mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);  
  4.             }  
  5.   
  6.             final boolean mounted;  
  7.             if (Environment.isExternalStorageEmulated()) {  
  8.                 mounted = true;  
  9.             } else {  
  10.                 final String status = Environment.getExternalStorageState();  
  11.                 mounted = (Environment.MEDIA_MOUNTED.equals(status)  
  12.                         || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status));  
  13.             }  
  14.   
  15.             if (mounted) {  
  16.                 final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle);  
  17.   
  18.                 final File externalCacheDir = userEnv  
  19.                         .getExternalStorageAppCacheDirectory(mStats.packageName);  
  20.                 final long externalCacheSize = mContainerService  
  21.                         .calculateDirectorySize(externalCacheDir.getPath());  
  22.                 mStats.externalCacheSize = externalCacheSize;  
  23.   
  24.                 final File externalDataDir = userEnv  
  25.                         .getExternalStorageAppDataDirectory(mStats.packageName);  
  26.                 long externalDataSize = mContainerService.calculateDirectorySize(externalDataDir  
  27.                         .getPath());  
  28.   
  29.                 if (externalCacheDir.getParentFile().equals(externalDataDir)) {  
  30.                     externalDataSize -= externalCacheSize;  
  31.                 }  
  32.                 mStats.externalDataSize = externalDataSize;  
  33.   
  34.                 final File externalMediaDir = userEnv  
  35.                         .getExternalStorageAppMediaDirectory(mStats.packageName);  
  36.                 mStats.externalMediaSize = mContainerService  
  37.                         .calculateDirectorySize(externalMediaDir.getPath());  
  38.   
  39.                 final File externalObbDir = userEnv  
  40.                         .getExternalStorageAppObbDirectory(mStats.packageName);  
  41.                 mStats.externalObbSize = mContainerService.calculateDirectorySize(externalObbDir  
  42.                         .getPath());  
  43.             }  
  44.         }  
这里计算了sd卡上的磁盘占用。


内部磁盘的计算,是用linux的stat函数。

一些目录和文件的常量,定义在installd.h

认真看代码,则会得到文章开头的结果。

代码计算的结果得到的是占用磁盘的大小,但由于存储器有"4K对齐”的概念,所以占用磁盘大小不等于文件大小。例如一个实际为2.98K的文件,会在应用信息里显示为4K。

搜索源码的另一个方法是在线搜索。http://androidxref.com/

0 0