Skip to content

Commit

Permalink
perf(core): 去除加载插件对getPackageArchiveInfo的依赖
Browse files Browse the repository at this point in the history
由于PackageManager.getPackageArchiveInfo方法在解析较大的Manifest时速度很慢,
而且加载插件过程中只依赖很少量的字段,如className,theme等,所以在主路径不再依赖它,
而是依赖编译期生成好的PluginManifest类。

另一原因是getPackageArchiveInfo的返回类型没有公开API告诉我们Receiver的action字段。

在插件代码主动通过PackageManager获取信息时,再依赖getPackageArchiveInfo方法
获取MetaData等插件框架本身不需要的信息。

close Tencent#696
  • Loading branch information
shifujun committed Dec 29, 2021
1 parent de3be10 commit 301588f
Show file tree
Hide file tree
Showing 21 changed files with 344 additions and 678 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package com.tencent.shadow.core.loader

import android.content.Context
import android.content.pm.PackageInfo
import android.os.Handler
import android.os.Looper
import android.os.Parcel
Expand Down Expand Up @@ -69,11 +68,6 @@ abstract class ShadowPluginLoader(hostAppContext: Context) : DelegateProvider, D
*/
abstract fun getComponentManager():ComponentManager

/**
* @GuardedBy("mLock")
*/
private val mPluginPackageInfoSet: MutableSet<PackageInfo> = hashSetOf()

private lateinit var mPluginServiceManager: PluginServiceManager

private val mPluginContentProviderManager: PluginContentProviderManager = PluginContentProviderManager()
Expand Down Expand Up @@ -158,8 +152,6 @@ abstract class ShadowPluginLoader(hostAppContext: Context) : DelegateProvider, D

return LoadPluginBloc.loadPlugin(
mExecutorService,
mPluginPackageInfoSet,
::allPluginPackageInfo,
mComponentManager,
mLock,
mPluginPartsMap,
Expand All @@ -168,12 +160,6 @@ abstract class ShadowPluginLoader(hostAppContext: Context) : DelegateProvider, D
loadParameters)
}

private fun allPluginPackageInfo(): Array<PackageInfo> {
mLock.withLock {
return mPluginPackageInfoSet.toTypedArray()
}
}

override fun getHostActivityDelegate(aClass: Class<out HostActivityDelegator>): HostActivityDelegate {
return if (HostNativeActivityDelegator::class.java.isAssignableFrom(aClass)) {
ShadowNativeActivityDelegate(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,16 @@
package com.tencent.shadow.core.loader.blocs

import android.content.Context
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.*
import com.tencent.shadow.core.runtime.PluginManifest

/**
* 解析插件apk逻辑
*
* @author cubershi
*/
object ParsePluginApkBloc {
/**
* 解析插件apk
*
* @param pluginFile 插件apk文件
* @return 解析信息
* @throws ParsePluginApkException 解析失败时抛出
*/
object CheckPackageNameBloc {
@Throws(ParsePluginApkException::class)
fun parse(
packageArchiveInfo: PackageInfo,
manifestInfo: ManifestInfo,
loadParameters: LoadParameters,
hostAppContext: Context
): PluginInfo {
if (packageArchiveInfo.applicationInfo.packageName != hostAppContext.packageName) {
fun check(
pluginManifest: PluginManifest,
hostAppContext: Context
) {
if (pluginManifest.applicationPackageName != hostAppContext.packageName) {
/*
要求插件和宿主包名一致有两方面原因:
1.正常的构建过程中,aapt会将包名写入到arsc文件中。插件正常安装运行时,如果以
Expand All @@ -63,41 +46,7 @@ object ParsePluginApkBloc {
我们也可以始终认为Shadow App是宿主的扩展代码,使用是宿主的一部分,那么采用宿主的包名就是理所应当的了。
*/
throw ParsePluginApkException("插件和宿主包名不一致。宿主:${hostAppContext.packageName} 插件:${packageArchiveInfo.applicationInfo.packageName}")
}

/*
partKey的作用是用来区分一个Component是来自于哪个插件apk的
*/
val partKey = loadParameters.partKey

val pluginInfo = PluginInfo(
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))
throw ParsePluginApkException("插件和宿主包名不一致。宿主:${hostAppContext.packageName} 插件:${pluginManifest.applicationPackageName}")
}
return pluginInfo
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ package com.tencent.shadow.core.loader.blocs
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.res.Resources
import com.tencent.shadow.core.load_parameters.LoadParameters
import com.tencent.shadow.core.loader.classloaders.PluginClassLoader
import com.tencent.shadow.core.loader.exceptions.CreateApplicationException
import com.tencent.shadow.core.loader.infos.PluginInfo
import com.tencent.shadow.core.loader.managers.ComponentManager
import com.tencent.shadow.core.runtime.PluginManifest
import com.tencent.shadow.core.runtime.ShadowAppComponentFactory
import com.tencent.shadow.core.runtime.ShadowApplication

Expand All @@ -37,33 +38,32 @@ object CreateApplicationBloc {
@Throws(CreateApplicationException::class)
fun createShadowApplication(
pluginClassLoader: PluginClassLoader,
pluginInfo: PluginInfo,
loadParameters: LoadParameters,
pluginManifest: PluginManifest,
resources: Resources,
hostAppContext: Context,
componentManager: ComponentManager,
applicationInfo: ApplicationInfo,
pluginApplicationInfo: ApplicationInfo,
appComponentFactory: ShadowAppComponentFactory
): ShadowApplication {
try {
val appClassName = pluginInfo.applicationClassName
?: ShadowApplication::class.java.name
val appClassName = pluginManifest.applicationClassName
?: ShadowApplication::class.java.name
val shadowApplication =
appComponentFactory.instantiateApplication(pluginClassLoader, appClassName)
val partKey = pluginInfo.partKey
appComponentFactory.instantiateApplication(pluginClassLoader, appClassName)
val partKey = loadParameters.partKey
shadowApplication.setPluginResources(resources)
shadowApplication.setPluginClassLoader(pluginClassLoader)
shadowApplication.setPluginComponentLauncher(componentManager)
shadowApplication.setBroadcasts(pluginInfo.mReceivers.map { receiveInfo ->
receiveInfo.className!! to receiveInfo.actions
}.toMap())
shadowApplication.setBroadcasts(pluginManifest.receivers)
shadowApplication.setAppComponentFactory(appComponentFactory)
shadowApplication.applicationInfo = applicationInfo
shadowApplication.setBusinessName(pluginInfo.businessName)
shadowApplication.applicationInfo = pluginApplicationInfo
shadowApplication.setBusinessName(loadParameters.businessName)
shadowApplication.setPluginPartKey(partKey)

//和ShadowActivityDelegate.initPluginActivity一样,attachBaseContext放到最后
shadowApplication.setHostApplicationContextAsBase(hostAppContext)
shadowApplication.setTheme(applicationInfo.theme)
shadowApplication.setTheme(pluginManifest.applicationTheme)
return shadowApplication
} catch (e: Exception) {
throw CreateApplicationException(e)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.tencent.shadow.core.loader.blocs

import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Build
import com.tencent.shadow.core.common.InstalledApk
import com.tencent.shadow.core.load_parameters.LoadParameters
import com.tencent.shadow.core.runtime.PluginManifest
import com.tencent.shadow.core.runtime.ShadowContext
import java.io.File

object CreatePluginApplicationInfoBloc {
fun create(installedApk: InstalledApk,
loadParameters: LoadParameters,
pluginManifest: PluginManifest,
hostAppContext: Context): ApplicationInfo {
val result = ApplicationInfo(hostAppContext.applicationInfo)
result.sourceDir = installedApk.apkFilePath
result.nativeLibraryDir = installedApk.libraryPath
result.dataDir = makeDataDir(loadParameters, hostAppContext).absolutePath

result.packageName = pluginManifest.applicationPackageName
result.className = pluginManifest.applicationClassName
result.theme = pluginManifest.applicationTheme
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
result.appComponentFactory = pluginManifest.appComponentFactory
}
return result
}

fun makeDataDir(loadParameters: LoadParameters, hostAppContext: Context): File {
val tempContext = ShadowContext(hostAppContext, 0).apply {
setBusinessName(loadParameters.businessName)
}
val dataDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tempContext.dataDir
} else {
File(tempContext.filesDir, "dataDir")
}
dataDir.mkdirs()
return dataDir
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
package com.tencent.shadow.core.loader.blocs

import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Resources
import android.os.Handler
Expand All @@ -28,7 +28,7 @@ import android.webkit.WebView
import java.util.concurrent.CountDownLatch

object CreateResourceBloc {
fun create(packageArchiveInfo: PackageInfo, archiveFilePath: String, hostAppContext: Context): Resources {
fun create(archiveFilePath: String, hostAppContext: Context): Resources {
//先用宿主context初始化一个WebView,以便WebView的逻辑去修改sharedLibraryFiles,将webview.apk添加进去
val latch = CountDownLatch(1)
Handler(Looper.getMainLooper()).post {
Expand All @@ -38,11 +38,14 @@ object CreateResourceBloc {
latch.await()

val packageManager = hostAppContext.packageManager
packageArchiveInfo.applicationInfo.publicSourceDir = archiveFilePath
packageArchiveInfo.applicationInfo.sourceDir = archiveFilePath
packageArchiveInfo.applicationInfo.sharedLibraryFiles = hostAppContext.applicationInfo.sharedLibraryFiles
val applicationInfo = ApplicationInfo()
applicationInfo.packageName = hostAppContext.applicationInfo.packageName
applicationInfo.uid = hostAppContext.applicationInfo.uid
applicationInfo.publicSourceDir = archiveFilePath
applicationInfo.sourceDir = archiveFilePath
applicationInfo.sharedLibraryFiles = hostAppContext.applicationInfo.sharedLibraryFiles
try {
return packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo)
return packageManager.getResourcesForApplication(applicationInfo)
} catch (e: PackageManager.NameNotFoundException) {
throw RuntimeException(e)
}
Expand Down
Loading

0 comments on commit 301588f

Please sign in to comment.