Skip to content

Commit d27e984

Browse files
xuedizishifujun
authored andcommitted
feat: 从AndroidManifest中解析Receiver的action信息
ComponentManager子类不再需要手动配置静态广播。
1 parent 352561d commit d27e984

File tree

11 files changed

+216
-85
lines changed

11 files changed

+216
-85
lines changed

projects/sample/source/sample-plugin/sample-loader/src/main/java/com/tencent/shadow/sample/plugin/loader/SampleComponentManager.java

-18
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@
2323

2424
import com.tencent.shadow.core.loader.infos.ContainerProviderInfo;
2525
import com.tencent.shadow.core.loader.managers.ComponentManager;
26-
import com.tencent.shadow.sample.constant.Constant;
27-
28-
import java.util.ArrayList;
29-
import java.util.List;
3026

3127
public class SampleComponentManager extends ComponentManager {
3228

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

73-
@Override
74-
public List<BroadcastInfo> getBroadcastInfoList(String partKey) {
75-
List<ComponentManager.BroadcastInfo> broadcastInfos = new ArrayList<>();
76-
if (partKey.equals(Constant.PART_KEY_PLUGIN_MAIN_APP)) {
77-
broadcastInfos.add(
78-
new ComponentManager.BroadcastInfo(
79-
"com.tencent.shadow.sample.plugin.app.lib.usecases.receiver.MyReceiver",
80-
new String[]{"com.tencent.test.action"}
81-
)
82-
);
83-
}
84-
return broadcastInfos;
85-
}
86-
8769
}

projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/CreateApplicationBloc.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@ object CreateApplicationBloc {
4646
): ShadowApplication {
4747
try {
4848
val appClassName = pluginInfo.applicationClassName
49-
?: ShadowApplication::class.java.name
50-
val shadowApplication = appComponentFactory.instantiateApplication(pluginClassLoader, appClassName)
49+
?: ShadowApplication::class.java.name
50+
val shadowApplication =
51+
appComponentFactory.instantiateApplication(pluginClassLoader, appClassName)
5152
val partKey = pluginInfo.partKey
5253
shadowApplication.setPluginResources(resources)
5354
shadowApplication.setPluginClassLoader(pluginClassLoader)
5455
shadowApplication.setPluginComponentLauncher(componentManager)
55-
shadowApplication.setBroadcasts(componentManager.getBroadcastsByPartKey(partKey))
56+
shadowApplication.setBroadcasts(pluginInfo.mReceivers.map { receiveInfo ->
57+
receiveInfo.className!! to receiveInfo.actions
58+
}.toMap())
5659
shadowApplication.setAppComponentFactory(appComponentFactory)
5760
shadowApplication.applicationInfo = applicationInfo
5861
shadowApplication.setBusinessName(pluginInfo.businessName)

projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/LoadPluginBloc.kt

+9-2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ object LoadPluginBloc {
7171
or PackageManager.GET_META_DATA
7272
or PackageManager.GET_SERVICES
7373
or PackageManager.GET_PROVIDERS
74+
or PackageManager.GET_RECEIVERS
7475
or PackageManager.GET_SIGNATURES
7576
)
7677
?: throw NullPointerException("getPackageArchiveInfo return null.archiveFilePath==$archiveFilePath")
@@ -87,16 +88,22 @@ object LoadPluginBloc {
8788

8889
packageArchiveInfo.applicationInfo.nativeLibraryDir = installedApk.libraryPath
8990
packageArchiveInfo.applicationInfo.dataDir = dataDir.absolutePath
90-
packageArchiveInfo.applicationInfo.processName = hostAppContext.applicationInfo.processName
91+
packageArchiveInfo.applicationInfo.processName =
92+
hostAppContext.applicationInfo.processName
9193
packageArchiveInfo.applicationInfo.uid = hostAppContext.applicationInfo.uid
9294

9395
lock.withLock { pluginPackageInfoSet.add(packageArchiveInfo) }
9496
packageArchiveInfo
9597
})
9698

99+
val buildManifestInfo = executorService.submit(Callable {
100+
ParseManifestBloc.parse(hostAppContext, installedApk)
101+
})
102+
97103
val buildPluginInfo = executorService.submit(Callable {
98104
val packageInfo = getPackageInfo.get()
99-
ParsePluginApkBloc.parse(packageInfo, loadParameters, hostAppContext)
105+
val manifestInfo = buildManifestInfo.get()
106+
ParsePluginApkBloc.parse(packageInfo, manifestInfo, loadParameters, hostAppContext)
100107
})
101108

102109
val buildPackageManager = executorService.submit(Callable {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.tencent.shadow.core.loader.blocs
2+
3+
import android.content.Context
4+
import android.content.IntentFilter
5+
import android.content.res.Resources
6+
import com.tencent.shadow.core.common.InstalledApk
7+
import com.tencent.shadow.core.loader.infos.ManifestInfo
8+
import com.tencent.shadow.core.loader.infos.ManifestInfo.Receiver
9+
import com.tencent.shadow.core.loader.infos.ManifestInfo.ReceiverIntentInfo
10+
import org.xmlpull.v1.XmlPullParser
11+
12+
/**
13+
* 解析插件的AndroidManifest.xml文件
14+
* 由于系统开放接口不提供广播的action信息,此处采用手动解析的方式处理,减少插件化的适配工作
15+
* 后续对于AndroidManifest.xml的处理可在此基础上扩展
16+
*
17+
18+
*/
19+
object ParseManifestBloc {
20+
21+
private const val ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android"
22+
private const val TAG_RECEIVER = "receiver"
23+
private const val TAG_INTENT_FILTER = "intent-filter"
24+
private const val TAG_ACTION = "action"
25+
private const val ATTR_NAME = "name"
26+
27+
@Throws(Exception::class)
28+
fun parse(context: Context, installedApk: InstalledApk): ManifestInfo = ManifestInfo().apply {
29+
val resources: Resources = newResource(context, installedApk)
30+
val parser = resources.assets.openXmlResourceParser("AndroidManifest.xml")
31+
val outerDepth = parser.depth
32+
var type: Int
33+
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT
34+
&& (type != XmlPullParser.END_TAG || parser.depth > outerDepth)
35+
) {
36+
if (type == XmlPullParser.START_TAG) {
37+
parseBroadcastReceiver(parser, this)
38+
}
39+
}
40+
}
41+
42+
/**
43+
* 此处如果使用[LoadPluginBloc.loadPlugin]构建的resources,将包含WebView的resources,故需要重新创建
44+
*/
45+
private fun newResource(context: Context, installedApk: InstalledApk): Resources {
46+
val packageArchiveInfo =
47+
context.packageManager.getPackageArchiveInfo(installedApk.apkFilePath, 0)!!
48+
val packageManager = context.packageManager
49+
packageArchiveInfo.applicationInfo?.publicSourceDir = installedApk.apkFilePath
50+
packageArchiveInfo.applicationInfo?.sourceDir = installedApk.apkFilePath
51+
return packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo)
52+
}
53+
54+
private fun parseBroadcastReceiver(parser: XmlPullParser, manifestInfo: ManifestInfo) {
55+
if (TAG_RECEIVER == parser.name) {
56+
val receiver = Receiver(parser.getAttributeValue(ANDROID_RESOURCES, ATTR_NAME))
57+
val outerDepth = parser.depth
58+
var type: Int
59+
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT
60+
&& (type != XmlPullParser.END_TAG
61+
|| parser.depth > outerDepth)) {
62+
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
63+
continue
64+
}
65+
if (TAG_INTENT_FILTER == parser.name) {
66+
val receiverInfo = ReceiverIntentInfo()
67+
parserIntent(parser, receiverInfo)
68+
receiver.intents.add(receiverInfo)
69+
}
70+
}
71+
manifestInfo.receivers.add(receiver)
72+
}
73+
}
74+
75+
private fun parserIntent(parser: XmlPullParser, intentFilter: IntentFilter) {
76+
val outerDepth = parser.depth
77+
var type: Int
78+
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT
79+
&& (type != XmlPullParser.END_TAG || parser.depth > outerDepth)
80+
) {
81+
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
82+
continue
83+
}
84+
if (TAG_ACTION == parser.name) {
85+
val value = parser.getAttributeValue(ANDROID_RESOURCES, ATTR_NAME)
86+
intentFilter.addAction(value)
87+
}
88+
}
89+
}
90+
}

projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParsePluginApkBloc.kt

+22-9
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ import android.content.pm.PackageInfo
2323
import android.os.Build
2424
import com.tencent.shadow.core.load_parameters.LoadParameters
2525
import com.tencent.shadow.core.loader.exceptions.ParsePluginApkException
26-
import com.tencent.shadow.core.loader.infos.PluginActivityInfo
27-
import com.tencent.shadow.core.loader.infos.PluginInfo
28-
import com.tencent.shadow.core.loader.infos.PluginProviderInfo
29-
import com.tencent.shadow.core.loader.infos.PluginServiceInfo
26+
import com.tencent.shadow.core.loader.infos.*
3027

3128
/**
3229
* 解析插件apk逻辑
@@ -42,7 +39,12 @@ object ParsePluginApkBloc {
4239
* @throws ParsePluginApkException 解析失败时抛出
4340
*/
4441
@Throws(ParsePluginApkException::class)
45-
fun parse(packageArchiveInfo: PackageInfo, loadParameters: LoadParameters, hostAppContext: Context): PluginInfo {
42+
fun parse(
43+
packageArchiveInfo: PackageInfo,
44+
manifestInfo: ManifestInfo,
45+
loadParameters: LoadParameters,
46+
hostAppContext: Context
47+
): PluginInfo {
4648
if (packageArchiveInfo.applicationInfo.packageName != hostAppContext.packageName) {
4749
/*
4850
要求插件和宿主包名一致有两方面原因:
@@ -70,17 +72,28 @@ object ParsePluginApkBloc {
7072
val partKey = loadParameters.partKey
7173

7274
val pluginInfo = PluginInfo(
73-
loadParameters.businessName
74-
, partKey
75-
, packageArchiveInfo.applicationInfo.packageName
76-
, packageArchiveInfo.applicationInfo.className
75+
loadParameters.businessName,
76+
partKey,
77+
packageArchiveInfo.applicationInfo.packageName,
78+
packageArchiveInfo.applicationInfo.className
7779
)
7880
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
7981
pluginInfo.appComponentFactory = packageArchiveInfo.applicationInfo.appComponentFactory
8082
}
8183
packageArchiveInfo.activities?.forEach {
8284
pluginInfo.putActivityInfo(PluginActivityInfo(it.name, it.themeResource, it))
8385
}
86+
87+
val receiveMap = manifestInfo.receivers.map { it.name to it }.toMap()
88+
packageArchiveInfo.receivers?.forEach {
89+
pluginInfo.putReceiverInfo(
90+
PluginReceiverInfo(
91+
it.name,
92+
it,
93+
receiveMap[it.name]?.actions()
94+
)
95+
)
96+
}
8497
packageArchiveInfo.services?.forEach { pluginInfo.putServiceInfo(PluginServiceInfo(it.name)) }
8598
packageArchiveInfo.providers?.forEach {
8699
pluginInfo.putPluginProviderInfo(PluginProviderInfo(it.name, it.authority, it))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.tencent.shadow.core.loader.infos
2+
3+
import android.content.IntentFilter
4+
5+
/**
6+
* 插件AndroidManifest.xml信息存储类
7+
*
8+
9+
*/
10+
class ManifestInfo {
11+
val receivers = mutableListOf<Receiver>()
12+
13+
class Receiver(val name: String) {
14+
var intents = mutableListOf<ReceiverIntentInfo>()
15+
16+
fun actions(): MutableList<String> {
17+
val actions = mutableListOf<String>()
18+
this.intents.forEach { intentInfo ->
19+
intentInfo.actionsIterator().forEach { action ->
20+
actions.add(action)
21+
}
22+
}
23+
return actions
24+
}
25+
}
26+
27+
class ReceiverIntentInfo : IntentFilter()
28+
}

projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/infos/PluginInfo.kt

+7
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ class PluginInfo(
2727
private val _mActivities: MutableSet<PluginActivityInfo> = HashSet()
2828
private val _mServices: MutableSet<PluginServiceInfo> = HashSet()
2929
private val _mProviders: MutableSet<PluginProviderInfo> = HashSet()
30+
private val _mReceivers: MutableSet<PluginReceiverInfo> = HashSet()
3031
internal val mActivities: Set<PluginActivityInfo>
3132
get() = _mActivities
3233
internal val mServices: Set<PluginServiceInfo>
3334
get() = _mServices
3435
internal val mProviders: Set<PluginProviderInfo>
3536
get() = _mProviders
37+
internal val mReceivers: Set<PluginReceiverInfo>
38+
get() = _mReceivers
3639

3740
internal var appComponentFactory: String? = null
3841

@@ -47,4 +50,8 @@ class PluginInfo(
4750
fun putPluginProviderInfo(pluginProviderInfo: PluginProviderInfo) {
4851
_mProviders.add(pluginProviderInfo)
4952
}
53+
54+
fun putReceiverInfo(pluginReceiverInfo: PluginReceiverInfo) {
55+
_mReceivers.add(pluginReceiverInfo)
56+
}
5057
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.tencent.shadow.core.loader.infos
2+
3+
import android.content.pm.ActivityInfo
4+
import android.os.Parcel
5+
import android.os.Parcelable
6+
import android.os.Parcelable.Creator
7+
8+
/**
9+
* 插件广播数据
10+
11+
*/
12+
class PluginReceiverInfo(
13+
className: String?,
14+
private val activityInfo: ActivityInfo?,
15+
val actions: List<String>?
16+
) : Parcelable, PluginComponentInfo(className) {
17+
constructor(parcel: Parcel) : this(
18+
parcel.readString(),
19+
parcel.readParcelable(ActivityInfo::class.java.classLoader),
20+
parcel.createStringArrayList()
21+
)
22+
23+
override fun writeToParcel(parcel: Parcel, flags: Int) {
24+
parcel.writeString(className)
25+
parcel.writeParcelable(activityInfo, flags)
26+
parcel.writeStringList(actions)
27+
}
28+
29+
override fun describeContents(): Int {
30+
return 0
31+
}
32+
33+
companion object CREATOR : Creator<PluginReceiverInfo> {
34+
override fun createFromParcel(parcel: Parcel): PluginReceiverInfo {
35+
return PluginReceiverInfo(parcel)
36+
}
37+
38+
override fun newArray(size: Int): Array<PluginReceiverInfo?> {
39+
return arrayOfNulls(size)
40+
}
41+
}
42+
}

projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/managers/ComponentManager.kt

+12-25
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ abstract class ComponentManager : PluginComponentLauncher {
6060

6161
abstract fun onBindContainerContentProvider(pluginContentProvider: ComponentName): ContainerProviderInfo
6262

63-
abstract fun getBroadcastInfoList(partKey: String): List<BroadcastInfo>?
64-
6563
override fun startActivity(shadowContext: ShadowContext, pluginIntent: Intent, option: Bundle?): Boolean {
6664
return if (pluginIntent.isPluginComponent()) {
6765
shadowContext.superStartActivity(pluginIntent.toActivityContainerIntent(), option)
@@ -159,8 +157,6 @@ abstract class ComponentManager : PluginComponentLauncher {
159157
private val pluginComponentInfoMap: MutableMap<ComponentName, PluginComponentInfo> = hashMapOf()
160158

161159

162-
private var application2broadcastInfo: MutableMap<String, MutableMap<String, List<String>>> = HashMap()
163-
164160
fun addPluginApkInfo(pluginInfo: PluginInfo) {
165161
fun common(pluginComponentInfo: PluginComponentInfo,componentName:ComponentName) {
166162
packageNameMap[pluginComponentInfo.className!!] = pluginInfo.packageName
@@ -179,12 +175,21 @@ abstract class ComponentManager : PluginComponentLauncher {
179175

180176
pluginInfo.mServices.forEach {
181177
val componentName = ComponentName(pluginInfo.packageName, it.className!!)
182-
common(it,componentName)
178+
common(it, componentName)
183179
}
184180

185181
pluginInfo.mProviders.forEach {
186182
val componentName = ComponentName(pluginInfo.packageName, it.className!!)
187-
mPluginContentProviderManager!!.addContentProviderInfo(pluginInfo.partKey,it,onBindContainerContentProvider(componentName))
183+
mPluginContentProviderManager!!.addContentProviderInfo(
184+
pluginInfo.partKey,
185+
it,
186+
onBindContainerContentProvider(componentName)
187+
)
188+
}
189+
190+
pluginInfo.mReceivers.forEach {
191+
val componentName = ComponentName(pluginInfo.packageName, it.className!!)
192+
common(it, componentName)
188193
}
189194
}
190195

@@ -253,22 +258,4 @@ abstract class ComponentManager : PluginComponentLauncher {
253258
containerIntent.putExtra(PROCESS_ID_KEY, DelegateProviderHolder.sCustomPid)
254259
return containerIntent
255260
}
256-
257-
258-
class BroadcastInfo(val className: String, val actions: Array<String>)
259-
260-
fun getBroadcastsByPartKey(partKey: String): MutableMap<String, List<String>> {
261-
if (application2broadcastInfo[partKey] == null) {
262-
application2broadcastInfo[partKey] = HashMap()
263-
val broadcastInfoList = getBroadcastInfoList(partKey)
264-
if (broadcastInfoList != null) {
265-
for (broadcastInfo in broadcastInfoList) {
266-
application2broadcastInfo[partKey]!![broadcastInfo.className] =
267-
broadcastInfo.actions.toList()
268-
}
269-
}
270-
}
271-
return application2broadcastInfo[partKey]!!
272-
}
273-
274-
}
261+
}

0 commit comments

Comments
 (0)