一、解析插件Apk
DLPluginManager pluginManager = DLPluginManager.getInstance(BaseApplication.getInstance()); DLPluginPackage pluginPackage = pluginManager.loadApk(pluginApkPath); DLIntent intent = new DLIntent(pluginPackage.packageName, pluginPackage.defaultActivity); pluginManager.startPluginActivity(this, intent);
1、loadApk方法
public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib){ mFrom = DLConstants.From_external; int packageInfoFlags = PackageManager.Get_activities | PackageManager.Get_services; PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath, packageInfoFlags); if(packageInfo == null) return null; DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath); if(hasSoLib){ copySoLib(dexPath); } return pluginPackage; }
2、preparePluginEnv方法
private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath){ DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName); if(pluginPackage != null) return pluginPackage; DexClassLoader dexClassLoader = createDexClassLoader(dexPath); AssetManager assetManager = createAssetManager(dexPath); Resources resources = createResources(assetManager); // create PluginPackage pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo); mPackageHolder.put(packageInfo.packageName, pluginPackage); return pluginPackage; } private DexClassLoader createDexClassLoader(String dexPath){ File dexOutputDir = mContext.getDir("dex", Context.Mode_private); dexOutputPath = dexOutputDir.getAbsolutePath(); DexClassLoader loader = new DexClassLoader( dexPath, dexOutputPaht, mNativeLibDir, mContext.getClassLoader() ); return loader; } private AssetManager createAssetManager(String dexPath){ try{ AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, dexPath); return assetManager; }catch(Exception e){} } private Resources createResources(AssetManager assetManager){ Resources superRes = mContext.getResources(); Resources resources = new Resources( assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration() ); return resources; }
二、运行插件Apk
启动代理的Activity,分别可以:在DLPluginManager里调用startPluginActivityForResult(一般宿主使用),还可以在DLPluginActivity里使用startPluginActivity(在插件里使用)。
DLIntent intent = new DLIntent(getPackageName(), SecondActivity.class); startPluginActivity(intent);
上面是调用Activity方法,再往下看:
public int startPluginActivity(DLIntent dlIntent){ return startPluginActivityForResult(dlIntent, -1); } /** @return may be {@link #Start_result_success}, {@link #Start_result_no_pkg}, {@link #Start_result_no_class}, {@link #Start_result_type_error} */ public int startPluginActivityForResult(DLIntent dlIntent, int requestCode){ if(mFrom == DLConstants.From_external){ if(dlIntent.getPluginPackage() == null){ dlIntent.setPluginPackage(mPluginPackage.packageName); } } return mPluginManager.startPluginActivityForResult(that, dlIntent, requestCode); }
之后交给PluginManager启动Activity:
public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode){ if(mFrom == DLConstants.From_internal){ dlIntent.setClassName(context, dlIntent.getPluginClass()); preformStartActivityForResult(context, dlIntent, requestCode); return DLPluginManager.Start_result_success; } String packageName = dlIntent.getPluginPackage(); if(TextUtils.isEmpty(packageName)){ throw new NullPointerException("disallow null packageName."); } DLPluginPackage pluginPackage = mPackageHolder.get(packageName); if(pluginPackage == null){ return Start_result_no_pkg; } final String className = getPluginActivityFullPath(dlIntent, pluginPackage); Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className); if(clazz == null){ return Start_result_no_class; } Class<? extends Activity> activityClass = getProxyActivityClass(clazz); if(activityClass == null){ return Start_result_type_error; } dlIntent.putExtra(DLConstants.Extra_class, className); dlIntent.putExtra(DLConstants.Extra_package, packageName); dlIntent.setClass(mContext, activityClass); performStartActivityForResult(context, dlIntent, requestCode); return Start_result_success; }
先进行一些判断,然后获取要启动的Activity的全称,然后通过函数loadPluginClass()利用反射获取插件实例activityClass
函数loadPluginClass:
private Class<?> loadPluginClass(ClassLoader classLoader, String className){ Class<?> clazz = null; try{ clazz = Class.forName(className, true, classLoader); }catch(ClassNotFoundException e){} return clazz; }
通过函数getProxyActivityClass(),根据插件实例获取它对应在宿主里对应的代理类:
private Class<? extends Activity> getProxyActivityClass(Class<?> clazz){ Class<? extends Activity> activityClass = null; if(DLBasePluginActivity.class.isAssignableFrom(clazz)){ activityClass = DLProxyActivity.class; }else if(DLBasePluginFragmentActivity.class.isAssignableFrom(clazz)){ activityClass = DLProxyFragmentActivity.class; }else if(Activity.class.isAssignableFrom(clazz)){ activityClass = Activity.class; } return activityClass; }
当这一步基本上完成偷天换柱的任务,表面上要启动的是插件类,其实启动的是一个宿主代理类。
在把插件包名和要启动的插件类名放到Intent中,传递一个代理类,最后直接启动代理Activity就好了。
private void performStartActivityForResult(Context context, DLIntent dlIntent, int requestCode){ if(context instanceof Activity){ ((Activity)context).startActivityForResult(dlIntent, requestCode); }else{ context.startActivity(dlIntent); } }
三、通过代理类运行插件类
在这里分析DLProxyActivity,经前面分析,已经启动了代理类,当然就执行到代理类的onCreate了:
protected DLPlugin mRemoteActivity; private DLProxyImpl impl = new DLProxyImpl(this); @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); impl.onCreate(getIntent()); }
接着流程就跑到DLProxyImpl类的onCreate()方法中:
public void onCreate(Intent intent){ intent.setExtrasClassLoader(DLConfigs.sPluginClassLoader); mPackageName = intent.getStringExtra(DLConstants.Extra_package); mClass = intent.getStringExtra(DLConstants.Extra_class); mPluginManager = DLPluginManager.getInstance(mProxyActivity); mPluginPackage = mPluginManager.getPackage(mPackageName); mAssetManager = mPluginPackage.assetManager; mResources = mPluginPackage.resources; initializeActivityInfo(); handleActivityInfo(); launchTargetActivity(); }
1、在initializeActivityInfo()之前是获取intent传递过来的插件包名、插件类,并根据它们获取插件信息PluginPackage 2、PluginPackage里面存放了插件的resource和assetManager 3、initializeActivityInfo()和handleActivityInfo()主要是进行一些主题处理 4、重点在launchTargetActivity()方法,初始化插件类
protected void launchTargetActivity(){ try{ Class<?> localClass = getClassLoader().loadClass(mClass); Constructor<?> localConstructor = localClass.getConstructor(new Class[]{}); Object instance = localConstructor.newInstance(new Object[]{}); mPluginActivity = (DLPlugin)instance; ((DLAttachable)mProxyActivity).attach(mPluginActivity, mPluginManager); mPluginActivity.attach(mProxyActivity, mPluginPackage); Bundle bundle = new Bundle(); bundle.putInt(DLConstants.From, DLConstants.From_external); mPluginActivity.onCreate(bundle); }catch(Exception e){} }
创建好了代理类对象和插件类对象,就需要通过两个attach()方法将它们绑定在一起。
第一个是代理类的attach()方法:
public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager){ mRemoteActivity = remoteActivity; }
第二个是插件类的attach()方法:
public void attach(Activity proxyActivity, DLPluginPackage pluginPackage){ mProxyActivity = proxyActivity; that = mProxyActivity; mPluginPackage = pluginPackage; }
可以看到插件类里的that被赋值了代理类对象,所以在编写插件代码的时候this的功能将被废弃,不能使用this,必须使用that