插件化概念
个人总结为:它是由宿主和插件应用结合而成,两者遵守一些标准规范的情况下,插件无需安装,即可按需加载使用!
插件化优点
- 宿主和插件分开编译
- 并发进行开发
- 动态更新插件
- 按需下载模块
- 方法数和变量数 65536问题
插件化步骤
- 插件化分为宿主应用和插件应用
- 新建Project - 也就是宿主应用
- 新建一个Module,具有生命周期接口,宿主跳插件应用需要遵守一些标准规范
- 新建Table Module - 也就是插件应用,同时在BaseActivity里面实现生命周期接口
- Module通过attach方法注入上下文context,因为插件apk未安装,所以不能直接使用插件的上下文
- 插件里面重写setContextView(),getWindowManger,getClassLoader这些需要使用上下文的方法,然后使用注入的that进行替换super调用
- 在宿主app里面创建ProxyActivity,通过插桩的方式进行注册插件的Activity。ProxyActivity里面获取跳转传入的className,同时重写getClassLoader和getResource的方法,替换为PluginManager创建的ClassLoader和Resource
- 创建PluginManager,单例的形式创建,实现loadPath方法,去加载apk(DexClassLoader)。因为 未安装就不能通过getClassLoader的方式Class.forName这样的方式了
- 加载未安装apk(DexClassLoader)
- 插件二级Activity的地方回传到宿主APP里面,BaseActivity里面startActivity
主要代码有:
1、初始化插件的ClassLoader,插件的主Activity,插件的Resources
object PluginManager {
private lateinit var dexClassLoader:DexClassLoader
private lateinit var resource:Resources
private var entryActivityName:String = ""
fun loadPath(context:Context,path: String){
//获取插件的ClassLoader
val dexOutFile = context.getDir("dex",Context.MODE_PRIVATE)
dexClassLoader = DexClassLoader(path,dexOutFile.absolutePath,null,context.classLoader)
//插件的第一个Activity
val packageManager = context.packageManager
val packageInfo = packageManager.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES)
entryActivityName = packageInfo.activities[0].name
//实例化Resource
val assetManager = AssetManager::class.java.newInstance()
AssetManager::class.java.getDeclaredMethod("addAssetPath",String::class.java).invoke(assetManager,path)
resource = Resources(assetManager,context.resources.displayMetrics,context.resources.configuration)
}
fun getClassLoader() = dexClassLoader
fun getResource() = resource
fun getEntryActivityName() = entryActivityName
}
2、插桩的形式创建ProxyActivity
class ProxyActivity : Activity() {
private var className: String? = null
private var aliPayInterface: IAliPay? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
className = intent.getStringExtra("className")
try {
val activityClass = classLoader.loadClass(className)
val constructor = activityClass.getConstructor(*arrayOf())
val instance = constructor.newInstance(*arrayOf())
aliPayInterface = instance as IAliPay
aliPayInterface!!.attach(this)
val bundle = Bundle()
aliPayInterface!!.onCreate(bundle)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
} catch (e: NoSuchMethodException) {
e.printStackTrace()
} catch (e: SecurityException) {
e.printStackTrace()
} catch (e: InstantiationException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
}
override fun onStart() {
super.onStart()
aliPayInterface!!.onStart()
}
override fun onResume() {
super.onResume()
aliPayInterface!!.onResume()
}
override fun onPause() {
super.onPause()
aliPayInterface!!.onPause()
}
override fun onStop() {
super.onStop()
aliPayInterface!!.onStop()
}
override fun onDestroy() {
super.onDestroy()
aliPayInterface!!.onDestroy()
}
override fun getClassLoader(): ClassLoader {
return PluginManager.getClassLoader()
}
override fun getResources(): Resources {
return PluginManager.getResource()
}
override fun startActivity(intent: Intent) {
val classNameFromTaoPiaoPiao = intent.getStringExtra("className")
val newIntent = Intent(this, ProxyActivity::class.java)
newIntent.putExtra("className", classNameFromTaoPiaoPiao)
startActivity(newIntent)
}
}
通过插件的classLoader
和传入的className
获取到约定规范的AliPay
接口,然后调用插件里面主Activity
的生命周期,同时重写ProxyActivity
的跳转,从而实现插件的二级页面的跳转。
详细Demo查看Github-GoachAlipay