我们如何获取手机中安装应用的包信息呢,没错在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的相关方法,并返回结果。