使用ClassLoader加载插件类
初始化DexClassLoader
这里假设插件类在SDCard上,我们可以使用DexClassLoader加载这个类,使用方法也很简单。
// Dex解压目录
mDexOutPath = getDir("dex", MODE_PRIVATE);
// 这个依赖的lib路径,可以传null
mLibPath = getDir("pluginlib", MODE_PRIVATE);
// 实际的Apk文件,这里我们随便模拟一个
// 但是这个Apk需要真实存在
mDexPath = SDCARD.getAbsolutePath() + "/plugin_test/plugina-debug.apk";
// 创建DexClassLoader
mMyClassLoader = new DexClassLoader(mDexPath
, mDexOutPath.getAbsolutePath(), mLibPath.getAbsolutePath(), getClassLoader());
使用反射调用加载类的生命周期
由于我们通过反射创建的这个类实例,所以它没有向AMS注册,就没有相关的生命周期回调。这里我们通过代理的方式解决这个问题,即启动一个宿主Activity,他的生命周期是受系统管理的,然后让这个Activity代理插件Activity的声明周期即可。
// 插件Activity基类提供一个代理Activity的设置方法。
/**
* Created by yangtianrui on 2018/3/4.
* 与宿主Activity交互
*/
@SuppressLint("Registered")
public class BaseProxyActivity extends Activity {
protected Activity mDelegate;
public void setProxy(Activity delegate) {
mDelegate = delegate;
}
@Override
public void setContentView(View view) {
if (mDelegate == null) {
super.setContentView(view);
} else {
mDelegate.setContentView(view);
}
}
}
// 插件A的Activity代码
public class PluginMainActivity extends BaseProxyActivity {
private static final String TAG = "pluginA";
// onCreate()之类的生命周期,由宿主负责回调
@Override
protected void onCreate(Bundle savedInstanceState) {
if (mDelegate == null) {
super.onCreate(savedInstanceState);
}
Log.d(TAG, "PluginA Activity onCreate: ");
//setContentView(createContentView());
if (mDelegate != null) {
// 如果不重写
mDelegate.setContentView(R.layout.plugin_a_activity);
} else {
setContentView(R.layout.plugin_a_activity);
}
}
private View createContentView() {
final Context context = mDelegate != null ? mDelegate : this;
final TextView tv = new TextView(context);
tv.setText("PluginA Activity");
tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
return tv;
}
}
宿主中加载插件Activity的代码
try {
//1.先加载到类
// 使用ClassLoader#loadClass时,需要完成的报名和类名。
// com.example.plugina.PluginMainActivity
final Class clazz = mMyClassLoader.loadClass(packageInfo.activities[0].name);
Log.d(TAG, "onCreate: get class " + clazz.toString());
// 2. 获取构造器对象
Log.d(TAG, "onCreate: constructors: " + Arrays.toString(clazz.getConstructors()));
Constructor<?> constructor = clazz.getConstructor();
if (constructor == null) {
return;
}
// 3. 初始化对象
final Object remote = constructor.newInstance();
// 4. 回调onCreate()
// fixme getDeclaredMethod和getMethod区别
final Method onCreate = clazz.getDeclaredMethod("onCreate", Bundle.class);
onCreate.setAccessible(true);
final Method setProxy = clazz.getMethod("setProxy", Activity.class);
setProxy.setAccessible(true);
setProxy.invoke(remote, this);
onCreate.invoke(remote, savedInstanceState);
} catch (Exception e) {
e.printStackTrace();
}
这个补充一个小知识点, getMethod()和getDecaledMethod()的区别,getMethod()的作用是获取当前类和父类的所有public方法,但是没有办法获取所有的非public方法。getDecaledMethod()可以获取当前类的所有方法,但是没有办法获取父类的任何方法,注意别弄混了。
资源管理
资源管理实际上是通过ContextImpl执行的,只要我们重写这个getResource(),和getAsset()资源的问题就解决了。
/** Return an AssetManager instance for your application's package. */
public abstract AssetManager getAssets();
/** Return a Resources instance for your application's package. */
public abstract Resources getResources();
宿主中的资源加载
// 宿主Activity的onCreate中调用
private void loadResource() {
try {
AssetManager asset = AssetManager.class.newInstance();
Method addAsset = AssetManager.class.getMethod("addAssetPath", String.class);
addAsset.setAccessible(true);
addAsset.invoke(asset, mDexPath);
mAssetManager = asset;
mResources = new Resources(asset, getResources().getDisplayMetrics()
, getResources().getConfiguration());
} catch (Exception e) {
e.printStackTrace();
}
}
//然后重写这个Activity的getResource(),getAsset()
,返回刚才用反射创建的实例即可。
@Override
public Resources getResources() {
if (mResources == null) {
return super.getResources();
}
return mResources;
}
@Override
public AssetManager getAssets() {
if (mAssetManager == null) {
return super.getAssets();
}
return;
宿主Activity完整代码
package com.example.yangtianrui.viewsystem;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import dalvik.system.DexClassLoader;
public class DLPluginActivity extends AppCompatActivity {
private static final String TAG = "dlp";
private static final File SDCARD = Environment.getExternalStorageDirectory();
private File mDexOutPath;
private File mLibPath;
private ClassLoader mMyClassLoader;
private String mDexPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dlplugin_activitiy);
mDexOutPath = getDir("dex", MODE_PRIVATE);
mLibPath = getDir("pluginlib", MODE_PRIVATE);
mDexPath = SDCARD.getAbsolutePath() + "/plugin_test/plugina-debug.apk";
mMyClassLoader = new DexClassLoader(mDexPath
, mDexOutPath.getAbsolutePath(), mLibPath.getAbsolutePath(), getClassLoader());
//mMyClassLoader = createDexClassLoader(mDexPath);
loadResource();
// 使用PackageInfo获取到类名
PackageInfo packageInfo = getPackageManager().getPackageArchiveInfo(mDexPath, PackageManager.GET_ACTIVITIES);
if (packageInfo.activities == null) {
Log.d(TAG, "onCreate: activities are null!!!");
return;
}
Log.d(TAG, "onCreate: packageInfo activities " + Arrays.toString(packageInfo.activities));
try {
//1.先加载到类
// 使用ClassLoader#loadClass时,需要完成的报名和类名。
// com.example.plugina.PluginMainActivity
final Class clazz = mMyClassLoader.loadClass(packageInfo.activities[0].name);
Log.d(TAG, "onCreate: get class " + clazz.toString());
// 2. 获取构造器对象
Log.d(TAG, "onCreate: constructors: " + Arrays.toString(clazz.getConstructors()));
Constructor<?> constructor = clazz.getConstructor();
if (constructor == null) {
return;
}
Log.d(TAG, "onCreate: get constructor ");
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals("setProxy")) {
Log.d(TAG, "onCreate: find setProxy " + methods[i]);
}
Log.d(TAG, "onCreate: method " + methods[i].getName());
}
// 3. 初始化对象
final Object remote = constructor.newInstance();
// 4. 回调onCreate()
// fixme getDeclaredMethod和getMethod区别
final Method onCreate = clazz.getDeclaredMethod("onCreate", Bundle.class);
onCreate.setAccessible(true);
final Method setProxy = clazz.getMethod("setProxy", Activity.class);
setProxy.setAccessible(true);
setProxy.invoke(remote, this);
onCreate.invoke(remote, savedInstanceState);
} catch (Exception e) {
e.printStackTrace();
}
}
private Resources mResources;
private AssetManager mAssetManager;
private void loadResource() {
try {
AssetManager asset = AssetManager.class.newInstance();
Method addAsset = AssetManager.class.getMethod("addAssetPath", String.class);
addAsset.setAccessible(true);
addAsset.invoke(asset, mDexPath);
mAssetManager = asset;
mResources = new Resources(asset, getResources().getDisplayMetrics()
, getResources().getConfiguration());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onContentChanged() {
Log.d(TAG, "onContentChanged: ");
super.onContentChanged();
}
@Override
public Resources getResources() {
if (mResources == null) {
return super.getResources();
}
return mResources;
}
@Override
public AssetManager getAssets() {
if (mAssetManager == null) {
return super.getAssets();
}
return mAssetManager;
}
private ClassLoader createDexClassLoader(String dex) {
return new DexClassLoader(dex
, mDexOutPath.getAbsolutePath(), mLibPath.getAbsolutePath(), getClassLoader());
}
}
演示效果
成功加载宿主中的layout资源。