思路:開啟外掛包裡邊的activity時 統一都用 一個ProxyActivity作為代理
1、首先宿主APP提供ProxyActivity,當宿主需要開啟外掛包中的Activity時,一律是啟動的ProxyActivity,
在啟動ProxyActivity的intent中攜帶我們真正需要去開啟的外掛包中Activity的類全名。
public class ProxyActivity extends Activity { private IPluginActivity mPluginActivity;//用來接收外掛包所有Activity的介面物件 @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); String className = getIntent().getStringExtra("className"); try { Class activityClass = getClassLoader().loadClass(className); Constructor constructor = activityClass.getConstructor(new Class[]{}); Object instance = constructor.newInstance(new Object[]{}); mPluginActivity = (PluginBaseActivity) instance; mPluginActivity.onCreate(savedInstanceState); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onStart() { super.onStart(); mPluginActivity.onStart(); } @Override protected void onResume() { super.onResume(); mPluginActivity.onResume(); } @Override protected void onPause() { super.onPause(); mPluginActivity.onPause(); } @Override protected void onStop() { super.onStop(); mPluginActivity.onStop(); } @Override protected void onDestroy() { super.onDestroy(); mPluginActivity.onDestroy(); } }
在Activity中有很重要的兩個回撥方法:
@Override public ClassLoader getClassLoader() { return PluginManager.getInstance().getPluginBean().getDexClassLoader(); } @Override public Resources getResources() { return PluginManager.getInstance().getPluginBean().getResources(); }
- getClassLoader返回的是ClassLoader 物件,Activity內部在使用反射new物件時,都會去使用這裡返回的ClassLoader 來進行反射,所以我們ProxyActivity中需要提供的應該是當前載入外掛包對應的ClassLoader 。
- getResources返回的是Resources 物件,Activity內部在使用資原始檔時,都會去使用這裡返回的Resources 來獲取資源,所以我們ProxyActivity中需要提供的應該是當前載入外掛包對應的Resources 。
我們宿主APP提供了一個ProxyActivity,我們開啟外掛包Activity其實都是開啟的ProxyActivity,只不過ProxyActivity不做任何其他事情,只負責例項化外掛包Activity,並且將ProxyActivity中的所有事 件驅動通知給外掛包Activity,也就是ProxyActivity呼叫了外掛包Activity中的程式碼來實現外掛包要實現的功能。
獲得classLoader和resource:
File pluginFile;//外掛包檔案 PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageArchiveInfo(pluginFile.getAbsolutePath(), PackageManager.GET_ACTIVITIES); File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE); DexClassLoader dexClassLoader = new DexClassLoader(pluginFile.getAbsolutePath(), dexOutFile.getAbsolutePath() , null, context.getClassLoader()); AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, pluginFile.getAbsolutePath()); Resources resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
PackageInfo 可以獲取外掛包manifest中所有註冊了的元件資訊,例如主Activity的全類名的獲取為:
packageInfo.activities[0].name
下面是完整的
public abstract class PluginBaseActivity extends AppCompatActivity implements IPluginActivity { protected Activity that;//宿主Activity @Override public void attach(ProxyActivity proxyActivity) { if (that != null) throw new RuntimeException("Plugin's activity already has been attached!"); this.that = proxyActivity; attachBaseContext(proxyActivity); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { if (that == null) { super.onCreate(savedInstanceState); } } @Override public void onStart() { if (that == null) { super.onStart(); } } @Override public void onResume() { if (that == null) { super.onResume(); } } @Override public void onPause() { if (that == null) { super.onPause(); } } @Override public void onStop() { if (that == null) { super.onStop(); } } @Override public void onDestroy() { if (that == null) { super.onDestroy(); } } @Override public void onSaveInstanceState(Bundle outState) { if (that == null) { super.onSaveInstanceState(outState); } } @Override public void onRestoreInstanceState(Bundle savedInstanceState) { if (that == null) { super.onRestoreInstanceState(savedInstanceState); } } @Override public boolean onTouchEvent(MotionEvent event) { if (that == null) { return super.onTouchEvent(event); } return false; } @Override public void onBackPressed() { if (that == null) { super.onBackPressed(); } } @Override public void setContentView(View view) { if (that == null) { super.setContentView(view); } else { that.setContentView(view); } } @Override public void setContentView(int layoutResID) { if (that == null) { super.setContentView(layoutResID); } else { that.setContentView(layoutResID); } } @Override public void startActivity(Intent intent) { if (that == null) { super.startActivity(intent); } else {//篡改intent開啟頁面為proxyActivity intent.putExtra("className", intent.getComponent().getClassName()); intent.setClassName(intent.getComponent().getPackageName(), ProxyActivity.class.getName()); that.startActivity(intent); } } @Override public ComponentName startService(Intent intent) { if (that == null) { return super.startService(intent); } else { intent.putExtra("className", intent.getComponent().getClassName()); intent.setClassName(intent.getComponent().getPackageName(), ProxyService.class.getName()); return that.startService(intent); } } @Override public View findViewById(int id) { if (that == null) { return super.findViewById(id); } else { return that.findViewById(id); } } @Override public Intent getIntent() { if (that == null) { return super.getIntent(); } else { return that.getIntent(); } } @Override public Window getWindow() { if (that == null) { return super.getWindow(); } else { return that.getWindow(); } } @Override public WindowManager getWindowManager() { if (that == null) { return super.getWindowManager(); } else { return that.getWindowManager(); } } }
public interface IPluginActivity { void attach(ProxyActivity proxyActivity); void onCreate(@Nullable Bundle savedInstanceState); void onStart(); void onResume(); void onPause(); void onStop(); void onDestroy(); void onSaveInstanceState(Bundle outState); void onRestoreInstanceState(Bundle savedInstanceState); boolean onTouchEvent(MotionEvent event); void onBackPressed(); void setContentView(View view); void setContentView(int layoutResID); void startActivity(Intent intent); ComponentName startService(Intent intent); View findViewById(int id); Intent getIntent(); Window getWindow(); WindowManager getWindowManager(); }
以上步驟實則每次新開啟的外掛裡的頁面其實都是ProxyActivity 做的代理