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语法,可用关键字“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

这里就是计算的源码了。

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

时间: 2024-12-21 15:45:51

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

android 应用程序开发中,清除缓存的功能怎么做啊?

问题描述 android 应用程序开发中,清除缓存的功能怎么做啊? android 应用程序开发中,清除缓存的功能怎么做啊?清除此应用程序的缓存,怎么做? 解决方案 getCacheDir()能够得到当前项目的缓存地址 在项目中经常会使用到WebView 控件,当加载html 页面时,会在/data/data/应用package 目录下生成database与cache 两个文件夹.请求的url 记录是保存在WebViewCache.db,而url 的内容是保存在WebViewCache 文件夹下

android最新联系人信息的存储结构

从Android 2.0(API Level 5)开始,Android平台提供了一个改进的Contacts API,以适应一个联系人可以有多个帐户的需求,比如说手机通讯录和GMAIL通讯录,两个通讯录中的两条记录可以是同一个人.新的Contacts API主要是由ContactsContract及其相关的类来管理,旧的API(android.provider.Contacts)已不赞成使用,但为了兼容仍可以使用,只不过像以前一样,只能返回第一个帐户的信息. 在新的Contacts API中,联系

Android系统辅助功能中的放大手势机制介绍(Magnification Gesture Mechanism)

前言: Magnification Gesture是Android系统辅助设置中提供的一种功能,它能够提供全局任意窗口的放大浏览功能,以帮助视力不是特别好的用户也能无障碍的使用,提升用户体验.本文档基于Android4.2.2进行分析,包括从设置中开启.用户触发到机制运行一整套流程. 文章目录: 一.关于Magnification Gesture 二.与Magnification Gesture相关的系统服务启动流程 三.来自用户的操作--打开Magnification Gesture开关的执行

《Android和PHP开发最佳实践》一2.4 Android系统四大组件

2.4 Android系统四大组件 之前我们已经学习了Android应用框架的四大核心要点,对Android的应用框架有了一个总体性的了解,接下来我们要学习Android应用程序中的四个重要组成部分,也就是我们一般所说的"应用组件".在前面讲解四大核心要点的篇幅中,我们曾经提到了控件(View控件)的概念,现在我们再来学习一下Android应用框架中的组件的概念.那么何谓组件呢?顾名思义,组件当然要比控件复杂,简而言之,组件是用于工业化组装的部件.要达到组件的标准,必须符合三个要求,以

iOS/Android系统多任务浅析

多任务,伪多任务.iOS与Aandroid操作系统的争论一直不休.双方的支持者都不甘示弱,都在谈,可谁又都谈不明白. 说到多任务,我们就不得不谈Pushmail.这是一个很经典的(伪)多任务,黑莓靠这个一度称霸北美市场.开启Pushmail的黑莓手机不是靠后台的实时进程,而是通过消息提醒,类似现在的信息推送.这样的做法可以极大的减少电量的消耗,而那些真正实时查看的后台进程,无一例外的都是电量消耗大户. 移动设备对于后台进程有着自己的管理方式,如果无限制的让进程在后台任意堆栈,那电量很快就会被耗尽

Android系统五大布局详解Layout

  我们知道Android系统应用程序一般是由多个Activity组成,而这些Activity以视图的形式展现在我们面前,视图都是由一个一个的组件构成的.组件就是我们常见的Button.TextEdit等等.那么我们平时看到的Android手机中那些漂亮的界面是怎么显示出来的呢?这就要用到Android的布局管理器了,网上有人比喻的很好:布局好比是建筑里的框架,组件按照布局的要求依次排列,就组成了用于看见的漂亮界面了.      在分析布局之前,我们首先看看控件:Android中任何可视化的控件

Android系统的五种数据存储形式实例(一)_Android

Android系统有五种数据存储形式,分别是文件存储.SP存储.数据库存储.contentprovider 内容提供者.网络存储.其中,前四个是本地存储.存储的类型包括简单文本.窗口状态存储.音频视频数据.XML注册文件的各种数据.各种存储形式的特点不尽相同,因此对于不同的数据类型有着固定的存储形式,本文为演示方便给出的案例基本相同,都是是采用账号登录来演示数据存储,保存账号和密码信息,下次登录时记住账号和密码.重在说明各种存储形式的原理. 文件存储: 以I/O流的形式把数据存入手机内存或SD卡

Android中使用二级缓存、异步加载批量加载图片完整案例

一.问题描述 Android应用中经常涉及从网络中加载大量图片,为提升加载速度和效率,减少网络流量都会采用二级缓存和异步加载机制,所谓二级缓存就是通过先从内存中获取.再从文件中获取,最后才会访问网络.内存缓存(一级)本质上是Map集合以key-value对的方式存储图片的url和Bitmap信息,由于内存缓存会造成堆内存泄露, 管理相对复杂一些,可采用第三方组件,对于有经验的可自己编写组件,而文件缓存比较简单通常自己封装一下即可.下面就通过案例看如何实现网络图片加载的优化. 二.案例介绍 案例新

Android系统的五种数据存储形式实例(一)

Android系统有五种数据存储形式,分别是文件存储.SP存储.数据库存储.contentprovider 内容提供者.网络存储.其中,前四个是本地存储.存储的类型包括简单文本.窗口状态存储.音频视频数据.XML注册文件的各种数据.各种存储形式的特点不尽相同,因此对于不同的数据类型有着固定的存储形式,本文为演示方便给出的案例基本相同,都是是采用账号登录来演示数据存储,保存账号和密码信息,下次登录时记住账号和密码.重在说明各种存储形式的原理. 文件存储: 以I/O流的形式把数据存入手机内存或SD卡