Binder原理之PMS工作原理和代碼模擬

我們如何獲取手機中安裝應用的包信息呢,沒錯在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的相關方法,並返回結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章