PackageStats类
该类与应用程序包大小信息相关。
常用字段
packageName:应用包名。
codeSize:应用程序大小,如APK大小。
dataSize:应用程序的内部数据,如/data/data/<app>。
cacheSize:应用程序占用的缓存大小,如/data/data/<app>/cache。
因此,应用程序的总大小=codeSize+dataSize+cacheSize。
也就是说只要获得了安装包的PackageStats对象,就可以获取安装包的数据大小了。那么,该如何获取PackageStats对象呢?
getPackageSizeInfo
概况
在PackageManager类中提供了getPackageSizeInfo方法,该方法检索包的大小信息,检索结果的获取是通过回调实现IPackageStatsObserver的类得到的,该结果保存在onGetStatsCompleted方法的参数中,参数PackageStats对象包含安装包的程序大小、数据大小、缓存大小;boolean对象表示检索结果的状态。源码如下:
/**
* 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 userHandle The user whose size information should 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, int userHandle,
IPackageStatsObserver observer);
/**
* Like {@link #getPackageSizeInfo(String, int, IPackageStatsObserver)}, but
* returns the size for the calling user.
*
* @hide
*/
public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
getPackageSizeInfo(packageName, UserHandle.myUserId(), observer);
}
从源码中看出该方法是隐藏的方法,因此不能直接通过PackageManager对象调用,只能通过反射机制调用。下面会讲解具体调用过程。
使用方法
1、上面说到了只能通过反射机制调用getPackageSizeInfo方法,方式如下:
public void initPackageSizeInfo() {
// TODO Auto-generated method stub
Method getPackageSizeInfo = null;
try {
// 通过反射机制获得该隐藏函数
getPackageSizeInfo = mPackageManager.getClass().getMethod("getPackageSizeInfo", String.class,
IPackageStatsObserver.class);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SecurityPackageStatsObserver mSecurityPackageStatsObserver = new SecurityPackageStatsObserver();
if (getPackageSizeInfo != null) {
try {
// 调用该函数,并且给其分配参数 ,待调用流程完成后会回调SecurityPackageStatsObserver类的函数
getPackageSizeInfo.invoke(mPackageManager, securityPackageName, mSecurityPackageStatsObserver);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2、由于需要获取系统级的类,如果没有获取到系统的framework.jar包,那么需要加入Android系统形成的两个aidl文件,分别是IPackageStatsObserver.aidl和PackageStats.aidl文件,将这两个文件放到android.content.pm包下。
PackageStats.aidl内容如下:
package android.content.pm;
parcelable PackageStats;
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、第二个参数,是IPackageStatsObserver的对象,因此反射调用时需要定义一个继承IPackageStatsObserver类的类。
class SecurityPackageStatsObserver extends IPackageStatsObserver.Stub {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
// TODO Auto-generated method stub
mCacheSize = pStats.cacheSize;
mCodeSize = pStats.codeSize;
mDataSize = pStats.dataSize;
mTotalSize = mCacheSize + mCodeSize + mDataSize;
Log.d(TAG, "cacheSize = " + mCacheSize + "; codeSize = " + mCodeSize + "; dataSize = " + mDataSize
+ "; totalSize = " + mTotalSize);
}
}
当调用getPackageSizeInfo方法时,该函数启动中间流程去获取相关包的大小信息,获取完毕后回调至该类的onGetStatsCompleted(PackageStats pStats, boolean succeeded)方法中,大小信息封装到PackageStats对象中,参数succeeded表示回调结果的状态。
4、最后要注意添加权限:
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
示例代码
package com.yulong.android.activity;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Formatter;
import android.app.Activity;
import android.content.Context;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.widget.TextView;
public class PackageSizeInfoActivity extends Activity {
protected static final int MSG_INIT_PACKAGEINFO_COMPLETE = 0;
public static final String TAG = "PackageSizeInfoActivity";
private TextView packageName;
private TextView codeSize;
private TextView cacheSize;
private TextView dataSize;
private TextView totalSize;
private PackageManager mPackageManager;
private Context mContext;
private String securityPackageName = "com.yulong.android.security";
// 全局变量,保存当前查询包得信息
private long mCodeSize;
private long mCacheSize;
private long mDataSize;
private long mTotalSize;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INIT_PACKAGEINFO_COMPLETE:
displayPackageSizeInfoView();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle arg0) {
// TODO Auto-generated method stub
super.onCreate(arg0);
setContentView(R.layout.package_sizeinfo);
packageName = (TextView) findViewById(R.id.package_name);
codeSize = (TextView) findViewById(R.id.code_size);
cacheSize = (TextView) findViewById(R.id.cache_size);
dataSize = (TextView) findViewById(R.id.data_size);
totalSize = (TextView) findViewById(R.id.total_size);
mContext = this;
mPackageManager = mContext.getPackageManager();
}
protected void displayPackageSizeInfoView() {
// TODO Auto-generated method stub
packageName.setText("PackageName : " + securityPackageName);
codeSize.setText("CodeSize : " + formatFileSize(mCodeSize));
cacheSize.setText("CacheSize : " + formatFileSize(mCacheSize));
dataSize.setText("DataSize : " + formatFileSize(mDataSize));
totalSize.setText("TotalSize : " + formatFileSize(mTotalSize));
}
private String formatFileSize(long fileSize) {
// TODO Auto-generated method stub
return android.text.format.Formatter.formatFileSize(mContext, fileSize);
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
initPackageSizeInfoView();
}
private void initPackageSizeInfoView() {
// TODO Auto-generated method stub
new Thread(new PackageSizeInfoRunnable()).start();
}
class PackageSizeInfoRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
initPackageSizeInfo();
Message.obtain(mHandler, MSG_INIT_PACKAGEINFO_COMPLETE).sendToTarget();
}
}
public void initPackageSizeInfo() {
// TODO Auto-generated method stub
Method getPackageSizeInfo = null;
try {
// 通过反射机制获得该隐藏函数
getPackageSizeInfo = mPackageManager.getClass().getMethod("getPackageSizeInfo", String.class,
IPackageStatsObserver.class);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SecurityPackageStatsObserver mSecurityPackageStatsObserver = new SecurityPackageStatsObserver();
if (getPackageSizeInfo != null) {
try {
// 调用该函数,并且给其分配参数 ,待调用流程完成后会回调SecurityPackageStatsObserver类的函数
getPackageSizeInfo.invoke(mPackageManager, securityPackageName, mSecurityPackageStatsObserver);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class SecurityPackageStatsObserver extends IPackageStatsObserver.Stub {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
// TODO Auto-generated method stub
mCacheSize = pStats.cacheSize;
mCodeSize = pStats.codeSize;
mDataSize = pStats.dataSize;
mTotalSize = mCacheSize + mCodeSize + mDataSize;
Log.d(TAG, "cacheSize = " + mCacheSize + "; codeSize = " + mCodeSize + "; dataSize = " + mDataSize
+ "; totalSize = " + mTotalSize);
}
}
}