转载请标明出处:【顾林海的博客】
本篇文章已授权微信公众号 顾林海 独家发布

Activity的插件化解决的一个根本性问题就是插件中的Activity并没有在宿主的AndroidManifest.xml中进行注册,也就是说我们需要启动一个未注册的Activity,因此需要对Activity的启动过程有个了解。

启动Activity时会请求AMS创建Activity,这里的AMS指的是ActivityManagerService,AMS所属的进程与宿主(发起者)不属于同一个进程,AMS位于SystemServer进程中。

应用程序进程与AMS之间的通信是通过Binder来实现的,AMS要管理所有APP的启动请求,因此我们不能在SystemServer进程中进行相应的Hook,那么我们只能在应用进程中进行相应的Hook。

如果我们启动一个未注册的Activity,AMS会去检查AndroidManifest中是否注册了该Activity,如果未注册会报错。

为了让AMS验证通过,需要启动一个预先在AndroidManifest中注册的Activity,我们称之为占坑,在启动插件Activity时替换为占坑Activity,达到一个欺上瞒下的作用,当AMS验证通过之后,需要将启动的占坑Activity替换为插件Activity。

总结下来Activity的插件化需要做两件事:

  • 将请求启动的插件Activity替换为占坑Activity。
  • 绕过AMS验证后,将占坑Activity替换为插件Activity。

什么时候将插件Activity替换为占坑Activity?又是什么时候还原插件Activity?这需要我们对Activity的启动流程有个相应的认识。

Hook Instrumentation

我们在Activity中调用startActivity方法如下:

    @Overridepublic void startActivity(Intent intent) {this.startActivity(intent, null);}@Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {startActivityForResult(intent, -1);}}

调用startActivityForResult方法:

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {//Activity启动options = transferSpringboardActivityOptions(options);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) {mStartedActivity = true;}cancelInputsAndStartExitTransition(options);windows.} else {if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {mParent.startActivityFromChild(this, intent, requestCode);}}}

startActivityForResult方法中通过调用mInstrumentation的execStartActivity方法来启动Activity,这个mInstrumentation是Activity的成员变量,在ActivityThread的performLaunchActivity方法中通过Activity的attach方法传入,同时Activity的创建也是在performLaunchActivity方法中创建的,通过mInstrumentation.newActivity。

//:/frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...try {java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}}...activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback);...
}

综上所述Instrumentation提供了execStartActivity方法来启动Activity,newActivity方法来创建Activity。因此,第一种方案就是用代理Instrumentation来替代Activity的Instrumentation,并在代理Instrumentation的execStartActivity方法中替换为占坑Activity,在newActivity方法还原插件Activity。

现在我们基于第一种方案Hook Instrumentation来实现Activity的插件化。

首先创建占坑Activity:

public class StubActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_stub);}
}

创建插件Activity:

public class TargetActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_target);}
}

并在AndroidManifest.xml中注册占坑Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.glh.haiproject01"><applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"tools:ignore="AllowBackup,GoogleAppIndexingWarning"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".StubActivity" /></application></manifest>

在AndroidManifest.xml中没有注册插件Activity,这时如果启动插件Activity会报错。

最后Hook Instrumentation,将ActivityThread中的成员变量Instrumentation替换成代理的Instrumentation。

创建代理Instrumentation类:

public class InstrumentationProxy extends Instrumentation {private Instrumentation mInstrumentation;private PackageManager mPackageManager;public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {this.mInstrumentation = instrumentation;this.mPackageManager = packageManager;}public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {List<ResolveInfo> resolveInfo = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);//判断启动的插件Activity是否在AndroidManifest.xml中注册过if (null == resolveInfo || resolveInfo.size() == 0) {//保存目标插件intent.putExtra(HookHelper.REQUEST_TARGET_INTENT_NAME, intent.getComponent().getClassName());//设置为占坑Activityintent.setClassName(who, "com.glh.haiproject01.StubActivity");}try {Method execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity",Context.class, IBinder.class, IBinder.class, Activity.class,Intent.class, int.class, Bundle.class);return (ActivityResult) execStartActivity.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException,IllegalAccessException, ClassNotFoundException {String intentName=intent.getStringExtra(HookHelper.REQUEST_TARGET_INTENT_NAME);if(!TextUtils.isEmpty(intentName)){return super.newActivity(cl,intentName,intent);}return super.newActivity(cl,className,intent);}}

代理类InstrumentationProxy的execStartActivity方法先判断插件Activity是否在AndroidManifest.xml中注册过,如果没有注册过就需要替换占坑的Activity,在newActivity方法中还原插件Activity。

代理类InstrumentationProxy写完后,需要对ActivityThread的成员变量mInstrumentation进行替换。

public class MyApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);hookActivityThreadInstrumentation();}private void hookActivityThreadInstrumentation(){try {Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");Field activityThreadField=activityThreadClass.getDeclaredField("sCurrentActivityThread");activityThreadField.setAccessible(true);//获取ActivityThread对象sCurrentActivityThreadObject activityThread=activityThreadField.get(null);Field instrumentationField=activityThreadClass.getDeclaredField("mInstrumentation");instrumentationField.setAccessible(true);//从sCurrentActivityThread中获取成员变量mInstrumentationInstrumentation instrumentation= (Instrumentation) instrumentationField.get(activityThread);//创建代理对象InstrumentationProxyInstrumentationProxy proxy=new InstrumentationProxy(instrumentation,getPackageManager());//将sCurrentActivityThread中成员变量mInstrumentation替换成代理类InstrumentationProxyinstrumentationField.set(activityThread,proxy);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

这时我们在主界面点击跳转插件Activity:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.btn_startActivity).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent=new Intent(MainActivity.this,TargetActivity.class);startActivity(intent);}});}
}

运行效果:

Hook IActivityManager

第一种方案Hook Instrumentation已经顺利完成,接下来我们看第二个方案,还是看这段代码:

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {//Activity启动options = transferSpringboardActivityOptions(options);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) {mStartedActivity = true;}cancelInputsAndStartExitTransition(options);windows.} else {if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {mParent.startActivityFromChild(this, intent, requestCode);}}}

通过mInstrumentation的execStartActivity方法启动Activity,在execStartActivity方法中会获取AMS的代理,Android 7.0通过ActivityManagerNative的getDefault方法获取一个ActivityManagerProxy,这个ActivityManagerProxy内部封装了IBinder类型的ActivityManagerService的代理类,这样在应用程序进程中就可以通过这个ActivityManagerProxy与SystemServer进程的ActivityManagerService进行通信,而在Android 8.0去除了ActivityManagerProxy这个代理类,由IActivityManager代替,这里的IActivityManager.aidl通过AIDL工具自动生成IActivityManager.java。

Android 7.0-Activity启动

ActivityManager是一个和AMS相关联的类,它主要对运行中的Activity进行管理,ActivityManager中相关管理方法最终会通过ActivityManagerNative的getDefault方法来得到ActivityManagerProxy,再调用ActivityManagerProxy的相关管理方法,ActivityManagerProxy就是AMS的代理类,通过这个代理类就可以和AMS进行通信。

Android7.0的Activity启动过程会调用Instrumentation的execStartActivity方法,代码如下:

public ActivityResult execStartActivity(...try {...int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target, requestCode, 0, null, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;}

在execStartActivity方法中会调用ActivityManagerNative的getDefault方法来获取ActivityManagerProxy,ActivityManagerProxy又是AMS的代理类,这样的话就可以通过ActivityManagerProxy向AMS发送startActivity的请求。

ActivityManagerNative的getDefault方法的代码如下:

   private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {protected IActivityManager create() {IBinder b = ServiceManager.getService("activity");...IActivityManager am = asInterface(b);...return am;}};static public IActivityManager getDefault() {return gDefault.get();}

在getDefault方法中调用了gDefault的get方法,gDefault是一个单例类。通过ServiceManager的getService方法获取一个IBinder类型的AMS的引用,再将它通过asInterface方法转换成ActivityManagerProxy类型的对象。

asInterface方法:

static public IActivityManager asInterface(IBinder obj) {if (obj == null) {return null;}//检查本地进程是否有IActivityManager接口的实现IActivityManager in =(IActivityManager)obj.queryLocalInterface(descriptor);if (in != null) {return in;}//本地进程没有IActivityManager接口的实现,将IBinder类型的AMS引用封装成AMPreturn new ActivityManagerProxy(obj);}

在asInterface方法中分两种情况,首先会检查本地进程是否有IActivityManager接口的实现,如果有就直接返回;如果没有,就将IBinder类型的AMS引用封装成ActivityManagerProxy。

public abstract class ActivityManagerNative extends Binder implements IActivityManager{...class ActivityManagerProxy implements IActivityManager{public ActivityManagerProxy(IBinder remote){mRemote = remote;}...}...
}

ActivityManagerProxy是ActivityManagerNative的内部类,在ActivityManagerProxy的构造方法中将AMS的引用赋值给变量mRemote,这样在ActivityManagerProxy中就可以使用AMS了。

继续回到Instrumentation的execStartActivity方法,代码如下:

public ActivityResult execStartActivity(...try {...int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target, requestCode, 0, null, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;}

通过ActivityManagerNative的getDefault方法获取AMS的代理类ActivityManagerProxy,再调用ActivityManagerProxy的startActivity方法。

ActivityManagerProxy的startActivity方法:

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {...//向AMS发送START_ACTIVITY_TRANSACTION类型的进程间通信请求mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);reply.readException();int result = reply.readInt();reply.recycle();data.recycle();return result;}

在ActivityManagerProxy的startActivity方法中,通过mRemote,也就是AMS的引用,向服务端的AMS发送一个START_ACTIVITY_TRANSACTION类型的进程间通信请求,服务端AMS就会从Binder线程池中读取客户端发来的数据,最终会调用ActivityManagerNative的onTransact方法。

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {switch (code) {case START_ACTIVITY_TRANSACTION:{...int result = startActivity(app, callingPackage, intent, resolvedType,...return true;}...}return super.onTransact(code, data, reply, flags);
}

同时ActivityManagerService继承自ActivityManagerNative,因此onTransact方法的switch语句的START_ACTIVITY_TRANSACTION分支会调用AMS的startActivity。

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {@Override    public final int startActivity(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());}
}
Android 8.0-Activity启动

Android8.0和7.0的Activity启动过程都会调用Instrumentation的execStartActivity方法,代码如下:

    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target,Intent intent, int requestCode, Bundle options) {...try {...int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target, requestCode, 0, null, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;}

在execStartActivity方法中通过ActivityManager的getService获取IActivityManager对象,并通过IActivityManager对象的startActivity方法通知AMS启动Activity。

public static IActivityManager getService() {return IActivityManagerSingleton.get();}private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}};

IActivityManagerSingleto是一个单例类,在它的create方法中获取iBinder类型的AMS引用,接着通过AIDL,将AMS应用转换成IActivityManager类型的对象。相比于Android 7.0来说,这里去掉了ActivityManagerProxy这个代理类,由IActivityManager代替,这里的IActivityManager.aidl通过AIDL工具自动生成IActivityManager.java。

通过Android 7.0和Android 8.0的Activity启动流程可以得出Activity插件化的另一种方案:Hook IActivityManager,通过动态代理实现。

首先创建占坑Activity:

public class StubActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_stub);}
}

创建插件Activity:

public class TargetActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_target);}
}

并在AndroidManifest.xml中注册占坑Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.glh.haiproject01"><applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"tools:ignore="AllowBackup,GoogleAppIndexingWarning"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".StubActivity" /></application></manifest>

在AndroidManifest.xml中没有注册插件Activity,这时如果启动插件Activity会报错。

接着开始Hook IActivityManager,创建代理类IActivityManagerProxy:

public class IActivityManagerProxy implements InvocationHandler {private Object mActivityManager;public IActivityManagerProxy(Object activityManager){this.mActivityManager=activityManager;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("startActivity".equals(method.getName())){//拦截startActivityIntent intent=null;int index=0;for(int i=0,length=args.length;i<length;i++){if(args[i] instanceof Intent){index=i;break;}}//获取插件Activity的Intentintent= (Intent) args[index];//创建占坑Activity的IntentIntent subIntent=new Intent();subIntent.setClassName("com.glh.haiproject01","com.glh.haiproject01.StubActivity");//保存插件Activity的IntentsubIntent.putExtra(HookHelper.REQUEST_TARGET_INTENT_NAME,intent);//替换为占坑Activityargs[index]=subIntent;}return method.invoke(mActivityManager,args);}
}

IActivityManagerProxy代理类的invoke非常简单,就是将插件Activity的Intent替换为占坑Activity的Intent,并保存插件Activity的Intent,方便后续还原。

    private void hookIActivityManager(){Object defaultSingleton;if(Build.VERSION.SDK_INT==26){//Android 8.0defaultSingleton=getIActivityManagerSingleton();}else{defaultSingleton=getDefault();}try {Class<?> singletonClazz=Class.forName("android.util.Singleton");Field instanceField=singletonClazz.getDeclaredField("mInstance");instanceField.setAccessible(true);//获取defaultSingleton中IActivityManager类型的mInstance成员变量Object iActivityManager=instanceField.get(defaultSingleton);Class<?> iActivityManagerClazz=Class.forName("android.app.IActivityManager");Object proxy=Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class<?>[]{iActivityManagerClazz},new IActivityManagerProxy(iActivityManager));//替换为代理类IActivityManagerProxyinstanceField.set(defaultSingleton,proxy);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}private Object getIActivityManagerSingleton(){try {Class<?> activityManagerClazz=Class.forName("android.app.ActivityManager");Field field=activityManagerClazz.getDeclaredField("IActivityManagerSingleton");field.setAccessible(true);return field.get(null);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}private Object getDefault(){try {Class<?> activityManagerClazz=Class.forName("android.app.ActivityManagerNative");Field field=activityManagerClazz.getDeclaredField("gDefault");field.setAccessible(true);return field.get(null);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}

以上代码主要就是将Singleton的IActivityManager类型的成员变量mInstance替换成通过动态代理对象。当我们向AMS请求启动Activity时,会执行代理类IActivityManagerProxy的invoke方法进行狸猫换太子。

当AMS通过验证后需要还原插件Activity的Intent,在Android 8.0和Android 7.0(其他版本源码有可能不同)中,AMS通过Binder跨进程调用scheduleLaunchActivity,scheduleLaunchActivity方法在应用程序进程的Binder线程池中,通过mH发送LAUNCH_ACTIVITY消息(100)切换到主线程中创建并启动Activity。

mH的类型是ActivityThread中的内部类H,H继承自Handler并实现了handleMessage方法,在Handler源码中,有这么一段代码:

    public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

当mCallback不为null时会调用handleMessage方法,mCallback类型是Callback接口,因此我们可以Hook Callback,用自定的Callback替换Handler的mCallback。

代理Callback:

public class CallBackProxy implements Handler.Callback {private Handler mHandler;public CallBackProxy(Handler handler){this.mHandler=handler;}@Overridepublic boolean handleMessage(Message msg) {if(msg.what==100){Object o=msg.obj;try {Field field=o.getClass().getDeclaredField("intent");field.setAccessible(true);//获取占坑Activity的IntentIntent intent= (Intent) field.get(o);//获取之前保存的插件Activity的IntentIntent targetIntent=intent.getParcelableExtra(HookHelper.REQUEST_TARGET_INTENT_NAME);//将占坑的Activity替换为插件Activityintent.setComponent(targetIntent.getComponent());} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}mHandler.handleMessage(msg);return true;}
}

将Handler的mCallback替换为代理类CallBackProxy:

    private  void hookHandler(){try {Class<?> activityThreadClazz=Class.forName("android.app.ActivityThread");Field currentActivityThreadField=activityThreadClazz.getDeclaredField("sCurrentActivityThread");currentActivityThreadField.setAccessible(true);Object currentActivityThread=currentActivityThreadField.get(null);Field handlerField=activityThreadClazz.getDeclaredField("mH");handlerField.setAccessible(true);Handler mH= (Handler) handlerField.get(currentActivityThread);Field callbackField=Handler.class.getDeclaredField("mCallback");callbackField.setAccessible(true);//Handler的mCallback替换为CallBackProxycallbackField.set(mH,new CallBackProxy(mH));} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}

至此,Hook IActivityManager方案已经完成,在主界面中启动插件Activity:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.btn_startActivity).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent=new Intent(MainActivity.this,TargetActivity.class);startActivity(intent);}});}
}

运行效果:

深入浅出Activity插件化相关原理相关推荐

  1. Android插件化原理(一)Activity插件化

    title: " Android插件化原理(一)Activity插件化" date: 2018-5-28 00:16 photos: https://s2.ax1x.com/201 ...

  2. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 深入浅出Service插件化原理

    关注公众号:顾林海 Service插件化的重点是保证它的优先级,需要一个真正的Service来实现,当启动插件Service时,就会先启动代理Service,当这个代理Service运行起来后,在它的 ...

  4. 《Android插件化技术——原理篇》

    | 导语 插件化技术最早从2012年诞生至今,已经走过了5个年头.从最初只支持Activity的动态加载发展到可以完全模拟app运行时的沙箱系统,各种开源项目层出不穷,在此挑选了几个代表性的框架,总结 ...

  5. 插件化的原理分析及实现

    学习插件化前需要了解类加载器.反射及动态代理等基本知识  技术方案:  1.宿主apk和插件apk都是使用PathClassLoader加载,合并宿主和插件的ClassLoader  2.宿主apk资 ...

  6. 插件化实现原理(学习笔记六)

    介绍: 插件化本质上来说是运行没有安装的apk,支持插件的app一般称为宿主.宿主提供上下文环境通常作为主APP在运行时加载和运行插件,这样便可将app中一些不常用的功能模块做成插件,一方面可以减少安 ...

  7. 【android】插件化技术原理详解

      作为移动端的黑科技,插件化技术一直受大厂的青睐.插件化技术有减少宿主Apk体积,可以独立更新,模块化开发等优点,让宿主APP极具扩展性.那么,现在就来聊聊其中的技术实现,国际惯例,先上效果图 这篇 ...

  8. Activity的插件化(三)

    上面一篇文章已经实现了Intent解析和targetClass的还原工作,这篇文章会来说说: 1. 插件apk的解析 #### 2. ClassLoader的问题 3. 资源的问题 4. 插件的下载, ...

  9. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

最新文章

  1. Jetson TX2刷机
  2. ipch文件夹和.sdf文件
  3. Java学习之路-4_集合知识总结
  4. optee中关于异常向量表、中断等的深入思考
  5. php网页微信登录验证失败,php 微信添加分账接收方-验证签名失败
  6. set和dict理论和操作
  7. 极大似然估计与贝叶斯定理
  8. GIT_服务器与本地环境构建
  9. c++的虚拟继承 的一些思考吧
  10. [VBA] 设置行高和列宽,以及全选单元格
  11. Java虚拟机详解04----GC算法和种类
  12. 前端工程化/构建自动化
  13. MySQL之数据操作
  14. 精美网站登录界面 php,window_教你自己做xp精美登录界面,    一、在你的电脑 - phpStudy...
  15. 【常用传感器】DS18B20温度传感器原理详解及例程代码
  16. 什么软件可以压缩图片大小?这几个软件值得收藏
  17. python 面板数据分析_stata面板数据模型分析的详细步骤和命令
  18. A股动量策略有效性验证
  19. react 报 Objects are not valid as a React child (found: object with keys {}). If you meant to render.
  20. win10在BIOS开启Intel VT-x

热门文章

  1. ubuntu如何将一个.iso转化为.img
  2. 7月17python03
  3. Python网络协议编程之HTTP协议详解
  4. 【优化版】(终稿)C++实现科学计算器主函数代码(含调用函数)
  5. 【iOS开发】—— 自定义选择器实现日期选择器
  6. 这一天到底是一年中的第几天
  7. Windows 11最新Beta系统更新!
  8. linux安装步骤 从分区开始,从硬盘安装Linux系统方法步骤.doc
  9. git pull 配置免密_git仓库免密码登陆配置
  10. 碧瑶答疑网之选题报告