zoukankan      html  css  js  c++  java
  • Android系统应用信息中存储和缓存的计算方法

    进行例如以下操作:

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

    会到达例如以下界面:

    能够看到这个应用占用的磁盘空间。

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

    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语法,可用keyword“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的方式来通信。

    ->

    查找文件内容。keyword为 "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

    这里就是计算的源代码了。

    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://androidxref.com/


    转载请注明出处:http://blog.csdn.net/hursing

  • 相关阅读:
    《代码之道》试读:规范书变更请求
    解读ASP.NET MVC 4 规划路线图
    淘宝数据魔方技术架构解析
    《程序员实用算法》试读:1.2.2主要的优化:函数调用
    《软件框架设计的艺术》试读:2.2 模块化应用程序
    磁盘分割原理
    无锡云计算中心3年内到底做了什么
    模式识别的一些资料
    边缘检测算法
    用递归方法来搜索连通区域
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7308479.html
Copyright © 2011-2022 走看看