Android插件化学习(二):如何调用插件的组件类

写在前面的话:本篇文章基于API Level 26!未做其他版本适配!未做资源处理!插件的资源引用一定要注释!

基于上一篇文章,为什么不能用反射调用插件的Activity

运用反射确实可以调用插件的Activity的方法,但这样的启动的Activity是没有生命的周期!就像下面代码,没啥卵用。

MainActivity activity = new MainActivity();

正常的启动应该是 startActivity() ,这样的Activity才是有生命周期的!如下:

Intent intent = new Intent();
intent.setComponent(new ComponentName("com.miss.myplugin", "com.miss.myplugin.MainActivity"));
startActivity(intent);

下面是startActivity背后的流程:
在这里插入图片描述

调用startActivity方法后跨进程调用AMS中的方法,AMS会检查要打开的Activity是否在Manifest中注册,如果已经注册再跨进程用ActivityThread启动目标Activity。所以我们要启动插件中的Activity首要问题就是解决如何瞒过AMS的检测!

怎样绕过AMS的检测

Hook,运用反射和动态代理瞒天过海,先看下图:
在这里插入图片描述

咱们启动宿主的Activity不犯法吧,肯定能通过AMS检测。那我们可以这样,intent中填插件的路径和类名,在AMS之前用hook替换成宿主的Activity瞒过检测,AMS检测后再把Activity替换回来,如此启动插件的Activity是有正常生命周期的!

先分析前半部分逻辑

既然是startActivity启动的,咱们先深入下他,所有的startActivity最后调用startActivityForResult的三参方法,如下

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            //  TODO : 在这使用的 intent , 咱们从这深入
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

咱们在这里深入,这里有intent

 //  TODO : 在这使用的 intent , 咱们从这深入,
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);

寻找Instrumentation.java 中 execStartActivity() 实现

...
...
try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            //  这里就是我们的 Hook 点
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
...
...

我们可以通过反射拿到 ActivityManager.getService() 的对象,再用动态代理改写 startActivity 的实现。看下getService()的实现

    /**
     *    getService 返回的是 IActivityManager 对象,IActivityManager 是接口,即 IActivityManager 的 startActivity 方法
     */
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
            
            
//  下面是 Singleton 
public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}
看代码有点乱在这里总结下:需要的是偷梁换柱intent,intent 在 IActivityManager 的 startActivity 方法被使用,要找到具体实现此方法的对象!ActivityManager.getService() 返回的 是 IActivityManagerSingleton.get() ,IActivityManagerSingleton 是个 Singleton对象,Singleton 的 get()方法返回 mInstance,最后是 mInstance 实现的 startActivity 方法!通过反射拿到 mInstance 对象,再用动态代理改写 startActivity 逻辑,这就是插件化的灵魂!
下面是代码的具体实现(API:26)

public class HookUtil {

    private static final String TAG = "TAG";

    public static void hookAMS() {

        //  Hook 可以加载 Activity ,但不能有资源文件

        /**
         *      TODO : hook 点
         *int result = ActivityManager.getService()
         *                 .startActivity(whoThread, who.getBasePackageName(), intent,
         *                         intent.resolveTypeIfNeeded(who.getContentResolver()),
         *                         token, target != null ? target.mEmbeddedID : null,
         *                         requestCode, 0, null, options);
         *
         *      利用反射拿到对象,再用动态代理修改 startActivity 的逻辑  =====》ActivityManager.getService()
         *
         *      getService()  返回的是 IActivityManagerSingleton.get()  ===》  IActivityManager 是个接口
         *
         *
         *      private static final Singleton<IActivityManager> IActivityManagerSingleton =    ...
         *
         *      利用反射,可以拿到 属性 IActivityManagerSingleton(这是个静态类,是我们需要的)
         *
         *      拿到 IActivityManagerSingleton 再利用反射 获得 IActivityManager ,有了他再用动态代理修改startActivity的实现逻辑
         *
         *
         */

        try {
            Class<?> classActivityManager = Class.forName("android.app.ActivityManager");
            Field fieldIActivityManagerSingleton = classActivityManager.getField("IActivityManagerSingleton");
            fieldIActivityManagerSingleton.setAccessible(true);
            //  这是个静态变量,所以传入 null
            Object singleton = fieldIActivityManagerSingleton.get(null);

            Class<?> classSingleton = Class.forName("android.util.Singleton");
            Field fieldMInstance = classSingleton.getDeclaredField("mInstance");
            fieldMInstance.setAccessible(true);

            //  实现动态代理的类
            Object mInstance = fieldMInstance.get(singleton);

            //  动态代理要用的接口
            Class<?> iActivityManagerClass = Class.forName("android.app.IActivityManager");

            /**
             *  ActivityManager.getService()
             *                  .startActivity(whoThread, who.getBasePackageName(), intent,
             *                          intent.resolveTypeIfNeeded(who.getContentResolver()),
             *                   token, target != null ? target.mEmbeddedID : null,
             *                   requestCode, 0, null, options);
             *
             */
            
            Object o = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{iActivityManagerClass},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                            /***
                             *      在此做逻辑更改
                             */
                            //  方法过滤
                            if ("startActivity".equals(method.getName())) {

                                //  记录参数位置
                                int index = -1;
                                for (int i = 0; i < args.length; i++) {
                                    //  筛选出参数列表中的 intent
                                    if (args[i] instanceof Intent) {
                                        index = i;
                                        break;
                                    }
                                }
                                //  这个是启动插件的intent
                                Intent intent = (Intent) args[index];
                                //  代理intent
                                Intent intentProxy = new Intent();
                                //  启动宿主的代理 Activity
                                intentProxy.setClassName("com.miss.plugin", "com.miss.plugin.ProxyActivity");
                                intentProxy.putExtra(TAG, intent);
                                //  狸猫换太子,嘴里叼着太子
                                args[index] = intentProxy;
                            }

                            return method.invoke(mInstance, args);
                        }
                    });
            //  反射调用
            fieldMInstance.set(singleton, o);

        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}

到这里我们就完成了宿主启动插件activity时宿主activity对插件activity的替换,瞒过AMS的注册检测!AMS检测结束后跨进程调用ActivityThread启动activity,这时候我们再把intent替换回来,把宿主activity替换成插件的activity!

再分析后半部分逻辑

跟上面一样先找hook点

AMS检测结束后会调用ActivityThread的scheduleLaunchActivity()方法

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

    updateProcessState(procState, false);

    ActivityClientRecord r = new ActivityClientRecord();

    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;

    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;

    r.startsNotResumed = notResumed;
    r.isForward = isForward;

    r.profilerInfo = profilerInfo;

    r.overrideConfig = overrideConfig;
    updatePendingConfiguration(curConfig);

    sendMessage(H.LAUNCH_ACTIVITY, r);
}

和上面一样,继续找intent,很明显此处没有,咱们从这里深入

sendMessage(H.LAUNCH_ACTIVITY, r);

此处是sendMessage,H 是 Handler ,若是这样咱们可以看看Handler的处理

 public void handleMessage(Message msg) {
      if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
      switch (msg.what) {
          case LAUNCH_ACTIVITY: {
              Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
              final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

              r.packageInfo = getPackageInfoNoCheck(
                      r.activityInfo.applicationInfo, r.compatInfo);
              handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
              Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
          } break;
}

此处依旧没有intent,咱们继续深入**handleLaunchActivity()**的实现

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    if (r.profilerInfo != null) {
        mProfiler.setProfiler(r.profilerInfo);
        mProfiler.startProfiling();
    }

    // Make sure we are running with the most recent config.
    handleConfigurationChanged(null, null);

    if (localLOGV) Slog.v(
        TAG, "Handling launch of " + r);

    // Initialize before creating the activity
    WindowManagerGlobal.initialize();

    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

        if (!r.activity.mFinished && r.startsNotResumed) {
            performPauseActivityIfNeeded(r, reason);
            if (r.isPreHoneycomb()) {
                r.state = oldState;
            }
        }
    } else {
        // If there was an error, for any reason, tell the activity manager to stop us.
        try {
            ActivityManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

此处依旧没有,继续深入performLaunchActivity(r, customIntent);

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ActivityInfo aInfo = r.activityInfo;
      if (r.packageInfo == null) {
          r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                  Context.CONTEXT_INCLUDE_CODE);
      }

      ComponentName component = r.intent.getComponent();
      if (component == null) {
          component = r.intent.resolveActivity(
              mInitialApplication.getPackageManager());
          r.intent.setComponent(component);
      }

      if (r.activityInfo.targetActivity != null) {
          component = new ComponentName(r.activityInfo.packageName,
                  r.activityInfo.targetActivity);
      }
      ...
      ...
}

成功找到intent

ComponentName component = r.intent.getComponent();

r中有intent,r 是 ActivityClientRecord ,找到最初赋值的地方

 public void handleMessage(Message msg) {
      if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
      switch (msg.what) {
          case LAUNCH_ACTIVITY: {
              Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
              //	r 在这里被赋值, msg.obj 中含有 intent
              final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
              r.packageInfo = getPackageInfoNoCheck(
                      r.activityInfo.applicationInfo, r.compatInfo);
              handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
              Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
          } break;

既然如此,看下Handler的源码

 /**
  * Subclasses must implement this to receive messages.
  */
 public void handleMessage(Message msg) {
 }
 
 /**
  * Handle system messages here.
  */
 public void dispatchMessage(Message msg) {
     if (msg.callback != null) {
         handleCallback(msg);
     } else {
         if (mCallback != null) {
         	//	注意!!!创建的	mCallback 返回的必须是 false !!!
             if (mCallback.handleMessage(msg)) {
                 return;
             }
         }
         handleMessage(msg);
     }
 }

handleMessage方法是必须重写的,在dispatchMessage方法中,如果mCallback不为空,则会先执行mCallback.handleMessage(msg),而ActivityThread的Handler调用的是默认的构造方法,默认构造方法callback为null

public Handler() {
        this(null, false);
    }
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
总结:这样是不是就有思路了。创建mCallBack替换系统的,从而拿到msg.obj,这样一来就可以拿到intent了,有了intent岂不是为所欲为。把插件的Activity替换回来,大功告成!
后半部分的代码
public class HookUtil {

。。。
。。。
    public static void hookHandler() {
        try {
            // 获取 ActivityThread 类的 Class 对象
            Class<?> clazz = Class.forName("android.app.ActivityThread");

            // 获取 ActivityThread 对象
            Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
            activityThreadField.setAccessible(true);
            Object activityThread = activityThreadField.get(null);

            // 获取 mH 对象
            Field mHField = clazz.getDeclaredField("mH");
            mHField.setAccessible(true);
            final Handler mH = (Handler) mHField.get(activityThread);

            Field mCallbackField = Handler.class.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);

            // 创建的 callback
            Handler.Callback callback = new Handler.Callback() {

                @Override
                public boolean handleMessage(@NonNull Message msg) {
                    // 通过msg  可以拿到 Intent,可以换回执行插件的Intent

                    // 找到 Intent的方便替换的地方  --- 在这个类里面 ActivityClientRecord --- Intent intent 非静态
                    // msg.obj == ActivityClientRecord
                    switch (msg.what) {
                        case 100:
                            try {
                                Field intentField = msg.obj.getClass().getDeclaredField("intent");
                                intentField.setAccessible(true);
                                // 启动代理Intent
                                Intent proxyIntent = (Intent) intentField.get(msg.obj);
                                // 启动插件的 Intent
                                Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
                                if (intent != null) {
                                    intentField.set(msg.obj, intent);
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        case 159:
                            try {
                                // 获取 mActivityCallbacks 对象
                                Field mActivityCallbacksField = msg.obj.getClass()
                                        .getDeclaredField("mActivityCallbacks");

                                mActivityCallbacksField.setAccessible(true);
                                List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);

                                for (int i = 0; i < mActivityCallbacks.size(); i++) {
                                    if (mActivityCallbacks.get(i).getClass().getName()
                                            .equals("android.app.servertransaction.LaunchActivityItem")) {
                                        Object launchActivityItem = mActivityCallbacks.get(i);

                                        // 获取启动代理的 Intent
                                        Field mIntentField = launchActivityItem.getClass()
                                                .getDeclaredField("mIntent");
                                        mIntentField.setAccessible(true);
                                        Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);

                                        // 目标 intent 替换 proxyIntent
                                        Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
                                        if (intent != null) {
                                            mIntentField.set(launchActivityItem, intent);
                                        }
                                    }
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                    }
                    // 必须 return false
                    return false;
                }
            };

            // 替换系统的 callBack
            mCallbackField.set(mH, callback);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

写在最后的思考

插件化最麻烦的不是业务,而是对Android版本升级的维护!一直要盯着Android源码的变动,对不同版本进行适配。Hook点不是唯一的,选的时候尽量选静态变量,这样能保证变量的唯一性。如果选择的hook点不是静态变量,万一执行的时候没被执行呢?前半部分主要是寻找hook点、反射、动态代理,顺便把原本的intent存起来。前半部分写完后面就简单多了,没有动态代理,主要是反射!

插件化一定要做好Android版本适配!

本模块还没解决资源的相关问题,插件调用资源的代码一定要注释

setContentView(R.layout.activity_main);

本篇文章还没写完,后续更新!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值