获取安装应用Apk包大小、缓存大小以及应用占用存储总大小
可以通过 PackageManager类的getPackageSizeInfo方法获取,但是这个方法被Google标记成隐藏方法,不能直接调用;
解决方法:可以通过反射调用,但在Android8.0及以上通过反射调用会报错,不过Android8.0及以上可以通过StorageManager 获取
Android8.0以下获取Apk大小方法:
通过反射调用getPackageSizeInfo获取
- 创建AIDL文件
- 创建文件 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);
}
- 创建文件 PackageStats.aidl
package android.content.pm;
parcelable PackageStats;
注意包名路径创建正确
如下图:
创建完成后依次点击 Build --> Make Project 以生成AIDL文件
- 在AndroidManifest.xml 中添加权限
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
- 代码实现
public static void handlePackageSizeInfo(Context context, String packageName) {
PackageManager mPackageManager = context.getPackageManager();
Method getPackageSizeInfo = null;
try {
getPackageSizeInfo = mPackageManager.getClass().getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
MyPackageStatsObserver mSecurityPackageStatsObserver = new MyPackageStatsObserver();
if (getPackageSizeInfo != null) {
try {
getPackageSizeInfo.setAccessible(true);
// 调用该函数,待调用完成后会回调MyPackageStatsObserver
getPackageSizeInfo.invoke(mPackageManager, packageName, mSecurityPackageStatsObserver);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
static class MyPackageStatsObserver extends IPackageStatsObserver.Stub {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
long mCacheSize = pStats.cacheSize;
long mCodeSize = pStats.codeSize;
long mDataSize = pStats.dataSize;
long mTotalSize = mCacheSize + mCodeSize + mDataSize;
Log.d("AppLog", succeeded+ " cacheSize = " + mCacheSize + "; appSize = " + mCodeSize + "; " + "dataSize = " + mDataSize + "; totalSize = " + mTotalSize);
}
}
Android8.0及以上获取Apk大小方法:
通过系统服务 StorageManager 获取Apk大小,需要申请权限 PACKAGE_USAGE_STATS
- 在AndroidManifest.xml 中添加权限
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
- 代码实现
@RequiresApi(api = Build.VERSION_CODES.O)
public static void getAppSize(Context context, String packageName){
if (!hasUsageStatsPermission(context)){
requestAppUsagePermission(context);
} else {
new Thread(() -> {
final StorageStatsManager storageStatsManager = (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
final StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
final List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
final UserHandle user = android.os.Process.myUserHandle();
try {
for (StorageVolume storageVolume : storageVolumes) {
final String uuidStr = storageVolume.getUuid();
final UUID uuid = uuidStr == null ? StorageManager.UUID_DEFAULT : UUID.fromString(uuidStr);
Log.d("AppLog", "storage:" + uuid + " : " + storageVolume.getDescription(context) + " : " + storageVolume.getState());
Log.d("AppLog", "getFreeBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getFreeBytes(uuid)));
Log.d("AppLog", "getTotalBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getTotalBytes(uuid)));
StorageStats storageStats = storageStatsManager.queryStatsForPackage(uuid, packageName, user);
Log.d("AppLog", "storage stats for app of package name:" + packageName + " : ");
Log.d("AppLog", "getAppBytes:" + Formatter.formatShortFileSize(context, storageStats.getAppBytes()) + " getCacheBytes:" + Formatter.formatShortFileSize(context,
storageStats.getCacheBytes()) + " getDataBytes:" + Formatter.formatShortFileSize(context, storageStats.getDataBytes()));
}
}catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
public static boolean hasUsageStatsPermission(Context context) {
UsageStatsManager usageStatsManager = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
}
if (usageStatsManager == null) {
return false;
}
long currentTime = System.currentTimeMillis();
// try to get app usage state in last 2 min
List<UsageStats> stats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, currentTime - 2 * 60 * 1000, currentTime);
return stats != null && stats.size() > 0;
}
public static void requestAppUsagePermission(Context context) {
Intent intent = new Intent(android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e("AppLog", "Start usage access settings activity fail!", e);
}
}
总结
public static void getInstallApkSize(Context context, String packageName){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getAppSize(context, packageName);
} else {
handlePackageSizeInfo(context, packageName);
}
}
Android8.0以下可以通过反射getPackageSizeInfo方法获取Apk大小;
Android8.0及以上可以通过申请权限 PACKAGE_USAGE_STATS 获取其他已安装应用的Apk大小
还有一种比较复杂方法是 编译一下的android2.3的Framework框架,把jar包拷出来,再把框架导入到library,之后就可以直接调用已经hide 的方法 getPackageSizeInfo() 了,具体操作可以搜一下