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的相关方法,并返回结果。

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