宿主App无法直接加载插件apk的Activity,因此需要一个代理的Activity,所以称之为占位式插件化。
具体看操作,开始上代码:
宿主Activity 调用插件Activity 不能直接startActivity 所以只能通过start ProxyActivity来开启插件Activity。
//开启插件Activity
public void startPlugin(View view) {
String path = Environment.getExternalStorageDirectory().getPath()+ File.separator+"plugin.apk";
// 获取插件包 里面的 Activity
PackageInfo packageArchiveInfo = getPackageManager().getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
ActivityInfo activity = packageArchiveInfo.activities[packageArchiveInfo.activities.length-1];
Intent intent = new Intent(this,ProxyActivity.class);
intent.putExtra("className",activity.name);
startActivity(intent);
}
//加载插件
public void loadPlugin(View view) {
PluginManager.getInstance(this).loadPlugin();
}
//加载插件
public void loadPlugin(){
String path = Environment.getExternalStorageDirectory().getPath()+File.separator+"plugin.apk";
File file = new File(path);
if (!file.exists()){
return;
}
// dexClassLoader需要一个缓存目录 /data/data/当前应用的包名/plugin
File fileDir = context.getDir("plugin", Context.MODE_PRIVATE);
classLoader = new DexClassLoader(path,fileDir.getPath(),null,context.getClassLoader());
try {
// 加载资源
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPathMethod.invoke(assetManager, path); // 插件包的路径 pluginPaht
Resources r = context.getResources(); // 宿主的资源配置信息
// 特殊的 Resources,加载插件里面的资源的 Resources
resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration()); // 参数2 3 资源配置信息
} catch (Exception e){
e.printStackTrace();
}
}
ProxyActivity 通过宿主APP 和 插件APP定义的标准 (ActivityInterface )通过反射创建插件Activity对象,并调用插件的onCreate 方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_proxy);
// 真正的加载 插件里面的 Activity
String className = getIntent().getStringExtra("className");
try {
Class<?> mPluginActivityClass = getClassLoader().loadClass(className);
Constructor<?> mPluginActivityConstructor = mPluginActivityClass.getConstructor(new Class[]{});
Object mPluginActivity = mPluginActivityConstructor.newInstance(new Object[]{});
ActivityInterface activityInterface = (ActivityInterface) mPluginActivity;
// 注入
activityInterface.mainAppContext(this);
Bundle bundle = new Bundle();
bundle.putString("appName", "我是宿主传递过来的信息");
// 执行插件里面的onCreate方法
activityInterface.onCreate(bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void startActivity(Intent intent) {
String className = intent.getStringExtra("className");
Intent proxyIntent = new Intent(this, ProxyActivity.class);
proxyIntent.putExtra("className", className); // 包名+插件中的activity
super.startActivity(proxyIntent);
}
//替换成插件的 ClassLoader
@Override
public ClassLoader getClassLoader() {
return PluginManager.getInstance(this).getClassLoader();
}
//替换成插件的 资源文件
@Override
public Resources getResources() {
return PluginManager.getInstance(this).getResources();
}
public interface ActivityInterface {
void mainAppContext(Activity activity);
void onCreate(Bundle savedInstanceState);
void onStart();
void onResume();
void onDestroy();
}
public class BaseActivity extends Activity implements ActivityInterface {
//宿主Activity 对象
protected Activity mainActivity;
@Override
public void mainAppContext(Activity activity) {
this.mainActivity = activity;
}
@SuppressLint("MissingSuperCall")
@Override
public void onCreate(Bundle savedInstanceState) {
}
@SuppressLint("MissingSuperCall")
@Override
public void onStart() {
}
@SuppressLint("MissingSuperCall")
@Override
public void onResume() {
}
@SuppressLint("MissingSuperCall")
@Override
public void onDestroy() {
}
public void setContentView(int resId) {
mainActivity.setContentView(resId);
}
public View findViewById(int layoutId) {
return mainActivity.findViewById(layoutId);
}
@Override
public void startActivity(Intent intent) {
Intent intentNew= new Intent();
intentNew.putExtra("className", intent.getComponent().getClassName());
mainActivity.startActivity(intentNew);
}
}
当代理Activity 调用 PluginMainActivity的onCreate方法的时候,setContentView(R.layout.activity_main) 其实就相当于代理Activity 调用了setContentView(R.layout.activity_main);方法。
插件apk 跳转自己包中的Activity时其实就是代理Activity 开启一个自身Activity。
public class PluginMainActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(mainActivity, "我是插件", Toast.LENGTH_SHORT).show();
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mainActivity,PluginActivity2.class);
startActivity(intent);
}
});
}
}
未完待续。。。