个人博客
http://www.milovetingting.cn
Android中插件化的简单实现:启动未注册的Activity 前言 本文介绍在Android中启动未在AndroidManifest中注册的Activity的一个解决方案。主要需要掌握以下知识点:
反射
类加载
Activity的启动过程
Resource加载过程
启动应用内未注册的Activity Activity默认都需要在AndroidManifest中注册,未注册的应用无法启动。AMS在启动应用时,会检测是否已经注册。因此,如果想要启动未注册的Activity,那么需要在Activity前,替换启动应用的Intent为已经注册过的Activity,因此可以新建一个Activity,用于占位。在检测通过后,真正启动Activity前再替换回需要启动的未注册的Activity。
获取替换Intent的Hook点 调用startActivity方法后,最后都会在Instrumentation的execStartActivity方法中调用AMS的远程方法进行处理。Android6.0及以下和Android6.0以上,在execStartActivity中调用AMS的方法有所不同,因此需要做兼容处理。
6.0 1 2 3 4 5 6 7 8 9 10 11 public ActivityResult execStartActivity ( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null , requestCode, 0 , null , options); }
通过调用ActivityManagerNative.getDefault()来获取AMS。
8.0 1 2 3 4 5 6 7 8 9 10 11 12 public ActivityResult execStartActivity ( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null , requestCode, 0 , null , options); }
通过调用ActivityManager.getService()来获取AMS。
替换Intent 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public static void hookAMS () { try { Field singletonField; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { singletonField = getField(Class.forName("android.app.ActivityManager" ), "IActivityManagerSingleton" ); } else { singletonField = getField(Class.forName("android.app.ActivityManagerNative" ), "gDefault" ); } Object singleton = singletonField.get(null ); Field mInstanceField = getField(Class.forName("android.util.Singleton" ), "mInstance" ); final Object mInstance = mInstanceField.get(singleton); final Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class []{Class.forName("android.app.IActivityManager" )}, new InvocationHandler () { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if ("startActivity" .equals(method.getName())) { int index = 0 ; for (int i = 0 ; i < args.length; i++) { if (args[i] instanceof Intent) { index = i; break ; } } Intent intent = (Intent) args[index]; Intent proxyIntent = new Intent (intent); proxyIntent.setClassName("com.wangyz.plugindemo" , "com.wangyz.plugindemo.ProxyActivity" ); proxyIntent.putExtra("target_intent" , intent); args[index] = proxyIntent; } return method.invoke(mInstance, args); } }); mInstanceField.set(singleton, proxyInstance); } catch (Exception e) { e.printStackTrace(); } }
获取还原Intent的Hook点 Android8.0及以下 启动Activity的消息,会回调到ActivityThread中的mH的dispatchMessage方法,可以通过给mH设置一个callBack,在callBack的handleMessage中,然后替换回真正要启动的Intent,然后返回false,让handleMessage再继续处理。
1 2 3 4 5 6 7 8 9 10 11 12 public void dispatchMessage (Message msg) { if (msg.callback != null ) { handleCallback(msg); } else { if (mCallback != null ) { if (mCallback.handleMessage(msg)) { return ; } } handleMessage(msg); } }
Android8.0及以下,在ActivityThread的mH中的handleMessage方法中,会处理LAUNCH_ACTIVITY类型的消息,在这里调用了handleLaunchActivity方法来启动Activity。
1 2 3 4 5 6 7 8 9 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 ); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break ;
Android9.0 和8.0一样,设置callBack,然后修改Intent。
在ActivityThread的mH中的handleMessage方法中,会处理EXECUTE_TRANSACTION类型的消息,在这里调用了TransactionExecutor.execute方法
1 2 3 4 5 6 7 8 9 10 11 case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; mTransactionExecutor.execute(transaction); if (isSystem()) { transaction.recycle(); } break ;
execute方法中会调用executeCallbacks
1 2 3 4 5 6 7 8 9 10 public void execute (ClientTransaction transaction) { final IBinder token = transaction.getActivityToken(); log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token); executeCallbacks(transaction); executeLifecycleState(transaction); mPendingActions.clear(); log("End resolving transaction" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public void executeCallbacks (ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null ) { return ; } log("Resolving callbacks" ); final IBinder token = transaction.getActivityToken(); ActivityClientRecord r = mTransactionHandler.getActivityClient(token); final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest(); final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState() : UNDEFINED; final int lastCallbackRequestingState = lastCallbackRequestingState(transaction); final int size = callbacks.size(); for (int i = 0 ; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); log("Resolving callback: " + item); final int postExecutionState = item.getPostExecutionState(); final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, item.getPostExecutionState()); if (closestPreExecutionState != UNDEFINED) { cycleToPath(r, closestPreExecutionState); } item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); if (r == null ) { r = mTransactionHandler.getActivityClient(token); } if (postExecutionState != UNDEFINED && r != null ) { final boolean shouldExcludeLastTransition = i == lastCallbackRequestingState && finalState == postExecutionState; cycleToPath(r, postExecutionState, shouldExcludeLastTransition); } } }
这个方法里会调用ClientTransactionItem的execute方法。ClientTransactionItem是在ActivityStackSupervisor中的realStartActivityLocked中添加的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 final boolean realStartActivityLocked (ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread, r.appToken); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent (r.intent), System.identityHashCode(r), r.info, mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, mService.isNextTransitionForward(), profilerInfo)); }
因此,ClientTransactionItem对应的具体类为LaunchActivityItem,它对应的execute方法
1 2 3 4 5 6 7 8 9 10 11 @Override public void execute (ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart" ); ActivityClientRecord r = new ActivityClientRecord (token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mIsForward, mProfilerInfo, client); client.handleLaunchActivity(r, pendingActions, null ); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); }
在它的方法里又调用了ClientTransactionHandler的handleLaunchActivity,而ClientTransactionHandler就是在ActivityThread中定义的
1 private final TransactionExecutor mTransactionExecutor = new TransactionExecutor (this );
ActivityThread继承了ClientTransactionHandler,那么它就会实现handleLaunchActivity。最终在这个方法里启动Activity
1 2 3 4 5 6 7 public final class ActivityThread extends ClientTransactionHandler { @Override public Activity handleLaunchActivity (ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { } }
还原Intent 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public static void hookHandler () { try { Field sCurrentActivityThreadThread = getField(Class.forName("android.app.ActivityThread" ), "sCurrentActivityThread" ); Object activityThread = sCurrentActivityThreadThread.get(null ); Field mHField = getField(Class.forName("android.app.ActivityThread" ), "mH" ); Object mH = mHField.get(activityThread); Field mCallbackField = getField(Class.forName("android.os.Handler" ), "mCallback" ); mCallbackField.set(mH, new Handler .Callback() { @Override public boolean handleMessage (Message msg) { switch (msg.what) { case 100 : { try { Field intentField = getField(msg.obj.getClass(), "intent" ); Intent proxyIntent = (Intent) intentField.get(msg.obj); Intent targetIntent = proxyIntent.getParcelableExtra("target_intent" ); if (targetIntent != null ) { intentField.set(msg.obj, targetIntent); } } catch (Exception e) { e.printStackTrace(); } } break ; case 159 : { try { Field mActivityCallbacksField = getField(msg.obj.getClass(), "mActivityCallbacks" ); 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); Field mIntentField = getField(launchActivityItem.getClass(), "mIntent" ); Intent intent = (Intent) mIntentField.get(launchActivityItem); Intent proxyIntent = intent.getParcelableExtra("target_intent" ); if (proxyIntent != null ) { mIntentField.set(launchActivityItem, proxyIntent); } } } } catch (Exception e) { e.printStackTrace(); } } break ; default : break ; } return false ; } }); } catch (Exception e) { e.printStackTrace(); } }
在Application创建时Hook 1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public void onCreate () { super .onCreate(); String pluginPath = getDir("plugin" , Context.MODE_PRIVATE).getAbsolutePath(); pluginPath = pluginPath + "/plugin.apk" ; if (!new File (pluginPath).exists()) { FileUtil.copyFile(PLUGIN_PATH, pluginPath); } HookUtil.loadPlugin(this , pluginPath); HookUtil.hookAMS(); HookUtil.hookHandler(); }
到这里,就可以启用同一应用内未注册的Activity。
启动插件应用内的Activity 启动非同一应用内的Activity,相比启动同一应用内的Activity,需要多几个步骤。由于不在一个应用内,所以需要把插件的APK先加载进来,然后同样也需要在AMS检测前替换Intent为占位的Intent,在检测后,启动Activity前替换回为需要启动Activity的Intent。另外,由于插件是动态加载进去的,也需要解决资源加载的问题。
加载插件 加载插件主要是用到类加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public static void loadPlugin (Context context, String dexPath) { File dex = new File (dexPath); if (!dex.exists()) { return ; } try { PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader(); Field pathListField = getField(pathClassLoader.getClass(), "pathList" ); Object pathListObject = pathListField.get(pathClassLoader); Field dexElementsField = getField(pathListObject.getClass(), "dexElements" ); Object[] dexElementsObject = (Object[]) dexElementsField.get(pathListObject); File odex = context.getDir("odex" , Context.MODE_PRIVATE); DexClassLoader dexClassLoader = new DexClassLoader (dexPath, odex.getAbsolutePath(), null , pathClassLoader); Field pluginPathListField = getField(dexClassLoader.getClass(), "pathList" ); Object pluginPathListObject = pluginPathListField.get(dexClassLoader); Field pluginDexElementsField = getField(pluginPathListObject.getClass(), "dexElements" ); Object[] pluginDexElementsObject = (Object[]) pluginDexElementsField.get(pluginPathListObject); Class<?> elementClazz = dexElementsObject.getClass().getComponentType(); Object newDexElements = Array.newInstance(elementClazz, pluginDexElementsObject.length + dexElementsObject.length); System.arraycopy(pluginDexElementsObject, 0 , newDexElements, 0 , pluginDexElementsObject.length); System.arraycopy(dexElementsObject, 0 , newDexElements, pluginDexElementsObject.length, dexElementsObject.length); dexElementsField.set(pathListObject, newDexElements); } catch (Exception e) { e.printStackTrace(); } }
替换Intent 这个过程和应用内的情况是一样的,不再赘述
加载资源 加载资源主要用到AssetManager的addAssetPath方法,通过反射来加载
1 2 3 4 5 6 7 8 9 10 11 12 13 private static Resources loadResource (Context context) { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPathField = assetManager.getClass().getDeclaredMethod("addAssetPath" , String.class); addAssetPathField.setAccessible(true ); addAssetPathField.invoke(assetManager, PATH); Resources resources = context.getResources(); return new Resources (assetManager, resources.getDisplayMetrics(), resources.getConfiguration()); } catch (Exception e) { e.printStackTrace(); } return null ; }
源码 https://github.com/milovetingting/Samples/tree/master/PluginDemo