我們如何獲取手機中安裝應用的包信息呢,沒錯在activity中通過this.getPackage().getPackageInfo()即可拿到包信息。那麼this.getPackage()返回的是什麼呢?從當前程序時如何拿到其他程序的包名信息呢?其實最終還是離不開PMS。隨手畫了一張流程圖,僅供參考
下面從源碼層進行分析
一、ContextWrapper.java
點擊this.getPackageManager()方法,會進入這個類中,然後調用mBase.getPackageManager(),代碼如下:
public class ContextWrapper extends Context {
Context mBase;
...
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
...
}
注意這裏的mBase是定義爲Context類型,但是我們知道Context是一個接口,所以mBase.getPackageManager()實際上是交給Context的實現類ContextImpl.java處理
二、ContextImpl.java
果然,在裏面找到了該方法,代碼如下:
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
//------------------1、拿到PMS的binder引用
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
//-------------2、創建並返回ApplicationPackageManager
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
註釋1處通過ActivityThread.getPackageManager()方法拿到PMS的binder引用,那麼是如何拿到的呢?進入這個方法,代碼如下:
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
沒錯,最總通過ServiceManager.getService(“package”)方法。之前有分析過如何將PMS註冊到ServiceManager中,鏈接地址爲
Binder原理之PMS、AMS註冊到ServerManager
回到上面的註釋2處,分析ApplicationPackageManager是什麼鬼?
三、ApplicationPackageManager.java
找到構造方法,代碼如下:
public class ApplicationPackageManager extends PackageManager {
...
private final IPackageManager mPM;
...
protected ApplicationPackageManager(ContextImpl context,IPackageManager pm) {
mContext = context;
//將PMS的binder引用進行賦值
mPM = pm;
}
}
所以現在可以得出結論this.getPackageManager()得到的就是ApplicationPackageManager 實例化對象。最終會調用它的getPackagetInfo()方法。代碼如下:
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
//-------------1、調用下面的方法
return getApplicationInfoAsUser(packageName, flags, mContext.getUsrId());
}
@Override
public ApplicationInfo getApplicationInfoAsUser(String packageName, -int flags, int userId)
throws NameNotFoundException {
...
//------------2、最後調用PMS的該方法,返回ApplicationInfo實體。
ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, userId);
...
}
以上就完成了如何跨進程拿到一個包的信息。
三、代碼模擬PMS工作原理
上面的分析可以總結出需要的幾個java類和aidl接口
- this:我們定義成MyContext.java類
- PackageManager:定義成MyPackageManager.java類
- ApplicationPackageManager:定義成MyApplicationPackageManager.java
- IPackageManager:定義成IMyPackageManager.aidl
- PackageManagerService:定義成MyPackageManagerService.java
1、IMyPackageManager.aidl
package com.pms;
import android.content.pm.PackageInfo;
interface IMyPackageManagerInterface {
/**
* 提供方法
*/
PackageInfo getPackageInfo(String packageName, int flags, int userId);
}
此時我們編譯一下項目,會在編譯目錄下生成IMyPackageManagerInterface .java類
2、MyContext.java
public class MyContext {
public MyPackageManager getPackageManager(){
MyPackageManagerService pms = (MyPackageManagerService) IMyPackageManagerInterface.Stub.asInterface(new Binder());
return new MyApplicationPackageManager(pms);
}
}
3、MyPackageManager.java
public abstract class MyPackageManager {
public abstract PackageInfo getPackageInfo(String packageName, int flags);
}
4、MyApplicationPackageManagert.java
public class MyApplicationPackageManager extends MyPackageManager {
private IMyPackageManagerInterface mPm;
public MyApplicationPackageManager(IMyPackageManagerInterface mPm) {
this.mPm = mPm;
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags) {
try {
return mPm.getPackageInfo(packageName,flags,0);
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
}
5、MyPackageManagerService.java
/**
* 服務端處理結果並返回
*/
public class MyPackageManagerService extends IMyPackageManagerInterface.Stub {
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) throws RemoteException {
return null;
}
}
6、使用
MyContext mContext = new MyContext();
mContext.getPackageManager().getPackageInfo(null,0);
總結
1、ContextWrapper類中持有ContextImpl對象的引用mBase
2、在ContextImpl類中實例化ApplicationPackageManager對象,並且將PMS的binder引用傳給該對象
3、ApplicationPackageManager對象最後會通過binder引用調用PMS的相關方法,並返回結果。