插件化概念
個(gè)人總結(jié)為:它是由宿主和插件應(yīng)用結(jié)合而成,兩者遵守一些標(biāo)準(zhǔn)規(guī)范的情況下,插件無需安裝,即可按需加載使用!
插件化優(yōu)點(diǎn)
- 宿主和插件分開編譯
- 并發(fā)進(jìn)行開發(fā)
- 動(dòng)態(tài)更新插件
- 按需下載模塊
- 方法數(shù)和變量數(shù) 65536問題
插件化步驟
- 插件化分為宿主應(yīng)用和插件應(yīng)用
- 新建Project - 也就是宿主應(yīng)用
- 新建一個(gè)Module,具有生命周期接口,宿主跳插件應(yīng)用需要遵守一些標(biāo)準(zhǔn)規(guī)范
- 新建Table Module - 也就是插件應(yīng)用,同時(shí)在BaseActivity里面實(shí)現(xiàn)生命周期接口
- Module通過attach方法注入上下文context,因?yàn)椴寮pk未安裝,所以不能直接使用插件的上下文
- 插件里面重寫setContextView(),getWindowManger,getClassLoader這些需要使用上下文的方法,然后使用注入的that進(jìn)行替換super調(diào)用
- 在宿主app里面創(chuàng)建ProxyActivity,通過插樁的方式進(jìn)行注冊(cè)插件的Activity。ProxyActivity里面獲取跳轉(zhuǎn)傳入的className,同時(shí)重寫getClassLoader和getResource的方法,替換為PluginManager創(chuàng)建的ClassLoader和Resource
- 創(chuàng)建PluginManager,單例的形式創(chuàng)建,實(shí)現(xiàn)loadPath方法,去加載apk(DexClassLoader)。因?yàn)?未安裝就不能通過getClassLoader的方式Class.forName這樣的方式了
- 加載未安裝apk(DexClassLoader)
- 插件二級(jí)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)
//插件的第一個(gè)Activity
val packageManager = context.packageManager
val packageInfo = packageManager.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES)
entryActivityName = packageInfo.activities[0].name
//實(shí)例化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、插樁的形式創(chuàng)建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獲取到約定規(guī)范的AliPay接口,然后調(diào)用插件里面主Activity的生命周期,同時(shí)重寫ProxyActivity的跳轉(zhuǎn),從而實(shí)現(xiàn)插件的二級(jí)頁面的跳轉(zhuǎn)。
詳細(xì)Demo查看Github-GoachAlipay