要實現卸載程序、清除數據、停止正在運行的服務這幾大模塊,現在將代碼粗略總結如下:
主要運用到的類有
PackageManager
ActivityManager
ApplicationInfo
RunningServiceInfo
Method
還有兩個android.pm下的源文件用於生成樁,IPackageStatsObserver.java
和 IPackageDataObserver.java,由名字可以看出,他們是跟包的狀態和大小有關的,在網上找到這兩個文件的源碼後,把他們放在工程src目錄下的android.pm包下,自己建包。
首先要獲得系統中已經裝了的apk,apk分為兩類第一是系統的apk,第二是第三方的apk,所以在獲取apk時可以指定一個過濾器,見如下代碼:
java代碼
-
// 添加過濾 ,得到全部程序,系統程序,用戶自己安裝的程序
-
-
private List queryFilterAppInfo(int filter) {
-
// 查詢所有已經安裝的應用程序
-
List listAppcations =
-
pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
-
Collections.sort(listAppcations,new
-
ApplicationInfo.DisplayNameComparator(pm));// 排序
-
List appInfos = new ArrayList(); // 保存過濾查到的AppInfo
-
// 根據條件來過濾
-
switch (filter) {
-
case FILTER_ALL_APP: // 所有應用程序
-
appInfos.clear();
-
for (ApplicationInfo app : listAppcations) {
-
if (app.packageName.equals("com.android.appmanager")) { // 過濾自己
-
continue;
-
}
-
appInfos.add(getAppInfo(app));
-
}
-
return appInfos;
-
case FILTER_SYSTEM_APP: // 系統程序
-
appInfos.clear();
-
for (ApplicationInfo app : listAppcations) {
-
if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-
if (app.packageName.equals("com.android.appmanager""font-family:Arial,
-
Helvetica, sans-serif;">)// wifi { // 過濾自己
-
continue;
-
}
-
appInfos.add(getAppInfo(app));
-
}
-
}
-
return appInfos;
-
case FILTER_THIRD_APP: // 第三方應用程序
-
appInfos.clear();
-
for (ApplicationInfo app : listAppcations) {
-
// 非系統程序
-
if ((app.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {
-
if (app.packageName.equals("com.android.appmanager"))
-
continue;
-
}
-
appInfos.add(getAppInfo(app));
-
}
-
// 本來是系統程序,被用戶手動更新後,該系統程序也成為第三方應用程序了
-
else if ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)
-
{
-
if (app.packageName.equals("geeya.android.appmanage")) { // 過濾自己
-
continue;
-
}
-
appInfos.add(getAppInfo(app));
-
}
-
}
-
break;
-
default:
-
return null;
-
}
-
return appInfos;
-
}
復制代碼
AppInfo是我自己定義的一個類,裡面包含了應用程序的包名、數據區大小、代碼區大小、等等一些屬性。
好,現在我們來獲取app包的數據區大小、緩存區大小、代碼區大小,這裡要用反射的機制去獲取PackageManager類的隱藏方法getPackageSizeInfo(),這個方法的具體實現是通過回調函數來實現的,這裡要用到IPackageStatsObserver這個類生成的樁。
java代碼
-
// aidl文件形成的Bindler機制服務類
-
-
public class PkgSizeObserver extends IPackageStatsObserver.Stub {
-
/***
-
* 回調函數,
-
*
-
* @param pStatus
-
* ,返回數據封裝在PackageStats對象中
-
* @param succeeded
-
* 代表回調成功
-
*/
-
@Override
-
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
-
throws RemoteException {
-
long cachesize; // 緩存大小
-
long datasize; // 數據大小
-
long codesize; // 應用程序大小
-
long totalsize; // 總大小
-
// System.out.println("data start init!");
-
synchronized (Integer.class) {
-
cachesize = pStats.cacheSize; // 緩存大小
-
datasize = pStats.dataSize; // 數據大小
-
codesize = pStats.codeSize; // 應用程序大小
-
totalsize = cachesize + datasize + codesize;
-
Message msg = mHandler.obtainMessage();
-
msg.what = MSG_SIZE_CHANGE;
-
Bundle bundle = new Bundle();
-
bundle.putLong("cachesize", cachesize);
-
bundle.putLong("datasize", datasize);
-
bundle.putLong("codesize", codesize);
-
bundle.putLong("totalsize", totalsize);
-
bundle.putString("packageName", pStats.packageName);
-
msg.obj = bundle;
-
mHandler.sendMessage(msg);
-
}
-
}
-
}
-
//獲取每個apk的大小信息,包括數據區、代碼區、緩存區
-
// 查詢包的大小信息
-
public void queryPacakgeSize(String pkgName) throws Exception {
-
if (pkgName != null) {
-
// 使用放射機制得到PackageManager類的隱藏函數getPackageSizeInfo
-
PackageManager pm = getPackageManager(); // 得到pm對象
-
try {
-
// 通過反射機制獲得該隱藏函數
-
Method getPackageSizeInfo =
-
pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,
-
IPackageStatsObserver.class);
-
getPackageSizeInfo.invoke(pm, pkgName, new PkgSizeObserver());
-
} catch (Exception ex) {
-
// Log.e(TAG, "NoSuchMethodException");
-
ex.printStackTrace();
-
throw ex; // 拋出異常
-
}
-
}
-
}
復制代碼
或得到app的大小數據後,封裝成消息發送出去,這是最好的方法!!
這裡也介紹一個將long型數據轉換成文件大小格式的數據。
java代碼
-
// 系統函數,字符串轉換 long -String (kb)
-
-
private String formateFileSize(long size) {
-
return Formatter.formatFileSize(MainActivity.this, size);
-
}
復制代碼
好,現在我們來清除用戶數據,這裡要用到之前下載的那個文件IPackageDataObserver,跟獲取app大小一樣的,通過回調來實現。
java代碼
-
// 清除用戶數據的操作
-
-
class ClearUserDataObserver extends IPackageDataObserver.Stub {
-
public void onRemoveCompleted(final String packageName,final boolean
-
succeeded) {
-
final Message msg = mHandler.obtainMessage();
-
if (succeeded) {
-
msg.what = CLEAR_USER_DATA;
-
} else {
-
msg.what = NOT_CLEAR_USER_DATA;
-
}
-
mHandler2.sendMessage(msg);
-
}
-
}
-
// 清除apk的數據
-
public void clearAppUserData(String pkgname){
-
// 經測試,該方法不能用反射得到,沒辦法,我只好這樣寫,只能在源碼下編譯。
-
pm.clearApplicationUserData(pkgname, new ClearUserDataObserver());
-
}
復制代碼
好,現在到卸載程序的時候了,看代碼
java代碼
-
// 卸載apk
-
-
ublic void unInstallApp(String pkgname) {
-
Log.e("unInstallApp(String pkgname)","pkgname is"+ pkgname);
-
Intent intent = new Intent();
-
// 該動作是我在android框架層自己定義的一個動作,DELETE.HIDE,表明直接就卸載了。不經過系統原生的那一個對話。
-
intent.setAction("android.intent.action.DELETE.HIDE"); //
-
自己定義的動作,DELETE.HIDE,不需要經過系統的確認卸載界面。直接卸載!
-
Uri packageURI = Uri.parse("package:" + pkgname);
-
intent.setData(packageURI);
-
startActivity(intent);
復制代碼
關於apk的管理就差不多了,現在來看看正在運行的服務的管理
首先,獲取正在運行的服務:
這裡我的RunningInfo是我自己定義的一個類,主要服務的一些屬性,比如包名、uid、pid等等那些
java代碼
-
// 得到正在運行的服務
-
-
public List getRunningService() {
-
List runServiceList = am.getRunningServices(30);
-
List Services_List = new ArrayList(); //
-
保存過濾查到的AppInfo
-
Log.e("getRunningService.size = ",
-
new Integer(runServiceList.size()).toString());
-
String pkgname = "";
-
ApplicationInfo appByPkgName = null;
-
for (RunningServiceInfo info : runServiceList) {
-
pkgname = info.service.getPackageName();
-
// System.out.println("service package is :" + pkgname +
-
// " pid = "+ info.pid); // 程序的ID號
-
// 過濾掉這些系統本身的服務。只顯示用戶安裝的服務
-
if (pkgname.equals("com.android.appmanager") ) { // 過濾自己
-
continue;
-
}
-
try {
-
appByPkgName = pm.getApplicationInfo(pkgname,
-
PackageManager.GET_UNINSTALLED_PACKAGES); // 最後一個參數一般為0
-
// 就好。
-
} catch (NameNotFoundException e) {
-
// Log.e("MainActivity 841 line","appByPkgName =
-
pm.getApplicationInfo(pkgname, PackageManager.GET_UNINSTALLED_PACKAGES)
-
Exception !");
-
e.printStackTrace();
-
}
-
Services_List.add(getRunningInfo(appByPkgName)); //
-
裡面含有相同的包的服務。這裡哦我們只要求顯示一個即可。
-
}
-
// 放入set中過濾相同包名的, 這裡我復寫了RunningInfo 的compareTo方法你 規則是
-
pkgName相等兩個對象就算相等!
-
Set set = new HashSet();
-
for (RunningInfo x : Services_List) {
-
set.add(x);
-
}
-
for (RunningInfo y : set) {
-
Services_List.add(y);
-
}
-
return Services_List;
-
}
復制代碼
好,獲取到了正在運行的服務之後,就可以隨意停止服務了,停止服務的代碼是:
java代碼
-
// 強行停止一個app
-
-
ublic boolean stopApp(String pkgname) {
-
boolean flag = false;
-
ActivityManager am = (ActivityManager)
-
mContext.getSystemService(Context.ACTIVITY_SERVICE);
-
try {
-
Method forceStopPackage;
-
forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage",
-
String.class); // 反射得到隱藏方法(hide)
-
forceStopPackage.setAccessible(true);//獲取私有成員變量的值
-
forceStopPackage.invoke(am, pkgname);
-
flag = true;
-
} catch (IllegalArgumentException e) {
-
e.printStackTrace();
-
flag = false;
-
} catch (IllegalAccessException e) {
-
e.printStackTrace();
-
flag = false;
-
} catch (InvocationTargetException e) {
-
e.printStackTrace();
-
flag = false;
-
} catch (SecurityException e) {
-
e.printStackTrace();
-
flag = false;
-
} catch (NoSuchMethodException e) {
-
e.printStackTrace();
-
flag = false;
-
}
-
return flag;
復制代碼
同樣也是用反射的機制來得到隱藏類。
到這裡,應用程序管理的功能就差不多了,
剩下就只是界面上的事情和程序的處理流程上的事情,應該還好!