Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions projects/sdk/core/gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation "com.android.tools.build:gradle:$build_gradle_version"
implementation 'com.googlecode.json-simple:json-simple:1.1'
implementation project(':transform')
implementation project(':manifest-parser')
testImplementation 'junit:junit:4.12'
testImplementation gradleTestKit()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@

package com.tencent.shadow.core.gradle

import com.android.build.gradle.AppExtension
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest
import com.tencent.shadow.core.gradle.extensions.PackagePluginExtension
import com.tencent.shadow.core.manifest_parser.generatePluginManifest
import com.tencent.shadow.core.transform.ShadowTransform
import com.tencent.shadow.core.transform_kit.AndroidClassPoolBuilder
import com.tencent.shadow.core.transform_kit.ClassPoolBuilder
import org.gradle.api.*
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.provider.Property
import java.io.File
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.jvm.isAccessible
Expand Down Expand Up @@ -61,6 +65,8 @@ class ShadowPlugin : Plugin<Project> {
initAndroidClassPoolBuilder(baseExtension, project)

createPackagePluginTasks(project)

createGeneratePluginManifestTasks(project)
}
}

Expand All @@ -83,6 +89,42 @@ class ShadowPlugin : Plugin<Project> {
}
}

private fun createGeneratePluginManifestTasks(project: Project) {
val appExtension: AppExtension = project.extensions.getByType(AppExtension::class.java)
appExtension.applicationVariants.filter { variant ->
variant.productFlavors.any { flavor ->
flavor.dimension == ShadowTransform.DimensionName &&
flavor.name == ShadowTransform.ApplyShadowTransformFlavorName
}
}.forEach { pluginVariant ->
val output = pluginVariant.outputs.first()
val processManifestTask = output.processManifestProvider.get()
val manifestFile = (processManifestTask as ProcessMultiApkApplicationManifest).mainMergedManifest.get().asFile
val variantName = manifestFile.parentFile.name
val outputDir = File(project.buildDir, "generated/source/pluginManifest/$variantName")

// 添加生成PluginManifest.java任务
val task = project.tasks.register("generate${variantName.capitalize()}PluginManifest") {
it.dependsOn(processManifestTask)
it.inputs.file(manifestFile)
it.outputs.dir(outputDir).withPropertyName("outputDir")
val packageForR = (project.tasks.getByName("processPluginDebugResources").property("namespace") as Property<String>).get()
it.doLast {
generatePluginManifest(manifestFile,
outputDir,
"com.tencent.shadow.core.manifest_parser",
packageForR)
}
}
project.tasks.getByName("compile${variantName.capitalize()}JavaWithJavac").dependsOn(task)

// 把PluginManifest.java添加为源码
appExtension.sourceSets.getByName(variantName).java {
srcDir(outputDir)
}
}
}

private fun addFlavorForTransform(baseExtension: BaseExtension) {
baseExtension.flavorDimensionList.add(ShadowTransform.DimensionName)
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class PackageMultiPluginTest {
.withProjectDir(ROOT_PROJECT_DIR)
.withPluginClasspath()
.withArguments(listOf(
"-xgeneratePluginDebugPluginManifest",
"-Pdisable_shadow_transform=true",
":plugin1:PackageMultiPlugin"
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class PackageOnlyPluginTest {
.withProjectDir(PLUGIN1_PROJECT_DIR)
.withPluginClasspath()
.withArguments(listOf(
"-xgeneratePluginDebugPluginManifest",
"-Pdisable_shadow_transform=true",
":plugin1:packageOnlyApkPlugin"
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class PackagePluginTaskTest {
.withProjectDir(ROOT_PROJECT_DIR)
.withPluginClasspath()
.withArguments(listOf(
"-xgeneratePluginDebugPluginManifest",
"-Pdisable_shadow_transform=true",
":plugin1:packageDebugPlugin"
))
Expand Down
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