我是靠谱客的博主 文艺手套,最近开发中收集的这篇文章主要介绍Android获取应用磁盘空间占用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、应用详情数据含义

Android下如何获取应用所占用的磁盘空间呢? 带着这个疑问我们先看一下应用详细信息里的各项数据都代表什么含义。
在这里插入图片描述
如图所示:

  • 应用大小: 值得是apk解压后,所占用的磁盘空间

  • 缓存:指的是:/data/data/ p a c k a g e N a m e / c a c h e / + / s d c a r d / A n d r o i d / {packageName}/cache/ +/sdcard/Android/ packageName/cache/+/sdcard/Android/{packageName}/cache/ 的占用大小

  • 用户数据:这个数据不同的手机表现不一样,上述截图是google pixel手机的显示, 此处的数据是计算是:/data/data/ p a c k a g e N a m e / + / s d c a r d / A n d r o i d / {packageName}/ + /sdcard/Android/ packageName/+/sdcard/Android/{packageName} - 缓存目录大小

  • 用户数据部分,很多国产手机的计算方式也是不一样的,例如华为手机:数据部分是没有减去缓存目录大小的,只是单纯的/data/data/ p a c k a g e N a m e / + / s d c a r d / A n d r o i d / {packageName}/ + /sdcard/Android/ packageName/+/sdcard/Android/{packageName}之和。

  • 总计: 总计部分,不同手机和厂商有会有一定的差异性,google原生系统显示的是:应用大小+用户数据+缓存;然而有些国产手机显示的是:应用大小+数据-缓存 比如华为手机

二、获取应用占用磁盘大小代码

1、8.0以下设备获取方式

查看Android中PackageManager源码,找到getPackageSizeInfo方法:

/**
 * Retrieve the size information for a package.
 * Since this may take a little while, the result will
 * be posted back to the given observer.  The calling context
 * should have the {@link android.Manifest.permission#GET_PACKAGE_SIZE} permission.
 *
 * @param packageName The name of the package whose size information is to be retrieved
 * @param observer An observer callback to get notified when the operation
 * is complete.
 * {@link android.content.pm.IPackageStatsObserver#onGetStatsCompleted(PackageStats, boolean)}
 * The observer's callback is invoked with a PackageStats object(containing the
 * code, data and cache sizes of the package) and a boolean value representing
 * the status of the operation. observer may be null to indicate that
 * no callback is desired.
 *
 * @hide
 */
public abstract void getPackageSizeInfo(String packageName,IPackageStatsObserver observer);

getPackageSizeInfo方法有两个参数,第一个是需要计算的App包名,第二个是一个回调。不过IPackageStatesObserver这个class在API里貌似找不到,找了点儿资料,需要通过Android AIDL的方式来搞。

(1)、在该包下新建PackageStats.aidl文件,内容如下:

package android.content.pm;
parcelable PackageStats;

(2)、在该包下新建IPackageStatsObserver.aidl接口文件,内容如下:

package android.content.pm;

import android.content.pm.PackageStats;
/**
 * API for package data change related callbacks from the Package Manager.
 * Some usage scenarios include deletion of cache directory, generate
 * statistics related to code, data, cache usage(TODO)
 * {@hide}
 */
oneway interface IPackageStatsObserver {
    void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);
}

(3)、getPackageSizeInfo方法不能通过context.getPackageManager.getPackageSizeInfo的方式来调用,因为它其实是一个invoke受限的方法,所以必须通过反射实现:

/**
 * 获取Android Native App的缓存大小、数据大小、应用程序大小
 *
 * @param context
 *            Context对象
 * @param pkgName
 *            需要检测的Native App包名
 * @throws NoSuchMethodException
 * @throws InvocationTargetException
 * @throws IllegalAccessException
 */
public static void getPkgSize(final Context context, String pkgName) throws NoSuchMethodException,
        InvocationTargetException,
        IllegalAccessException {
    // getPackageSizeInfo是PackageManager中的一个private方法,所以需要通过反射的机制来调用
    Method method = PackageManager.class.getMethod("getPackageSizeInfo",
            new Class[] { String.class, IPackageStatsObserver.class });
    // 调用 getPackageSizeInfo 方法,需要两个参数:1、需要检测的应用包名;2、回调
    method.invoke(context.getPackageManager(), 
            pkgName, new IPackageStatsObserver.Stub() {
                @Override
                public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
                    // 从pStats中提取各个所需数据
                    Log.i(TAG, "缓存大小=" + Formatter.formatFileSize(context, pStats.cacheSize));
                    Log.i(TAG, "数据大小=" + Formatter.formatFileSize(context, pStats.dataSize));
                    Log.i(TAG, "程序大小=" + Formatter.formatFileSize(context, pStats.codeSize));
                }
            });
    }

(4)、根据PackageManager中getPackageSizeInfo注释中的提示,还需要在AndroidManifest.xml中加入permission:

<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />

2、8.0以上设备获取方式

8.0以上设备提供了,更加简单的API, 但是有一点是需要注意的,apk运行时获取当前自己进程的磁盘占用是不需要权限的,如果想要获取其他进程的占用是需要让用户进入到指定设置界面进行授权的。

/**
 * API 26以上获取App磁盘空间占用
 */
@RequiresApi(api = Build.VERSION_CODES.O)
private static void getAppsizeTop26(Context context, String packageName, CommCallback<AppSpaceInfo> callback) {
    //26以上的获取方法
    //调用前需要检查权限, 查询自己apk的磁盘占用不需要申请权限,查询非自己需要申请权限
    //非自己的包名
    if (!packageName.equals(context.getPackageName()) && !checkPackageUsageStats(context)) {
        //检查权限(查询自己apk的磁盘占用不需要申请权限,查询非自己需要申请权限)
        callback.onFinished(false, null);
        return;
    }
    AppSpaceInfo result = null;
    boolean exception = false;
    try {
        StorageStatsManager storageStatsManager = (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        File path = new File(context.getDataDir().getParent(), packageName);
        if (path.exists()) {
            try {
                UUID uuid = storageManager.getUuidForPath(path);
                int uid = context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA).uid;
                StorageStats storageStats = storageStatsManager.queryStatsForUid(uuid, uid);
                if (storageStats != null) {
                    result = new AppSpaceInfo(storageStats.getAppBytes(), storageStats.getDataBytes(), storageStats. getCacheBytes());
                    result.pacakgeName = packageName;
                }
            } catch (IOException | PackageManager.NameNotFoundException  e) {
                e.printStackTrace();
                exception = true;
            }
        }
    } catch (Throwable e) {
        e.printStackTrace();
        exception = true;
    }
    callback.onFinished(!exception, result);
}

public static class AppSpaceInfo {
/**
    * 设置-应用信息里的"应用"占用
    */
   public long apkBytes;

   /**
    * 设置-应用信息里的"数据"占用
    */
   public long dataBytes;

   /**
    * 设置-应用信息里的"缓存"占用
    */
   public long cacheByte;

   public String pacakgeName;


   AppSpaceInfo(long apkBytes, long dataBytes, long cacheByte){
       this.apkBytes = apkBytes;
       this.dataBytes = dataBytes;
       this.cacheByte = cacheByte;
   }
}

最后

以上就是文艺手套为你收集整理的Android获取应用磁盘空间占用的全部内容,希望文章能够帮你解决Android获取应用磁盘空间占用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部