override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val type = intent.getStringExtra(“type”) ?: “NewsList”
setContent {
MaterialTheme {
if (type == “NewsList”) {
NewsList()
} else if (type == “NewsDetail”) {
NewsDetail()
}
}
}
}
}
fun NewsList() {
LazyColumn(
Modifier.fillMaxSize().background(Color.Gray),
contentPadding = PaddingValues(15.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(50) { index ->
NewsItem(“我是第 $index 条新闻”)
}
}
}
@Composable
private fun NewsItem(
text : String,
modifier: Modifier = Modifier,
bgColor: Color = Color.White,
fontColor: Color = Color.Black,
) {
Card(
elevation = 8.dp,
modifier = modifier.fillMaxWidth(),
backgroundColor = bgColor
) {
Box(
Modifier.fillMaxWidth().padding(15.dp),
contentAlignment = Alignment.Center
) {
Text(text = text, fontSize = 20.sp, color = fontColor)
}
}
}
fun NewsDetail() {
Column {
Text(text = “我是插件中的新闻详情页面”.repeat(100))
}
}
import android.content.Context
import dalvik.system.DexClassLoader
import java.io.File
import java.lang.reflect.Array.newInstance
import java.lang.reflect.Field
class PluginManager private constructor() {
companion object {
var pluginClassLoader : DexClassLoader? = null
fun loadPlugin(context: Context) {
val inputStream = context.assets.open(“news_lib.apk”)
val filesDir = context.externalCacheDir
val apkFile = File(filesDir?.absolutePath, “news_lib.apk”)
apkFile.writeBytes(inputStream.readBytes())
val dexFile = File(filesDir, “dex”)
if (!dexFile.exists()) dexFile.mkdirs()
println(“输出dex路径: $dexFile“)
pluginClassLoader = DexClassLoader(apkFile.absolutePath, dexFile.absolutePath, null, this.javaClass.classLoader)
}
fun loadClass(className: String): Class<*>? {
try {
if (pluginClassLoader == null) {
println(“pluginClassLoader is null”)
}
return pluginClassLoader?.loadClass(className)
} catch (e: ClassNotFoundException) {
println(“loadClass ClassNotFoundException: $className“)
}
return null
}
/**
* 合并DexElement数组: 宿主新dexElements = 宿主原始dexElements + 插件dexElements
* 1、创建插件的 DexClassLoader 类加载器,然后通过反射获取插件的 dexElements 值。
* 2、获取宿主的 PathClassLoader 类加载器,然后通过反射获取宿主的 dexElements 值。
* 3、合并宿主的 dexElements 与 插件的 dexElements,生成新的 Element[]。
* 4、最后通过反射将新的 Element[] 赋值给宿主的 dexElements。
*/
@SuppressLint(“DiscouragedPrivateApi”)
fun mergeDexElement(context: Context) : Boolean{
try {
val clazz = Class.forName(“dalvik.system.BaseDexClassLoader”)
val pathListField: Field = clazz.getDeclaredField(“pathList”)
pathListField.isAccessible = true
val dexPathListClass = Class.forName(“dalvik.system.DexPathList”)
val dexElementsField = dexPathListClass.getDeclaredField(“dexElements”)
dexElementsField.isAccessible = true
// 宿主的 类加载器
val pathClassLoader: ClassLoader = context.classLoader
// DexPathList类的对象
val hostPathListObj = pathListField[pathClassLoader]
// 宿主的 dexElements
val hostDexElements = dexElementsField[hostPathListObj] as Array<*>
// 插件的 类加载器
val dexClassLoader = pluginClassLoader ?: return false
// DexPathList类的对象
val pluginPathListObj = pathListField[dexClassLoader]
// 插件的 dexElements
val pluginDexElements = dexElementsField[pluginPathListObj] as Array<*>
val hostDexSize = hostDexElements.size
val pluginDexSize = pluginDexElements.size
// 宿主dexElements = 宿主dexElements + 插件dexElements
// 创建一个新数组
val newDexElements = hostDexElements.javaClass.componentType?.let {
newInstance(it, hostDexSize + pluginDexSize)
} as Array<*>
System.arraycopy(hostDexElements, 0, newDexElements, 0, hostDexSize)
System.arraycopy(pluginDexElements, 0, newDexElements, hostDexSize, pluginDexSize)
// 赋值 hostDexElements = newDexElements
dexElementsField[hostPathListObj] = newDexElements
return true
} catch (e: Exception) {
println(“mergeDexElement: $e“)
}
return false
}
}
}
private val _isPluginLoadSuccess = MutableStateFlow(false)
val isPluginLoadSuccess = _isPluginLoadSuccess.asStateFlow()
private val _isMergeDexSuccess = MutableStateFlow(false)
val isMergeDexSuccess = _isMergeDexSuccess.asStateFlow()
var pluginActivityClass by mutableStateOf?>(null)
private set
fun loadPlugin(context: Context) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
PluginManager.loadPlugin(context)
if (PluginManager.pluginClassLoader != null) {
_isPluginLoadSuccess.value = true
}
}
}
}
fun mergeDex(context: Context) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
if (PluginManager.mergeDexElement(context)) {
_isMergeDexSuccess.value = true
}
}
}
}
fun loadClass(name: String) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
pluginActivityClass = PluginManager.loadClass(name)
}
}
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun HostScreen(viewModel: PluginViewModel = viewModel()) {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Text(text = “当前是宿主中的Composable页面”)
Button(onClick = { viewModel.loadPlugin(context) }) {
Text(text = “点击加载插件Classloader”)
}
val isLoadSuccess = viewModel.isPluginLoadSuccess.collectAsStateWithLifecycle()
Text(text = “插件Classloader是否加载成功:${isLoadSuccess.value}“)
if (isLoadSuccess.value) {
Button(onClick = { viewModel.mergeDex(context) }) {
Text(text = “点击合并插件Dex到宿主中”)
}
val isMergeDexSuccess = viewModel.isMergeDexSuccess.collectAsStateWithLifecycle()
Text(text = “合并插件Dex到宿主是否成功:${isMergeDexSuccess.value}“)
if (isMergeDexSuccess.value) {
Button(onClick = { viewModel.loadClass(PluginActivityClassName) }) {
Text(text = “点击加载插件中的 PluginActivity.Class”)
}
if (viewModel.pluginActivityClass != null) {
Text(text = “加载插件中的 PluginActivity.Class 的结果:n${viewModel.pluginActivityClass?.canonicalName}“)
val intent = Intent(context, viewModel.pluginActivityClass)
Button(onClick = {
context.startActivity(intent.apply { putExtra(“type”, “NewsList”) })
}) {
Text(text = “点击显示插件中的 NewsList 页面”)
}
Button(onClick = {
context.startActivity(intent.apply { putExtra(“type”, “NewsDetail”) })
}) {
Text(text = “点击显示插件中的 NewsDetail 页面”)
}
}
}
}
}
}
val content1 : (@Composable () -> Unit) = {
MyBox(Color.Red, “我是插件中的Composable组件1”)
}
val content2 : (@Composable () -> Unit) = {
MyBox(Color.Magenta, “我是插件中的Composable组件2”)
}
@Composable
fun MyBox(color: Color, text: String) {
Box(
modifier = Modifier.requiredSize(150.dp).background(color).padding(10.dp),
contentAlignment = Alignment.Center
) {
Text(text = text, color = Color.White, fontSize = 15.sp)
}
}
}
// …省略其它无关代码
val composeProxyClassName = “com.fly.compose.plugin.news.ComposeProxy”
var pluginComposable1 by mutableStateOf<@Composable () -> Unit>({})
var pluginComposable2 by mutableStateOf<@Composable () -> Unit>({})
var isLoadPluginComposablesSuccess by mutableStateOf(false)
fun loadPluginComposables() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val composeProxyClass = PluginManager.loadClass(composeProxyClassName)
composeProxyClass?.let { proxyClass ->
val getContent1Method: Method = proxyClass.getDeclaredMethod(“getContent1”)
val getContent2Method: Method = proxyClass.getDeclaredMethod(“getContent2”)
val obj = proxyClass.newInstance()
pluginComposable1 = getContent1Method.invoke(obj) as (@Composable () -> Unit)
pluginComposable2 = getContent2Method.invoke(obj) as (@Composable () -> Unit)
isLoadPluginComposablesSuccess = true
}
}
}
}
}
fun HostScreen(viewModel: PluginViewModel = viewModel()) {
CommonLayout(viewModel) {
Button(onClick = { viewModel.loadPluginComposables() }) {
Text(text = “点击加载插件中的 Composables”)
}
// 加载成功后调用插件中的Composable函数
if (viewModel.isLoadPluginComposablesSuccess) {
viewModel.pluginComposable1()
viewModel.pluginComposable2()
}
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun CommonLayout(
viewModel: PluginViewModel = viewModel(),
content: @Composable () -> Unit
) {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Text(text = “当前是宿主中的Composable页面”)
Button(onClick = { viewModel.loadPlugin(context) }) {
Text(text = “点击加载插件Classloader”)
}
val isLoadSuccess = viewModel.isPluginLoadSuccess.collectAsStateWithLifecycle()
Text(text = “插件Classloader是否加载成功:${isLoadSuccess.value}“)
if (isLoadSuccess.value) {
Button(onClick = { viewModel.mergeDex(context) }) {
Text(text = “点击合并插件Dex到宿主中”)
}
val isMergeDexSuccess = viewModel.isMergeDexSuccess.collectAsStateWithLifecycle()
Text(text = “合并插件Dex到宿主是否成功:${isMergeDexSuccess.value}“)
if (isMergeDexSuccess.value) {
content()
}
}
}
}
fun SomeComposable() {
AndroidView(factory = { context ->
// android.webkit.WebView
WebView(context).apply {
settings.javaScriptEnabled = true
webViewClient = WebViewClient()
loadUrl(“https://xxxx.com”)
}
}, modifier = Modifier.fillMaxSize())
}
setContent {
ComposableExample()
}
}
fun SomeComposable() {
AndroidView(factory = { context ->
ComposeView(context).apply {
setContent {
ComposableExample()
}
}
}, modifier = Modifier.fillMaxSize())
}
val pluginView: (Context.(String) -> ComposeView) = { name ->
ComposeView(this).apply {
setContent {
if (name == “content1”) {
MyBox(Color.Red, “我是插件中的Composable组件1”)
} else if (name == “content2”) {
MyBox(Color.Magenta, “我是插件中的Composable组件2”)
}
}
}
}
@Composable
fun MyBox(color: Color, text: String) {
Box(
modifier = Modifier.requiredSize(150.dp).background(color).padding(10.dp),
contentAlignment = Alignment.Center
) {
Text(text = text, color = Color.White, fontSize = 15.sp)
}
}
}
// …省略其它无关代码
val composeViewProxyClassName = “com.fly.compose.plugin.news.ComposeViewProxy”
var pluginView by mutableStateOf ComposeView>({ComposeView(this)})
var isLoadPluginViewSuccess by mutableStateOf(false)
fun loadPluginView() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val composeViewProxyClass = PluginManager.loadClass(composeViewProxyClassName)
composeViewProxyClass?.let { proxyClass ->
val getPluginViewMethod: Method = proxyClass.getDeclaredMethod(“getPluginView”)
val obj = proxyClass.newInstance()
pluginView = getPluginViewMethod.invoke(obj) as (Context.(String) -> ComposeView)
isLoadPluginViewSuccess = true
}
}
}
}
}
fun HostScreen(viewModel: PluginViewModel = viewModel()) {
CommonLayout(viewModel) {
Button(onClick = { viewModel.loadPluginView() }) {
Text(text = “点击加载插件中的 ComposeView”)
}
// 加载成功后调用插件中的ComposeView
if (viewModel.isLoadPluginViewSuccess) {
SimpleAndroidView { context ->
viewModel.pluginView(context, “content1”)
}
SimpleAndroidView { context ->
viewModel.pluginView(context, “content2”)
}
}
}
}
@Composable
private fun SimpleAndroidView(factory: (Context) -> T) {
AndroidView(
factory = { context -> factory(context) },
modifier = Modifier.wrapContentSize()
)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun CommonLayout(
viewModel: PluginViewModel = viewModel(),
content: @Composable () -> Unit
) {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Text(text = “当前是宿主中的Composable页面”)
Button(onClick = { viewModel.loadPlugin(context) }) {
Text(text = “点击加载插件Classloader”)
}
val isLoadSuccess = viewModel.isPluginLoadSuccess.collectAsStateWithLifecycle()
Text(text = “插件Classloader是否加载成功:${isLoadSuccess.value}“)
if (isLoadSuccess.value) {
Button(onClick = { viewModel.mergeDex(context) }) {
Text(text = “点击合并插件Dex到宿主中”)
}
val isMergeDexSuccess = viewModel.isMergeDexSuccess.collectAsStateWithLifecycle()
Text(text = “合并插件Dex到宿主是否成功:${isMergeDexSuccess.value}“)
if (isMergeDexSuccess.value) {
content()
}
}
}
}
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=15280,转载请注明出处。
评论0