日期:
来源:郭霖收集编辑:高级攻城狮
https://juejin.cn/post/7143869920193822733
/system/app:系统应用 /system/priv-app:系统应用 /data/app:用户应用
classes.dex:Java 代码字节码 res:资源文件 lib:so 文件 assets:静态资产文件 AndroidManifest.xml:清单文件
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
dexPath:dex文件路径 optimizedDirectory:dex文件首次加载时会进行优化操作,这个参数即为优化后的odex文件的存放目录,官方推荐使用应用私有目录来缓存优化后的dex文件,dexOutputDir = context.getDir(“dex”, 0); librarySearchPath:动态库路径 parent:当前类加载器的父类加载器
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
private void loadClass() {
init();
try {
// 从优化后的dex文件中加载APK_HELLO_CLASS_PATH类
clazz = pluginClassLoader.loadClass("com.iflytek.test.HelloWorld");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private fun init() {
extractPlugin()
pluginPath = File(filesDir.absolutePath, "plugin.apk").absolutePath
nativeLibDir = File(filesDir, "pluginlib").absolutePath
dexOutPath = File(filesDir, "dexout").absolutePath
// 生成 DexClassLoader 用来加载插件类
pluginClassLoader = DexClassLoader(pluginPath, dexOutPath, nativeLibDir, this::class.java.classLoader)
}
// 从 assets 中拿出插件 apk 放到内部存储空间
private fun extractPlugin() {
var inputStream = assets.open("plugin.apk")
File(filesDir.absolutePath, "plugin.apk").writeBytes(inputStream.readBytes())
}
val loadClass = pluginClassLoader.loadClass(activityName)
loadClass.getMethod("hello",null).invoke(loadClass)
PluginClassLoader.load(“插件名”,"需要加载的插件类权限定名")
PackageManager#getPackageArchiveInfo:根据 Apk 路径解析一个未安装的 Apk 的 PackageInfo
PackageManager#getResourcesForApplication:根据 ApplicationInfo 创建一个 Resources 实例
PackageManager packageManager = getPackageManager();
PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(
pluginApkPath,
PackageManager.GET_ACTIVITIES
| PackageManager.GET_META_DATA
| PackageManager.GET_SERVICES
| PackageManager.GET_PROVIDERS
| PackageManager.GET_SIGNATURES
);
packageArchiveInfo.applicationInfo.sourceDir = pluginApkPath;
packageArchiveInfo.applicationInfo.publicSourceDir = pluginApkPath;
Resources injectResources = null;
try {
injectResources = packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
// ...
}
public class PluginResources extends Resources {
private Resources hostResources;
private Resources injectResources;
public PluginResources(Resources hostResources, Resources injectResources) {
super(injectResources.getAssets(), injectResources.getDisplayMetrics(), injectResources.getConfiguration());
this.hostResources = hostResources;
this.injectResources = injectResources;
}
@Override
public String getString(int id, Object... formatArgs) throws NotFoundException {
try {
return injectResources.getString(id, formatArgs);
} catch (NotFoundException e) {
return hostResources.getString(id, formatArgs);
}
}
// ...
}
public class PluginContext extends ContextThemeWrapper {
//插件ClassLoader
private final ClassLoader pluginClassLoader;
//插件Resources
private final Resources pluginResource;
//插件名称,一般根据插件的apk名称来
private final String mPlugin;
...
@Override
public ClassLoader getClassLoader() {
if (pluginClassLoader != null) {
return pluginClassLoader;
}
return super.getClassLoader();
}
@Override
public Resources getResources() {
if (pluginResource != null) {
return pluginResource;
}
return super.getResources();
}
@Override
public AssetManager getAssets() {
if (pluginResource != null) {
return pluginResource.getAssets();
}
return super.getAssets();
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.music.anna.pluginactivity">S
<application
...
<activity android:name=".StubActivity"/>
</application>
</manifest>
public class Instrumentation {
//启动Activity的时候,调用此方法时,替换调Intent
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
}
//AMS检测后,创建Activity之前替换回Intent
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
}
}
public class InstrumentationProxy extends Instrumentation {
private Instrumentation mInstrumentation;
private PackageManager mPackageManager;
public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {
mInstrumentation = instrumentation;
mPackageManager = packageManager;
}
//启动Activity的时候,调用此方法时,替换调Intent
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.TARGET_INTENsT_NAME, intent.getComponent().getClassName());//1
intent.setClassName(who, "com.music.anna.pluginactivity.StubActivity");//2
}
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;
}
//AMS检测后,创建Activity之前替换回Intent
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
String intentName = intent.getStringExtra(HookHelper.TARGET_INTENT_NAME);
if (!TextUtils.isEmpty(intentName)) {
return super.newActivity(cl, intentName, intent);
}
return super.newActivity(cl, className, intent);
}
}
public static void hookInstrumentation(Context context) throws Exception {
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
Field mMainThreadField =FieldUtil.getField(contextImplClass,"mMainThread");//1
Object activityThread = mMainThreadField.get(context);//2
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field mInstrumentationField=FieldUtil.getField(activityThreadClass,"mInstrumentation");//3
FieldUtil.setField(activityThreadClass,activityThread,"mInstrumentation",new InstrumentationProxy((Instrumentation) mInstrumentationField.get(activityThread),
context.getPackageManager()));
}
frameworks/base/core/java/android/app/ActivityThread.java
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
...
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;
...
}
...
}
frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public class HCallback implements Handler.Callback{
public static final int LAUNCH_ACTIVITY = 100;
Handler mHandler;
public HCallback(Handler handler) {
mHandler = handler;
}
@Override
public boolean handleMessage(Message msg) {
if (msg.what == LAUNCH_ACTIVITY) {
Object r = msg.obj;
try {
//得到消息中的Intent(启动SubActivity的Intent)
Intent intent = (Intent) FieldUtil.getField(r.getClass(), r, "intent");
//得到此前保存起来的Intent(启动TargetActivity的Intent)
Intent target = intent.getParcelableExtra(HookHelper.TARGET_INTENT);
//将启动SubActivity的Intent替换为启动TargetActivity的Intent
intent.setComponent(target.getComponent());
} catch (Exception e) {
e.printStackTrace();
}
}
mHandler.handleMessage(msg);
return true;
}
}
public static void hookHandler() throws Exception {
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Object currentActivityThread= FieldUtil.getField(activityThreadClass ,null,"sCurrentActivityThread");//1
Field mHField = FieldUtil.getField(activityThread,"mH");//2
Handler mH = (Handler) mHField.get(currentActivityThread);//3
FieldUtil.setField(Handler.class,mH,"mCallback",new HCallback(mH));
}
在注释1处获取ActivityThread中定义的sCurrentActivityThread对象。 注释2处获取ActivityThread类的mH字段, 接着在注释3处获取当前ActivityThread对象中的mH对象, 最后用HCallback来替换mH中的mCallback。
RePluginClassLoader:继承PathClassLoader所以只能用于加载宿主中的已经安装的类。 PluginDexClassLoader:继承自DexClassLoader,前面我们分析过,DexClassLoader可以加载外部路径上的类
mPackageInfo = pm.getPackageArchiveInfo(mPath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_META_DATA);
/**
* 初始化ComponentList对象 <p>
* 注意:仅框架内部使用
*/
public ComponentList(PackageInfo pi, String path, PluginInfo pli) {
if (pi.activities != null) {
for (ActivityInfo ai : pi.activities) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "activity=" + ai.name);
}
ai.applicationInfo.sourceDir = path;
// todo extract to function
if (ai.processName == null) {
ai.processName = ai.applicationInfo.processName;
}
if (ai.processName == null) {
ai.processName = ai.packageName;
}
mActivities.put(ai.name, ai);
}
}
if (pi.providers != null) {
for (ProviderInfo ppi : pi.providers) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "provider=" + ppi.name + "; auth=" + ppi.authority);
}
if (ppi.processName == null) {
ppi.processName = ppi.applicationInfo.processName;
}
if (ppi.processName == null) {
ppi.processName = ppi.packageName;
}
mProvidersByName.put(ppi.name, ppi);
mProvidersByAuthority.put(ppi.authority, ppi);
}
}
if (pi.services != null) {
for (ServiceInfo si : pi.services) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "service=" + si.name);
}
if (si.processName == null) {
si.processName = si.applicationInfo.processName;
}
if (si.processName == null) {
si.processName = si.packageName;
}
mServices.put(si.name, si);
}
}
if (pi.receivers != null) {
for (ActivityInfo ri : pi.receivers) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "receiver=" + ri.name);
}
if (ri.processName == null) {
ri.processName = ri.applicationInfo.processName;
}
if (ri.processName == null) {
ri.processName = ri.packageName;
}
mReceivers.put(ri.name, ri);
}
}
// 解析 Apk 中的 AndroidManifest.xml
String manifest = getManifestFromApk(path);
}
// 远程分配坑位
container = client.allocActivityContainer(plugin, process, ai.name, intent);
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
String pkg = intent != null && intent.getComponent() != null
? intent.getComponent().getPackageName() : null;
return getFactory(pkg).instantiateActivity(cl, className, intent);
}
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity) cl.loadClass(className).newInstance();
}
cl:还是宿主的PluginClassLoader, className:占坑Activity类名
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
//
Class<?> c = null;
c = PMF.loadClass(className, resolve);
if (c != null) {
return c;
}
//
try {
c = mOrig.loadClass(className);
// 只有开启“详细日志”才会输出,防止“刷屏”现象
if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
LogDebug.d(TAG, "loadClass: load other class, cn=" + className);
}
return c;
} catch (Throwable e) {
//
}
//
return super.loadClass(className, resolve);
}
final Class<?> loadClass(String className, boolean resolve) {
// 加载Service中介坑位
if (className.startsWith(PluginPitService.class.getName())) {
return PluginPitService.class;
}
//
if (mContainerActivities.contains(className)) {
Class<?> c = mClient.resolveActivityClass(className);
if (c != null) {
return c;
}
return DummyActivity.class;
}
//
if (mContainerServices.contains(className)) {
Class<?> c = loadServiceClass(className);
if (c != null) {
return c;
}
return DummyService.class;
}
//
if (mContainerProviders.contains(className)) {
Class<?> c = loadProviderClass(className);
if (c != null) {
return c;
}
return DummyProvider.class;
}
...
return loadDefaultClass(className);
}
@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
// 插件自己的Class。从自己开始一直到BootClassLoader,采用正常的双亲委派模型流程,读到了就直接返回
Class<?> pc = null;
ClassNotFoundException cnfException = null;
try {
pc = super.loadClass(className, resolve);
if (pc != null) {
...
return pc;
}
} catch (ClassNotFoundException e) {
if (PluginDexClassLoaderPatch.need2LoadFromHost(className)) {
try {
return loadClassFromHost(className, resolve);
} catch (ClassNotFoundException e1) {
}
}
}
// 若插件里没有此类,则会从宿主ClassLoader中找,找到了则直接返回
// 注意:需要读取isUseHostClassIfNotFound开关。默认为关闭的。可参见该开关的说明
if (RePlugin.getConfig().isUseHostClassIfNotFound()) {
try {
return loadClassFromHost(className, resolve);
} catch (ClassNotFoundException e) {
}
}
// At this point we can throw the previous exception
if (cnfException != null) {
throw cnfException;
}
return null;
}
pluginName pluginApkPath pluginActivityName
转发所有来自系统的生命周期回调至插件 Activity 接受 Activity 方法的系统调用,并转发回系统
public class ContainerActivity extends Activity {
private PluginActivity pluginActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
String pluginActivityName = getIntent().getString("pluginActivityName", "");
pluginActivity = PluginLoader.loadActivity(pluginActivityName, this);
if (pluginActivity == null) {
super.onCreate(savedInstanceState);
return;
}
pluginActivity.onCreate();
}
@Override
protected void onResume() {
if (pluginActivity == null) {
super.onResume();
return;
}
pluginActivity.onResume();
}
@Override
protected void onPause() {
if (pluginActivity == null) {
super.onPause();
return;
}
pluginActivity.onPause();
}
// ...
}
完整的:让插件运行起来“像单品那样”,支持大部分特性 稳定的:如此灵活完整的情况下,其框架崩溃率仅为业内很低的“万分之一” 适合全面使用的:其目的是让应用内的“所有功能皆为插件” 占坑类:以稳定为前提的Manifest占坑思路 插件化方案:基于Android原生API和语言来开发,充分利用原生特性
四大组件 Fragment(代码添加和Xml添加) DataBinding(无需特别支持,但已验证可正常工作) 跨进程使用插件Service 自定义Theme 插件访问宿主类 So加载 分段加载插件(多Apk分别加载或多Apk以此依赖加载) 一个Activity中加载多个Apk中的View 等等……