一、前言
最近仿微信存儲空間做了個demo,其中最麻煩的地方就是獲取APP應用大小了。做完之後自己總結把獲取方式封裝成了工具,方便以後再次使用。期望大家喜歡。如果感覺還不錯的話,不妨頂一個,收藏一下...。如果有什麼不妥的地方也可以留言指出,相互學習,共同進步
二、效果預覽
先看看Demo、不滿意請使勁懟作者、不要懟我哈
三、實現步驟
- 開啓權限
- AIDL文件導入
- 引用工具類
- 使用方式
(1)開啓權限
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
(2)AIDL文件導入
Android8.0之前沒有對應的API提供,需要引入兩個AIDL文件。
IPackageStatsObserver.aidl
package android.content.pm;
import android.content.pm.PackageStats;
interface IPackageStatsObserver {
oneway void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);
}
PackageStats.aidl
package android.content.pm;
parcelable PackageStats;
導入AIDL文件後需要編譯一下,系統自動生成java文件,不清楚AIDL是什麼的可以百度一下..
這裏就不解釋了。長話短說也解釋不清楚,讓大家自己學習,還可能獲得更多成就感
(3)引用工具類
AppSizeUtils.class
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.Build;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.support.annotation.RequiresApi;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
/**
* TODO 獲取APP應用 緩存大小 數據大小 應用大小
*/
public class AppSizeUtils {
private static AppSizeUtils mApiUrl;
private AppSizeUtils() {
}
public static AppSizeUtils getInstance() {
if (mApiUrl == null) {
synchronized (AppSizeUtils.class) {
if (mApiUrl == null) {
mApiUrl = new AppSizeUtils();
}
}
}
return mApiUrl;
}
/**
* TODO 獲取應用總大小
*/
public void init(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getAppSizeO(context);
} else {
getAppsize(context);
}
}
/**
* 獲取應用的大小
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public void getAppSizeO(Context context) {
StorageStatsManager storageStatsManager = (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
//獲取所有應用的StorageVolume列表
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
for (StorageVolume item : storageVolumes) {
String uuidStr = item.getUuid();
UUID uuid;
if (uuidStr == null) {
uuid = StorageManager.UUID_DEFAULT;
} else {
uuid = UUID.fromString(uuidStr);
}
int uid = getUid(context, context.getPackageName());
//通過包名獲取uid
StorageStats storageStats = null;
try {
storageStats = storageStatsManager.queryStatsForUid(uuid, uid);
} catch (IOException e) {
e.printStackTrace();
}
if (onBackListent != null) {
onBackListent.backData(storageStats.getCacheBytes(), storageStats.getDataBytes(), storageStats.getAppBytes());
}
}
}
/**
* 根據應用包名獲取對應uid
*/
public int getUid(Context context, String pakName) {
try {
return context.getPackageManager().getApplicationInfo(pakName, PackageManager.GET_META_DATA).uid;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return -1;
}
/**
* 獲取應用大小8.0以下
*/
public void getAppsize(Context context) {
try {
Method method = PackageManager.class.getMethod("getPackageSizeInfo", new Class[]{String.class, IPackageStatsObserver.class});
// 調用 getPackageSizeInfo 方法,需要兩個參數:1、需要檢測的應用包名;2、回調
method.invoke(context.getPackageManager(), context.getPackageName(), new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
if (onBackListent != null) {
onBackListent.backData(pStats.cacheSize, pStats.dataSize, pStats.codeSize);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public OnBackListent onBackListent;
public interface OnBackListent {
void backData(long cacheSize, long dataSize, long codeSize);
}
public AppSizeUtils setDatasListent(OnBackListent listent) {
onBackListent = listent;
return this;
}
}
(4)使用方式
AppSizeUtils.getInstance().setDatasListent(new AppSizeUtils.OnBackListent() {
@Override
public void backData(long cacheSize, long dataSize, long codeSize) {
Log.e(TAG, "緩存大小: "+cacheSize + " 數據大小: " + dataSize + " 應用大小: " + codeSize );
}
}).init(MyApplication.getContext());
/**
* 將文件大小顯示爲GB,MB等形式
*/
public static String size(long size)
{
if (size / (1024 * 1024 * 1024) > 0)
{
float tmpSize = (float) (size) / (float) (1024 * 1024 * 1024);
DecimalFormat df = new DecimalFormat("#.##");
return "" + df.format(tmpSize) + "GB";
}
else if (size / (1024 * 1024) > 0)
{
float tmpSize = (float) (size) / (float) (1024 * 1024);
DecimalFormat df = new DecimalFormat("#.##");
return "" + df.format(tmpSize) + "MB";
}
else if (size / 1024 > 0)
{
return "" + (size / (1024)) + "KB";
}
else
return "" + size + "B";
}
順便提供一下轉化方法,省的自己寫麻煩..
這裏就不貼Demo源碼了,主要功能代碼已經貼出來。大家可以根據需求隨便更改,想要源碼的也可以在底部鏈接下載。。
四、功能分解
(1)單例模式
private static AppSizeUtils mApiUrl;
private AppSizeUtils() { }
public static AppSizeUtils getInstance() {
if (mApiUrl == null) {
synchronized (AppSizeUtils.class) {
if (mApiUrl == null) {
mApiUrl = new AppSizeUtils();
}
}
}
return mApiUrl;
}
這個就不解釋了,不瞭解的可以看看《JAVA 設計模式——單例模式》
(2)設置監聽數據獲取回調
爲什麼要寫這個回調呢?
在獲取App大小的時候有延遲(有耗時操作),所以不能立馬得到數據。所以當時寫了個廣播用來通知什麼時候獲取到數據,不過那樣的話寫起來有點麻煩,所以寫了個回調。
public OnBackListent onBackListent;
public interface OnBackListent {
void backData(long cacheSize, long dataSize, long codeSize);
}
public AppSizeUtils setDatasListent(OnBackListent listent) {
onBackListent = listent;
return this;
}
具體調用方法時機看下面↓↓↓
(3)開始獲取數據
只需調用init(Context context)
/**
* TODO 獲取應用總大小
*/
public void init(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getAppSizeO(context, context.getPackageName());
} else {
getAppsize(context);
}
}
因爲android8.0之後有提供新的API用來獲取應用大小
所以8.0之前和8.0之後獲取方式不一樣
Android8.0之前
/**
* 獲取應用大小8.0以下
*/
public void getAppsize(Context context) {
try {
Method method = PackageManager.class.getMethod("getPackageSizeInfo", new Class[]{String.class, IPackageStatsObserver.class});
// 調用 getPackageSizeInfo 方法,需要兩個參數:1、需要檢測的應用包名;2、回調
method.invoke(context.getPackageManager(), context.getPackageName(), new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
//數據獲取時機 使用回調
onBackListent.backData(pStats.cacheSize, pStats.dataSize, pStats.codeSize);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Android8.0之後
/**
* 8.0獲取應用的大小
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public void getAppSizeO(Context context) {
StorageStatsManager storageStatsManager = (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
//獲取所有應用的StorageVolume列表
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
for (StorageVolume item : storageVolumes) {
String uuidStr = item.getUuid();
UUID uuid;
if (uuidStr == null) {
uuid = StorageManager.UUID_DEFAULT;
} else {
uuid = UUID.fromString(uuidStr);
}
int uid = getUid(context, context.getPackageName());
//通過包名獲取uid
StorageStats storageStats = null;
try {
storageStats = storageStatsManager.queryStatsForUid(uuid, uid);
} catch (IOException e) {
e.printStackTrace();
}
onBackListent.backData(storageStats.getCacheBytes(), storageStats.getDataBytes(), storageStats.getAppBytes());
}
}
文采不好,就不多廢話了。不過寫了這麼多,大家記得捧捧場哈
五、Demo地址
六、參考文檔
七、內容推薦
《Android Rxjava+Retrofit網絡請求框架封裝(一)》
如果你覺得我寫的不錯或者對您有所幫助的話
不妨頂一個【微笑】,別忘了點贊、收藏、加關注哈
看在我花了這麼多時間整理寫成文章分享給大家的份上,記得手下留情哈
您的每個舉動都是對我莫大的支持