从源码看滴滴插件化框架VirtualApk

滴滴开源插件框架地址: https://github.com/didi/VirtualAPK

大神鸿洋的框架分析: http://blog.csdn.net/lmj623565791/article/details/75000580

框架接入: http://www.jianshu.com/p/013510c19391

先看看从git上clone下来的工程的项目结构:

主要是四个module

1、AndroidStub

声明了一些和framework同名的类,用来在编译的时候通过检查,在实际运行的时候会被rom中的同名类替换掉。

里面的类所有方法都是抛出一个异常。该工程被CoreLibrary引用,因为在核心工程里面会用到一些引用了framework类的地方。

throw new RuntimeException("Stub!");

2、CoreLibray

核心库,里面插件框架的主要逻辑。被宿主工程引用

3、PluginDemo

一个独立的demo工程,可以单独使用,但是作为插件apk的时候,不能通过AS run的方式生成apk,必须通过gradle编译,因为在编译的过程中会去掉一些和宿主工程一样的引用类库,比如说宿主工程应用了v7包,插件工程在开发的时候也引用了v7包,那么在编译的时候不去掉这个v7包,在加载插件的时候就会出现这个问题:

 
  1. 07-18 21:09:23.640 4747-4747/com.didi.virtualapk I/VAInstrumentation: newActivity[com.didi.virtualapk.core.A$1 : com.didi.virtualapk.demo.aidl.BookManagerActivity]

  2. 07-18 21:09:23.648 4747-4747/com.didi.virtualapk W/dalvikvm: Class resolved by unexpected DEX: Lcom/didi/virtualapk/demo/aidl/BookManagerActivity;(0x4193c570):0x5acfc000 ref [Landroid/support/v7/app/AppCompatActivity;] Landroid/support/v7/app/AppCompatActivity;(0x4193c570):0x5aad5000

  3. 07-18 21:09:23.648 4747-4747/com.didi.virtualapk W/dalvikvm: (Lcom/didi/virtualapk/demo/aidl/BookManagerActivity; had used a different Landroid/support/v7/app/AppCompatActivity; during pre-verification)

  4. 07-18 21:09:23.648 4747-4747/com.didi.virtualapk W/dalvikvm: Unable to resolve superclass of Lcom/didi/virtualapk/demo/aidl/BookManagerActivity; (1537)

  5. 07-18 21:09:23.648 4747-4747/com.didi.virtualapk W/dalvikvm: Link of class 'Lcom/didi/virtualapk/demo/aidl/BookManagerActivity;' failed

  6. 07-18 21:09:23.648 4747-4747/com.didi.virtualapk D/AndroidRuntime: Shutting down VM

  7. 07-18 21:09:23.648 4747-4747/com.didi.virtualapk W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x40aea2a0)

  8. 07-18 21:09:23.656 4747-4747/com.didi.virtualapk E/AndroidRuntime: FATAL EXCEPTION: main

  9. java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

  10. at dalvik.system.DexFile.defineClass(Native Method)

  11. at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:211)

  12. at dalvik.system.DexPathList.findClass(DexPathList.java:315)

  13. at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:58)

  14. at java.lang.ClassLoader.loadClass(ClassLoader.java:501)

  15. at java.lang.ClassLoader.loadClass(ClassLoader.java:495)

  16. at java.lang.ClassLoader.loadClass(ClassLoader.java:461)

  17. at android.app.Instrumentation.newActivity(Instrumentation.java:1053)

  18. at com.didi.virtualapk.internal.VAInstrumentation.newActivity(VAInstrumentation.java:101)

  19. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1984)

  20. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2094)

  21. at android.app.ActivityThread.access$600(ActivityThread.java:134)

  22. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1202)

  23. at android.os.Handler.dispatchMessage(Handler.java:99)

  24. at android.os.Looper.loop(Looper.java:137)

  25. at android.app.ActivityThread.main(ActivityThread.java:4767)

  26. at java.lang.reflect.Method.invokeNative(Native Method)

  27. at java.lang.reflect.Method.invoke(Method.java:511)

  28. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:800)

  29. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:567)

  30. at dalvik.system.NativeStart.main(Native Method)

  31. 07-18 21:09:29.898 4747-4747/com.didi.virtualapk I/Process: Sending signal. PID: 4747 SIG: 9

had used a different Landroid/support/v7/app/AppCompatActivity; during pre-verification)

java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

重复的类型。

gradle编译插件,看github上的wiki。

4、app

这个是宿主工程的主module,里面只有一个Activity和Application。

在application创建的时候,通过单例模式初始化了插件管理类PluginManager对象,同时通过宿主的context拿到当前宿主进程的activityThread对象,并通过这个对象,进而拿到instrumentation(一个进程里面只有一个instrumentation,activity中的instrumentation就是通过构造contextImpl对象的时候传进去的,该对象负责接管AMS来执行activity的生命周期)和AMS在客户端的代理对象ActivityManagerProxy(IActivityManager类型)。(后面在使用contentProvider的时候,也会去hook一个IContentProvider对象,为什么不在这里开始的时候就hook?因为IContentProvider对象是在ContentResolver执行query等几个方法的时候,去ActivityThread中查询是否有这个IContentProvider对象,没有才去告诉AMS去创建一个IContentProvider对象,所以还没到使用contentProvider的时候)

看PluginManager的构造:

 
  1. private PluginManager(Context context) {

  2. Context app = context.getApplicationContext();

  3. if (app == null) {

  4. this.mContext = context;

  5. } else {

  6. this.mContext = ((Application)app).getBaseContext();

  7. }

  8. prepare();

  9. }

拿到了宿主的context保存起来,执行prepare,去hookinstrumentation和AMS的代理

 
  1. private void prepare() {

  2. Systems.sHostContext = getHostContext();

  3. this.hookInstrumentationAndHandler();

  4. this.hookSystemServices();

  5. }

通过反射的方式,拿到进程的activityThread对象,并根据这个对象获取ActivityThread中的instrumentation变量,通过静态代理的方式,创建一个VAInstrumentation对象,该对象里面包含了原生instrumentation的引用。把该对象重新赋值给activityThread中的instrumentation变量。这样以后宿主执行activity的创建和生命周期都由这个新对象来完成。

 
  1. private void hookInstrumentationAndHandler() {

  2. try {

  3. Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);

  4. if (baseInstrumentation.getClass().getName().contains("lbe")) {

  5. // reject executing in paralell space, for example, lbe.

  6. System.exit(0);

  7. }

  8.  
  9. final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);

  10. Object activityThread = ReflectUtil.getActivityThread(this.mContext);

  11. ReflectUtil.setInstrumentation(activityThread, instrumentation);

  12. ReflectUtil.setHandlerCallback(this.mContext, instrumentation);

  13. this.mInstrumentation = instrumentation;

  14. } catch (Exception e) {

  15. e.printStackTrace();

  16. }

  17. }

看看这个VAInstrumentation里复写了什么方法:

 
  1. public ActivityResult execStartActivity(

  2. Context who, IBinder contextThread, IBinder token, Activity target,

  3. Intent intent, int requestCode, Bundle options) {

  4. mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);

  5. // null component is an implicitly intent

  6. if (intent.getComponent() != null) {

  7. Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(),

  8. intent.getComponent().getClassName()));

  9. // resolve intent with Stub Activity if needed

  10. this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);

  11. }

  12.  
  13. ActivityResult result = realExecStartActivity(who, contextThread, token, target,

  14. intent, requestCode, options);

  15.  
  16. return result;

  17.  
  18. }

这个方法是在什么时候调用的呢?

先回到主工程,在这个宿主工程的activity中的onCreate方法中,执行了加载插件apk的逻辑(通过DexClassLoader加载外部apk),并保存在一个LoadedPlugin对象中。

在这个activity中有个button,点击该button回去执行一个启动activity的代码:

 
  1. Intent intent = new Intent();

  2. intent.setClassName(pkg, "com.didi.virtualapk.demo.aidl.BookManagerActivity");

  3. startActivity(intent);

这里我们看到要启动一个插件里的activity。那么来看看startActivity的流程:

startActivity  ---> startActivityForResult ---> mInstrumentation.execStartActivity。

由于我们activity的mInstrumentation引用的是ActivityThread里面的(具体是在activityThread创建activity的时候,通过activity的attach方法传进来的),而activityThread中的instrumentation已经被hook成VAInstrumentation对象了。所以必然会执行到VAInstrumentation对象的execStartActivity方法中,也就是上面的代码。

在上面的execStartActivity中,首先根据intent去插件的包里面,找到对应的插件activity的信息,包括在menifest中设置的启动模式,风格等。拿到这些信息后,根据启动模式和风格,去找到相应的占坑的activity(在核心库中的xml中注册的)。重新组装一个inent,调用realExecStartActivity方法:

 
  1. private ActivityResult realExecStartActivity(

  2. Context who, IBinder contextThread, IBinder token, Activity target,

  3. Intent intent, int requestCode, Bundle options) {

  4. ActivityResult result = null;

  5. try {

  6. Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class,

  7. int.class, Bundle.class};

  8. result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase,

  9. "execStartActivity", parameterTypes,

  10. who, contextThread, token, target, intent, requestCode, options);

  11. } catch (Exception e) {

  12. e.printStackTrace();

  13. }

  14.  
  15. return result;

  16. }

看,直接通过反射,直接调用了原生instrumentation的execStartActivity方法,该方法里面调用AMS的代理来告诉AMS我要启动一个activity

 
  1. try {

  2. intent.migrateExtraStreamToClipData();

  3. intent.prepareToLeaveProcess(who);

  4. int result = ActivityManagerNative.getDefault()

  5. .startActivity(whoThread, who.getBasePackageName(), intent,

  6. intent.resolveTypeIfNeeded(who.getContentResolver()),

  7. token, target != null ? target.mEmbeddedID : null,

  8. requestCode, 0, null, options);

  9. checkStartActivityResult(result, intent);

  10. } catch (RemoteException e) {

  11. throw new RuntimeException("Failure from system", e);

  12. }

这里传递给AMS的intent就是占坑的activity的信息,这个acitivy是在宿主的核心库中注册的,AMS必然能通过检测。

AMS通过客户端(ApplicationThread)在systemServer的代理ApplicationThreadProxy发送创建activity的指令,通过binder驱动,客户端也就是宿主进程里的ActivityThread的内部类ApplicationThread,执行scheduleLauncherActivity,通过ActivityThread的内部对象H发送msg,执行handleLaunchActivity -- > performLaunchActivity:

 
  1. try {

  2. ClassLoader e = r.packageInfo.getClassLoader();

  3. activity = this.mInstrumentation.newActivity(e, component.getClassName(), r.intent);

  4. StrictMode.incrementExpectedActivityCount(activity.getClass());

  5. r.intent.setExtrasClassLoader(e);

  6. r.intent.prepareToEnterProcess();

  7. if(r.state != null) {

  8. r.state.setClassLoader(e);

  9. }

  10. }

在performLaunchActivity这个方法里面,又调用了instrumentation的newActivity,于是我们看VAInstrumentation的该方法是做了什么内容:

 
  1. public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

  2. try {

  3. cl.loadClass(className);

  4. } catch (ClassNotFoundException e) {

  5. LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);

  6. String targetClassName = PluginUtil.getTargetActivity(intent);

  7.  
  8. Log.i(TAG, String.format("newActivity[%s : %s]", className, targetClassName));

  9.  
  10. if (targetClassName != null) {

  11. Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);

  12. activity.setIntent(intent);

  13.  
  14. try {

  15. // for 4.1+

  16. ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());

  17. } catch (Exception ignored) {

  18. // ignored.

  19. }

  20.  
  21. return activity;

  22. }

  23. }

  24.  
  25. return mBase.newActivity(cl, className, intent);

  26. }

根据intent信息,还原我们的插件activity的信息,直接通过原来的instrumentation 来new一个activity出来。之后的流程还是继续走正常的。

接下来看hook AMS(也就是hook AMS在客户端的代理ActivityManagerProxy),在PluginManager的hookSystemServices方法中,通过动态代理的方式,创建了一个代理IActivityManager,通过反射把该代理替换掉原来的AMP对象。以后所有执行该代理的方法,都是默认先走到我们自己定义的ActivityManagerProxy(继承自InvocationHanlder)的invoke方法中(这是动态代理的机制)。

 
  1. private void hookSystemServices() {

  2. try {

  3. Singleton<IActivityManager> defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");

  4. IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());

  5.  
  6. // Hook IActivityManager from ActivityManagerNative

  7. ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);

  8.  
  9. if (defaultSingleton.get() == activityManagerProxy) {

  10. this.mActivityManager = activityManagerProxy;

  11. }

  12. } catch (Exception e) {

  13. e.printStackTrace();

  14. }

  15. }

看下ActivityManagerProxy(这个类并不是真正意义上的AMS的代理了,只是实现了InvocationHandler接口,有点混淆),主要看invoke方法:

 
  1. if ("startService".equals(method.getName())) {

  2. try {

  3. return startService(proxy, method, args);

  4. } catch (Throwable e) {

  5. Log.e(TAG, "Start service error", e);

  6. }

  7. } else if ("stopService".equals(method.getName())) {

  8. try {

  9. return stopService(proxy, method, args);

  10. } catch (Throwable e) {

  11. Log.e(TAG, "Stop Service error", e);

  12. }

  13. } else if ("stopServiceToken".equals(method.getName())) {

  14. try {

  15. return stopServiceToken(proxy, method, args);

  16. } catch (Throwable e) {

  17. Log.e(TAG, "Stop service token error", e);

  18. }

  19. } else if ("bindService".equals(method.getName())) {

  20. try {

  21. return bindService(proxy, method, args);

  22. } catch (Throwable e) {

  23. e.printStackTrace();

  24. }

  25. } else if ("unbindService".equals(method.getName())) {

  26. try {

  27. return unbindService(proxy, method, args);

  28. } catch (Throwable e) {

  29. e.printStackTrace();

  30. }

  31. } else if ("getIntentSender".equals(method.getName())) {

  32. try {

  33. getIntentSender(method, args);

  34. } catch (Exception e) {

  35. e.printStackTrace();

  36. }

  37. } else if ("overridePendingTransition".equals(method.getName())){

  38. try {

  39. overridePendingTransition(method, args);

  40. } catch (Exception e){

  41. e.printStackTrace();

  42. }

  43. }

看这个方法里面的method的name是不是很熟悉,就是我们主动调用startService的时候,由ContextImpl传递到AMS的代理执行的。

比如在一个activity中启动一个service是这样的

startService ---> 最终调用contextImpl的startServie。

 
  1. public ComponentName startService(Intent service) {

  2. this.warnIfCallingFromSystemProcess();

  3. return this.startServiceCommon(service, this.mUser);

  4. }

 
  1. private ComponentName startServiceCommon(Intent service, UserHandle user) {

  2. try {

  3. this.validateServiceIntent(service);

  4. service.prepareToLeaveProcess(this);

  5. ComponentName e = ActivityManagerNative.getDefault().startService(this.mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(this.getContentResolver()), this.getOpPackageName(), user.getIdentifier());

  6. if(e != null) {

  7. if(e.getPackageName().equals("!")) {

  8. throw new SecurityException("Not allowed to start service " + service + " without permission " + e.getClassName());

  9. }

  10.  
  11. if(e.getPackageName().equals("!!")) {

  12. throw new SecurityException("Unable to start service " + service + ": " + e.getClassName());

  13. }

  14. }

  15.  
  16. return e;

  17. } catch (RemoteException var4) {

  18. throw var4.rethrowFromSystemServer();

  19. }

  20. }

看,最终调用了AMS的代理的startService,而该代理被我们hook了,也就会执行到上面的ActivityManagerProxy(继承自InvocationHanlder)的invoke方法中。

其他的方法,比如stop等等都会被这个invoke拦截,在invoke中通过不同的方法名调用了不同的函数,比如startService

 
  1. private Object startService(Object proxy, Method method, Object[] args) throws Throwable {

  2. IApplicationThread appThread = (IApplicationThread) args[0];

  3. Intent target = (Intent) args[1];

  4. ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);

  5. if (null == resolveInfo || null == resolveInfo.serviceInfo) {

  6. // is host service

  7. return method.invoke(this.mActivityManager, args);

  8. }

  9.  
  10. return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);

  11. }

最终,如果是要执行代理的组件,就会去执行startDelegateServiceFortarget方法,看看其他的方法,比如stopService,最终都会调用这个函数。也就是说所有的AMS的操作service的命令,最终都会执行到这里。看看这个函数做了什么。

 
  1. private ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {

  2. Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);

  3. return mPluginManager.getHostContext().startService(wrapperIntent);

  4. }

所有跟service生命周期相关的调用都被替换为执行启动宿主的service,也就是每次都会回调宿主service的onStartCommand方法,必然,会在该方法里面去创建我们插件的service的生命周期了。



最后,ContextProvider的处理,也是通过动态代理的方式,hook掉AMS的IContentProvider,所有执行query等操作都有invoke来分发,通过包裹uri,去调用占坑的contentProvider,占坑的contentProvider在根据这些uri,解析出真正要执行的插件中的contentProvider,然后启动该provider。

执行一个contentProvider的query流程是这样的:

在service、activity或application中调用getConentResolver.query,

getContentResolver由父类ContextWrapper实现,其又调用了内部对象mBase(是个ContextImpl的实例)的同名方法,那么具体看ContextImpl

 
  1. public ContentResolver getContentResolver() {

  2. return this.mContentResolver;

  3. }



mContentResolver是ContextImpl.ApplicationContentResolver对象,在ContextImpl构造函数中创建

this.mContentResolver = new ContextImpl.ApplicationContentResolver(this, mainThread, user);

直接看这个类的query方法,这个query其实是调用的父类ContentResolver的方法:

 
  1. public final Cursor query(@Read Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

  2. SeempLog.record_uri(13, uri);

  3. return this.query(uri, projection, selection, selectionArgs, sortOrder, (CancellationSignal)null);

  4. }

在其重载方法中会去调用acquireProvider,该方法是个抽象方法,具体在applicationContentResolver中实现:

 
  1. protected IContentProvider acquireProvider(Context context, String auth) {

  2. return this.mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), this.resolveUserIdFromAuthority(auth), true);

  3. }

看,直接调用的是activityThread的同名方法,

 
  1. public final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {

  2. IContentProvider provider = this.acquireExistingProvider(c, auth, userId, stable);

  3. if(provider != null) {

  4. return provider;

  5. } else {

  6. ContentProviderHolder holder = null;

  7.  
  8. try {

  9. holder = ActivityManagerNative.getDefault().getContentProvider(this.getApplicationThread(), auth, userId, stable);

  10. } catch (RemoteException var8) {

  11. throw var8.rethrowFromSystemServer();

  12. }

  13.  
  14. if(holder == null) {

  15. Slog.e("ActivityThread", "Failed to find provider info for " + auth);

  16. return null;

  17. } else {

  18. holder = this.installProvider(c, holder, holder.info, true, holder.noReleaseNeeded, stable);

  19. return holder.provider;

  20. }

  21. }

  22. }

调用了AMS的代理,获取一个IContentProvider对象存储到holder对象中。并执行了安装provider的操作,installProvider会把provider保存在activityThread的的provider的map中保存起来以便以后使用。

这是宿主启动占坑的contentProvider的主要流程,我们要hook的就是操作这个宿主占坑contentprovider对应的IContentProvider对象了,为什么这么说呢,因为插件的contentProvider都是通过宿主的contentprovider来显示的执行创建,query等操作的。

所以在操作插件contentProvider前先要启动占坑的contentProvider,这样才能拿到代理的IContentProvider对象进行hook。

所以在PluginManager的hook中,先执行了占坑contentProvider的创建过程。

 
  1. private void hookIContentProviderAsNeeded() {

  2. Uri uri = Uri.parse(PluginContentResolver.getUri(mContext));

  3. mContext.getContentResolver().call(uri, "wakeup", null, null);

  4. try {

  5. Field authority = null;

  6. Field mProvider = null;

  7. ActivityThread activityThread = (ActivityThread) ReflectUtil.getActivityThread(mContext);

  8. Map mProviderMap = (Map) ReflectUtil.getField(activityThread.getClass(), activityThread, "mProviderMap");

  9. Iterator iter = mProviderMap.entrySet().iterator();

  10. while (iter.hasNext()) {

  11. Map.Entry entry = (Map.Entry) iter.next();

  12. Object key = entry.getKey();

  13. Object val = entry.getValue();

  14. String auth;

  15. if (key instanceof String) {

  16. auth = (String) key;

  17. } else {

  18. if (authority == null) {

  19. authority = key.getClass().getDeclaredField("authority");

  20. authority.setAccessible(true);

  21. }

  22. auth = (String) authority.get(key);

  23. }

  24. if (auth.equals(PluginContentResolver.getAuthority(mContext))) {

  25. if (mProvider == null) {

  26. mProvider = val.getClass().getDeclaredField("mProvider");

  27. mProvider.setAccessible(true);

  28. }

  29. IContentProvider rawProvider = (IContentProvider) mProvider.get(val);

  30. IContentProvider proxy = IContentProviderProxy.newInstance(mContext, rawProvider);

  31. mIContentProvider = proxy;

  32. Log.d(TAG, "hookIContentProvider succeed : " + mIContentProvider);

  33. break;

  34. }

  35. }

  36. } catch (Exception e) {

  37. e.printStackTrace();

  38. }

  39. }

头两句,先获得宿主的uri,通过该uri,通过宿主的context获取到宿主的resolver对象,执行call方法,这样就回去执行宿主占坑的contentProvider的call方法,此时,操作占坑的provider的IcontentProvider对象已经存储到activityThread的map中了,

所以在该方法最后,直接从map变量中获取了IContentProvider,然后通过动态代理的方式,替换了原来的IContentProvider。

这个时候所有操作IContentProvider的所有方法都被IContentProviderProxy的invoke方法拦截,

 
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  2. Log.v(TAG, method.toGenericString() + " : " + Arrays.toString(args));

  3. wrapperUri(method, args);

  4.  
  5. try {

  6. return method.invoke(mBase, args);

  7. } catch (InvocationTargetException e) {

  8. throw e.getTargetException();

  9. }

  10. }

在该方法里面包着了uri,携带了启动插件provider的uri,

然后继续调用宿主 contentProvider,在占坑的provider中,各个方法里面

 
  1. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

  2. ContentProvider provider = getContentProvider(uri);

  3. Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI));

  4. if (provider != null) {

  5. return provider.query(pluginUri, projection, selection, selectionArgs, sortOrder);

  6. }

  7.  
  8. return null;

  9. }

getContentProvider方法拿到插件的provider,直接指向query方法。这样插件中的query方法就被调用了。就好像系统去调用了一样。

这里为什么hook宿主的provider时,要使用call方式来启动宿主provider呢

看这里: http://blog.csdn.net/zhangyongfeiyong/article/details/51860572

自我感觉,resolver执行call方法,比如导致provider来执行call,这里面不用做什么其他而我的操作,执行call方法必然就会让provider去执行onCreate方法来启动。感觉是个启动provider的好时机。

原文转载自:https://blog.csdn.net/anhenzhufeng/article/details/75411779

猜你喜欢

转载自blog.csdn.net/n_fly/article/details/113784119