如果要启动没有在Manifest中注册的Activity,应该从startActivity着手。一般启动Activity的方式有两种,一种是startActivity,一种是startActivityForResult。其实startActivity最终调用的也是startActivityForResult,如下所示:
//Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
当我们启动一个Activity时,AMS会对这个Activity是否在AndroidManifest中声明注册进行检查,如果没有注册,则会报错。要想启动没有在AndroidManifest中注册的Activity,则需要“欺骗AMS”,让其以为我们已经在AndroidManifest中注册了。
基本思路是:
修改Activity启动流程如图所示(图片来自网络)
AndroidManifest.xml
<application
android:name=".MainApplication"
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">
<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=".SubActivity"/>
application>
一共两个Activity,分别是MainActivity和SubActivity,一个MainApplication,SubActivity是一个壳Activity,用于欺骗AMS的。
MainActivity.java
private View.OnClickListener mListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, PluginTestActivity.class);
startActivity(intent);
}
};
PluginTestActivity并没有在AndroidManifest中注册,正常情况下会报错,所以我们需要欺骗AMS。
欺骗AMS的方式有几种,这里我们选择对ActivityThread的mInstrumentation进行hook。代码如下:
public class HookHelper {
public static final String PLUGIN_INTENT = "plugin_intent";
public static void hookInstrumentation(Context context,String className) throws Exception{
//获取ActivityThread的字节码对象
Class<?> clazz = Class.forName("android.app.ActivityThread");
//获取ActivityThread中的sCurrentActivityThread字段
Field sCurrentActivityThreadField = ReflectUtils.getField(clazz,"sCurrentActivityThread");
//获取ActivityThread中的mInstrumentation字段
Field mInstrumentationField = ReflectUtils.getField(clazz,"mInstrumentation");
//获取ActivityThread中的sCurrentActivityThread的值并赋值给currentActivityThread
Object currentActivityThread = sCurrentActivityThreadField.get(clazz);
//获取ActivityThread中的mInstrumentation的值并赋值给instrumentation
Instrumentation instrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
//创建一个PackageManager对象packageManager
PackageManager packageManager = context.getPackageManager();
//创建InstrumentationProxy对象,InstrumentationProxy是继承Instrumentation的类,下面详细介绍
InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation,packageManager,className);
//将ActivityThread中的mInstrumentation赋值为instrumentationProxy
ReflectUtils.setFieldObject(clazz,currentActivityThread,"mInstrumentation",instrumentationProxy);
}
}
InstrumentationProxy类如下
package com.example.plugintest;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
import java.lang.reflect.Method;
import java.util.List;
/**
* Created by yds
* on 2020/2/14.
*/
public class InstrumentationProxy extends Instrumentation{
private Instrumentation mInstrumentation;
private PackageManager mPackageManager;
private String className;
public InstrumentationProxy(Instrumentation mInstrumentation, PackageManager mPackageManager, String className) {
this.mInstrumentation = mInstrumentation;
this.mPackageManager = mPackageManager;
this.className = className;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
List<ResolveInfo> infos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
if (infos == null || infos.size() == 0) {
intent.putExtra(HookHelper.PLUGIN_INTENT,intent.getComponent().getClassName());
intent.setClassName(who,className);
}
try {
Method execMethod = Instrumentation.class.getDeclaredMethod("execStartActivity",
Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class,Bundle.class);
return (ActivityResult) execMethod.invoke(mInstrumentation,who,contextThread,token,target,intent,requestCode,options);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Activity newActivity(ClassLoader cl,String className,Intent intent) throws
InstantiationException,IllegalAccessException,ClassNotFoundException{
String intentName = intent.getStringExtra(HookHelper.PLUGIN_INTENT);
if(!TextUtils.isEmpty(intentName)){
return super.newActivity(cl,intentName,intent);
}
return super.newActivity(cl,className,intent);
}
}
只要将InstrumentationProxy赋值给ActivityThread的mInstrumentation,调用startActivity时,最终会调用InstrumentationProxy的execStartActivity和newActivity。我们只要在execStartActivity中将未注册的Activity保存起来,并使用已注册的SubActivity,然后在newActivity中将保存起来未注册的Activity替换SubActivity就可以了。
源码地址:https://github.com/Yedongsheng/PluginTest01