Skip to content

Commit

Permalink
feat: 从AndroidManifest中解析Receiver的action信息
Browse files Browse the repository at this point in the history
ComponentManager子类不再需要手动配置静态广播。
  • Loading branch information
xuedizi authored and shifujun committed Nov 16, 2021
1 parent 352561d commit d27e984
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@

import com.tencent.shadow.core.loader.infos.ContainerProviderInfo;
import com.tencent.shadow.core.loader.managers.ComponentManager;
import com.tencent.shadow.sample.constant.Constant;

import java.util.ArrayList;
import java.util.List;

public class SampleComponentManager extends ComponentManager {

Expand Down Expand Up @@ -70,18 +66,4 @@ public ContainerProviderInfo onBindContainerContentProvider(ComponentName plugin
context.getPackageName() + ".contentprovider.authority.dynamic");
}

@Override
public List<BroadcastInfo> getBroadcastInfoList(String partKey) {
List<ComponentManager.BroadcastInfo> broadcastInfos = new ArrayList<>();
if (partKey.equals(Constant.PART_KEY_PLUGIN_MAIN_APP)) {
broadcastInfos.add(
new ComponentManager.BroadcastInfo(
"com.tencent.shadow.sample.plugin.app.lib.usecases.receiver.MyReceiver",
new String[]{"com.tencent.test.action"}
)
);
}
return broadcastInfos;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,16 @@ object CreateApplicationBloc {
): ShadowApplication {
try {
val appClassName = pluginInfo.applicationClassName
?: ShadowApplication::class.java.name
val shadowApplication = appComponentFactory.instantiateApplication(pluginClassLoader, appClassName)
?: ShadowApplication::class.java.name
val shadowApplication =
appComponentFactory.instantiateApplication(pluginClassLoader, appClassName)
val partKey = pluginInfo.partKey
shadowApplication.setPluginResources(resources)
shadowApplication.setPluginClassLoader(pluginClassLoader)
shadowApplication.setPluginComponentLauncher(componentManager)
shadowApplication.setBroadcasts(componentManager.getBroadcastsByPartKey(partKey))
shadowApplication.setBroadcasts(pluginInfo.mReceivers.map { receiveInfo ->
receiveInfo.className!! to receiveInfo.actions
}.toMap())
shadowApplication.setAppComponentFactory(appComponentFactory)
shadowApplication.applicationInfo = applicationInfo
shadowApplication.setBusinessName(pluginInfo.businessName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ object LoadPluginBloc {
or PackageManager.GET_META_DATA
or PackageManager.GET_SERVICES
or PackageManager.GET_PROVIDERS
or PackageManager.GET_RECEIVERS
or PackageManager.GET_SIGNATURES
)
?: throw NullPointerException("getPackageArchiveInfo return null.archiveFilePath==$archiveFilePath")
Expand All @@ -87,16 +88,22 @@ object LoadPluginBloc {

packageArchiveInfo.applicationInfo.nativeLibraryDir = installedApk.libraryPath
packageArchiveInfo.applicationInfo.dataDir = dataDir.absolutePath
packageArchiveInfo.applicationInfo.processName = hostAppContext.applicationInfo.processName
packageArchiveInfo.applicationInfo.processName =
hostAppContext.applicationInfo.processName
packageArchiveInfo.applicationInfo.uid = hostAppContext.applicationInfo.uid

lock.withLock { pluginPackageInfoSet.add(packageArchiveInfo) }
packageArchiveInfo
})

val buildManifestInfo = executorService.submit(Callable {
ParseManifestBloc.parse(hostAppContext, installedApk)
})

val buildPluginInfo = executorService.submit(Callable {
val packageInfo = getPackageInfo.get()
ParsePluginApkBloc.parse(packageInfo, loadParameters, hostAppContext)
val manifestInfo = buildManifestInfo.get()
ParsePluginApkBloc.parse(packageInfo, manifestInfo, loadParameters, hostAppContext)
})

val buildPackageManager = executorService.submit(Callable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.tencent.shadow.core.loader.blocs

import android.content.Context
import android.content.IntentFilter
import android.content.res.Resources
import com.tencent.shadow.core.common.InstalledApk
import com.tencent.shadow.core.loader.infos.ManifestInfo
import com.tencent.shadow.core.loader.infos.ManifestInfo.Receiver
import com.tencent.shadow.core.loader.infos.ManifestInfo.ReceiverIntentInfo
import org.xmlpull.v1.XmlPullParser

/**
* 解析插件的AndroidManifest.xml文件
* 由于系统开放接口不提供广播的action信息,此处采用手动解析的方式处理,减少插件化的适配工作
* 后续对于AndroidManifest.xml的处理可在此基础上扩展
*
* @author xuedizi2009@163.com
*/
object ParseManifestBloc {

private const val ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android"
private const val TAG_RECEIVER = "receiver"
private const val TAG_INTENT_FILTER = "intent-filter"
private const val TAG_ACTION = "action"
private const val ATTR_NAME = "name"

@Throws(Exception::class)
fun parse(context: Context, installedApk: InstalledApk): ManifestInfo = ManifestInfo().apply {
val resources: Resources = newResource(context, installedApk)
val parser = resources.assets.openXmlResourceParser("AndroidManifest.xml")
val outerDepth = parser.depth
var type: Int
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.depth > outerDepth)
) {
if (type == XmlPullParser.START_TAG) {
parseBroadcastReceiver(parser, this)
}
}
}

/**
* 此处如果使用[LoadPluginBloc.loadPlugin]构建的resources,将包含WebView的resources,故需要重新创建
*/
private fun newResource(context: Context, installedApk: InstalledApk): Resources {
val packageArchiveInfo =
context.packageManager.getPackageArchiveInfo(installedApk.apkFilePath, 0)!!
val packageManager = context.packageManager
packageArchiveInfo.applicationInfo?.publicSourceDir = installedApk.apkFilePath
packageArchiveInfo.applicationInfo?.sourceDir = installedApk.apkFilePath
return packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo)
}

private fun parseBroadcastReceiver(parser: XmlPullParser, manifestInfo: ManifestInfo) {
if (TAG_RECEIVER == parser.name) {
val receiver = Receiver(parser.getAttributeValue(ANDROID_RESOURCES, ATTR_NAME))
val outerDepth = parser.depth
var type: Int
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.depth > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue
}
if (TAG_INTENT_FILTER == parser.name) {
val receiverInfo = ReceiverIntentInfo()
parserIntent(parser, receiverInfo)
receiver.intents.add(receiverInfo)
}
}
manifestInfo.receivers.add(receiver)
}
}

private fun parserIntent(parser: XmlPullParser, intentFilter: IntentFilter) {
val outerDepth = parser.depth
var type: Int
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.depth > outerDepth)
) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue
}
if (TAG_ACTION == parser.name) {
val value = parser.getAttributeValue(ANDROID_RESOURCES, ATTR_NAME)
intentFilter.addAction(value)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ import android.content.pm.PackageInfo
import android.os.Build
import com.tencent.shadow.core.load_parameters.LoadParameters
import com.tencent.shadow.core.loader.exceptions.ParsePluginApkException
import com.tencent.shadow.core.loader.infos.PluginActivityInfo
import com.tencent.shadow.core.loader.infos.PluginInfo
import com.tencent.shadow.core.loader.infos.PluginProviderInfo
import com.tencent.shadow.core.loader.infos.PluginServiceInfo
import com.tencent.shadow.core.loader.infos.*

/**
* 解析插件apk逻辑
Expand All @@ -42,7 +39,12 @@ object ParsePluginApkBloc {
* @throws ParsePluginApkException 解析失败时抛出
*/
@Throws(ParsePluginApkException::class)
fun parse(packageArchiveInfo: PackageInfo, loadParameters: LoadParameters, hostAppContext: Context): PluginInfo {
fun parse(
packageArchiveInfo: PackageInfo,
manifestInfo: ManifestInfo,
loadParameters: LoadParameters,
hostAppContext: Context
): PluginInfo {
if (packageArchiveInfo.applicationInfo.packageName != hostAppContext.packageName) {
/*
要求插件和宿主包名一致有两方面原因:
Expand Down Expand Up @@ -70,17 +72,28 @@ object ParsePluginApkBloc {
val partKey = loadParameters.partKey

val pluginInfo = PluginInfo(
loadParameters.businessName
, partKey
, packageArchiveInfo.applicationInfo.packageName
, packageArchiveInfo.applicationInfo.className
loadParameters.businessName,
partKey,
packageArchiveInfo.applicationInfo.packageName,
packageArchiveInfo.applicationInfo.className
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
pluginInfo.appComponentFactory = packageArchiveInfo.applicationInfo.appComponentFactory
}
packageArchiveInfo.activities?.forEach {
pluginInfo.putActivityInfo(PluginActivityInfo(it.name, it.themeResource, it))
}

val receiveMap = manifestInfo.receivers.map { it.name to it }.toMap()
packageArchiveInfo.receivers?.forEach {
pluginInfo.putReceiverInfo(
PluginReceiverInfo(
it.name,
it,
receiveMap[it.name]?.actions()
)
)
}
packageArchiveInfo.services?.forEach { pluginInfo.putServiceInfo(PluginServiceInfo(it.name)) }
packageArchiveInfo.providers?.forEach {
pluginInfo.putPluginProviderInfo(PluginProviderInfo(it.name, it.authority, it))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.tencent.shadow.core.loader.infos

import android.content.IntentFilter

/**
* 插件AndroidManifest.xml信息存储类
*
* @author xuedizi2009@163.com
*/
class ManifestInfo {
val receivers = mutableListOf<Receiver>()

class Receiver(val name: String) {
var intents = mutableListOf<ReceiverIntentInfo>()

fun actions(): MutableList<String> {
val actions = mutableListOf<String>()
this.intents.forEach { intentInfo ->
intentInfo.actionsIterator().forEach { action ->
actions.add(action)
}
}
return actions
}
}

class ReceiverIntentInfo : IntentFilter()
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ class PluginInfo(
private val _mActivities: MutableSet<PluginActivityInfo> = HashSet()
private val _mServices: MutableSet<PluginServiceInfo> = HashSet()
private val _mProviders: MutableSet<PluginProviderInfo> = HashSet()
private val _mReceivers: MutableSet<PluginReceiverInfo> = HashSet()
internal val mActivities: Set<PluginActivityInfo>
get() = _mActivities
internal val mServices: Set<PluginServiceInfo>
get() = _mServices
internal val mProviders: Set<PluginProviderInfo>
get() = _mProviders
internal val mReceivers: Set<PluginReceiverInfo>
get() = _mReceivers

internal var appComponentFactory: String? = null

Expand All @@ -47,4 +50,8 @@ class PluginInfo(
fun putPluginProviderInfo(pluginProviderInfo: PluginProviderInfo) {
_mProviders.add(pluginProviderInfo)
}

fun putReceiverInfo(pluginReceiverInfo: PluginReceiverInfo) {
_mReceivers.add(pluginReceiverInfo)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.tencent.shadow.core.loader.infos

import android.content.pm.ActivityInfo
import android.os.Parcel
import android.os.Parcelable
import android.os.Parcelable.Creator

/**
* 插件广播数据
* @author xuedizi2009@163.com
*/
class PluginReceiverInfo(
className: String?,
private val activityInfo: ActivityInfo?,
val actions: List<String>?
) : Parcelable, PluginComponentInfo(className) {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readParcelable(ActivityInfo::class.java.classLoader),
parcel.createStringArrayList()
)

override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(className)
parcel.writeParcelable(activityInfo, flags)
parcel.writeStringList(actions)
}

override fun describeContents(): Int {
return 0
}

companion object CREATOR : Creator<PluginReceiverInfo> {
override fun createFromParcel(parcel: Parcel): PluginReceiverInfo {
return PluginReceiverInfo(parcel)
}

override fun newArray(size: Int): Array<PluginReceiverInfo?> {
return arrayOfNulls(size)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ abstract class ComponentManager : PluginComponentLauncher {

abstract fun onBindContainerContentProvider(pluginContentProvider: ComponentName): ContainerProviderInfo

abstract fun getBroadcastInfoList(partKey: String): List<BroadcastInfo>?

override fun startActivity(shadowContext: ShadowContext, pluginIntent: Intent, option: Bundle?): Boolean {
return if (pluginIntent.isPluginComponent()) {
shadowContext.superStartActivity(pluginIntent.toActivityContainerIntent(), option)
Expand Down Expand Up @@ -159,8 +157,6 @@ abstract class ComponentManager : PluginComponentLauncher {
private val pluginComponentInfoMap: MutableMap<ComponentName, PluginComponentInfo> = hashMapOf()


private var application2broadcastInfo: MutableMap<String, MutableMap<String, List<String>>> = HashMap()

fun addPluginApkInfo(pluginInfo: PluginInfo) {
fun common(pluginComponentInfo: PluginComponentInfo,componentName:ComponentName) {
packageNameMap[pluginComponentInfo.className!!] = pluginInfo.packageName
Expand All @@ -179,12 +175,21 @@ abstract class ComponentManager : PluginComponentLauncher {

pluginInfo.mServices.forEach {
val componentName = ComponentName(pluginInfo.packageName, it.className!!)
common(it,componentName)
common(it, componentName)
}

pluginInfo.mProviders.forEach {
val componentName = ComponentName(pluginInfo.packageName, it.className!!)
mPluginContentProviderManager!!.addContentProviderInfo(pluginInfo.partKey,it,onBindContainerContentProvider(componentName))
mPluginContentProviderManager!!.addContentProviderInfo(
pluginInfo.partKey,
it,
onBindContainerContentProvider(componentName)
)
}

pluginInfo.mReceivers.forEach {
val componentName = ComponentName(pluginInfo.packageName, it.className!!)
common(it, componentName)
}
}

Expand Down Expand Up @@ -253,22 +258,4 @@ abstract class ComponentManager : PluginComponentLauncher {
containerIntent.putExtra(PROCESS_ID_KEY, DelegateProviderHolder.sCustomPid)
return containerIntent
}


class BroadcastInfo(val className: String, val actions: Array<String>)

fun getBroadcastsByPartKey(partKey: String): MutableMap<String, List<String>> {
if (application2broadcastInfo[partKey] == null) {
application2broadcastInfo[partKey] = HashMap()
val broadcastInfoList = getBroadcastInfoList(partKey)
if (broadcastInfoList != null) {
for (broadcastInfo in broadcastInfoList) {
application2broadcastInfo[partKey]!![broadcastInfo.className] =
broadcastInfo.actions.toList()
}
}
}
return application2broadcastInfo[partKey]!!
}

}
}
Loading

0 comments on commit d27e984

Please sign in to comment.