From e6d592b081f2136fb40208ea54386dd15e8244d2 Mon Sep 17 00:00:00 2001 From: xiulin Date: Thu, 23 Nov 2017 20:59:02 +0800 Subject: [PATCH 01/66] support to NativeActivity --- .../main/java/com/didi/virtualapk/internal/LoadedPlugin.java | 2 ++ .../java/com/didi/virtualapk/internal/VAInstrumentation.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 88689f0..81085f6 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -162,6 +162,7 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags this.mPackageManager = new PluginPackageManager(); this.mPluginContext = new PluginContext(this); this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE); + this.mPackage.applicationInfo.nativeLibraryDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE).getAbsolutePath(); this.mResources = createResources(context, apk); this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader()); @@ -178,6 +179,7 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags // Cache activities Map activityInfos = new HashMap(); for (PackageParser.Activity activity : this.mPackage.activities) { + activity.info.metaData = activity.metaData; activityInfos.put(activity.getComponentName(), activity.info); } this.mActivityInfos = Collections.unmodifiableMap(activityInfos); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 8dc6892..2646f49 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -136,6 +136,9 @@ public void callActivityOnCreate(Activity activity, Bundle icicle) { if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { activity.setRequestedOrientation(activityInfo.screenOrientation); } + String targetClassName = PluginUtil.getTargetActivity(intent); + intent.setClassName(plugin.getPackageName(),targetClassName); + activity.setIntent(intent); } catch (Exception e) { e.printStackTrace(); } From 23d3cc354866c98e04f88daf25b8f1dc085c87fd Mon Sep 17 00:00:00 2001 From: xiulin Date: Fri, 24 Nov 2017 10:54:06 +0800 Subject: [PATCH 02/66] copy a new one --- .../com/didi/virtualapk/internal/VAInstrumentation.java | 6 ++++-- app/build.gradle | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 2646f49..86dc984 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -137,8 +137,10 @@ public void callActivityOnCreate(Activity activity, Bundle icicle) { activity.setRequestedOrientation(activityInfo.screenOrientation); } String targetClassName = PluginUtil.getTargetActivity(intent); - intent.setClassName(plugin.getPackageName(),targetClassName); - activity.setIntent(intent); + + Intent wrapperIntent = new Intent(intent); + wrapperIntent.setClassName(plugin.getPackageName(),targetClassName); + activity.setIntent(wrapperIntent); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/build.gradle b/app/build.gradle index 264c9de..73cc5b7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,8 +48,8 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - compile 'com.didi.virtualapk:core:0.9.1' + //compile 'com.didi.virtualapk:core:0.9.1' - //compile project (":CoreLibrary") + compile project (":CoreLibrary") } \ No newline at end of file From 438e5f51005fd682485317efd11fe9067545ce23 Mon Sep 17 00:00:00 2001 From: xiulin Date: Fri, 24 Nov 2017 10:57:02 +0800 Subject: [PATCH 03/66] use my lib path --- .../main/java/com/didi/virtualapk/internal/LoadedPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 81085f6..91dfeda 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -162,7 +162,7 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags this.mPackageManager = new PluginPackageManager(); this.mPluginContext = new PluginContext(this); this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE); - this.mPackage.applicationInfo.nativeLibraryDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE).getAbsolutePath(); + this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath(); this.mResources = createResources(context, apk); this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader()); From 763969c645673ddba536a3f2b8a117db8ebe9e3e Mon Sep 17 00:00:00 2001 From: xiulin Date: Fri, 22 Dec 2017 18:25:54 +0800 Subject: [PATCH 04/66] 1.add request permission 2.add support start activity from Fragment --- .../internal/VAInstrumentation.java | 77 +++++++++++++++++++ .../com/didi/virtualapk/MainActivity.java | 49 +++++++++++- build.gradle | 2 +- 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 8dc6892..6e5ffbb 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -17,6 +17,7 @@ package com.didi.virtualapk.internal; import android.app.Activity; +import android.app.Fragment; import android.app.Instrumentation; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -91,6 +92,82 @@ private ActivityResult realExecStartActivity( return result; } + public ActivityResult execStartActivity( + Context who, IBinder contextThread, IBinder token, Fragment target, + Intent intent, int requestCode, Bundle options) { + mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent); + // null component is an implicitly intent + if (intent.getComponent() != null) { + Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), + intent.getComponent().getClassName())); + // resolve intent with Stub Activity if needed + this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); + } + + ActivityResult result = realExecStartActivity(who, contextThread, token, target, + intent, requestCode, options); + + return result; + + } + + private ActivityResult realExecStartActivity( + Context who, IBinder contextThread, IBinder token, Fragment target, + Intent intent, int requestCode, Bundle options) { + ActivityResult result = null; + try { + Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Fragment.class, Intent.class, + int.class, Bundle.class}; + result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase, + "execStartActivity", parameterTypes, + who, contextThread, token, target, intent, requestCode, options); + } catch (Exception e) { + if (e.getCause() instanceof ActivityNotFoundException) { + throw (ActivityNotFoundException) e.getCause(); + } + e.printStackTrace(); + } + + return result; + } + + private ActivityResult realExecStartActivity( + Context who, IBinder contextThread, IBinder token, String target, + Intent intent, int requestCode, Bundle options) { + ActivityResult result = null; + try { + Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, String.class, Intent.class, + int.class, Bundle.class}; + result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase, + "execStartActivity", parameterTypes, + who, contextThread, token, target, intent, requestCode, options); + } catch (Exception e) { + if (e.getCause() instanceof ActivityNotFoundException) { + throw (ActivityNotFoundException) e.getCause(); + } + e.printStackTrace(); + } + + return result; + } + + public ActivityResult execStartActivity( + Context who, IBinder contextThread, IBinder token, String target, + Intent intent, int requestCode, Bundle options) { + mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent); + // null component is an implicitly intent + if (intent.getComponent() != null) { + Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), + intent.getComponent().getClassName())); + // resolve intent with Stub Activity if needed + this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); + } + + ActivityResult result = realExecStartActivity(who, contextThread, token, target, + intent, requestCode, options); + return null; + } + @Override public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try { diff --git a/app/src/main/java/com/didi/virtualapk/MainActivity.java b/app/src/main/java/com/didi/virtualapk/MainActivity.java index fe7aeca..c465ced 100644 --- a/app/src/main/java/com/didi/virtualapk/MainActivity.java +++ b/app/src/main/java/com/didi/virtualapk/MainActivity.java @@ -1,13 +1,16 @@ package com.didi.virtualapk; +import android.Manifest; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; @@ -22,6 +25,10 @@ public class MainActivity extends AppCompatActivity { + private static final int PERMISSION_REQUEST_CODE_STORAGE = 20171222; + + private static final String TAG = "MainActivity"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -35,10 +42,50 @@ protected void onCreate(Bundle savedInstanceState) { } textView.setText(cpuArch); Log.d("ryg", "onCreate cpu arch is "+ cpuArch); - this.loadPlugin(this); Log.d("ryg", "onCreate classloader is "+ getClassLoader()); + + if (hasPermission()) { + Log.d(TAG,"loadPlugin"); + + this.loadPlugin(this); + } else { + requestPermission(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (PERMISSION_REQUEST_CODE_STORAGE == requestCode) { + if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { + requestPermission(); + } else { + this.loadPlugin(this); + } + return; + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); } + + private boolean hasPermission() { + + Log.d(TAG,"hasPermission"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } + return true; + } + + private void requestPermission() { + + Log.d(TAG,"requestPermission"); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE_STORAGE); + } + } + + public void onButtonClick(View v) { if (v.getId() == R.id.button) { final String pkg = "com.didi.virtualapk.demo"; diff --git a/build.gradle b/build.gradle index 794b604..c2014cb 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ ext { VERSION_BUILD_TOOLS = '25.0.2' VERSION_MIN_SDK = 15 - VERSION_TARGET_SDK = 15 + VERSION_TARGET_SDK = 25 SOURCE_COMPATIBILITY = JavaVersion.VERSION_1_7 } From 8d0258224ff84bd085757a26a4273d85f498c797 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 31 Jan 2018 11:45:01 +0800 Subject: [PATCH 05/66] Supported com.android.tools.build:gradle:3.0.0. --- .gitignore | 1 + PluginDemo/app/build.gradle | 13 + PluginDemo/build.gradle | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- README.md | 4 +- build.gradle | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- virtualapk-gradle-plugin/build.gradle | 4 +- virtualapk-gradle-plugin/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../com.didi.virtualapk/VAHostPlugin.groovy | 23 +- .../collector/HostClassAndResCollector.groovy | 2 +- .../collector/HostJniLibsCollector.groovy | 2 +- .../collector/ResourceCollector.groovy | 2 +- .../dependence/AarDependenceInfo.groovy | 28 +- .../dependence/DependenceInfo.groovy | 6 +- .../dependence/JarDependenceInfo.groovy | 12 +- .../hooker/DxTaskHooker.groovy | 56 +- .../hooker/MergeAssetsHooker.groovy | 48 +- .../hooker/MergeJniLibsHooker.groovy | 9 +- .../hooker/MergeManifestsHooker.groovy | 106 ++- .../hooker/PrepareDependenciesHooker.groovy | 59 +- .../hooker/ProcessResourcesHooker.groovy | 38 +- .../hooker/ProguardHooker.groovy | 4 +- .../hooker/ShrinkResourcesHooker.groovy | 127 +++ .../hooker/TaskHookerManager.groovy | 9 +- .../tasks/AssemblePlugin.groovy | 26 +- .../StripClassAndResTransform.groovy | 21 +- .../transform/TransformWrapper.groovy | 107 +++ .../com.didi.virtualapk/utils/Reflect.java | 874 ++++++++++++++++++ 30 files changed, 1477 insertions(+), 124 deletions(-) create mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy create mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy create mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/Reflect.java diff --git a/.gitignore b/.gitignore index ea1103f..cb203ea 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ dependency-reduced-pom.xml buildNumber.properties local.properties +buildSrc diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 5ee7120..9c8b522 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -12,15 +12,27 @@ android { versionCode 1 } + flavorDimensions "demo" productFlavors { beijing { + dimension "demo" applicationId 'com.didi.virtualapk.demo' } shanghai { + dimension "demo" applicationId 'com.didi.virtualapk.demo' } } + signingConfigs { + release { + storeFile file("../../keystore/test.keystore") + storePassword "test123456" + keyAlias "test" + keyPassword "test123456" + } + } + buildTypes { debug { minifyEnabled false @@ -29,6 +41,7 @@ android { release { minifyEnabled true shrinkResources true + signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index ca18371..7406ebe 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -1,16 +1,18 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.didi.virtualapk:gradle:0.9.4' + classpath 'com.android.tools.build:gradle:3.0.0' + classpath 'com.didi.virtualapk:gradle:0.9.6-dev' } } allprojects { repositories { + google() jcenter() } } diff --git a/PluginDemo/gradle/wrapper/gradle-wrapper.properties b/PluginDemo/gradle/wrapper/gradle-wrapper.properties index 9a778d6..fc9b829 100644 --- a/PluginDemo/gradle/wrapper/gradle-wrapper.properties +++ b/PluginDemo/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/README.md b/README.md index 154e6e6..77c4a18 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.4' + classpath 'com.didi.virtualapk:gradle:0.9.6-dev' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.4' + classpath 'com.didi.virtualapk:gradle:0.9.6-dev' } ``` diff --git a/build.gradle b/build.gradle index c2014cb..d1c33ee 100644 --- a/build.gradle +++ b/build.gradle @@ -3,10 +3,11 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.didi.virtualapk:gradle:0.9.4' + classpath 'com.android.tools.build:gradle:3.0.0' + classpath 'com.didi.virtualapk:gradle:0.9.6-dev' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' @@ -27,6 +28,7 @@ allprojects { repositories { mavenCentral() jcenter() + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9ed5ec1..c6cd819 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/virtualapk-gradle-plugin/build.gradle b/virtualapk-gradle-plugin/build.gradle index bd5f28d..73a1376 100644 --- a/virtualapk-gradle-plugin/build.gradle +++ b/virtualapk-gradle-plugin/build.gradle @@ -1,6 +1,7 @@ buildscript { repositories { mavenLocal() + google() mavenCentral() jcenter() } @@ -20,6 +21,7 @@ tasks.withType(GroovyCompile) { repositories { mavenLocal() + google() mavenCentral() jcenter() } @@ -42,7 +44,7 @@ dependencies { compile 'commons-codec:commons-codec:1.6' compile 'org.ow2.asm:asm:4.0' compile 'org.javassist:javassist:3.18.2-GA' - compile 'com.android.tools.build:gradle:2.3.3' + compile 'com.android.tools.build:gradle:3.0.0' } diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 1d2f83e..42a61fc 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.4 +VERSION=0.9.6-dev diff --git a/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.properties index e3dfb79..1c500de 100644 --- a/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.properties +++ b/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy index d4dcaa4..d6c23d5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy @@ -1,12 +1,14 @@ package com.didi.virtualapk import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.pipeline.TransformTask import com.android.build.gradle.internal.transforms.ProGuardTransform -import com.android.build.gradle.tasks.ProcessAndroidResources; -import com.didi.virtualapk.utils.FileUtil; -import org.gradle.api.Plugin; -import org.gradle.api.Project; +import com.android.build.gradle.tasks.ProcessAndroidResources +import com.didi.virtualapk.utils.FileUtil +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration /** * VirtualAPK gradle plugin for host project, @@ -57,8 +59,13 @@ public class VAHostPlugin implements Plugin { project.configurations.each { String configName = it.name + if (!it.canBeResolved) { + deps.add("${configName} -> NOT READY") + return + } + it.resolvedConfiguration.resolvedArtifacts.each { - deps.add("${configName} -> ${it.moduleVersion.id}") + deps.add("${configName} -> id: ${it.moduleVersion.id}, type: ${it.type}, ext: ${it.extension}") } } Collections.sort(deps) @@ -67,7 +74,9 @@ public class VAHostPlugin implements Plugin { FileUtil.saveFile(vaHostDir, "versions", { List deps = new ArrayList() - project.configurations.getByName("_${applicationVariant.name}Compile").resolvedConfiguration.resolvedArtifacts.each { + Configuration compileClasspath = ((ApplicationVariantImpl) applicationVariant).variantData.variantDependency.compileClasspath + println "Used compileClasspath: ${compileClasspath.name}" + compileClasspath.resolvedConfiguration.resolvedArtifacts.each { deps.add("${it.moduleVersion.id} ${it.file.length()}") } Collections.sort(deps) @@ -86,7 +95,7 @@ public class VAHostPlugin implements Plugin { aaptTask.doLast { project.copy { - from new File(aaptTask.textSymbolOutputDir, 'R.txt') + from aaptTask.textSymbolOutputFile into vaHostDir rename { "Host_R.txt" } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostClassAndResCollector.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostClassAndResCollector.groovy index cb6e125..64237d4 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostClassAndResCollector.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostClassAndResCollector.groovy @@ -38,7 +38,7 @@ class HostClassAndResCollector { def flatToJarFiles(Collection stripDependencies, Collection jarFiles) { stripDependencies.each { jarFiles.add(it.jarFile) - if (it in AarDependenceInfo) { + if (it instanceof AarDependenceInfo) { it.localJars.each { jarFiles.add(it) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy index a1c08e8..3049ca7 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy @@ -41,7 +41,7 @@ class HostJniLibsCollector { */ def collect(Collection stripDependencies) { stripDependencies.each { - if (it.dependenceType == DependenceInfo.DependenceType.AAR) { + if (it instanceof AarDependenceInfo) { gatherListFromFolder(it as AarDependenceInfo, jniFileList) } else { gatherListFromJar(it as JarDependenceInfo, jniFileList) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy index 90c87f8..2e06f6d 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy @@ -59,7 +59,7 @@ class ResourceCollector { processResTask = par - allRSymbolFile = new File(par.textSymbolOutputDir, 'R.txt') + allRSymbolFile = par.textSymbolOutputFile hostRSymbolFile = virtualApk.hostSymbolFile } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy index 23ef2e3..9d302d3 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy @@ -1,6 +1,6 @@ package com.didi.virtualapk.collector.dependence -import com.android.builder.dependency.level2.AndroidDependency +import com.android.builder.model.AndroidLibrary import com.didi.virtualapk.collector.res.ResourceEntry import com.didi.virtualapk.collector.res.StyleableEntry import com.google.common.collect.ArrayListMultimap @@ -17,7 +17,7 @@ class AarDependenceInfo extends DependenceInfo { /** * Android library dependence in android build system, delegate of AarDependenceInfo */ - @Delegate AndroidDependency dependency + AndroidLibrary library /** * All resources(e.g. drawable, layout...) this library can access @@ -29,20 +29,32 @@ class AarDependenceInfo extends DependenceInfo { */ List aarStyleables = Lists.newArrayList() - AarDependenceInfo(String group, String artifact, String version, AndroidDependency dependency) { + AarDependenceInfo(String group, String artifact, String version, AndroidLibrary library) { super(group, artifact, version) - this.dependency = dependency + this.library = library } @Override File getJarFile() { - return dependency.jarFile + return library.jarFile } @Override DependenceType getDependenceType() { return DependenceType.AAR } + + File getAssetsFolder() { + return library.assetsFolder + } + + File getJniFolder() { + return library.jniFolder + } + + Collection getLocalJars() { + return library.localJars + } /** * Return collection of "resourceType:resourceName", parse from R symbol file @@ -52,7 +64,7 @@ class AarDependenceInfo extends DependenceInfo { def resKeys = [] as Set - def rSymbol = symbolFile + def rSymbol = library.symbolFile if (rSymbol.exists()) { rSymbol.eachLine { line -> if (!line.empty) { @@ -71,11 +83,11 @@ class AarDependenceInfo extends DependenceInfo { /** * Return the package name of this library, parse from manifest file - * manifest file are obtained by delegating to "dependency" + * manifest file are obtained by delegating to "library" * @return package name of this library */ public String getPackage() { - def xmlManifest = new XmlParser().parse(manifest) + def xmlManifest = new XmlParser().parse(library.manifest) return xmlManifest.@package } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/DependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/DependenceInfo.groovy index f35eb4e..be53ecc 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/DependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/DependenceInfo.groovy @@ -1,7 +1,7 @@ package com.didi.virtualapk.collector.dependence /** - * Represents a dependency in Android Project + * Represents a library in Android Project * * @author zhengtao */ @@ -69,4 +69,8 @@ public abstract class DependenceInfo { abstract File getJarFile() abstract DependenceType getDependenceType() + @Override + String toString() { + return "${group}:${artifact}:${version} -> ${jarFile} -> ${super.toString()}" + } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy index df4d438..cd24e5f 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy @@ -1,24 +1,24 @@ package com.didi.virtualapk.collector.dependence -import com.android.builder.dependency.level2.JavaDependency +import com.android.builder.model.JavaLibrary /** - * Represents a Jar dependency. This could be the output of a Java project. + * Represents a Jar library. This could be the output of a Java project. * * @author zhengtao */ class JarDependenceInfo extends DependenceInfo { - @Delegate JavaDependency dependency + JavaLibrary library - JarDependenceInfo(String group, String artifact, String version, JavaDependency jarDependency) { + JarDependenceInfo(String group, String artifact, String version, JavaLibrary library) { super(group, artifact, version) - this.dependency = jarDependency + this.library = library } @Override File getJarFile() { - return dependency.artifactFile + return library.jarFile } @Override diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy index 109713c..0f0b1e1 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy @@ -2,7 +2,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask -import com.didi.virtualapk.VAExtention +import com.google.common.io.Files import org.apache.commons.io.FilenameUtils import org.gradle.api.Project @@ -32,29 +32,43 @@ class DxTaskHooker extends GradleTaskHooker { @Override void beforeTaskExecute(TransformTask task) { task.inputs.files.each { input -> +// println "${task.name}: ${input.absoluteFile}" if(input.directory) { input.eachFileRecurse { file -> - if (file.directory && file.path.endsWith(virtualApk.packagePath)) { - - recompileSplitR(file) - - } else if (file.file && file.name.endsWith('.jar')) { - // Decompress jar file - File unzipJarDir = new File(file.parentFile, FilenameUtils.getBaseName(file.name)) - project.copy { - from project.zipTree(file) - into unzipJarDir - } - - // VirtualApk Package Dir - File pkgDir = new File(unzipJarDir, virtualApk.packagePath) - if (pkgDir.exists()) { - boolean compileResult = recompileSplitR(pkgDir) - if (compileResult) { - project.ant.zip(baseDir: unzipJarDir, destFile: file) - } - } + handleFile(file) + } + } else { + handleFile(input) + } + } + } + + void handleFile(File file) { + if (file.directory && file.path.endsWith(virtualApk.packagePath)) { + + recompileSplitR(file) + + } else if (file.file && file.name.endsWith('.jar')) { + // Decompress jar file + File unzipJarDir = new File(file.parentFile, FilenameUtils.getBaseName(file.name)) + project.copy { + from project.zipTree(file) + into unzipJarDir + } + + // VirtualApk Package Dir + File pkgDir = new File(unzipJarDir, virtualApk.packagePath) + if (pkgDir.exists()) { + boolean compileResult = recompileSplitR(pkgDir) + if (compileResult) { + File backupDir = new File(file.getParentFile(), 'original') + backupDir.deleteDir() + project.copy { + from file + into backupDir } + + project.ant.zip(baseDir: unzipJarDir, destFile: file) } } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy index c7b9567..29930e4 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy @@ -3,13 +3,15 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.tasks.MergeSourceSetFolders import com.android.ide.common.res2.AssetSet -import com.didi.virtualapk.collector.dependence.DependenceInfo +import com.didi.virtualapk.collector.dependence.AarDependenceInfo +import com.didi.virtualapk.utils.Reflect import org.gradle.api.Project import java.util.function.Predicate +import java.util.function.Supplier /** - * Remove the asset directory included in the excluded dependency before mergeAssets task + * Remove the asset directory included in the excluded library before mergeAssets task * * @author zhengtao */ @@ -31,25 +33,45 @@ class MergeAssetsHooker extends GradleTaskHooker { @Override void beforeTaskExecute(MergeSourceSetFolders task) { - Set stripedAssetPaths = virtualApk.stripDependencies.collect { - if (it.dependenceType == DependenceInfo.DependenceType.AAR) { + Set strippedAssetPaths = virtualApk.stripDependencies.collect { + if (it instanceof AarDependenceInfo) { return it.assetsFolder.path } return '' } - List assetSets = task.inputDirectorySets - assetSets.removeIf(new Predicate() { - @Override - boolean test(AssetSet assetSet) { - return stripedAssetPaths.contains(assetSet.sourceFiles.get(0).path) - } - }) - - task.inputDirectorySets = assetSets + Reflect reflect = Reflect.on(task) + reflect.set('assetSetSupplier', new FixedSupplier(reflect.get('assetSetSupplier'), strippedAssetPaths)) } @Override void afterTaskExecute(MergeSourceSetFolders task) { } + + static class FixedSupplier implements Supplier> { + + Supplier> origin + Set strippedAssetPaths + + FixedSupplier(Supplier> origin, Set strippedAssetPaths) { + this.origin = origin + this.strippedAssetPaths = strippedAssetPaths + } + + @Override + List get() { + List assetSets = origin.get() + assetSets.removeIf(new Predicate() { + @Override + boolean test(AssetSet assetSet) { + boolean ret = strippedAssetPaths.contains(assetSet.sourceFiles.get(0).path) + if (ret) { + println "Stripped asset of artifact: ${assetSet} -> ${assetSet.sourceFiles.get(0).path}" + } + return ret + } + }) + return assetSets + } + } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy index ab57007..6cdd1f0 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy @@ -3,14 +3,10 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.AndroidConfig import com.android.build.gradle.AppExtension import com.android.build.gradle.api.ApkVariant -import com.android.build.gradle.internal.packaging.ParsedPackagingOptions import com.android.build.gradle.internal.pipeline.TransformTask -import com.android.build.gradle.internal.transforms.MergeJavaResourcesTransform import com.didi.virtualapk.collector.HostJniLibsCollector import org.gradle.api.Project -import java.lang.reflect.Field - /** * Remove the Native libs(.so) in stripped dependencies before mergeJniLibs task * @@ -45,9 +41,8 @@ class MergeJniLibsHooker extends GradleTaskHooker { androidConfig.packagingOptions.exclude("/${it}") } - Field field = MergeJavaResourcesTransform.class.getDeclaredField('packagingOptions') - field.setAccessible(true) - field.set(task.transform, new ParsedPackagingOptions(androidConfig.packagingOptions)) +// Reflect.on(task.transform) +// .set('packagingOptions', new ParsedPackagingOptions(androidConfig.packagingOptions)) } @Override diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy index 3580445..ac4b221 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy @@ -1,13 +1,22 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant +import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.scope.TaskOutputHolder +import com.android.build.gradle.internal.variant.BaseVariantData import com.android.build.gradle.tasks.MergeManifests -import com.android.manifmerger.ManifestProvider import com.didi.virtualapk.collector.dependence.DependenceInfo +import com.didi.virtualapk.utils.Reflect import groovy.xml.QName import groovy.xml.XmlUtil import org.gradle.api.Project +import org.gradle.api.artifacts.ArtifactCollection +import org.gradle.api.artifacts.result.ResolvedArtifactResult +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.file.AbstractFileCollection +import org.gradle.api.tasks.TaskDependency +import java.util.function.Consumer import java.util.function.Predicate /** @@ -39,15 +48,9 @@ class MergeManifestsHooker extends GradleTaskHooker { "${dep.group}:${dep.artifact}:${dep.version}" } as Set - def manifestDependencies = task.providers - manifestDependencies.removeIf(new Predicate() { - @Override - boolean test(ManifestProvider manifestDependency) { - return stripAarNames.contains("${manifestDependency.name}") - } - }) - - task.providers = manifestDependencies + Reflect reflect = Reflect.on(task) + ArtifactCollection manifests = new FixedArtifactCollection(reflect.get('manifests'), stripAarNames) + reflect.set('manifests', manifests) } /** @@ -55,7 +58,13 @@ class MergeManifestsHooker extends GradleTaskHooker { */ @Override void afterTaskExecute(MergeManifests task) { - final File xml = task.manifestOutputFile + BaseVariantData variantData = ((ApplicationVariantImpl) apkVariant).variantData + variantData.outputScope.getOutputs(TaskOutputHolder.TaskOutputType.MERGED_MANIFESTS).each { + rewrite(it.outputFile) + } + } + + void rewrite(File xml) { if (xml?.exists()) { final Node manifest = new XmlParser().parse(xml) @@ -71,4 +80,79 @@ class MergeManifestsHooker extends GradleTaskHooker { }) } } + + private static class FixedArtifactCollection implements ArtifactCollection { + + private ArtifactCollection origin + def stripAarNames + + FixedArtifactCollection(ArtifactCollection origin, stripAarNames) { + this.origin = origin + this.stripAarNames = stripAarNames + } + + @Override + FileCollection getArtifactFiles() { + Set set = getArtifacts().collect { ResolvedArtifactResult result -> + result.file + } as Set + FileCollection fileCollection = origin.getArtifactFiles() + + return new AbstractFileCollection() { + @Override + String getDisplayName() { + return fileCollection.getDisplayName() + } + + @Override + TaskDependency getBuildDependencies() { + return fileCollection.getBuildDependencies() + } + + @Override + Set getFiles() { + Set files = new LinkedHashSet(fileCollection.getFiles()) + files.retainAll(set) + return files + } + } + } + + @Override + Set getArtifacts() { + Set set = origin.getArtifacts() + set.removeIf(new Predicate() { + @Override + boolean test(ResolvedArtifactResult result) { + boolean ret = stripAarNames.contains("${result.id.componentIdentifier.displayName}") + if (ret) { + println "Stripped manifest of artifact: ${result} -> ${result.file}" + } + return ret + } + }) + + return set + } + + @Override + Collection getFailures() { + return origin.getFailures() + } + + @Override + Iterator iterator() { + return getArtifacts().iterator() + } + + @Override + void forEach(Consumer action) { + getArtifacts().forEach(action) + } + + @Override + Spliterator spliterator() { + return getArtifacts().spliterator() + } + } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index 4db3243..e326da7 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -1,19 +1,27 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant -import com.android.build.gradle.internal.tasks.PrepareDependenciesTask +import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.ide.ArtifactDependencyGraph +import com.android.build.gradle.internal.tasks.AppPreBuildTask +import com.android.build.gradle.internal.variant.BaseVariantData +import com.android.builder.model.Dependencies +import com.android.builder.model.SyncIssue import com.didi.virtualapk.collector.dependence.AarDependenceInfo import com.didi.virtualapk.collector.dependence.DependenceInfo import com.didi.virtualapk.collector.dependence.JarDependenceInfo +import com.didi.virtualapk.utils.FileUtil import org.gradle.api.Project +import java.util.function.Consumer + /** * Gather list of dependencies(aar&jar) need to be stripped&retained after the PrepareDependenciesTask finished. * The entire stripped operation throughout the build lifecycle is based on the result of this hooker。 * * @author zhengtao */ -class PrepareDependenciesHooker extends GradleTaskHooker { +class PrepareDependenciesHooker extends GradleTaskHooker { //group:artifact:version def hostDependencies = [] as Set @@ -28,7 +36,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker final def module = columns[0].split(':') @@ -55,12 +63,18 @@ class PrepareDependenciesHooker extends GradleTaskHooker() { + @Override + void accept(SyncIssue syncIssue) { + println "Error: ${syncIssue}" + } + }) - virtualApk.variantData.variantConfiguration.compileDependencies.allAndroidDependencies.each { - def mavenCoordinates = it.coordinates + dependencies.libraries.each { + def mavenCoordinates = it.resolvedCoordinates if (hostDependencies.contains("${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}")) { stripDependencies.add( new AarDependenceInfo( @@ -72,15 +86,15 @@ class PrepareDependenciesHooker extends GradleTaskHooker { */ @Override void afterTaskExecute(ProcessAndroidResources par) { - def apFile = par.packageOutputFile + BaseVariantData variantData = ((ApplicationVariantImpl) apkVariant).variantData + variantData.outputScope.getOutputs(TaskOutputHolder.TaskOutputType.PROCESSED_RES).each { + repackage(par, it.outputFile) + } + } + + void repackage(ProcessAndroidResources par, File apFile) { def resourcesDir = new File(apFile.parentFile, Files.getNameWithoutExtension(apFile.name)) /* @@ -61,6 +71,14 @@ class ProcessResourcesHooker extends GradleTaskHooker { */ resourcesDir.deleteDir() + File backupFile = new File(apFile.getParentFile(), "${Files.getNameWithoutExtension(apFile.name)}-original.${Files.getFileExtension(apFile.name)}") + backupFile.delete() + project.copy { + from apFile + into apFile.getParentFile() + rename { backupFile.name } + } + /* * Unzip resources-${variant.name}.ap_ */ @@ -73,6 +91,13 @@ class ProcessResourcesHooker extends GradleTaskHooker { include 'res/**/*' } +// File backupDir = new File(resourcesDir.parentFile, resourcesDir.name + '-original') +// backupDir.deleteDir() +// project.copy { +// from project.fileTree(resourcesDir) +// into backupDir +// } + resourceCollector = new ResourceCollector(project, par) resourceCollector.collect() @@ -80,7 +105,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { def retainedStylealbes = convertStyleablesForAapt(resourceCollector.pluginStyleables) def resIdMap = resourceCollector.resIdMap - def rSymbolFile = new File(par.textSymbolOutputDir, 'R.txt') + def rSymbolFile = par.textSymbolOutputFile def libRefTable = ["${virtualApk.packageId}": par.packageForR] def filteredResources = [] as HashSet def updatedResources = [] as HashSet @@ -92,6 +117,12 @@ class ProcessResourcesHooker extends GradleTaskHooker { //Modify the arsc file, and replace ids of related xml files aapt.filterPackage(retainedTypes, retainedStylealbes, virtualApk.packageId, resIdMap, libRefTable, updatedResources) + File hostDir = resourcesDir.parentFile + FileUtil.saveFile(hostDir, "${taskName}-retainedTypes", retainedTypes) + FileUtil.saveFile(hostDir, "${taskName}-retainedStylealbes", retainedStylealbes) + FileUtil.saveFile(hostDir, "${taskName}-filteredResources", filteredResources) + FileUtil.saveFile(hostDir, "${taskName}-updatedResources", updatedResources) + /* * Delete filtered entries and then add updated resources into resources-${variant.name}.ap_ */ @@ -102,7 +133,8 @@ class ProcessResourcesHooker extends GradleTaskHooker { workingDir resourcesDir args 'add', apFile.path args updatedResources - standardOutput = new ByteArrayOutputStream() + standardOutput = System.out + errorOutput = System.err } updateRJava(aapt, par.sourceOutputDir) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy index cfe7062..18057a4 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy @@ -3,7 +3,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask import com.android.build.gradle.internal.transforms.ProGuardTransform -import com.didi.virtualapk.collector.dependence.DependenceInfo +import com.didi.virtualapk.collector.dependence.AarDependenceInfo import org.gradle.api.InvalidUserDataException import org.gradle.api.Project @@ -65,7 +65,7 @@ class ProguardHooker extends GradleTaskHooker { virtualApk.stripDependencies.each { proguardTransform.libraryJar(it.jarFile) - if (it.dependenceType == DependenceInfo.DependenceType.AAR) { + if (it instanceof AarDependenceInfo) { it.localJars.each { proguardTransform.libraryJar(it) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy new file mode 100644 index 0000000..f3e7973 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy @@ -0,0 +1,127 @@ +package com.didi.virtualapk.hooker + +import com.android.build.api.transform.* +import com.android.build.gradle.api.ApkVariant +import com.android.build.gradle.internal.pipeline.TransformTask +import com.android.build.gradle.internal.transforms.ShrinkResourcesTransform +import com.didi.virtualapk.utils.Reflect +import org.gradle.api.Project + +class ShrinkResourcesHooker extends GradleTaskHooker { + + ShrinkResourcesHooker(Project project, ApkVariant apkVariant) { + super(project, apkVariant) + } + + @Override + String getTaskName() { + return "shrinkRes" + } + + @Override + void beforeTaskExecute(TransformTask task) { + def shrinkResourcesTransform = task.transform as ShrinkResourcesTransform + Reflect.on(task).set('transform', new TransformWrapper(shrinkResourcesTransform)) + } + + @Override + void afterTaskExecute(TransformTask task) { + + } + + static class TransformWrapper extends Transform { + + Transform origin + + TransformWrapper(Transform transform) { + origin = transform + } + + @Override + String getName() { + return origin.getName() + } + + @Override + Set getInputTypes() { + return origin.getInputTypes() + } + + @Override + Set getOutputTypes() { + return origin.getOutputTypes() + } + + @Override + Set getScopes() { + return origin.getScopes() + } + + @Override + Set getReferencedScopes() { + return origin.getReferencedScopes() + } + + @Override + Collection getSecondaryFileInputs() { + return origin.getSecondaryFileInputs() + } + + @Override + Collection getSecondaryFiles() { + return origin.getSecondaryFiles() + } + + @Override + Collection getSecondaryFileOutputs() { + return origin.getSecondaryFileOutputs() + } + + @Override + Collection getSecondaryDirectoryOutputs() { + return origin.getSecondaryDirectoryOutputs() + } + + @Override + Map getParameterInputs() { + return origin.getParameterInputs() + } + + @Override + boolean isIncremental() { + return origin.isIncremental() + } + + @Override + void transform(Context context, Collection inputs, Collection referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException { + origin.transform(context, inputs, referencedInputs, outputProvider, isIncremental) + } + + @Override + void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException { + println "sourceDir: ${Reflect.on(origin).get('sourceDir')}" + + Collection referencedInputs = invocation.getReferencedInputs(); + for (TransformInput transformInput : referencedInputs) { + for (DirectoryInput directoryInput : transformInput.getDirectoryInputs()) { + println "classes dir: ${directoryInput.getFile()}" + } + for (JarInput jarInput : transformInput.getJarInputs()) { + println "classes jar: ${jarInput.getFile()}" + } + } + + origin.transform(invocation) + } + + @Override + boolean isCacheable() { + return origin.isCacheable() + } + + @Override + String toString() { + return origin.toString() + } + } +} \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy index f545465..1e3ec5d 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy @@ -3,12 +3,12 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.AppExtension import com.android.build.gradle.api.ApplicationVariant import com.android.build.gradle.internal.pipeline.TransformTask -import com.android.build.gradle.internal.transforms.ProGuardTransform import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.execution.TaskExecutionListener import org.gradle.api.tasks.TaskState import org.gradle.internal.reflect.Instantiator + /** * Manager of hookers, responsible for registration and scheduling execution * @@ -33,10 +33,15 @@ public class TaskHookerManager { public void registerTaskHookers() { project.afterEvaluate { android.applicationVariants.all { ApplicationVariant appVariant -> + if (!appVariant.buildType.name.equalsIgnoreCase("release")) { + return + } + registerTaskHooker(instantiator.newInstance(PrepareDependenciesHooker, project, appVariant)) registerTaskHooker(instantiator.newInstance(MergeAssetsHooker, project, appVariant)) registerTaskHooker(instantiator.newInstance(MergeManifestsHooker, project, appVariant)) registerTaskHooker(instantiator.newInstance(MergeJniLibsHooker, project, appVariant)) +// registerTaskHooker(instantiator.newInstance(ShrinkResourcesHooker, project, appVariant)) registerTaskHooker(instantiator.newInstance(ProcessResourcesHooker, project, appVariant)) registerTaskHooker(instantiator.newInstance(ProguardHooker, project, appVariant)) registerTaskHooker(instantiator.newInstance(DxTaskHooker, project, appVariant)) @@ -60,6 +65,7 @@ public class TaskHookerManager { @Override void beforeExecute(Task task) { +// println "beforeExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}" if (task.project == project) { if (task in TransformTask) { taskHookerMap[task.transform.name]?.beforeTaskExecute(task) @@ -71,6 +77,7 @@ public class TaskHookerManager { @Override void afterExecute(Task task, TaskState taskState) { +// println "afterExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}" if (task.project == project) { if (task in TransformTask) { taskHookerMap[task.transform.name]?.afterTaskExecute(task) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy index 3061b30..74b4580 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy @@ -1,14 +1,19 @@ package com.didi.virtualapk.tasks +import com.android.annotations.NonNull import com.android.build.gradle.api.ApkVariant -import com.android.build.gradle.internal.scope.ConventionMappingHelper import com.sun.istack.internal.NotNull import org.gradle.api.Action import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.internal.ConventionMapping import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction + +import java.util.concurrent.Callable + /** * Gradle task for assemble plugin apk * @author zhengtao @@ -56,19 +61,19 @@ public class AssemblePlugin extends DefaultTask{ @Override void execute(AssemblePlugin assemblePluginTask) { - ConventionMappingHelper.map(assemblePluginTask, "appPackageName") { + map(assemblePluginTask, "appPackageName") { variant.applicationId } - ConventionMappingHelper.map(assemblePluginTask, "apkTimestamp", { + map(assemblePluginTask, "apkTimestamp", { new Date().format("yyyyMMddHHmmss") }) - ConventionMappingHelper.map(assemblePluginTask, "originApkFile") { + map(assemblePluginTask, "originApkFile") { variant.outputs[0].outputFile } - ConventionMappingHelper.map(assemblePluginTask, "pluginApkDir") { + map(assemblePluginTask, "pluginApkDir") { new File(project.buildDir, "/outputs/plugin/${variant.name}") } @@ -76,6 +81,17 @@ public class AssemblePlugin extends DefaultTask{ assemblePluginTask.setDescription("Build ${variant.name.capitalize()} plugin apk") assemblePluginTask.dependsOn(variant.assemble.name) } + + public static void map(@NonNull Task task, @NonNull String key, @NonNull Callable value) { + if (task instanceof GroovyObject) { + ConventionMapping conventionMapping = + (ConventionMapping) ((GroovyObject) task).getProperty("conventionMapping"); + conventionMapping.map(key, value); + } else { + throw new IllegalArgumentException( + "Don't know how to apply convention mapping to task of type " + task.getClass().getName()); + } + } } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy index bc583b7..2cfc03f 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy @@ -4,8 +4,6 @@ import com.android.build.api.transform.* import com.android.build.gradle.internal.pipeline.TransformManager import com.didi.virtualapk.VAExtention import com.didi.virtualapk.collector.HostClassAndResCollector -import com.didi.virtualapk.hooker.PrepareDependenciesHooker -import com.didi.virtualapk.utils.ZipUtil import groovy.io.FileType import org.apache.commons.io.FileUtils import org.gradle.api.Project @@ -59,23 +57,34 @@ class StripClassAndResTransform extends Transform { transformInvocation.inputs.each { it.directoryInputs.each { directoryInput -> - directoryInput.file.traverse (type: FileType.FILES){ +// println "input dir: ${directoryInput.file.absoluteFile}" + def destDir = transformInvocation.outputProvider.getContentLocation( + directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) +// println "output dir: ${destDir.absoluteFile}" + directoryInput.file.traverse(type: FileType.FILES) { def entryName = it.path.substring(directoryInput.file.path.length() + 1) - def destName = directoryInput.name + '/' + entryName - def dest = transformInvocation.outputProvider.getContentLocation( - destName, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) +// println "found file: ${it.absoluteFile}" +// println "entryName: ${entryName}" if (!stripEntries.contains(entryName)) { + def dest = new File(destDir, entryName) FileUtils.copyFile(it, dest) + println "Copied to file: ${dest.absoluteFile}" + } else { + println "Stripped file: ${it.absoluteFile}" } } } it.jarInputs.each { jarInput -> +// println "${name} jar: ${jarInput.file.absoluteFile}" Set jarEntries = HostClassAndResCollector.unzipJar(jarInput.file) if (!stripEntries.containsAll(jarEntries)){ def dest = transformInvocation.outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR) FileUtils.copyFile(jarInput.file, dest) + println "Copied to jar: ${dest.absoluteFile}" + } else { + println "Stripped jar: ${jarInput.file.absoluteFile}" } } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy new file mode 100644 index 0000000..720352e --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy @@ -0,0 +1,107 @@ +package com.didi.virtualapk.transform + +import com.android.build.api.transform.* + +public class TransformWrapper extends Transform { + + Transform origin + + TransformWrapper(Transform transform) { + origin = transform + } + + @Override + String getName() { + return origin.getName() + } + + @Override + Set getInputTypes() { + return origin.getInputTypes() + } + + @Override + Set getOutputTypes() { + return origin.getOutputTypes() + } + + @Override + Set getScopes() { + return origin.getScopes() + } + + @Override + Set getReferencedScopes() { + return origin.getReferencedScopes() + } + + @Override + Collection getSecondaryFileInputs() { + return origin.getSecondaryFileInputs() + } + + @Override + Collection getSecondaryFiles() { + return origin.getSecondaryFiles() + } + + @Override + Collection getSecondaryFileOutputs() { + return origin.getSecondaryFileOutputs() + } + + @Override + Collection getSecondaryDirectoryOutputs() { + return origin.getSecondaryDirectoryOutputs() + } + + @Override + Map getParameterInputs() { + return origin.getParameterInputs() + } + + @Override + boolean isIncremental() { + return origin.isIncremental() + } + + @Override + void transform(Context context, Collection inputs, Collection referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException { + origin.transform(context, inputs, referencedInputs, outputProvider, isIncremental) + } + + @Override + void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException { + Collection inputs = invocation.getInputs() + for (TransformInput input : inputs) { + for (DirectoryInput directoryInput : input.getDirectoryInputs()) { + println "input dir: ${directoryInput.getFile()}" + } + for (JarInput jarInput : input.getJarInputs()) { + println "input jar: ${jarInput.getFile()}" + } + } + + Collection referencedInputs = invocation.getReferencedInputs(); + for (TransformInput transformInput : referencedInputs) { + for (DirectoryInput directoryInput : transformInput.getDirectoryInputs()) { + println "referenced input dir: ${directoryInput.getFile()}" + } + for (JarInput jarInput : transformInput.getJarInputs()) { + println "referenced input jar: ${jarInput.getFile()}" + } + } + + origin.transform(invocation) + } + + @Override + boolean isCacheable() { + return origin.isCacheable() + } + + @Override + String toString() { + return origin.toString() + } +} \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/Reflect.java b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/Reflect.java new file mode 100644 index 0000000..fb1fed9 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/Reflect.java @@ -0,0 +1,874 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.didi.virtualapk.utils; + +// ... +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A wrapper for an {@link Object} or {@link Class} upon which reflective calls + * can be made. + *

+ * An example of using Reflect is

+ * // Static import all reflection methods to decrease verbosity
+ * import static org.joor.Reflect.*;
+ *
+ * // Wrap an Object / Class / class name with the on() method:
+ * on("java.lang.String")
+ * // Invoke constructors using the create() method:
+ * .create("Hello World")
+ * // Invoke methods using the call() method:
+ * .call("toString")
+ * // Retrieve the wrapped object
+ *
+ * @author Lukas Eder
+ * @author Irek Matysiewicz
+ * @author Thomas Darimont
+ */
+public class Reflect {
+    
+    // ---------------------------------------------------------------------
+    // Static API used as entrance points to the fluent API
+    // ---------------------------------------------------------------------
+    
+    /**
+     * Wrap a class name.
+     * 

+ * This is the same as calling on(Class.forName(name)) + * + * @param name A fully qualified class name + * @return A wrapped class object, to be used for further reflection. + * @throws ReflectException If any reflection exception occurred. + * @see #on(Class) + */ + public static Reflect on(String name) throws ReflectException { + return on(forName(name)); + } + + /** + * Wrap a class name, loading it via a given class loader. + *

+ * This is the same as calling + * on(Class.forName(name, classLoader)) + * + * @param name A fully qualified class name. + * @param classLoader The class loader in whose context the class should be + * loaded. + * @return A wrapped class object, to be used for further reflection. + * @throws ReflectException If any reflection exception occurred. + * @see #on(Class) + */ + public static Reflect on(String name, ClassLoader classLoader) throws ReflectException { + return on(forName(name, classLoader)); + } + + /** + * Wrap a class. + *

+ * Use this when you want to access static fields and methods on a + * {@link Class} object, or as a basis for constructing objects of that + * class using {@link #create(Object...)} + * + * @param clazz The class to be wrapped + * @return A wrapped class object, to be used for further reflection. + */ + public static Reflect on(Class clazz) { + return new Reflect(clazz); + } + + /** + * Wrap an object. + *

+ * Use this when you want to access instance fields and methods on any + * {@link Object} + * + * @param object The object to be wrapped + * @return A wrapped object, to be used for further reflection. + */ + public static Reflect on(Object object) { + return new Reflect(object == null ? Object.class : object.getClass(), object); + } + + private static Reflect on(Class type, Object object) { + return new Reflect(type, object); + } + + /** + * Conveniently render an {@link AccessibleObject} accessible. + *

+ * To prevent {@link SecurityException}, this is only done if the argument + * object and its declaring class are non-public. + * + * @param accessible The object to render accessible + * @return The argument object rendered accessible + */ + public static T accessible(T accessible) { + if (accessible == null) { + return null; + } + + if (accessible instanceof Member) { + Member member = (Member) accessible; + + if (Modifier.isPublic(member.getModifiers()) && + Modifier.isPublic(member.getDeclaringClass().getModifiers())) { + + return accessible; + } + } + + // [jOOQ #3392] The accessible flag is set to false by default, also for public members. + if (!accessible.isAccessible()) { + accessible.setAccessible(true); + } + + return accessible; + } + + // --------------------------------------------------------------------- + // Members + // --------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + /** + * The type of the wrapped object. + */ + private final Class type; + + /** + * The wrapped object. + */ + private final Object object; + + // --------------------------------------------------------------------- + // Constructors + // --------------------------------------------------------------------- + + private Reflect(Class type) { + this(type, type); + } + + private Reflect(Class type, Object object) { + this.type = type; + this.object = object; + } + + // --------------------------------------------------------------------- + // Fluent Reflection API + // --------------------------------------------------------------------- + + /** + * Get the wrapped object + * + * @param A convenience generic parameter for automatic unsafe casting + */ + @SuppressWarnings("unchecked") + public T get() { + return (T) object; + } + + /** + * Set a field value. + *

+ * This is roughly equivalent to {@link Field#set(Object, Object)}. If the + * wrapped object is a {@link Class}, then this will set a value to a static + * member field. If the wrapped object is any other {@link Object}, then + * this will set a value to an instance member field. + *

+ * This method is also capable of setting the value of (static) final + * fields. This may be convenient in situations where no + * {@link SecurityManager} is expected to prevent this, but do note that + * (especially static) final fields may already have been inlined by the + * javac and/or JIT and relevant code deleted from the runtime verison of + * your program, so setting these fields might not have any effect on your + * execution. + *

+ * For restrictions of usage regarding setting values on final fields check: + * http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection + * ... and http://pveentjer.blogspot.co.at/2017/01/final-static-boolean-jit.html + * + * @param name The field name + * @param value The new field value + * @return The same wrapped object, to be used for further reflection. + * @throws ReflectException If any reflection exception occurred. + */ + public Reflect set(String name, Object value) throws ReflectException { + try { + Field field = field0(name); + if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + } + field.set(object, unwrap(value)); + return this; + } + catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * Get a field value. + *

+ * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped + * object is a {@link Class}, then this will get a value from a static + * member field. If the wrapped object is any other {@link Object}, then + * this will get a value from an instance member field. + *

+ * If you want to "navigate" to a wrapped version of the field, use + * {@link #field(String)} instead. + * + * @param name The field name + * @return The field value + * @throws ReflectException If any reflection exception occurred. + * @see #field(String) + */ + public T get(String name) throws ReflectException { + return field(name).get(); + } + + /** + * Get a wrapped field. + *

+ * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped + * object is a {@link Class}, then this will wrap a static member field. If + * the wrapped object is any other {@link Object}, then this wrap an + * instance member field. + * + * @param name The field name + * @return The wrapped field + * @throws ReflectException If any reflection exception occurred. + */ + public Reflect field(String name) throws ReflectException { + try { + Field field = field0(name); + return on(field.getType(), field.get(object)); + } + catch (Exception e) { + throw new ReflectException(e); + } + } + + private Field field0(String name) throws ReflectException { + Class t = type(); + + // Try getting a public field + try { + return accessible(t.getField(name)); + } + + // Try again, getting a non-public field + catch (NoSuchFieldException e) { + do { + try { + return accessible(t.getDeclaredField(name)); + } + catch (NoSuchFieldException ignore) {} + + t = t.getSuperclass(); + } + while (t != null); + + throw new ReflectException(e); + } + } + + /** + * Get a Map containing field names and wrapped values for the fields' + * values. + *

+ * If the wrapped object is a {@link Class}, then this will return static + * fields. If the wrapped object is any other {@link Object}, then this will + * return instance fields. + *

+ * These two calls are equivalent

+     * on(object).field("myField");
+     * on(object).fields().get("myField");
+     * 
+ * + * @return A map containing field names and wrapped values. + */ + public Map fields() { + Map result = new LinkedHashMap(); + Class t = type(); + + do { + for (Field field : t.getDeclaredFields()) { + if (type != object ^ Modifier.isStatic(field.getModifiers())) { + String name = field.getName(); + + if (!result.containsKey(name)) + result.put(name, field(name)); + } + } + + t = t.getSuperclass(); + } + while (t != null); + + return result; + } + + /** + * Call a method by its name. + *

+ * This is a convenience method for calling + * call(name, new Object[0]) + * + * @param name The method name + * @return The wrapped method result or the same wrapped object if the + * method returns void, to be used for further + * reflection. + * @throws ReflectException If any reflection exception occurred. + * @see #call(String, Object...) + */ + public Reflect call(String name) throws ReflectException { + return call(name, new Object[0]); + } + + /** + * Call a method by its name. + *

+ * This is roughly equivalent to {@link Method#invoke(Object, Object...)}. + * If the wrapped object is a {@link Class}, then this will invoke a static + * method. If the wrapped object is any other {@link Object}, then this will + * invoke an instance method. + *

+ * Just like {@link Method#invoke(Object, Object...)}, this will try to wrap + * primitive types or unwrap primitive type wrappers if applicable. If + * several methods are applicable, by that rule, the first one encountered + * is called. i.e. when calling

+     * on(...).call("method", 1, 1);
+     * 
The first of the following methods will be called: + *
+     * public void method(int param1, Integer param2);
+     * public void method(Integer param1, int param2);
+     * public void method(Number param1, Number param2);
+     * public void method(Number param1, Object param2);
+     * public void method(int param1, Object param2);
+     * 
+ *

+ * The best matching method is searched for with the following strategy: + *

    + *
  1. public method with exact signature match in class hierarchy
  2. + *
  3. non-public method with exact signature match on declaring class
  4. + *
  5. public method with similar signature in class hierarchy
  6. + *
  7. non-public method with similar signature on declaring class
  8. + *
+ * + * @param name The method name + * @param args The method arguments + * @return The wrapped method result or the same wrapped object if the + * method returns void, to be used for further + * reflection. + * @throws ReflectException If any reflection exception occurred. + */ + public Reflect call(String name, Object... args) throws ReflectException { + Class[] types = types(args); + + // Try invoking the "canonical" method, i.e. the one with exact + // matching argument types + try { + Method method = exactMethod(name, types); + return on(method, object, args); + } + + // If there is no exact match, try to find a method that has a "similar" + // signature if primitive argument types are converted to their wrappers + catch (NoSuchMethodException e) { + try { + Method method = similarMethod(name, types); + return on(method, object, args); + } catch (NoSuchMethodException e1) { + throw new ReflectException(e1); + } + } + } + + /** + * Searches a method with the exact same signature as desired. + *

+ * If a public method is found in the class hierarchy, this method is returned. + * Otherwise a private method with the exact same signature is returned. + * If no exact match could be found, we let the {@code NoSuchMethodException} pass through. + */ + private Method exactMethod(String name, Class[] types) throws NoSuchMethodException { + Class t = type(); + + // first priority: find a public method with exact signature match in class hierarchy + try { + return t.getMethod(name, types); + } + + // second priority: find a private method with exact signature match on declaring class + catch (NoSuchMethodException e) { + do { + try { + return t.getDeclaredMethod(name, types); + } + catch (NoSuchMethodException ignore) {} + + t = t.getSuperclass(); + } + while (t != null); + + throw new NoSuchMethodException(); + } + } + + /** + * Searches a method with a similar signature as desired using + * {@link #isSimilarSignature(java.lang.reflect.Method, String, Class[])}. + *

+ * First public methods are searched in the class hierarchy, then private + * methods on the declaring class. If a method could be found, it is + * returned, otherwise a {@code NoSuchMethodException} is thrown. + */ + private Method similarMethod(String name, Class[] types) throws NoSuchMethodException { + Class t = type(); + + // first priority: find a public method with a "similar" signature in class hierarchy + // similar interpreted in when primitive argument types are converted to their wrappers + for (Method method : t.getMethods()) { + if (isSimilarSignature(method, name, types)) { + return method; + } + } + + // second priority: find a non-public method with a "similar" signature on declaring class + do { + for (Method method : t.getDeclaredMethods()) { + if (isSimilarSignature(method, name, types)) { + return method; + } + } + + t = t.getSuperclass(); + } + while (t != null); + + throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + "."); + } + + /** + * Determines if a method has a "similar" signature, especially if wrapping + * primitive argument types would result in an exactly matching signature. + */ + private boolean isSimilarSignature(Method possiblyMatchingMethod, String desiredMethodName, Class[] desiredParamTypes) { + return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes); + } + + /** + * Call a constructor. + *

+ * This is a convenience method for calling + * create(new Object[0]) + * + * @return The wrapped new object, to be used for further reflection. + * @throws ReflectException If any reflection exception occurred. + * @see #create(Object...) + */ + public Reflect create() throws ReflectException { + return create(new Object[0]); + } + + /** + * Call a constructor. + *

+ * This is roughly equivalent to {@link Constructor#newInstance(Object...)}. + * If the wrapped object is a {@link Class}, then this will create a new + * object of that class. If the wrapped object is any other {@link Object}, + * then this will create a new object of the same type. + *

+ * Just like {@link Constructor#newInstance(Object...)}, this will try to + * wrap primitive types or unwrap primitive type wrappers if applicable. If + * several constructors are applicable, by that rule, the first one + * encountered is called. i.e. when calling

+     * on(C.class).create(1, 1);
+     * 
The first of the following constructors will be applied: + *
+     * public C(int param1, Integer param2);
+     * public C(Integer param1, int param2);
+     * public C(Number param1, Number param2);
+     * public C(Number param1, Object param2);
+     * public C(int param1, Object param2);
+     * 
+ * + * @param args The constructor arguments + * @return The wrapped new object, to be used for further reflection. + * @throws ReflectException If any reflection exception occurred. + */ + public Reflect create(Object... args) throws ReflectException { + Class[] types = types(args); + + // Try invoking the "canonical" constructor, i.e. the one with exact + // matching argument types + try { + Constructor constructor = type().getDeclaredConstructor(types); + return on(constructor, args); + } + + // If there is no exact match, try to find one that has a "similar" + // signature if primitive argument types are converted to their wrappers + catch (NoSuchMethodException e) { + for (Constructor constructor : type().getDeclaredConstructors()) { + if (match(constructor.getParameterTypes(), types)) { + return on(constructor, args); + } + } + + throw new ReflectException(e); + } + } + + /** + * Create a proxy for the wrapped object allowing to typesafely invoke + * methods on it using a custom interface + * + * @param proxyType The interface type that is implemented by the proxy + * @return A proxy for the wrapped object + */ + @SuppressWarnings("unchecked") + public

P as(final Class

proxyType) { + final boolean isMap = (object instanceof Map); + final InvocationHandler handler = new InvocationHandler() { + @SuppressWarnings("null") + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + + // Actual method name matches always come first + try { + return on(type, object).call(name, args).get(); + } + + // [#14] Emulate POJO behaviour on wrapped map objects + catch (ReflectException e) { + if (isMap) { + Map map = (Map) object; + int length = (args == null ? 0 : args.length); + + if (length == 0 && name.startsWith("get")) { + return map.get(property(name.substring(3))); + } + else if (length == 0 && name.startsWith("is")) { + return map.get(property(name.substring(2))); + } + else if (length == 1 && name.startsWith("set")) { + map.put(property(name.substring(3)), args[0]); + return null; + } + } + + + + + + + + + + + + throw e; + } + } + }; + + return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[] { proxyType }, handler); + } + + /** + * Get the POJO property name of an getter/setter + */ + private static String property(String string) { + int length = string.length(); + + if (length == 0) { + return ""; + } + else if (length == 1) { + return string.toLowerCase(); + } + else { + return string.substring(0, 1).toLowerCase() + string.substring(1); + } + } + + // --------------------------------------------------------------------- + // Object API + // --------------------------------------------------------------------- + + /** + * Check whether two arrays of types match, converting primitive types to + * their corresponding wrappers. + */ + private boolean match(Class[] declaredTypes, Class[] actualTypes) { + if (declaredTypes.length == actualTypes.length) { + for (int i = 0; i < actualTypes.length; i++) { + if (actualTypes[i] == NULL.class) + continue; + + if (wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) + continue; + + return false; + } + + return true; + } + else { + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return object.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Reflect) { + return object.equals(((Reflect) obj).get()); + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return object.toString(); + } + + // --------------------------------------------------------------------- + // Utility methods + // --------------------------------------------------------------------- + + /** + * Wrap an object created from a constructor + */ + private static Reflect on(Constructor constructor, Object... args) throws ReflectException { + try { + return on(constructor.getDeclaringClass(), accessible(constructor).newInstance(args)); + } + catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * Wrap an object returned from a method + */ + private static Reflect on(Method method, Object object, Object... args) throws ReflectException { + try { + accessible(method); + + if (method.getReturnType() == void.class) { + method.invoke(object, args); + return on(object); + } + else { + return on(method.invoke(object, args)); + } + } + catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * Unwrap an object + */ + private static Object unwrap(Object object) { + if (object instanceof Reflect) { + return ((Reflect) object).get(); + } + + return object; + } + + /** + * Get an array of types for an array of objects + * + * @see Object#getClass() + */ + private static Class[] types(Object... values) { + if (values == null) { + return new Class[0]; + } + + Class[] result = new Class[values.length]; + + for (int i = 0; i < values.length; i++) { + Object value = values[i]; + result[i] = value == null ? NULL.class : value.getClass(); + } + + return result; + } + + /** + * Load a class + * + * @see Class#forName(String) + */ + private static Class forName(String name) throws ReflectException { + try { + return Class.forName(name); + } + catch (Exception e) { + throw new ReflectException(e); + } + } + + private static Class forName(String name, ClassLoader classLoader) throws ReflectException { + try { + return Class.forName(name, true, classLoader); + } + catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * Get the type of the wrapped object. + * + * @see Object#getClass() + */ + public Class type() { + return type; + } + + /** + * Get a wrapper type for a primitive type, or the argument type itself, if + * it is not a primitive type. + */ + public static Class wrapper(Class type) { + if (type == null) { + return null; + } + else if (type.isPrimitive()) { + if (boolean.class == type) { + return Boolean.class; + } + else if (int.class == type) { + return Integer.class; + } + else if (long.class == type) { + return Long.class; + } + else if (short.class == type) { + return Short.class; + } + else if (byte.class == type) { + return Byte.class; + } + else if (double.class == type) { + return Double.class; + } + else if (float.class == type) { + return Float.class; + } + else if (char.class == type) { + return Character.class; + } + else if (void.class == type) { + return Void.class; + } + } + + return type; + } + + private static class NULL {} + + /** + * A unchecked wrapper for any of Java's checked reflection exceptions: + *

+ * These exceptions are + *

    + *
  • {@link ClassNotFoundException}
  • + *
  • {@link IllegalAccessException}
  • + *
  • {@link IllegalArgumentException}
  • + *
  • {@link InstantiationException}
  • + *
  • {@link InvocationTargetException}
  • + *
  • {@link NoSuchMethodException}
  • + *
  • {@link NoSuchFieldException}
  • + *
  • {@link SecurityException}
  • + *
+ * + * @author Lukas Eder + */ + public static class ReflectException extends RuntimeException { + + /** + * Generated UID + */ + private static final long serialVersionUID = -6213149635297151442L; + + public ReflectException(String message) { + super(message); + } + + public ReflectException(String message, Throwable cause) { + super(message, cause); + } + + public ReflectException() { + super(); + } + + public ReflectException(Throwable cause) { + super(cause); + } + } +} \ No newline at end of file From b91b2ff28b777c8b7e645192d93b53646dad8d15 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Fri, 9 Feb 2018 18:39:01 +0800 Subject: [PATCH 06/66] FixBug: length of string's eof. --- .../com.didi.virtualapk/aapt/AssetEditor.groovy | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/AssetEditor.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/AssetEditor.groovy index 311a107..88acf1b 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/AssetEditor.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/AssetEditor.groovy @@ -107,6 +107,9 @@ public class AssetEditor extends CppHexEditor { s.styleLens = [] s.styleStringIds = [] as HashSet //string pool index of array item b ... s.isUtf8 = (s.flags & ResStringFlag.UTF8_FLAG) != 0 + byte[] eof = new byte[s.isUtf8 ? 1 : 2] + Arrays.fill(eof, (byte) 0x0) + s.stringsEOF = eof // Read offsets for (int i = 0; i < s.stringCount; i++) { @@ -123,8 +126,8 @@ public class AssetEditor extends CppHexEditor { def len = decodeLength(s.isUtf8) s.stringLens[i] = len.data s.strings[i] = readBytes(len.value) - s.stringsSize += len.value + len.data.length + 1 // 1 for 0x0 - skip(1) // 0x0 + s.stringsSize += len.value + len.data.length + eof.length // len for 0x0 + skip(eof.length) // 0x0 } def endPos = pos + s.header.size @@ -207,7 +210,7 @@ public class AssetEditor extends CppHexEditor { s.strings.eachWithIndex { it, i -> writeBytes(s.stringLens[i]) writeBytes(it) - writeByte(0x0) + writeBytes(s.stringsEOF) } if (s.stringPadding > 0) writeBytes(new byte[s.stringPadding]) @@ -363,7 +366,7 @@ public class AssetEditor extends CppHexEditor { def lenData = sp.stringLens[it] lens.add(lenData) def l = s.length - stringOffset += l + lenData.length + 1 // 1 for 0x0 + stringOffset += l + lenData.length + sp.stringsEOF.length // len for 0x0 } def newStringCount = strings.size() def d = (sp.stringCount - newStringCount) * 4 From 92aebb3bfb85d252835c8a5de80707420a96cbdd Mon Sep 17 00:00:00 2001 From: superq_sky Date: Fri, 9 Feb 2018 18:41:12 +0800 Subject: [PATCH 07/66] FixBug: null value in int[]. --- .../com.didi.virtualapk/collector/ResourceCollector.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy index 2e06f6d..1369217 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy @@ -184,7 +184,9 @@ class ResourceCollector { List values = styleableEntry.valueAsList values.eachWithIndex { hexResId, idx -> ResourceEntry resEntry = attrEntries.find { it.hexResourceId == hexResId } - values[idx] = resEntry?.hexNewResourceId + if (resEntry != null) { + values[idx] = resEntry.hexNewResourceId + } } styleableEntry.value = values } From 508c81e3a5010164a3640cd77c6d74f077180ab9 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Fri, 9 Feb 2018 18:45:58 +0800 Subject: [PATCH 08/66] FixBug: id out of range. --- .../groovy/com.didi.virtualapk/aapt/ArscEditor.groovy | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy index ad71c83..10bfe26 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy @@ -73,6 +73,11 @@ public class ArscEditor extends AssetEditor { retainedStringIds.add(index++) } + // Create the mapping of type ids + LinkedHashMap typeIdMap = new LinkedHashMap<>() + t.typeList.specs.eachWithIndex { it, i -> + typeIdMap.put(it.id.intValue(), i) + } // Filter typeSpecs retainedTypes.each { @@ -85,7 +90,8 @@ public class ArscEditor extends AssetEditor { return } - def ts = t.typeList.specs[it.id - 1] + def specIndex = typeIdMap.get(it.id) + def ts = t.typeList.specs[specIndex] def es = it.entries def newEntryCount = es.size() def d = (ts.entryCount - newEntryCount) * 4 From b1d1ea72aadbf3b1455b5816bfc4135ae7ce45ba Mon Sep 17 00:00:00 2001 From: superq_sky Date: Fri, 9 Feb 2018 19:11:18 +0800 Subject: [PATCH 09/66] Updated gradle plugin version to 0.9.7-dev. --- PluginDemo/app/build.gradle | 2 +- PluginDemo/build.gradle | 2 +- README.md | 4 ++-- app/build.gradle | 1 + build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 9c8b522..5282899 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -50,7 +50,7 @@ android { dependencies { // the following aars are also compiled in host project, so they will be filterd when build plugin apk. // but, wo can still visit their Class and Resources. - compile 'com.android.support:appcompat-v7:22.2.0' + compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.didi.virtualapk:core:0.9.1' } diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index 7406ebe..188d1ff 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.6-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7-dev' } } diff --git a/README.md b/README.md index 77c4a18..82315b6 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.6-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7-dev' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.6-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7-dev' } ``` diff --git a/app/build.gradle b/app/build.gradle index 264c9de..e28db4b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,7 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.didi.virtualapk:core:0.9.1' //compile project (":CoreLibrary") diff --git a/build.gradle b/build.gradle index d1c33ee..32f0d9c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.6-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7-dev' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 42a61fc..827a5c4 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.6-dev +VERSION=0.9.7-dev From 4065898f435a0a5bb50ce464e85555a4cebc319c Mon Sep 17 00:00:00 2001 From: superq_sky Date: Sun, 11 Feb 2018 18:20:47 +0800 Subject: [PATCH 10/66] Optimized the code. --- .../com.didi.virtualapk/BasePlugin.groovy | 6 +- .../com.didi.virtualapk/VAExtention.groovy | 7 +- .../com.didi.virtualapk/VAHostPlugin.groovy | 6 +- .../com.didi.virtualapk/VAPlugin.groovy | 20 ++-- .../hooker/DxTaskHooker.groovy | 1 - .../hooker/GradleTaskHooker.groovy | 12 +- .../hooker/MergeAssetsHooker.groovy | 2 +- .../hooker/MergeJniLibsHooker.groovy | 1 + .../hooker/MergeManifestsHooker.groovy | 5 +- .../hooker/PrepareDependenciesHooker.groovy | 11 +- .../hooker/ProcessResourcesHooker.groovy | 10 +- .../hooker/ShrinkResourcesHooker.groovy | 108 ++---------------- .../hooker/TaskHookerManager.groovy | 30 +++-- 13 files changed, 65 insertions(+), 154 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy index b1aca07..126deb5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy @@ -3,6 +3,7 @@ package com.didi.virtualapk import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.TaskContainerAdaptor import com.android.build.gradle.internal.TaskFactory +import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.didi.virtualapk.tasks.AssemblePlugin import org.gradle.api.Action; import org.gradle.api.Plugin; @@ -47,9 +48,10 @@ public class BasePlugin implements Plugin { taskFactory = new TaskContainerAdaptor(project.tasks) project.afterEvaluate { - project.android.applicationVariants.each { ApkVariant variant -> + + project.android.applicationVariants.each { ApplicationVariantImpl variant -> if (variant.buildType.name.equalsIgnoreCase("release")) { - final def variantPluginTaskName = "assemblePlugin${variant.name.capitalize()}" + final def variantPluginTaskName = variant.variantData.scope.getTaskName('assemble', 'Plugin') final def configAction = new AssemblePlugin.ConfigAction(project, variant) taskFactory.create(variantPluginTaskName, AssemblePlugin, configAction) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy index 63476d7..28d42bf 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy @@ -1,6 +1,6 @@ package com.didi.virtualapk -import com.android.build.gradle.internal.variant.BaseVariantData +import com.android.build.gradle.internal.scope.VariantScope import com.didi.virtualapk.collector.dependence.AarDependenceInfo import com.didi.virtualapk.collector.dependence.DependenceInfo @@ -38,7 +38,10 @@ public class VAExtention { /** File of split R.java */ File splitRJavaFile - BaseVariantData variantData + public File getBuildDir(VariantScope scope) { + return new File(scope.getGlobalScope().getIntermediatesDir(), + "virtualapk/" + scope.getVariantConfiguration().getDirName()) + } public void exclude(final String...filters) { if (null != filters) { diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy index d6c23d5..c127aa1 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy @@ -37,7 +37,7 @@ public class VAHostPlugin implements Plugin { project.afterEvaluate { - project.android.applicationVariants.each { ApplicationVariant variant -> + project.android.applicationVariants.each { ApplicationVariantImpl variant -> generateDependencies(variant) backupHostR(variant) backupProguardMapping(variant) @@ -50,7 +50,7 @@ public class VAHostPlugin implements Plugin { /** * Generate ${project.buildDir}/VAHost/versions.txt */ - def generateDependencies(ApplicationVariant applicationVariant) { + def generateDependencies(ApplicationVariantImpl applicationVariant) { applicationVariant.javaCompile.doLast { @@ -74,7 +74,7 @@ public class VAHostPlugin implements Plugin { FileUtil.saveFile(vaHostDir, "versions", { List deps = new ArrayList() - Configuration compileClasspath = ((ApplicationVariantImpl) applicationVariant).variantData.variantDependency.compileClasspath + Configuration compileClasspath = applicationVariant.variantData.variantDependency.compileClasspath println "Used compileClasspath: ${compileClasspath.name}" compileClasspath.resolvedConfiguration.resolvedArtifacts.each { deps.add("${it.moduleVersion.id} ${it.file.length()}") diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 7dc2f7d..c466b3e 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -1,10 +1,8 @@ package com.didi.virtualapk -import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.didi.virtualapk.hooker.TaskHookerManager import com.didi.virtualapk.transform.StripClassAndResTransform - - import com.didi.virtualapk.utils.FileBinaryCategory import org.gradle.api.InvalidUserDataException import org.gradle.api.Project @@ -44,6 +42,7 @@ class VAPlugin extends BasePlugin { super.apply(project) if (!isBuildingPlugin) { + println "Skipped all virtualapk's configurations!" return } @@ -54,12 +53,11 @@ class VAPlugin extends BasePlugin { project.android.registerTransform(new StripClassAndResTransform(project)) - taskHookerManager = new TaskHookerManager(project, instantiator) - taskHookerManager.registerTaskHookers() - - project.afterEvaluate { - project.android.applicationVariants.each { ApplicationVariant variant -> + taskHookerManager = new TaskHookerManager(project, instantiator) + taskHookerManager.registerTaskHookers() + + project.android.applicationVariants.each { ApplicationVariantImpl variant -> checkConfig() @@ -100,7 +98,7 @@ class VAPlugin extends BasePlugin { File hostLocalDir = new File(targetHost) if (!hostLocalDir.exists()) { - def err = "The directory of host application doesn't exist! Dir: ${hostLocalDir.absoluteFile}" + def err = "The directory of host application doesn't exist! Dir: ${hostLocalDir.absolutePath}" throw new InvalidUserDataException(err) } @@ -111,7 +109,7 @@ class VAPlugin extends BasePlugin { dst << hostR } } else { - def err = new StringBuilder("Can't find ${hostR.path}, please check up your host application\n") + def err = new StringBuilder("Can't find ${hostR.absolutePath}, please check up your host application\n") err.append(" need apply com.didi.virtualapk.host in build.gradle of host application\n ") throw new InvalidUserDataException(err.toString()) } @@ -123,7 +121,7 @@ class VAPlugin extends BasePlugin { dst << hostVersions } } else { - def err = new StringBuilder("Can't find ${hostVersions.path}, please check up your host application\n") + def err = new StringBuilder("Can't find ${hostVersions.absolutePath}, please check up your host application\n") err.append(" need apply com.didi.virtualapk.host in build.gradle of host application \n") throw new InvalidUserDataException(err.toString()) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy index 0f0b1e1..be73d86 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy @@ -2,7 +2,6 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask -import com.google.common.io.Files import org.apache.commons.io.FilenameUtils import org.gradle.api.Project diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy index 0aef101..b178e87 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy @@ -1,6 +1,9 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant +import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.scope.VariantScope +import com.android.build.gradle.internal.variant.BaseVariantData import com.didi.virtualapk.VAExtention import org.gradle.api.Project import org.gradle.api.Task @@ -34,11 +37,18 @@ public abstract class GradleTaskHooker { return this.project } - public ApkVariant getApkVariant() { return this.apkVariant } + public BaseVariantData getVariantData() { + return ((ApplicationVariantImpl) this.apkVariant).variantData + } + + public VariantScope getScope() { + return variantData.scope + } + public VAExtention getVirtualApk() { return this.virtualApk } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy index 29930e4..c95269d 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy @@ -23,7 +23,7 @@ class MergeAssetsHooker extends GradleTaskHooker { @Override String getTaskName() { - return "merge${apkVariant.name.capitalize()}Assets" + return scope.getTaskName('merge', 'Assets') } /** diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy index 6cdd1f0..20def89 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy @@ -39,6 +39,7 @@ class MergeJniLibsHooker extends GradleTaskHooker { excludeJniFiles.each { androidConfig.packagingOptions.exclude("/${it}") + println "Stripped jni file: ${it}" } // Reflect.on(task.transform) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy index ac4b221..32acb3a 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy @@ -1,9 +1,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant -import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.scope.TaskOutputHolder -import com.android.build.gradle.internal.variant.BaseVariantData import com.android.build.gradle.tasks.MergeManifests import com.didi.virtualapk.collector.dependence.DependenceInfo import com.didi.virtualapk.utils.Reflect @@ -34,7 +32,7 @@ class MergeManifestsHooker extends GradleTaskHooker { @Override String getTaskName() { - return "process${apkVariant.name.capitalize()}Manifest" + return scope.getTaskName('process', 'Manifest') } @Override @@ -58,7 +56,6 @@ class MergeManifestsHooker extends GradleTaskHooker { */ @Override void afterTaskExecute(MergeManifests task) { - BaseVariantData variantData = ((ApplicationVariantImpl) apkVariant).variantData variantData.outputScope.getOutputs(TaskOutputHolder.TaskOutputType.MERGED_MANIFESTS).each { rewrite(it.outputFile) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index e326da7..2911d11 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -1,10 +1,8 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant -import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.ide.ArtifactDependencyGraph import com.android.build.gradle.internal.tasks.AppPreBuildTask -import com.android.build.gradle.internal.variant.BaseVariantData import com.android.builder.model.Dependencies import com.android.builder.model.SyncIssue import com.didi.virtualapk.collector.dependence.AarDependenceInfo @@ -36,7 +34,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { @Override String getTaskName() { - return "pre${apkVariant.name.capitalize()}Build" + return scope.getTaskName('pre', 'Build') } /** @@ -64,9 +62,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { */ @Override void afterTaskExecute(AppPreBuildTask task) { - BaseVariantData variantData = ((ApplicationVariantImpl) apkVariant).variantData - - Dependencies dependencies = new ArtifactDependencyGraph().createDependencies(variantData.scope, false, new Consumer() { + Dependencies dependencies = new ArtifactDependencyGraph().createDependencies(scope, false, new Consumer() { @Override void accept(SyncIssue syncIssue) { println "Error: ${syncIssue}" @@ -113,12 +109,11 @@ class PrepareDependenciesHooker extends GradleTaskHooker { } - File hostDir = task.fakeOutputDirectory + File hostDir = virtualApk.getBuildDir(scope) FileUtil.saveFile(hostDir, "${taskName}-stripDependencies", stripDependencies) FileUtil.saveFile(hostDir, "${taskName}-retainedAarLibs", retainedAarLibs) FileUtil.saveFile(hostDir, "${taskName}-retainedJarLib", retainedJarLib) - virtualApk.variantData = variantData virtualApk.stripDependencies = stripDependencies virtualApk.retainedAarLibs = retainedAarLibs } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy index 1dc3e1b..f24c21b 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy @@ -3,9 +3,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.AndroidConfig import com.android.build.gradle.AppExtension import com.android.build.gradle.api.ApkVariant -import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.scope.TaskOutputHolder -import com.android.build.gradle.internal.variant.BaseVariantData import com.android.build.gradle.tasks.ProcessAndroidResources import com.android.sdklib.BuildToolInfo import com.didi.virtualapk.aapt.Aapt @@ -16,6 +14,7 @@ import com.didi.virtualapk.utils.FileUtil import com.google.common.collect.ListMultimap import com.google.common.io.Files import org.gradle.api.Project + /** * Filter the host resources out of the plugin apk. * Modify the .arsc file to delete host element, @@ -41,7 +40,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { @Override String getTaskName() { - return "process${apkVariant.name.capitalize()}Resources" + return scope.getTaskName('process', 'Resources') } @Override @@ -57,7 +56,6 @@ class ProcessResourcesHooker extends GradleTaskHooker { */ @Override void afterTaskExecute(ProcessAndroidResources par) { - BaseVariantData variantData = ((ApplicationVariantImpl) apkVariant).variantData variantData.outputScope.getOutputs(TaskOutputHolder.TaskOutputType.PROCESSED_RES).each { repackage(par, it.outputFile) } @@ -117,7 +115,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { //Modify the arsc file, and replace ids of related xml files aapt.filterPackage(retainedTypes, retainedStylealbes, virtualApk.packageId, resIdMap, libRefTable, updatedResources) - File hostDir = resourcesDir.parentFile + File hostDir = virtualApk.getBuildDir(scope) FileUtil.saveFile(hostDir, "${taskName}-retainedTypes", retainedTypes) FileUtil.saveFile(hostDir, "${taskName}-retainedStylealbes", retainedStylealbes) FileUtil.saveFile(hostDir, "${taskName}-filteredResources", filteredResources) @@ -155,7 +153,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { def rSourceFile = new File(sourceOutputDir, "${virtualApk.packagePath}${File.separator}R.java") aapt.generateRJava(rSourceFile, apkVariant.applicationId, resourceCollector.allResources, resourceCollector.allStyleables) - def splitRSourceFile = new File(sourceOutputDir.parentFile, "va${File.separator}${virtualApk.packagePath}${File.separator}R.java") + def splitRSourceFile = new File(virtualApk.getBuildDir(scope), "source${File.separator}r${File.separator}${virtualApk.packagePath}${File.separator}R.java") aapt.generateRJava(splitRSourceFile, apkVariant.applicationId, resourceCollector.pluginResources, resourceCollector.pluginStyleables) virtualApk.splitRJavaFile = splitRSourceFile diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy index f3e7973..4e83f0a 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy @@ -1,9 +1,11 @@ package com.didi.virtualapk.hooker -import com.android.build.api.transform.* +import com.android.build.api.transform.TransformException +import com.android.build.api.transform.TransformInvocation import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask import com.android.build.gradle.internal.transforms.ShrinkResourcesTransform +import com.didi.virtualapk.transform.TransformWrapper import com.didi.virtualapk.utils.Reflect import org.gradle.api.Project @@ -21,107 +23,17 @@ class ShrinkResourcesHooker extends GradleTaskHooker { @Override void beforeTaskExecute(TransformTask task) { def shrinkResourcesTransform = task.transform as ShrinkResourcesTransform - Reflect.on(task).set('transform', new TransformWrapper(shrinkResourcesTransform)) + Reflect.on(task).set('transform', new TransformWrapper(shrinkResourcesTransform) { + @Override + void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException { + println "sourceDir: ${Reflect.on(origin).get('sourceDir')}" + super.transform(invocation) + } + }) } @Override void afterTaskExecute(TransformTask task) { } - - static class TransformWrapper extends Transform { - - Transform origin - - TransformWrapper(Transform transform) { - origin = transform - } - - @Override - String getName() { - return origin.getName() - } - - @Override - Set getInputTypes() { - return origin.getInputTypes() - } - - @Override - Set getOutputTypes() { - return origin.getOutputTypes() - } - - @Override - Set getScopes() { - return origin.getScopes() - } - - @Override - Set getReferencedScopes() { - return origin.getReferencedScopes() - } - - @Override - Collection getSecondaryFileInputs() { - return origin.getSecondaryFileInputs() - } - - @Override - Collection getSecondaryFiles() { - return origin.getSecondaryFiles() - } - - @Override - Collection getSecondaryFileOutputs() { - return origin.getSecondaryFileOutputs() - } - - @Override - Collection getSecondaryDirectoryOutputs() { - return origin.getSecondaryDirectoryOutputs() - } - - @Override - Map getParameterInputs() { - return origin.getParameterInputs() - } - - @Override - boolean isIncremental() { - return origin.isIncremental() - } - - @Override - void transform(Context context, Collection inputs, Collection referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException { - origin.transform(context, inputs, referencedInputs, outputProvider, isIncremental) - } - - @Override - void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException { - println "sourceDir: ${Reflect.on(origin).get('sourceDir')}" - - Collection referencedInputs = invocation.getReferencedInputs(); - for (TransformInput transformInput : referencedInputs) { - for (DirectoryInput directoryInput : transformInput.getDirectoryInputs()) { - println "classes dir: ${directoryInput.getFile()}" - } - for (JarInput jarInput : transformInput.getJarInputs()) { - println "classes jar: ${jarInput.getFile()}" - } - } - - origin.transform(invocation) - } - - @Override - boolean isCacheable() { - return origin.isCacheable() - } - - @Override - String toString() { - return origin.toString() - } - } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy index 1e3ec5d..8d896d7 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy @@ -1,7 +1,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.AppExtension -import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.pipeline.TransformTask import org.gradle.api.Project import org.gradle.api.Task @@ -29,23 +29,20 @@ public class TaskHookerManager { project.gradle.addListener(new VirtualApkTaskListener()) } - public void registerTaskHookers() { - project.afterEvaluate { - android.applicationVariants.all { ApplicationVariant appVariant -> - if (!appVariant.buildType.name.equalsIgnoreCase("release")) { - return - } - - registerTaskHooker(instantiator.newInstance(PrepareDependenciesHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(MergeAssetsHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(MergeManifestsHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(MergeJniLibsHooker, project, appVariant)) -// registerTaskHooker(instantiator.newInstance(ShrinkResourcesHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(ProcessResourcesHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(ProguardHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(DxTaskHooker, project, appVariant)) + android.applicationVariants.all { ApplicationVariantImpl appVariant -> + if (!appVariant.buildType.name.equalsIgnoreCase("release")) { + return } + + registerTaskHooker(instantiator.newInstance(PrepareDependenciesHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(MergeAssetsHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(MergeManifestsHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(MergeJniLibsHooker, project, appVariant)) +// registerTaskHooker(instantiator.newInstance(ShrinkResourcesHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(ProcessResourcesHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(ProguardHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(DxTaskHooker, project, appVariant)) } } @@ -60,7 +57,6 @@ public class TaskHookerManager { return taskHookerMap[taskName] as T } - private class VirtualApkTaskListener implements TaskExecutionListener { @Override From 4249ed4e8b6e2764d9f6cbd649127341a42b517a Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 1 Mar 2018 16:36:55 +0800 Subject: [PATCH 11/66] Supported calling task by variant name. --- .../com.didi.virtualapk/BasePlugin.groovy | 102 +++++++++++++++--- .../com.didi.virtualapk/VAPlugin.groovy | 21 ++-- 2 files changed, 102 insertions(+), 21 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy index 126deb5..b5e5415 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy @@ -1,30 +1,38 @@ package com.didi.virtualapk -import com.android.build.gradle.api.ApkVariant +import com.android.build.gradle.AppPlugin import com.android.build.gradle.internal.TaskContainerAdaptor import com.android.build.gradle.internal.TaskFactory import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.variant.VariantFactory +import com.android.builder.core.VariantConfiguration +import com.android.builder.core.VariantType import com.didi.virtualapk.tasks.AssemblePlugin -import org.gradle.api.Action; -import org.gradle.api.Plugin; +import com.didi.virtualapk.utils.Reflect +import org.gradle.api.Action +import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.internal.reflect.Instantiator import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry +import org.gradle.util.NameMatcher -import javax.inject.Inject; +import javax.inject.Inject +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy /** * Base class of VirtualApk plugin, we create assemblePlugin task here * @author zhengtao */ -public class BasePlugin implements Plugin { +public abstract class BasePlugin implements Plugin { protected Project project protected Instantiator instantiator protected TaskFactory taskFactory - protected boolean isBuildingPlugin = false + boolean checkVariantFactoryInvoked @Inject public BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) { @@ -35,23 +43,41 @@ public class BasePlugin implements Plugin { public void apply(Project project) { this.project = project - def startParameter = project.gradle.startParameter - def targetTasks = startParameter.taskNames + AppPlugin appPlugin = project.plugins.findPlugin(AppPlugin) - targetTasks.each { - if (it.contains("assemblePlugin") || it.contains("aP")) { - isBuildingPlugin = true - } - } + Reflect reflect = Reflect.on(appPlugin.variantManager) + + VariantFactory variantFactory = Proxy.newProxyInstance(this.class.classLoader, [VariantFactory.class] as Class[], + new InvocationHandler() { + Object delegate = reflect.get('variantFactory') + + @Override + Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ('preVariantWork' == method.name) { + checkVariantFactoryInvoked = true + println "Evaluating VirtualApk's configurations..." + boolean isBuildingPlugin = evaluateBuildingPlugin(appPlugin, project) + beforeCreateAndroidTasks(isBuildingPlugin) + } + + return method.invoke(delegate, args) + } + }) + reflect.set('variantFactory', variantFactory) project.extensions.create('virtualApk', VAExtention) taskFactory = new TaskContainerAdaptor(project.tasks) project.afterEvaluate { + if (!checkVariantFactoryInvoked) { + throw new RuntimeException('Evaluating VirtualApk\'s configurations has failed!') + } + project.android.applicationVariants.each { ApplicationVariantImpl variant -> - if (variant.buildType.name.equalsIgnoreCase("release")) { - final def variantPluginTaskName = variant.variantData.scope.getTaskName('assemble', 'Plugin') + if ('release' == variant.buildType.name) { + String variantAssembleTaskName = variant.variantData.scope.getTaskName('assemble', 'Plugin') + def final variantPluginTaskName = createPluginTaskName(variantAssembleTaskName) final def configAction = new AssemblePlugin.ConfigAction(project, variant) taskFactory.create(variantPluginTaskName, AssemblePlugin, configAction) @@ -69,6 +95,52 @@ public class BasePlugin implements Plugin { project.task('assemblePlugin', dependsOn: "assembleRelease", group: 'build', description: 'Build plugin apk') } + String createPluginTaskName(String name) { + return name.replace('Release', '') + } + + private boolean evaluateBuildingPlugin(AppPlugin appPlugin, Project project) { + def startParameter = project.gradle.startParameter + def targetTasks = startParameter.taskNames + + def pluginTasks = ['assemblePlugin'] as List + + appPlugin.variantManager.buildTypes.each { + def buildType = it.value.buildType + if ('release' != buildType.name) { + return + } + if (appPlugin.variantManager.productFlavors.isEmpty()) { + return + } + + appPlugin.variantManager.productFlavors.each { + String variantName = VariantConfiguration.computeFullName(it.key, buildType, VariantType.DEFAULT, null) + def variantPluginTaskName = createPluginTaskName("assemble${variantName.capitalize()}Plugin".toString()) + pluginTasks.add(variantPluginTaskName) + } + } + +// pluginTasks.each { +// println "pluginTask: ${it}" +// } + + boolean isBuildingPlugin = false + NameMatcher nameMatcher = new NameMatcher() + targetTasks.every { + String taskName = nameMatcher.find(it, pluginTasks) + if (taskName != null) { +// println "Found task name '${taskName}' by given name '${it}'" + isBuildingPlugin = true + return false + } + return true + } + + return isBuildingPlugin + } + + protected abstract void beforeCreateAndroidTasks(boolean isBuildingPlugin) protected final VAExtention getVirtualApk() { return this.project.virtualApk diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index c466b3e..12e7158 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -27,6 +27,8 @@ class VAPlugin extends BasePlugin { */ private def hostDir + protected boolean isBuildingPlugin = false + /** * TaskHooker manager, registers hookers when apply invoked */ @@ -38,22 +40,29 @@ class VAPlugin extends BasePlugin { } @Override - void apply(final Project project) { - super.apply(project) - + protected void beforeCreateAndroidTasks(boolean isBuildingPlugin) { + this.isBuildingPlugin = isBuildingPlugin if (!isBuildingPlugin) { - println "Skipped all virtualapk's configurations!" + println "Skipped all VirtualApk's configurations!" return } + project.android.registerTransform(new StripClassAndResTransform(project)) + } + + @Override + void apply(final Project project) { + super.apply(project) hostDir = new File(project.rootDir, "host") if (!hostDir.exists()) { hostDir.mkdirs() } - project.android.registerTransform(new StripClassAndResTransform(project)) - project.afterEvaluate { + if (!isBuildingPlugin) { + return + } + taskHookerManager = new TaskHookerManager(project, instantiator) taskHookerManager.registerTaskHookers() From db38c198b1f1dfd04ba0b5ff455c50d828f704ef Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 1 Mar 2018 21:58:58 +0800 Subject: [PATCH 12/66] Optimized some log. --- .../aapt/ArscEditor.groovy | 2 -- .../hooker/DxTaskHooker.groovy | 10 +++--- .../hooker/PrepareDependenciesHooker.groovy | 1 + .../hooker/ProcessResourcesHooker.groovy | 15 ++++++-- .../StripClassAndResTransform.groovy | 4 +-- .../com.didi.virtualapk/utils/FileUtil.groovy | 34 +++++++++++++++++++ 6 files changed, 56 insertions(+), 10 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy index 10bfe26..0d94aac 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy @@ -303,8 +303,6 @@ public class ArscEditor extends AssetEditor { if (DEBUG_NOISY) dumpTable() close() - - print '###############################################################' } /** diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy index be73d86..b5214c5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy @@ -45,7 +45,9 @@ class DxTaskHooker extends GradleTaskHooker { void handleFile(File file) { if (file.directory && file.path.endsWith(virtualApk.packagePath)) { - recompileSplitR(file) + if (recompileSplitR(file)) { + println "Recompiled R.java in dir: ${file.absoluteFile}" + } } else if (file.file && file.name.endsWith('.jar')) { // Decompress jar file @@ -58,9 +60,9 @@ class DxTaskHooker extends GradleTaskHooker { // VirtualApk Package Dir File pkgDir = new File(unzipJarDir, virtualApk.packagePath) if (pkgDir.exists()) { - boolean compileResult = recompileSplitR(pkgDir) - if (compileResult) { - File backupDir = new File(file.getParentFile(), 'original') + if (recompileSplitR(pkgDir)) { + println "Recompiled R.java in jar: ${file.absoluteFile}" + File backupDir = new File(virtualApk.getBuildDir(scope), 'origin/classes') backupDir.deleteDir() project.copy { from file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index 2911d11..4ef46b8 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -113,6 +113,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { FileUtil.saveFile(hostDir, "${taskName}-stripDependencies", stripDependencies) FileUtil.saveFile(hostDir, "${taskName}-retainedAarLibs", retainedAarLibs) FileUtil.saveFile(hostDir, "${taskName}-retainedJarLib", retainedJarLib) + println "Analyzed all dependencis. Get more infomation in dir: ${hostDir.absoluteFile}" virtualApk.stripDependencies = stripDependencies virtualApk.retainedAarLibs = retainedAarLibs diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy index f24c21b..fe8325e 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy @@ -147,20 +147,31 @@ class ProcessResourcesHooker extends GradleTaskHooker { * */ def updateRJava(Aapt aapt, File sourceOutputDir) { + File vaBuildDir = virtualApk.getBuildDir(scope) + File backupDir = new File(vaBuildDir, "origin/r") - sourceOutputDir.deleteDir() + project.ant.move(todir: backupDir) { + fileset(dir: sourceOutputDir) { + include(name: '**/R.java') + } + } + + FileUtil.deleteEmptySubfolders(sourceOutputDir) def rSourceFile = new File(sourceOutputDir, "${virtualApk.packagePath}${File.separator}R.java") aapt.generateRJava(rSourceFile, apkVariant.applicationId, resourceCollector.allResources, resourceCollector.allStyleables) + println "Updated R.java: ${rSourceFile.absoluteFile}" - def splitRSourceFile = new File(virtualApk.getBuildDir(scope), "source${File.separator}r${File.separator}${virtualApk.packagePath}${File.separator}R.java") + def splitRSourceFile = new File(vaBuildDir, "source${File.separator}r${File.separator}${virtualApk.packagePath}${File.separator}R.java") aapt.generateRJava(splitRSourceFile, apkVariant.applicationId, resourceCollector.pluginResources, resourceCollector.pluginStyleables) + println "Updated R.java: ${splitRSourceFile.absoluteFile}" virtualApk.splitRJavaFile = splitRSourceFile virtualApk.retainedAarLibs.each { def aarPackage = it.package def rJavaFile = new File(sourceOutputDir, "${aarPackage.replace('.'.charAt(0), File.separatorChar)}${File.separator}R.java") aapt.generateRJava(rJavaFile, aarPackage, it.aarResources, it.aarStyleables) + println "Updated R.java: ${rJavaFile.absoluteFile}" } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy index 2cfc03f..212b305 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy @@ -68,7 +68,7 @@ class StripClassAndResTransform extends Transform { if (!stripEntries.contains(entryName)) { def dest = new File(destDir, entryName) FileUtils.copyFile(it, dest) - println "Copied to file: ${dest.absoluteFile}" +// println "Copied to file: ${dest.absoluteFile}" } else { println "Stripped file: ${it.absoluteFile}" } @@ -82,7 +82,7 @@ class StripClassAndResTransform extends Transform { def dest = transformInvocation.outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR) FileUtils.copyFile(jarInput.file, dest) - println "Copied to jar: ${dest.absoluteFile}" +// println "Copied to jar: ${dest.absoluteFile}" } else { println "Stripped jar: ${jarInput.file.absoluteFile}" } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/FileUtil.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/FileUtil.groovy index 11c67f5..41f4293 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/FileUtil.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/FileUtil.groovy @@ -24,4 +24,38 @@ public class FileUtil { file.write(list.join('\r\n')) } + public static boolean deleteEmptySubfolders(File dir) { + if(dir == null || !dir.exists()) { + return true; + } + if(!dir.isDirectory()) { + return false; + } + File[] files = dir.listFiles(); + + if (files == null || files.length == 0) { + return dir.delete(); + } + + boolean result = true; + int len = files.length; + + for(int i = 0; i < len; ++i) { + File file = files[i]; + if(file.isDirectory()) { + if(!deleteEmptySubfolders(file)) { + result = false; + } + } + } + + File[] updatedFiles = dir.listFiles(); + if (updatedFiles == null || updatedFiles.length == 0) { + if (!dir.delete()) { + result = false; + } + } + + return result; + } } From 3f725ee33976a4be75f92010c6cb9ecd0cbbad18 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 26 Mar 2018 15:45:37 +0800 Subject: [PATCH 13/66] Refactored TaskHookerManager. --- .../com.didi.virtualapk/VAPlugin.groovy | 34 ++++++++++++++++++- .../hooker/TaskHookerManager.groovy | 30 ++++------------ .../tasks/AssemblePlugin.groovy | 12 ++----- .../com.didi.virtualapk/utils/TaskUtil.groovy | 25 ++++++++++++++ 4 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 12e7158..f4ef3bb 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -1,6 +1,13 @@ package com.didi.virtualapk import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.didi.virtualapk.hooker.DxTaskHooker +import com.didi.virtualapk.hooker.MergeAssetsHooker +import com.didi.virtualapk.hooker.MergeJniLibsHooker +import com.didi.virtualapk.hooker.MergeManifestsHooker +import com.didi.virtualapk.hooker.PrepareDependenciesHooker +import com.didi.virtualapk.hooker.ProcessResourcesHooker +import com.didi.virtualapk.hooker.ProguardHooker import com.didi.virtualapk.hooker.TaskHookerManager import com.didi.virtualapk.transform.StripClassAndResTransform import com.didi.virtualapk.utils.FileBinaryCategory @@ -63,7 +70,7 @@ class VAPlugin extends BasePlugin { return } - taskHookerManager = new TaskHookerManager(project, instantiator) + taskHookerManager = new VATaskHookerManager(project, instantiator) taskHookerManager.registerTaskHookers() project.android.applicationVariants.each { ApplicationVariantImpl variant -> @@ -143,4 +150,29 @@ class VAPlugin extends BasePlugin { } } } + + static class VATaskHookerManager extends TaskHookerManager { + + VATaskHookerManager(Project project, Instantiator instantiator) { + super(project, instantiator) + } + + @Override + void registerTaskHookers() { + android.applicationVariants.all { ApplicationVariantImpl appVariant -> + if (!appVariant.buildType.name.equalsIgnoreCase("release")) { + return + } + + registerTaskHooker(instantiator.newInstance(PrepareDependenciesHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(MergeAssetsHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(MergeManifestsHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(MergeJniLibsHooker, project, appVariant)) +// registerTaskHooker(instantiator.newInstance(ShrinkResourcesHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(ProcessResourcesHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(ProguardHooker, project, appVariant)) + registerTaskHooker(instantiator.newInstance(DxTaskHooker, project, appVariant)) + } + } + } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy index 8d896d7..d147ac8 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy @@ -14,13 +14,13 @@ import org.gradle.internal.reflect.Instantiator * * @author zhengtao */ -public class TaskHookerManager { +public abstract class TaskHookerManager { - private Map taskHookerMap = new HashMap<>() + protected Map taskHookerMap = new HashMap<>() - private Project project - private AppExtension android - private Instantiator instantiator + protected Project project + protected AppExtension android + protected Instantiator instantiator public TaskHookerManager(Project project, Instantiator instantiator) { this.project = project @@ -29,25 +29,9 @@ public class TaskHookerManager { project.gradle.addListener(new VirtualApkTaskListener()) } - public void registerTaskHookers() { - android.applicationVariants.all { ApplicationVariantImpl appVariant -> - if (!appVariant.buildType.name.equalsIgnoreCase("release")) { - return - } - - registerTaskHooker(instantiator.newInstance(PrepareDependenciesHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(MergeAssetsHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(MergeManifestsHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(MergeJniLibsHooker, project, appVariant)) -// registerTaskHooker(instantiator.newInstance(ShrinkResourcesHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(ProcessResourcesHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(ProguardHooker, project, appVariant)) - registerTaskHooker(instantiator.newInstance(DxTaskHooker, project, appVariant)) - } - } - + public abstract void registerTaskHookers() - private void registerTaskHooker(GradleTaskHooker taskHooker) { + protected void registerTaskHooker(GradleTaskHooker taskHooker) { taskHooker.setTaskHookerManager(this) taskHookerMap.put(taskHooker.taskName, taskHooker) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy index 74b4580..3d6d188 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy @@ -2,6 +2,7 @@ package com.didi.virtualapk.tasks import com.android.annotations.NonNull import com.android.build.gradle.api.ApkVariant +import com.didi.virtualapk.utils.TaskUtil import com.sun.istack.internal.NotNull import org.gradle.api.Action import org.gradle.api.DefaultTask @@ -82,15 +83,8 @@ public class AssemblePlugin extends DefaultTask{ assemblePluginTask.dependsOn(variant.assemble.name) } - public static void map(@NonNull Task task, @NonNull String key, @NonNull Callable value) { - if (task instanceof GroovyObject) { - ConventionMapping conventionMapping = - (ConventionMapping) ((GroovyObject) task).getProperty("conventionMapping"); - conventionMapping.map(key, value); - } else { - throw new IllegalArgumentException( - "Don't know how to apply convention mapping to task of type " + task.getClass().getName()); - } + static void map(@NonNull Task task, @NonNull String key, @NonNull Callable value) { + TaskUtil.map(task, key, value) } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy new file mode 100644 index 0000000..75ae193 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy @@ -0,0 +1,25 @@ +package com.didi.virtualapk.utils + +import com.android.annotations.NonNull +import org.gradle.api.Task +import org.gradle.api.internal.ConventionMapping + +import java.util.concurrent.Callable + +/** + * Created by qiaopu on 2018/3/19. + */ +public class TaskUtil { + + public static void map(@NonNull Task task, @NonNull String key, @NonNull Callable value) { + if (task instanceof GroovyObject) { + ConventionMapping conventionMapping = + (ConventionMapping) ((GroovyObject) task).getProperty("conventionMapping"); + conventionMapping.map(key, value); + } else { + throw new IllegalArgumentException( + "Don't know how to apply convention mapping to task of type " + task.getClass().getName()); + } + } + +} From dd883767d236c6988a514a2334c7cd55bd44a2e6 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Fri, 30 Mar 2018 16:59:00 +0800 Subject: [PATCH 14/66] Updated contact. --- .../src/main/groovy/com.didi.virtualapk/VAPlugin.groovy | 2 +- virtualapk-gradle-plugin/upload.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 7dc2f7d..49494df 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -84,7 +84,7 @@ class VAPlugin extends BasePlugin { err.append(' virtualApk {\n') err.append(' packageId = 0xXX \n') err.append(' }\n') - err.append('apply for the value of packageId, please contact with zhengtao@didichuxing.com\n') + err.append('apply for the value of packageId.\n') throw new InvalidUserDataException(err.toString()) } diff --git a/virtualapk-gradle-plugin/upload.gradle b/virtualapk-gradle-plugin/upload.gradle index 5cb15a4..0611452 100644 --- a/virtualapk-gradle-plugin/upload.gradle +++ b/virtualapk-gradle-plugin/upload.gradle @@ -31,7 +31,7 @@ install { developer { id 'zhengtao' //填写的一些基本信息 name 'DiDi' - email 'zhengtao@didichuxing.com' + email 'zhengtao620@gmail.com' } } scm { From 8af595cde34b7261b0a04b102eaaf12c5cb4a54e Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 2 Apr 2018 16:58:41 +0800 Subject: [PATCH 15/66] Checked the range of packageId. --- .../src/main/groovy/com.didi.virtualapk/VAPlugin.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index f4ef3bb..24e33ad 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -101,6 +101,9 @@ class VAPlugin extends BasePlugin { err.append('apply for the value of packageId, please contact with zhengtao@didichuxing.com\n') throw new InvalidUserDataException(err.toString()) } + if (packageId >= 0x7f || packageId <= 0x01) { + throw new IllegalArgumentException('the packageId must be in [0x02, 0x7E].') + } String targetHost = virtualApk.targetHost if (!targetHost) { From 5caf9bacbcbab521b31a43148cdebc1193688220 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 16 Apr 2018 15:16:35 +0800 Subject: [PATCH 16/66] Supported the feature of Android DataBinding. --- AndroidStub/build.gradle | 2 + .../android/databinding/DataBinderMapper.java | 36 +++++ .../databinding/DataBindingComponent.java | 31 ++++ CoreLibrary/build.gradle | 1 - .../databinding/DataBinderMapperProxy.java | 122 +++++++++++++++ .../com/didi/virtualapk/PluginManager.java | 34 +++++ CoreLibrary/upload.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- .../com.didi.virtualapk/BasePlugin.groovy | 7 +- .../com.didi.virtualapk/VAPlugin.groovy | 19 ++- .../ProcessDataBinding.java | 141 ++++++++++++++++++ .../javax.annotation.processing.Processor | 1 + 12 files changed, 392 insertions(+), 6 deletions(-) create mode 100644 AndroidStub/src/main/java/android/databinding/DataBinderMapper.java create mode 100644 AndroidStub/src/main/java/android/databinding/DataBindingComponent.java create mode 100644 CoreLibrary/src/main/java/android/databinding/DataBinderMapperProxy.java create mode 100644 virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java create mode 100644 virtualapk-gradle-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/AndroidStub/build.gradle b/AndroidStub/build.gradle index cbd6829..1c7309d 100644 --- a/AndroidStub/build.gradle +++ b/AndroidStub/build.gradle @@ -63,4 +63,6 @@ uploadArchives { dependencies { compile 'com.android.support:support-annotations:22.2.0' + compile 'com.android.databinding:library:1.3.1' + compile 'com.android.databinding:baseLibrary:3.0.0' } \ No newline at end of file diff --git a/AndroidStub/src/main/java/android/databinding/DataBinderMapper.java b/AndroidStub/src/main/java/android/databinding/DataBinderMapper.java new file mode 100644 index 0000000..0477ee8 --- /dev/null +++ b/AndroidStub/src/main/java/android/databinding/DataBinderMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding; +import android.view.View; +/** + * This class will be stripped from the jar and then replaced by the annotation processor + * as part of the code generation step. This class's existence is just to ensure that + * compile works and no reflection is needed to access the generated class. + */ +class DataBinderMapper { + public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view, + int layoutId) { + return null; + } + ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View[] view, int layoutId) { + return null; + } + public int getLayoutId(String tag) { return 0; } + public String convertBrIdToString(int id) { + return null; + } + public static int TARGET_MIN_SDK = 0; +} \ No newline at end of file diff --git a/AndroidStub/src/main/java/android/databinding/DataBindingComponent.java b/AndroidStub/src/main/java/android/databinding/DataBindingComponent.java new file mode 100644 index 0000000..a2cefb4 --- /dev/null +++ b/AndroidStub/src/main/java/android/databinding/DataBindingComponent.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding; +/** + * This interface is generated during compilation to contain getters for all used instance + * BindingAdapters. When a BindingAdapter is an instance method, an instance of the class + * implementing the method must be instantiated. This interface will be generated with a getter + * for each class with the name get* where * is simple class name of the declaring BindingAdapter + * class/interface. Name collisions will be resolved by adding a numeric suffix to the getter. + *

+ * An instance of this class may also be passed into static or instance BindingAdapters as the + * first parameter. + *

+ * If you are using Dagger 2, you should extend this interface and annotate the extended interface + * as a Component. + */ +public interface DataBindingComponent { +} \ No newline at end of file diff --git a/CoreLibrary/build.gradle b/CoreLibrary/build.gradle index 12e14f3..2656e8a 100644 --- a/CoreLibrary/build.gradle +++ b/CoreLibrary/build.gradle @@ -31,7 +31,6 @@ repositories { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:22.2.0' provided project(':AndroidStub') diff --git a/CoreLibrary/src/main/java/android/databinding/DataBinderMapperProxy.java b/CoreLibrary/src/main/java/android/databinding/DataBinderMapperProxy.java new file mode 100644 index 0000000..c4728c2 --- /dev/null +++ b/CoreLibrary/src/main/java/android/databinding/DataBinderMapperProxy.java @@ -0,0 +1,122 @@ +package android.databinding; + +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.View; + +import com.didi.virtualapk.PluginManager; +import com.didi.virtualapk.internal.LoadedPlugin; + +import java.util.LinkedList; + +/** + * Replace {@link DataBindingUtil#sMapper}. + * Created by qiaopu on 2018/4/11. + */ +public class DataBinderMapperProxy extends DataBinderMapper implements PluginManager.Callback { + public static final String TAG = "DataBinderMapperProxy"; + + private final LinkedList mMappers; + private DataBinderMapper[] mCache; + + public DataBinderMapperProxy(@NonNull Object source) { + mMappers = new LinkedList<>(); + + addMapper((DataBinderMapper) source); + } + + @Override + public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view, int layoutId) { + ViewDataBinding viewDataBinding; + + for (DataBinderMapper mapper : getCache()) { + viewDataBinding = mapper.getDataBinder(bindingComponent, view, layoutId); + if (viewDataBinding != null) { +// Log.d(TAG, "Found by mapper: " + mapper); + return viewDataBinding; + } + } + + return null; + } + + @Override + ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View[] view, int layoutId) { + ViewDataBinding viewDataBinding; + + for (DataBinderMapper mapper : getCache()) { + viewDataBinding = mapper.getDataBinder(bindingComponent, view, layoutId); + if (viewDataBinding != null) { +// Log.d(TAG, "Found by mapper: " + mapper); + return viewDataBinding; + } + } + + return null; + } + + @Override + public int getLayoutId(String tag) { + int layoutId; + + for (DataBinderMapper mapper : getCache()) { + layoutId = mapper.getLayoutId(tag); + if (layoutId != 0) { +// Log.d(TAG, "Found by mapper: " + mapper); + return layoutId; + } + } + + return 0; + } + + @Override + public String convertBrIdToString(int id) { + String brId; + + for (DataBinderMapper mapper : getCache()) { + brId = mapper.convertBrIdToString(id); + if (brId != null) { +// Log.d(TAG, "Found by mapper: " + mapper); + return brId; + } + } + + return null; + } + + @Override + public void onAddedLoadedPlugin(LoadedPlugin plugin) { + try { + String clsName = "android.databinding.DataBinderMapper_" + plugin.getPackageName().replace('.', '_'); + Log.d(TAG, "Try to find the class: " + clsName); + Class cls = Class.forName(clsName, true, plugin.getClassLoader()); + Object obj = cls.newInstance(); + + addMapper((DataBinderMapper) obj); + + } catch (Exception e) { + Log.w(TAG, e); + } + } + + private void addMapper(DataBinderMapper mapper) { + int size = 0; + synchronized (mMappers) { + mMappers.add(mapper); + mCache = null; + size = mMappers.size(); + } + + Log.d(TAG, "Added mapper: " + mapper + ", size: " + size); + } + + private DataBinderMapper[] getCache() { + synchronized (mMappers) { + if (mCache == null) { + mCache = mMappers.toArray(new DataBinderMapper[mMappers.size()]); + } + return mCache; + } + } +} diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java index c74237d..0bc2f08 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; +import android.databinding.DataBinderMapperProxy; import android.net.Uri; import android.os.Build; import android.util.Log; @@ -65,6 +66,7 @@ public class PluginManager { private Context mContext; private ComponentsHandler mComponentsHandler; private Map mPlugins = new ConcurrentHashMap<>(); + private final List mCallbacks = new ArrayList<>(); private Instrumentation mInstrumentation; // Hooked instrumentation private IActivityManager mActivityManager; // Hooked IActivityManager binder @@ -99,6 +101,7 @@ private void prepare() { } else { this.hookSystemServices(); } + hookDataBindingUtil(); } public void init() { @@ -113,6 +116,29 @@ public void run() { private void doInWorkThread() { } + + private void hookDataBindingUtil() { + try { + Class cls = Class.forName("android.databinding.DataBindingUtil"); + Object old = ReflectUtil.getField(cls, null, "sMapper"); + Callback callback = new DataBinderMapperProxy(old); + ReflectUtil.setField(cls, null, "sMapper", callback); + + addCallback(callback); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void addCallback(Callback callback) { + if (callback == null) { + return; + } + synchronized (mCallbacks) { + mCallbacks.add(callback); + } + } /** * hookSystemServices, but need to compatible with Android O in future. @@ -219,6 +245,11 @@ public void loadPlugin(File apk) throws Exception { LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk); if (null != plugin) { this.mPlugins.put(plugin.getPackageName(), plugin); + synchronized (mCallbacks) { + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onAddedLoadedPlugin(plugin); + } + } // try to invoke plugin's application plugin.invokeApplication(); } else { @@ -357,4 +388,7 @@ public List queryBroadcastReceivers(Intent intent, int flags) { return resolveInfos; } + public interface Callback { + void onAddedLoadedPlugin(LoadedPlugin plugin); + } } \ No newline at end of file diff --git a/CoreLibrary/upload.gradle b/CoreLibrary/upload.gradle index 42f9158..6b6a177 100644 --- a/CoreLibrary/upload.gradle +++ b/CoreLibrary/upload.gradle @@ -11,7 +11,7 @@ def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url group = GROUP_ID archivesBaseName = 'core' -version = "0.9.1" +version = "0.9.2-dev" install { diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 827a5c4..1156d31 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.7-dev +VERSION=0.9.8-dev diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy index b5e5415..aced95e 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy @@ -1,5 +1,6 @@ package com.didi.virtualapk +import com.android.build.gradle.AppExtension import com.android.build.gradle.AppPlugin import com.android.build.gradle.internal.TaskContainerAdaptor import com.android.build.gradle.internal.TaskFactory @@ -74,7 +75,7 @@ public abstract class BasePlugin implements Plugin { throw new RuntimeException('Evaluating VirtualApk\'s configurations has failed!') } - project.android.applicationVariants.each { ApplicationVariantImpl variant -> + android.applicationVariants.each { ApplicationVariantImpl variant -> if ('release' == variant.buildType.name) { String variantAssembleTaskName = variant.variantData.scope.getTaskName('assemble', 'Plugin') def final variantPluginTaskName = createPluginTaskName(variantAssembleTaskName) @@ -145,4 +146,8 @@ public abstract class BasePlugin implements Plugin { protected final VAExtention getVirtualApk() { return this.project.virtualApk } + + protected final AppExtension getAndroid() { + return this.project.android + } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 24e33ad..e5fb118 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -53,7 +53,18 @@ class VAPlugin extends BasePlugin { println "Skipped all VirtualApk's configurations!" return } - project.android.registerTransform(new StripClassAndResTransform(project)) + android.registerTransform(new StripClassAndResTransform(project)) + + android.defaultConfig.buildConfigField("int", "PACKAGE_ID", "0x" + Integer.toHexString(virtualApk.packageId)) + } + + File getJarPath() { + URL url = this.class.getResource("") + int index = url.path.indexOf('!') + if (index < 0) { + index = url.path.length() + } + return project.file(url.path.substring(0, index)) } @Override @@ -73,7 +84,11 @@ class VAPlugin extends BasePlugin { taskHookerManager = new VATaskHookerManager(project, instantiator) taskHookerManager.registerTaskHookers() - project.android.applicationVariants.each { ApplicationVariantImpl variant -> + if (android.dataBinding.enabled) { + project.dependencies.add('annotationProcessor', project.files(jarPath.absolutePath)) + } + + android.applicationVariants.each { ApplicationVariantImpl variant -> checkConfig() diff --git a/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java b/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java new file mode 100644 index 0000000..58ee521 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.didi.virtualapk.databinding.annotationprocessor; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.util.Set; +import java.util.regex.Pattern; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +@SupportedAnnotationTypes({ + "android.databinding.BindingAdapter", + "android.databinding.Untaggable", + "android.databinding.BindingMethods", + "android.databinding.BindingConversion", + "android.databinding.BindingBuildInfo"} +) +/** + * Parent annotation processor that dispatches sub steps to ensure execution order. + * Use initProcessingSteps to add a new step. + */ +public class ProcessDataBinding extends AbstractProcessor { + + private static final String PARAM_MODULE_PKG = "android.databinding.modulePackage"; + private static final String DATA_BINDER_MAPPER_PACKAGE = "android.databinding"; + private static final String DATA_BINDER_MAPPER_CLASS_NAME = "DataBinderMapper"; + private static final String DATA_BINDER_MAPPER_FULL_NAME = DATA_BINDER_MAPPER_PACKAGE + "." + DATA_BINDER_MAPPER_CLASS_NAME; + + private String modulePackage; + private FileObject fileObject; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element element : roundEnv.getRootElements()) { + if (element instanceof TypeElement) { + TypeElement typeElement = (TypeElement) element; + + if (DATA_BINDER_MAPPER_FULL_NAME.contentEquals(typeElement.getQualifiedName())) { + readDataBinderMapper(); + } + } + } + + return false; + } + + private void readDataBinderMapper() { + BufferedReader reader = null; + BufferedWriter writer = null; + try { + reader = new BufferedReader(fileObject.openReader(false)); + + String className = DATA_BINDER_MAPPER_CLASS_NAME + "_" + modulePackage.replace('.', '_'); + JavaFileObject javaFileObject = processingEnv.getFiler().createSourceFile(DATA_BINDER_MAPPER_PACKAGE + "." + className); + writer = new BufferedWriter(javaFileObject.openWriter()); + writer.write("// Generated by VirtualApk gradle plugin."); + writer.newLine(); + + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(DATA_BINDER_MAPPER_CLASS_NAME)) { + if (line.contains("class")) { + line = line.replace(DATA_BINDER_MAPPER_CLASS_NAME, className + " extends " + DATA_BINDER_MAPPER_CLASS_NAME); + + } else { + line = line.replace(DATA_BINDER_MAPPER_CLASS_NAME, className); + } + } + + writer.write(line); + writer.newLine(); + } + + System.out.print("Generated java source file: " + DATA_BINDER_MAPPER_PACKAGE + "." + className); + + } catch (Exception e) { + e.printStackTrace(); + + } finally { + closeSafely(reader); + closeSafely(writer); + } + } + + private static void closeSafely(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + + modulePackage = processingEnv.getOptions().getOrDefault(PARAM_MODULE_PKG, "unknown"); + try { + fileObject = processingEnv.getFiler().getResource(StandardLocation.SOURCE_OUTPUT, DATA_BINDER_MAPPER_PACKAGE, DATA_BINDER_MAPPER_CLASS_NAME + ".java"); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/virtualapk-gradle-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/virtualapk-gradle-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..bb427cd --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +com.didi.virtualapk.databinding.annotationprocessor.ProcessDataBinding \ No newline at end of file From 6a548dda7c2a202651e0a206769bcda3fa27c20d Mon Sep 17 00:00:00 2001 From: superq_sky Date: Tue, 17 Apr 2018 20:35:06 +0800 Subject: [PATCH 17/66] Added checking. --- virtualapk-gradle-plugin/gradle.properties | 2 +- .../com.didi.virtualapk/BasePlugin.groovy | 7 ++-- .../com.didi.virtualapk/VAExtention.groovy | 3 ++ .../com.didi.virtualapk/VAPlugin.groovy | 9 +++- .../hooker/DxTaskHooker.groovy | 10 +++-- .../hooker/GradleTaskHooker.groovy | 19 ++++++++- .../hooker/MergeAssetsHooker.groovy | 10 +++-- .../hooker/MergeJniLibsHooker.groovy | 6 ++- .../hooker/MergeManifestsHooker.groovy | 14 ++++--- .../hooker/PrepareDependenciesHooker.groovy | 6 ++- .../hooker/ProcessResourcesHooker.groovy | 8 ++-- .../hooker/ProguardHooker.groovy | 3 +- .../hooker/ShrinkResourcesHooker.groovy | 6 ++- .../hooker/TaskHookerManager.groovy | 9 ++-- .../tasks/AssemblePlugin.groovy | 11 ++++- .../StripClassAndResTransform.groovy | 28 +++++++++---- .../utils/CheckList.groovy | 42 +++++++++++++++++++ .../com.didi.virtualapk/utils/LogUtil.groovy | 14 +++++++ 18 files changed, 162 insertions(+), 45 deletions(-) create mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy create mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 1156d31..265c5d7 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8-dev +VERSION=0.9.8.1-dev diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy index aced95e..1e20e00 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy @@ -9,6 +9,7 @@ import com.android.build.gradle.internal.variant.VariantFactory import com.android.builder.core.VariantConfiguration import com.android.builder.core.VariantType import com.didi.virtualapk.tasks.AssemblePlugin +import com.didi.virtualapk.utils.Log import com.didi.virtualapk.utils.Reflect import org.gradle.api.Action import org.gradle.api.Plugin @@ -56,7 +57,7 @@ public abstract class BasePlugin implements Plugin { Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ('preVariantWork' == method.name) { checkVariantFactoryInvoked = true - println "Evaluating VirtualApk's configurations..." + Log.i 'Plugin', "Evaluating VirtualApk's configurations..." boolean isBuildingPlugin = evaluateBuildingPlugin(appPlugin, project) beforeCreateAndroidTasks(isBuildingPlugin) } @@ -123,7 +124,7 @@ public abstract class BasePlugin implements Plugin { } // pluginTasks.each { -// println "pluginTask: ${it}" +// Log.i 'Plugin', "pluginTask: ${it}" // } boolean isBuildingPlugin = false @@ -131,7 +132,7 @@ public abstract class BasePlugin implements Plugin { targetTasks.every { String taskName = nameMatcher.find(it, pluginTasks) if (taskName != null) { -// println "Found task name '${taskName}' by given name '${it}'" +// Log.i 'Plugin', "Found task name '${taskName}' by given name '${it}'" isBuildingPlugin = true return false } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy index 28d42bf..f1d8c79 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy @@ -3,6 +3,7 @@ package com.didi.virtualapk import com.android.build.gradle.internal.scope.VariantScope import com.didi.virtualapk.collector.dependence.AarDependenceInfo import com.didi.virtualapk.collector.dependence.DependenceInfo +import com.didi.virtualapk.utils.CheckList /** * VirtualApk extension for plugin projects. @@ -38,6 +39,8 @@ public class VAExtention { /** File of split R.java */ File splitRJavaFile + final CheckList checkList = new CheckList() + public File getBuildDir(VariantScope scope) { return new File(scope.getGlobalScope().getIntermediatesDir(), "virtualapk/" + scope.getVariantConfiguration().getDirName()) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index e5fb118..2a05b26 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -11,6 +11,7 @@ import com.didi.virtualapk.hooker.ProguardHooker import com.didi.virtualapk.hooker.TaskHookerManager import com.didi.virtualapk.transform.StripClassAndResTransform import com.didi.virtualapk.utils.FileBinaryCategory +import com.didi.virtualapk.utils.Log import org.gradle.api.InvalidUserDataException import org.gradle.api.Project import org.gradle.internal.reflect.Instantiator @@ -41,6 +42,8 @@ class VAPlugin extends BasePlugin { */ private TaskHookerManager taskHookerManager + private StripClassAndResTransform stripClassAndResTransform + @Inject public VAPlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) { super(instantiator, registry) @@ -50,10 +53,11 @@ class VAPlugin extends BasePlugin { protected void beforeCreateAndroidTasks(boolean isBuildingPlugin) { this.isBuildingPlugin = isBuildingPlugin if (!isBuildingPlugin) { - println "Skipped all VirtualApk's configurations!" + Log.i 'Plugin', "Skipped all VirtualApk's configurations!" return } - android.registerTransform(new StripClassAndResTransform(project)) + stripClassAndResTransform = new StripClassAndResTransform(project) + android.registerTransform(stripClassAndResTransform) android.defaultConfig.buildConfigField("int", "PACKAGE_ID", "0x" + Integer.toHexString(virtualApk.packageId)) } @@ -81,6 +85,7 @@ class VAPlugin extends BasePlugin { return } + stripClassAndResTransform.onProjectAfterEvaluate() taskHookerManager = new VATaskHookerManager(project, instantiator) taskHookerManager.registerTaskHookers() diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy index b5214c5..817aff2 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy @@ -2,6 +2,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask +import com.didi.virtualapk.utils.Log import org.apache.commons.io.FilenameUtils import org.gradle.api.Project @@ -18,7 +19,7 @@ class DxTaskHooker extends GradleTaskHooker { } @Override - String getTaskName() { + String getTransformName() { return "dex" } @@ -31,7 +32,7 @@ class DxTaskHooker extends GradleTaskHooker { @Override void beforeTaskExecute(TransformTask task) { task.inputs.files.each { input -> -// println "${task.name}: ${input.absoluteFile}" +// Log.i 'DxTaskHooker', "${task.name}: ${input.absoluteFile}" if(input.directory) { input.eachFileRecurse { file -> handleFile(file) @@ -46,7 +47,7 @@ class DxTaskHooker extends GradleTaskHooker { if (file.directory && file.path.endsWith(virtualApk.packagePath)) { if (recompileSplitR(file)) { - println "Recompiled R.java in dir: ${file.absoluteFile}" + Log.i 'DxTaskHooker', "Recompiled R.java in dir: ${file.absoluteFile}" } } else if (file.file && file.name.endsWith('.jar')) { @@ -61,7 +62,7 @@ class DxTaskHooker extends GradleTaskHooker { File pkgDir = new File(unzipJarDir, virtualApk.packagePath) if (pkgDir.exists()) { if (recompileSplitR(pkgDir)) { - println "Recompiled R.java in jar: ${file.absoluteFile}" + Log.i 'DxTaskHooker', "Recompiled R.java in jar: ${file.absoluteFile}" File backupDir = new File(virtualApk.getBuildDir(scope), 'origin/classes') backupDir.deleteDir() project.copy { @@ -105,6 +106,7 @@ class DxTaskHooker extends GradleTaskHooker { target: apkVariant.javaCompiler.targetCompatibility, destdir: new File(baseDir)) + mark() return true } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy index b178e87..efd89ec 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy @@ -31,6 +31,8 @@ public abstract class GradleTaskHooker { this.project = project this.apkVariant = apkVariant this.virtualApk = project.virtualApk + + virtualApk.checkList.addCheckPoint(apkVariant.name, taskName) } public Project getProject() { @@ -53,6 +55,10 @@ public abstract class GradleTaskHooker { return this.virtualApk } + public void mark() { + virtualApk.checkList.mark(apkVariant.name, taskName) + } + public void setTaskHookerManager(TaskHookerManager taskHookerManager) { this.taskHookerManager = taskHookerManager } @@ -66,9 +72,18 @@ public abstract class GradleTaskHooker { } /** - * Return the task name or transform name of the hooked task(transform task) + * Return the transform name of the hooked task(transform task) + */ + public String getTransformName() { + return "" + } + + /** + * Return the task name(exclude transform task) */ - public abstract String getTaskName() + public String getTaskName() { + return "${transformName}For${apkVariant.name.capitalize()}" + } /** * Callback function before the hooked task executes diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy index c95269d..c646b23 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy @@ -4,6 +4,7 @@ import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.tasks.MergeSourceSetFolders import com.android.ide.common.res2.AssetSet import com.didi.virtualapk.collector.dependence.AarDependenceInfo +import com.didi.virtualapk.utils.Log import com.didi.virtualapk.utils.Reflect import org.gradle.api.Project @@ -41,7 +42,7 @@ class MergeAssetsHooker extends GradleTaskHooker { } Reflect reflect = Reflect.on(task) - reflect.set('assetSetSupplier', new FixedSupplier(reflect.get('assetSetSupplier'), strippedAssetPaths)) + reflect.set('assetSetSupplier', new FixedSupplier(this, reflect.get('assetSetSupplier'), strippedAssetPaths)) } @Override @@ -50,10 +51,12 @@ class MergeAssetsHooker extends GradleTaskHooker { static class FixedSupplier implements Supplier> { + MergeAssetsHooker hooker Supplier> origin Set strippedAssetPaths - FixedSupplier(Supplier> origin, Set strippedAssetPaths) { + FixedSupplier(MergeAssetsHooker hooker, Supplier> origin, Set strippedAssetPaths) { + this.hooker = hooker this.origin = origin this.strippedAssetPaths = strippedAssetPaths } @@ -66,11 +69,12 @@ class MergeAssetsHooker extends GradleTaskHooker { boolean test(AssetSet assetSet) { boolean ret = strippedAssetPaths.contains(assetSet.sourceFiles.get(0).path) if (ret) { - println "Stripped asset of artifact: ${assetSet} -> ${assetSet.sourceFiles.get(0).path}" + Log.i 'MergeAssetsHooker', "Stripped asset of artifact: ${assetSet} -> ${assetSet.sourceFiles.get(0).path}" } return ret } }) + hooker.mark() return assetSets } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy index 20def89..c50047a 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy @@ -5,6 +5,7 @@ import com.android.build.gradle.AppExtension import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask import com.didi.virtualapk.collector.HostJniLibsCollector +import com.didi.virtualapk.utils.Log import org.gradle.api.Project /** @@ -24,7 +25,7 @@ class MergeJniLibsHooker extends GradleTaskHooker { } @Override - String getTaskName() { + String getTransformName() { return "mergeJniLibs" } @@ -39,9 +40,10 @@ class MergeJniLibsHooker extends GradleTaskHooker { excludeJniFiles.each { androidConfig.packagingOptions.exclude("/${it}") - println "Stripped jni file: ${it}" + Log.i 'MergeJniLibsHooker', "Stripped jni file: ${it}" } + mark() // Reflect.on(task.transform) // .set('packagingOptions', new ParsedPackagingOptions(androidConfig.packagingOptions)) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy index 32acb3a..6c77e5a 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy @@ -4,6 +4,7 @@ import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.scope.TaskOutputHolder import com.android.build.gradle.tasks.MergeManifests import com.didi.virtualapk.collector.dependence.DependenceInfo +import com.didi.virtualapk.utils.Log import com.didi.virtualapk.utils.Reflect import groovy.xml.QName import groovy.xml.XmlUtil @@ -47,7 +48,7 @@ class MergeManifestsHooker extends GradleTaskHooker { } as Set Reflect reflect = Reflect.on(task) - ArtifactCollection manifests = new FixedArtifactCollection(reflect.get('manifests'), stripAarNames) + ArtifactCollection manifests = new FixedArtifactCollection(this, reflect.get('manifests'), stripAarNames) reflect.set('manifests', manifests) } @@ -79,11 +80,13 @@ class MergeManifestsHooker extends GradleTaskHooker { } private static class FixedArtifactCollection implements ArtifactCollection { - + + private MergeManifestsHooker hooker private ArtifactCollection origin def stripAarNames - FixedArtifactCollection(ArtifactCollection origin, stripAarNames) { + FixedArtifactCollection(MergeManifestsHooker hooker, ArtifactCollection origin, stripAarNames) { + this.hooker = hooker this.origin = origin this.stripAarNames = stripAarNames } @@ -123,12 +126,13 @@ class MergeManifestsHooker extends GradleTaskHooker { boolean test(ResolvedArtifactResult result) { boolean ret = stripAarNames.contains("${result.id.componentIdentifier.displayName}") if (ret) { - println "Stripped manifest of artifact: ${result} -> ${result.file}" + Log.i 'MergeManifestsHooker', "Stripped manifest of artifact: ${result} -> ${result.file}" } return ret } }) - + + hooker.mark() return set } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index 4ef46b8..b438531 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -9,6 +9,7 @@ import com.didi.virtualapk.collector.dependence.AarDependenceInfo import com.didi.virtualapk.collector.dependence.DependenceInfo import com.didi.virtualapk.collector.dependence.JarDependenceInfo import com.didi.virtualapk.utils.FileUtil +import com.didi.virtualapk.utils.Log import org.gradle.api.Project import java.util.function.Consumer @@ -65,7 +66,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { Dependencies dependencies = new ArtifactDependencyGraph().createDependencies(scope, false, new Consumer() { @Override void accept(SyncIssue syncIssue) { - println "Error: ${syncIssue}" + Log.i 'PrepareDependenciesHooker', "Error: ${syncIssue}" } }) @@ -113,10 +114,11 @@ class PrepareDependenciesHooker extends GradleTaskHooker { FileUtil.saveFile(hostDir, "${taskName}-stripDependencies", stripDependencies) FileUtil.saveFile(hostDir, "${taskName}-retainedAarLibs", retainedAarLibs) FileUtil.saveFile(hostDir, "${taskName}-retainedJarLib", retainedJarLib) - println "Analyzed all dependencis. Get more infomation in dir: ${hostDir.absoluteFile}" + Log.i 'PrepareDependenciesHooker', "Analyzed all dependencis. Get more infomation in dir: ${hostDir.absoluteFile}" virtualApk.stripDependencies = stripDependencies virtualApk.retainedAarLibs = retainedAarLibs + mark() } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy index fe8325e..1ca6f57 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy @@ -11,6 +11,7 @@ import com.didi.virtualapk.collector.ResourceCollector import com.didi.virtualapk.collector.res.ResourceEntry import com.didi.virtualapk.collector.res.StyleableEntry import com.didi.virtualapk.utils.FileUtil +import com.didi.virtualapk.utils.Log import com.google.common.collect.ListMultimap import com.google.common.io.Files import org.gradle.api.Project @@ -136,6 +137,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { } updateRJava(aapt, par.sourceOutputDir) + mark() } /** @@ -160,18 +162,18 @@ class ProcessResourcesHooker extends GradleTaskHooker { def rSourceFile = new File(sourceOutputDir, "${virtualApk.packagePath}${File.separator}R.java") aapt.generateRJava(rSourceFile, apkVariant.applicationId, resourceCollector.allResources, resourceCollector.allStyleables) - println "Updated R.java: ${rSourceFile.absoluteFile}" + Log.i 'ProcessResourcesHooker', "Updated R.java: ${rSourceFile.absoluteFile}" def splitRSourceFile = new File(vaBuildDir, "source${File.separator}r${File.separator}${virtualApk.packagePath}${File.separator}R.java") aapt.generateRJava(splitRSourceFile, apkVariant.applicationId, resourceCollector.pluginResources, resourceCollector.pluginStyleables) - println "Updated R.java: ${splitRSourceFile.absoluteFile}" + Log.i 'ProcessResourcesHooker', "Updated R.java: ${splitRSourceFile.absoluteFile}" virtualApk.splitRJavaFile = splitRSourceFile virtualApk.retainedAarLibs.each { def aarPackage = it.package def rJavaFile = new File(sourceOutputDir, "${aarPackage.replace('.'.charAt(0), File.separatorChar)}${File.separator}R.java") aapt.generateRJava(rJavaFile, aarPackage, it.aarResources, it.aarStyleables) - println "Updated R.java: ${rJavaFile.absoluteFile}" + Log.i 'ProcessResourcesHooker', "Updated R.java: ${rJavaFile.absoluteFile}" } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy index 18057a4..7e2f9bd 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy @@ -22,7 +22,7 @@ class ProguardHooker extends GradleTaskHooker { } @Override - String getTaskName() { + String getTransformName() { return "proguard" } @@ -71,6 +71,7 @@ class ProguardHooker extends GradleTaskHooker { } } } + mark() } @Override diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy index 4e83f0a..29f67b3 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy @@ -6,6 +6,7 @@ import com.android.build.gradle.api.ApkVariant import com.android.build.gradle.internal.pipeline.TransformTask import com.android.build.gradle.internal.transforms.ShrinkResourcesTransform import com.didi.virtualapk.transform.TransformWrapper +import com.didi.virtualapk.utils.Log import com.didi.virtualapk.utils.Reflect import org.gradle.api.Project @@ -16,7 +17,7 @@ class ShrinkResourcesHooker extends GradleTaskHooker { } @Override - String getTaskName() { + String getTransformName() { return "shrinkRes" } @@ -26,8 +27,9 @@ class ShrinkResourcesHooker extends GradleTaskHooker { Reflect.on(task).set('transform', new TransformWrapper(shrinkResourcesTransform) { @Override void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException { - println "sourceDir: ${Reflect.on(origin).get('sourceDir')}" + Log.i 'ShrinkResourcesHooker', "sourceDir: ${Reflect.on(origin).get('sourceDir')}" super.transform(invocation) + mark() } }) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy index d147ac8..c3368b4 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy @@ -3,6 +3,7 @@ package com.didi.virtualapk.hooker import com.android.build.gradle.AppExtension import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.pipeline.TransformTask +import com.didi.virtualapk.utils.Log import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.execution.TaskExecutionListener @@ -45,10 +46,10 @@ public abstract class TaskHookerManager { @Override void beforeExecute(Task task) { -// println "beforeExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}" +// Log.i 'TaskHookerManager', "beforeExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}" if (task.project == project) { if (task in TransformTask) { - taskHookerMap[task.transform.name]?.beforeTaskExecute(task) + taskHookerMap["${task.transform.name}For${task.variantName.capitalize()}".toString()]?.beforeTaskExecute(task) } else { taskHookerMap[task.name]?.beforeTaskExecute(task) } @@ -57,10 +58,10 @@ public abstract class TaskHookerManager { @Override void afterExecute(Task task, TaskState taskState) { -// println "afterExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}" +// Log.i 'TaskHookerManager', "afterExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}" if (task.project == project) { if (task in TransformTask) { - taskHookerMap[task.transform.name]?.afterTaskExecute(task) + taskHookerMap["${task.transform.name}For${task.variantName.capitalize()}".toString()]?.afterTaskExecute(task) } else { taskHookerMap[task.name]?.afterTaskExecute(task) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy index 3d6d188..a46b24a 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy @@ -8,7 +8,6 @@ import org.gradle.api.Action import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.internal.ConventionMapping import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction @@ -19,7 +18,7 @@ import java.util.concurrent.Callable * Gradle task for assemble plugin apk * @author zhengtao */ -public class AssemblePlugin extends DefaultTask{ +public class AssemblePlugin extends DefaultTask { @OutputDirectory File pluginApkDir @@ -33,12 +32,16 @@ public class AssemblePlugin extends DefaultTask{ @Input File originApkFile + String variantName + /** * Copy the plugin apk to out/plugin directory and rename to * the format required for the backend system */ @TaskAction public void outputPluginApk() { + project.virtualApk.checkList.check(variantName) + getProject().copy { from originApkFile into pluginApkDir @@ -78,6 +81,10 @@ public class AssemblePlugin extends DefaultTask{ new File(project.buildDir, "/outputs/plugin/${variant.name}") } + map(assemblePluginTask, "variantName") { + variant.name + } + assemblePluginTask.setGroup("build") assemblePluginTask.setDescription("Build ${variant.name.capitalize()} plugin apk") assemblePluginTask.dependsOn(variant.assemble.name) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy index 212b305..dac37ea 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy @@ -1,9 +1,11 @@ package com.didi.virtualapk.transform import com.android.build.api.transform.* +import com.android.build.gradle.api.ApplicationVariant import com.android.build.gradle.internal.pipeline.TransformManager import com.didi.virtualapk.VAExtention import com.didi.virtualapk.collector.HostClassAndResCollector +import com.didi.virtualapk.utils.Log import groovy.io.FileType import org.apache.commons.io.FileUtils import org.gradle.api.Project @@ -23,6 +25,12 @@ class StripClassAndResTransform extends Transform { classAndResCollector = new HostClassAndResCollector() } + void onProjectAfterEvaluate() { + project.android.applicationVariants.each { ApplicationVariant variant -> + virtualApk.checkList.addCheckPoint(variant.name, name) + } + } + @Override String getName() { return 'stripClassAndRes' @@ -57,36 +65,38 @@ class StripClassAndResTransform extends Transform { transformInvocation.inputs.each { it.directoryInputs.each { directoryInput -> -// println "input dir: ${directoryInput.file.absoluteFile}" +// Log.i 'StripClassAndResTransform', "input dir: ${directoryInput.file.absoluteFile}" def destDir = transformInvocation.outputProvider.getContentLocation( directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) -// println "output dir: ${destDir.absoluteFile}" +// Log.i 'StripClassAndResTransform', "output dir: ${destDir.absoluteFile}" directoryInput.file.traverse(type: FileType.FILES) { def entryName = it.path.substring(directoryInput.file.path.length() + 1) -// println "found file: ${it.absoluteFile}" -// println "entryName: ${entryName}" +// Log.i 'StripClassAndResTransform', "found file: ${it.absoluteFile}" +// Log.i 'StripClassAndResTransform', "entryName: ${entryName}" if (!stripEntries.contains(entryName)) { def dest = new File(destDir, entryName) FileUtils.copyFile(it, dest) -// println "Copied to file: ${dest.absoluteFile}" +// Log.i 'StripClassAndResTransform', "Copied to file: ${dest.absoluteFile}" } else { - println "Stripped file: ${it.absoluteFile}" + Log.i 'StripClassAndResTransform', "Stripped file: ${it.absoluteFile}" } } } it.jarInputs.each { jarInput -> -// println "${name} jar: ${jarInput.file.absoluteFile}" +// Log.i 'StripClassAndResTransform', "${name} jar: ${jarInput.file.absoluteFile}" Set jarEntries = HostClassAndResCollector.unzipJar(jarInput.file) if (!stripEntries.containsAll(jarEntries)){ def dest = transformInvocation.outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR) FileUtils.copyFile(jarInput.file, dest) -// println "Copied to jar: ${dest.absoluteFile}" +// Log.i 'StripClassAndResTransform', "Copied to jar: ${dest.absoluteFile}" } else { - println "Stripped jar: ${jarInput.file.absoluteFile}" + Log.i 'StripClassAndResTransform', "Stripped jar: ${jarInput.file.absoluteFile}" } } } + + virtualApk.checkList.mark(transformInvocation.context.variantName, name) } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy new file mode 100644 index 0000000..d707eb7 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy @@ -0,0 +1,42 @@ +package com.didi.virtualapk.utils + +class CheckList { + Map mMap = new LinkedHashMap<>() + + void addCheckPoint(String variantName, String name) { + String key = "${variantName}:${name}" + if (mMap.containsKey(key)) { + throw new RuntimeException("[${key}] has already exists.") + } + mMap.put(key, false) +// Log.i('test', "addCheckPoint: ${key}") + } + + void mark(String variantName, String name) { + String key = "${variantName}:${name}" + mMap.put(key, true) +// Log.i('test', "mark: ${key}") + } + + void check(String variantName) { + boolean check = true + Map matched = mMap.findAll { +// Log.i 'CheckList', "all: ${it.key}: ${it.value}" + it.key.startsWith("${variantName}:") + } + + matched.each { + check &= it.value + } + + if (check) { + Log.i 'CheckList', "All checked ok." + return + } + + Log.i 'CheckList', "Checked WARNING:" + matched.each { + Log.i 'CheckList', "${it.key}: ${it.value}" + } + } +} \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy new file mode 100644 index 0000000..4595506 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy @@ -0,0 +1,14 @@ +package com.didi.virtualapk.utils + +public final class Log { + + private Log() { + + } + + public static int i(String tag, String msg) { + println "[INFO][${tag}] ${msg}" + return 0 + } + +} From 892e770b3d3a9aa26731196a035b9ce41fd635ec Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 18 Apr 2018 17:50:57 +0800 Subject: [PATCH 18/66] Supported Android P. --- .../android/content/pm/PackageParser.java | 5 + .../virtualapk/internal/LoadedPlugin.java | 119 +++++++++++++++++- .../virtualapk/utils/PackageParserCompat.java | 17 ++- app/build.gradle | 5 +- build.gradle | 2 +- 5 files changed, 138 insertions(+), 10 deletions(-) diff --git a/AndroidStub/src/main/java/android/content/pm/PackageParser.java b/AndroidStub/src/main/java/android/content/pm/PackageParser.java index 573c1bd..aa5f7c3 100644 --- a/AndroidStub/src/main/java/android/content/pm/PackageParser.java +++ b/AndroidStub/src/main/java/android/content/pm/PackageParser.java @@ -377,6 +377,7 @@ public final static class Package { // Signatures that were read from the package. public Signature[] mSignatures; + public SigningDetails mSigningDetails; public Certificate[][] mCertificates; // For use by package manager service for quick lookup of @@ -737,5 +738,9 @@ public PackageParserException(int error, String detailMessage, Throwable throwab } } + + public static class SigningDetails { + public Signature[] signatures; + } } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 88689f0..5ff18da 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -27,6 +27,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ChangedPackages; import android.content.pm.FeatureInfo; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; @@ -38,6 +39,8 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.VersionedPackage; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -47,6 +50,8 @@ import android.os.Bundle; import android.os.Process; import android.os.UserHandle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import com.didi.virtualapk.PluginManager; @@ -142,6 +147,21 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags private Application mApplication; +// void dumpClass(Class cls) { +// Log.w(TAG, "########################################"); +// Log.w(TAG, "cls: " + cls.getName()); +// Log.w(TAG, "cls.super: " + cls.getSuperclass()); +// Log.w(TAG, "field size: " + cls.getDeclaredFields().length); +// Log.w(TAG, "method size: " + cls.getDeclaredMethods().length); +// +// for (Field f : cls.getDeclaredFields()) { +// Log.w(TAG, "field: " + f.getType().getName() + " " + f.getName()); +// } +// for (Method m : cls.getDeclaredMethods()) { +// Log.w(TAG, "method: " + m.getReturnType() + " " + m.getName() + " " + Arrays.toString(m.getParameterTypes())); +// } +// } + LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws PackageParser.PackageParserException { this.mPluginManager = pluginManager; this.mHostContext = context; @@ -151,7 +171,20 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags this.mPackageInfo = new PackageInfo(); this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo; this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath(); - this.mPackageInfo.signatures = this.mPackage.mSignatures; + +// dumpClass(PackageParser.class); +// dumpClass(this.mPackage.getClass()); +// try { +// dumpClass(Class.forName("android.content.pm.PackageParser$SigningDetails")); +// } catch (ClassNotFoundException e) { +// Log.w(TAG, e); +// } + + if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview + this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures; + } else { + this.mPackageInfo.signatures = this.mPackage.mSignatures; + } this.mPackageInfo.packageName = this.mPackage.packageName; if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) { throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName); @@ -517,7 +550,19 @@ public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotF return this.mHostPackageManager.getPackageInfo(packageName, flags); } + + @TargetApi(Build.VERSION_CODES.O) + @Override + public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int i) throws NameNotFoundException { + LoadedPlugin plugin = mPluginManager.getLoadedPlugin(versionedPackage.getPackageName()); + if (null != plugin) { + return plugin.mPackageInfo; + } + + return this.mHostPackageManager.getPackageInfo(versionedPackage, i); + } + @Override public String[] currentToCanonicalPackageNames(String[] names) { return this.mHostPackageManager.currentToCanonicalPackageNames(names); @@ -679,12 +724,63 @@ public String getNameForUid(int uid) { public List getInstalledApplications(int flags) { return this.mHostPackageManager.getInstalledApplications(flags); } - + + @TargetApi(Build.VERSION_CODES.O) + @Override + public boolean isInstantApp() { + return this.mHostPackageManager.isInstantApp(); + } + + @TargetApi(Build.VERSION_CODES.O) + @Override + public boolean isInstantApp(String s) { + return this.mHostPackageManager.isInstantApp(s); + } + + @TargetApi(Build.VERSION_CODES.O) + @Override + public int getInstantAppCookieMaxBytes() { + return this.mHostPackageManager.getInstantAppCookieMaxBytes(); + } + + @TargetApi(Build.VERSION_CODES.O) + @NonNull + @Override + public byte[] getInstantAppCookie() { + return this.mHostPackageManager.getInstantAppCookie(); + } + + @TargetApi(Build.VERSION_CODES.O) + @Override + public void clearInstantAppCookie() { + this.mHostPackageManager.clearInstantAppCookie(); + } + + @TargetApi(Build.VERSION_CODES.O) + @Override + public void updateInstantAppCookie(@Nullable byte[] bytes) { + this.mHostPackageManager.updateInstantAppCookie(bytes); + } + @Override public String[] getSystemSharedLibraryNames() { return this.mHostPackageManager.getSystemSharedLibraryNames(); } - + + @TargetApi(Build.VERSION_CODES.O) + @NonNull + @Override + public List getSharedLibraries(int i) { + return this.mHostPackageManager.getSharedLibraries(i); + } + + @TargetApi(Build.VERSION_CODES.O) + @Nullable + @Override + public ChangedPackages getChangedPackages(int i) { + return this.mHostPackageManager.getChangedPackages(i); + } + @Override public FeatureInfo[] getSystemAvailableFeatures() { return this.mHostPackageManager.getSystemAvailableFeatures(); @@ -1015,6 +1111,7 @@ public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { return this.mHostPackageManager.getUserBadgedIcon(icon, user); } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public Drawable getUserBadgeForDensity(UserHandle user, int density) { try { Method method = PackageManager.class.getMethod("getUserBadgeForDensity", UserHandle.class, int.class); @@ -1185,13 +1282,25 @@ public int getApplicationEnabledSetting(String packageName) { public boolean isSafeMode() { return this.mHostPackageManager.isSafeMode(); } - + + @TargetApi(Build.VERSION_CODES.O) + @Override + public void setApplicationCategoryHint(@NonNull String s, int i) { + this.mHostPackageManager.setApplicationCategoryHint(s, i); + } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public PackageInstaller getPackageInstaller() { return this.mHostPackageManager.getPackageInstaller(); } - + + @TargetApi(Build.VERSION_CODES.O) + @Override + public boolean canRequestPackageInstalls() { + return this.mHostPackageManager.canRequestPackageInstalls(); + } + @TargetApi(24) public int[] getPackageGids(String s, int i) throws NameNotFoundException { return mHostPackageManager.getPackageGids(s); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java index 0ffa847..ba7c133 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java @@ -29,7 +29,11 @@ public final class PackageParserCompat { public static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) throws PackageParser.PackageParserException { if (Build.VERSION.SDK_INT >= 24) { - return PackageParserV24.parsePackage(context, apk, flags); + if (Build.VERSION.PREVIEW_SDK_INT == 0) { + return PackageParserV24.parsePackage(context, apk, flags); + } else { + return PackageParserPPreview.parsePackage(context, apk, flags); + } } else if (Build.VERSION.SDK_INT >= 21) { return PackageParserLollipop.parsePackage(context, apk, flags); } else { @@ -37,6 +41,17 @@ public static final PackageParser.Package parsePackage(final Context context, fi } } + private static final class PackageParserPPreview { + + static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException { + PackageParser parser = new PackageParser(); + PackageParser.Package pkg = parser.parsePackage(apk, flags); + ReflectUtil.invokeNoException(PackageParser.class, null, "collectCertificates", + new Class[]{PackageParser.Package.class, boolean.class}, pkg, false); + return pkg; + } + } + private static final class PackageParserV24 { static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException { diff --git a/app/build.gradle b/app/build.gradle index e28db4b..ba814ef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,8 +49,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.1' - - //compile project (":CoreLibrary") +// compile 'com.didi.virtualapk:core:0.9.1' + compile project (path: ':CoreLibrary', configuration: 'default') } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 32f0d9c..e45164d 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { } ext { - VERSION_COMPILE_SDK = 23 + VERSION_COMPILE_SDK = 'android-P' VERSION_BUILD_TOOLS = '25.0.2' VERSION_MIN_SDK = 15 From 541677260868f011093cfaa142045db57642d297 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 18 Apr 2018 21:04:46 +0800 Subject: [PATCH 19/66] Updated version. --- CoreLibrary/upload.gradle | 2 +- PluginDemo/app/build.gradle | 2 +- PluginDemo/build.gradle | 2 +- README.md | 8 ++++---- app/build.gradle | 4 ++-- app/proguard-rules.pro | 2 +- build.gradle | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CoreLibrary/upload.gradle b/CoreLibrary/upload.gradle index 6b6a177..daeac9e 100644 --- a/CoreLibrary/upload.gradle +++ b/CoreLibrary/upload.gradle @@ -11,7 +11,7 @@ def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url group = GROUP_ID archivesBaseName = 'core' -version = "0.9.2-dev" +version = "0.9.4-dev" install { diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 5282899..6d17da1 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -51,7 +51,7 @@ dependencies { // the following aars are also compiled in host project, so they will be filterd when build plugin apk. // but, wo can still visit their Class and Resources. compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.1' + compile 'com.didi.virtualapk:core:0.9.4-dev' } apply plugin: 'com.didi.virtualapk.plugin' diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index 188d1ff..fdb3bdf 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' } } diff --git a/README.md b/README.md index 82315b6..ec1617c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' } ``` @@ -41,7 +41,7 @@ apply plugin: 'com.didi.virtualapk.host' Compile VirtualAPK in application module of `build.gradle`. ``` java -compile 'com.didi.virtualapk:core:0.9.1' +compile 'com.didi.virtualapk:core:0.9.4-dev' ``` Initialize `PluginManager` in `YourApplication::attachBaseContext()`. @@ -61,7 +61,7 @@ Modify proguard rules to keep VirtualAPK related files. -keep class com.didi.virtualapk.internal.PluginContentResolver { *; } -dontwarn com.didi.virtualapk.** --dontwarn android.content.pm.** +-dontwarn android.** -keep class android.** { *; } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' } ``` diff --git a/app/build.gradle b/app/build.gradle index ba814ef..4215722 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' -// compile 'com.didi.virtualapk:core:0.9.1' - compile project (path: ':CoreLibrary', configuration: 'default') + compile 'com.didi.virtualapk:core:0.9.4-dev' +// compile project (path: ':CoreLibrary', configuration: 'default') } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2497d80..656714e 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -121,5 +121,5 @@ -keep class com.didi.virtualapk.internal.PluginContentResolver { *; } -dontwarn com.didi.virtualapk.** --dontwarn android.content.pm.** +-dontwarn android.** -keep class android.** { *; } \ No newline at end of file diff --git a/build.gradle b/build.gradle index e45164d..c183166 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' @@ -15,7 +15,7 @@ buildscript { } ext { - VERSION_COMPILE_SDK = 'android-P' + VERSION_COMPILE_SDK = 27 VERSION_BUILD_TOOLS = '25.0.2' VERSION_MIN_SDK = 15 From e6cf90c3dc098abfb2dcb541a42a680cdd0a5983 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 19 Apr 2018 15:56:20 +0800 Subject: [PATCH 20/66] Updated version. --- PluginDemo/build.gradle | 2 +- README.md | 4 ++-- build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index 188d1ff..a186dee 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7' } } diff --git a/README.md b/README.md index 82315b6..72c8df3 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7' } ``` diff --git a/build.gradle b/build.gradle index 32f0d9c..6be4cf1 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.7-dev' + classpath 'com.didi.virtualapk:gradle:0.9.7' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 827a5c4..bc476c7 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.7-dev +VERSION=0.9.7 From 51d7910843277ed6d5183234634a8826f9606f5b Mon Sep 17 00:00:00 2001 From: superq_sky Date: Sat, 5 May 2018 12:41:36 +0800 Subject: [PATCH 21/66] Fixe ResolveException. --- README.md | 4 +- app/build.gradle | 2 +- build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- .../com.didi.virtualapk/VAHostPlugin.groovy | 39 ++++++++++++++----- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ec1617c..ce76e8d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' } ``` diff --git a/app/build.gradle b/app/build.gradle index 4215722..1445ae3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,6 +50,6 @@ dependencies { compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.didi.virtualapk:core:0.9.4-dev' -// compile project (path: ':CoreLibrary', configuration: 'default') +// compile project (':CoreLibrary') } \ No newline at end of file diff --git a/build.gradle b/build.gradle index c183166..35a8f5d 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 265c5d7..a51ad6b 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.1-dev +VERSION=0.9.8.2-dev diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy index c127aa1..9ea078d 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy @@ -2,13 +2,17 @@ package com.didi.virtualapk import com.android.build.gradle.api.ApplicationVariant import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.ide.ArtifactDependencyGraph import com.android.build.gradle.internal.pipeline.TransformTask +import com.android.build.gradle.internal.publishing.AndroidArtifacts import com.android.build.gradle.internal.transforms.ProGuardTransform import com.android.build.gradle.tasks.ProcessAndroidResources import com.didi.virtualapk.utils.FileUtil import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.component.ComponentIdentifier +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.artifacts.component.ProjectComponentIdentifier /** * VirtualAPK gradle plugin for host project, @@ -64,8 +68,13 @@ public class VAHostPlugin implements Plugin { return } - it.resolvedConfiguration.resolvedArtifacts.each { - deps.add("${configName} -> id: ${it.moduleVersion.id}, type: ${it.type}, ext: ${it.extension}") + try { + it.resolvedConfiguration.resolvedArtifacts.each { + deps.add("${configName} -> id: ${it.moduleVersion.id}, type: ${it.type}, ext: ${it.extension}") + } + + } catch (Exception e) { + deps.add("${configName} -> ${e}") } } Collections.sort(deps) @@ -74,14 +83,26 @@ public class VAHostPlugin implements Plugin { FileUtil.saveFile(vaHostDir, "versions", { List deps = new ArrayList() - Configuration compileClasspath = applicationVariant.variantData.variantDependency.compileClasspath - println "Used compileClasspath: ${compileClasspath.name}" - compileClasspath.resolvedConfiguration.resolvedArtifacts.each { - deps.add("${it.moduleVersion.id} ${it.file.length()}") + println "Used compileClasspath: ${applicationVariant.name}" + Set compileArtifacts = ArtifactDependencyGraph.getAllArtifacts( + applicationVariant.variantData.scope, AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH, null); + + compileArtifacts.each { ArtifactDependencyGraph.HashableResolvedArtifactResult artifact -> + ComponentIdentifier id = artifact.id.componentIdentifier + if (id instanceof ProjectComponentIdentifier) { + deps.add("${id.projectPath.replace(':', '')}:${ArtifactDependencyGraph.getVariant(artifact)}:unspecified ${artifact.file.length()}") + + } else if (id instanceof ModuleComponentIdentifier) { + deps.add("${id.group}:${id.module}:${id.version} ${artifact.file.length()}") + + } else { + deps.add("${artifact.id.displayName.replace(':', '')}:unspecified:unspecified ${artifact.file.length()}") + } } + Collections.sort(deps) - return deps; - }); + return deps + }) } } From 5b986493eb63229dd691500011b6b814dfaeb0ca Mon Sep 17 00:00:00 2001 From: superq_sky Date: Sat, 5 May 2018 12:54:44 +0800 Subject: [PATCH 22/66] Updated version. --- PluginDemo/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index fdb3bdf..c24a8de 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.1-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' } } From 96581b57fa45ee88c0a15b71a6dae440a76f1b1f Mon Sep 17 00:00:00 2001 From: superq_sky Date: Tue, 8 May 2018 13:54:32 +0800 Subject: [PATCH 23/66] Updated the demo's proguard rules. --- PluginDemo/app/proguard-rules.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PluginDemo/app/proguard-rules.pro b/PluginDemo/app/proguard-rules.pro index 49853ae..5d295ae 100644 --- a/PluginDemo/app/proguard-rules.pro +++ b/PluginDemo/app/proguard-rules.pro @@ -122,5 +122,5 @@ -keep class com.didi.virtualapk.internal.PluginContentResolver { *; } -dontwarn com.didi.virtualapk.** --dontwarn android.content.pm.** +-dontwarn android.** -keep class android.** { *; } \ No newline at end of file From 039922d2f78a78c847bca38a0ec18154c63a640b Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 7 May 2018 20:48:18 +0800 Subject: [PATCH 24/66] Tried to load plugin from assets when sdcard was not mounted. --- .../com/didi/virtualapk/MainActivity.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/src/main/java/com/didi/virtualapk/MainActivity.java b/app/src/main/java/com/didi/virtualapk/MainActivity.java index c465ced..65a31bd 100644 --- a/app/src/main/java/com/didi/virtualapk/MainActivity.java +++ b/app/src/main/java/com/didi/virtualapk/MainActivity.java @@ -117,11 +117,34 @@ public void onButtonClick(View v) { } private void loadPlugin(Context base) { + if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + Toast.makeText(this, "sdcard was NOT MOUNTED!", Toast.LENGTH_SHORT).show(); + } PluginManager pluginManager = PluginManager.getInstance(base); File apk = new File(Environment.getExternalStorageDirectory(), "Test.apk"); if (apk.exists()) { try { pluginManager.loadPlugin(apk); + Log.i(TAG, "Loaded plugin from apk: " + apk); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + try { + File file = new File(base.getFilesDir(), "Test.apk"); + java.io.InputStream inputStream = base.getAssets().open("Test.apk", 2); + java.io.FileOutputStream outputStream = new java.io.FileOutputStream(file); + byte[] buf = new byte[1024]; + int len; + + while ((len = inputStream.read(buf)) > 0) { + outputStream.write(buf, 0, len); + } + + outputStream.close(); + inputStream.close(); + pluginManager.loadPlugin(file); + Log.i(TAG, "Loaded plugin from assets: " + file); } catch (Exception e) { e.printStackTrace(); } From 2738630c24102199b74a316b45502fa19be6f128 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 9 May 2018 14:58:54 +0800 Subject: [PATCH 25/66] Fixed bug: mActivityManager was not initialized on apiLevel 26+. --- .../com/didi/virtualapk/PluginManager.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java index 0bc2f08..9c83914 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java @@ -96,11 +96,7 @@ private PluginManager(Context context) { private void prepare() { Systems.sHostContext = getHostContext(); this.hookInstrumentationAndHandler(); - if (Build.VERSION.SDK_INT >= 26) { - this.hookAMSForO(); - } else { - this.hookSystemServices(); - } + this.hookSystemServices(); hookDataBindingUtil(); } @@ -145,7 +141,13 @@ public void addCallback(Callback callback) { */ private void hookSystemServices() { try { - Singleton defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault"); + Singleton defaultSingleton; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManager.class, null, "IActivityManagerSingleton"); + } else { + defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault"); + } IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get()); // Hook IActivityManager from ActivityManagerNative @@ -159,17 +161,6 @@ private void hookSystemServices() { } } - - private void hookAMSForO() { - try { - Singleton defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManager.class, null, "IActivityManagerSingleton"); - IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get()); - ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy); - } catch (Exception e) { - e.printStackTrace(); - } - } - private void hookInstrumentationAndHandler() { try { Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext); From ad024b1b7c5a8b476b174bbce83cdad35c6f2dd7 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 9 May 2018 16:20:06 +0800 Subject: [PATCH 26/66] Optimized newActivity(). --- .../com/didi/virtualapk/internal/VAInstrumentation.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 6e5ffbb..72aa53c 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -173,12 +173,13 @@ public Activity newActivity(ClassLoader cl, String className, Intent intent) thr try { cl.loadClass(className); } catch (ClassNotFoundException e) { - LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent); - String targetClassName = PluginUtil.getTargetActivity(intent); + ComponentName component = PluginUtil.getComponent(intent); + LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component); + String targetClassName = component.getClassName(); - Log.i(TAG, String.format("newActivity[%s : %s]", className, targetClassName)); + Log.i(TAG, String.format("newActivity[%s : %s/%s]", className, component.getPackageName(), targetClassName)); - if (targetClassName != null) { + if (plugin != null) { Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent); activity.setIntent(intent); From 1b7cfbf722e00fb1ea858ba8e356c32ce4e92ec0 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 9 May 2018 20:16:48 +0800 Subject: [PATCH 27/66] Optimized process of copying native libs. --- .../virtualapk/internal/LoadedPlugin.java | 45 +----- .../com/didi/virtualapk/utils/PluginUtil.java | 136 ++++++++++-------- CoreLibrary/upload.gradle | 2 +- PluginDemo/app/build.gradle | 2 +- README.md | 2 +- app/build.gradle | 2 +- 6 files changed, 91 insertions(+), 98 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 5ff18da..0d7e508 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -47,7 +47,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.support.annotation.NonNull; @@ -147,22 +146,7 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags private Application mApplication; -// void dumpClass(Class cls) { -// Log.w(TAG, "########################################"); -// Log.w(TAG, "cls: " + cls.getName()); -// Log.w(TAG, "cls.super: " + cls.getSuperclass()); -// Log.w(TAG, "field size: " + cls.getDeclaredFields().length); -// Log.w(TAG, "method size: " + cls.getDeclaredMethods().length); -// -// for (Field f : cls.getDeclaredFields()) { -// Log.w(TAG, "field: " + f.getType().getName() + " " + f.getName()); -// } -// for (Method m : cls.getDeclaredMethods()) { -// Log.w(TAG, "method: " + m.getReturnType() + " " + m.getName() + " " + Arrays.toString(m.getParameterTypes())); -// } -// } - - LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws PackageParser.PackageParserException { + LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception { this.mPluginManager = pluginManager; this.mHostContext = context; this.mLocation = apk.getAbsolutePath(); @@ -172,14 +156,6 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo; this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath(); -// dumpClass(PackageParser.class); -// dumpClass(this.mPackage.getClass()); -// try { -// dumpClass(Class.forName("android.content.pm.PackageParser$SigningDetails")); -// } catch (ClassNotFoundException e) { -// Log.w(TAG, e); -// } - if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures; } else { @@ -239,25 +215,18 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags Map receivers = new HashMap(); for (PackageParser.Activity receiver : this.mPackage.receivers) { receivers.put(receiver.getComponentName(), receiver.info); - - try { - BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance()); - for (PackageParser.ActivityIntentInfo aii : receiver.intents) { - this.mHostContext.registerReceiver(br, aii); - } - } catch (Exception e) { - e.printStackTrace(); + + BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance()); + for (PackageParser.ActivityIntentInfo aii : receiver.intents) { + this.mHostContext.registerReceiver(br, aii); } } this.mReceiverInfos = Collections.unmodifiableMap(receivers); this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]); } - private void tryToCopyNativeLib(File apk) { - Bundle metaData = this.mPackageInfo.applicationInfo.metaData; - if (metaData != null && metaData.getBoolean("VA_IS_HAVE_LIB")) { - PluginUtil.copyNativeLib(apk, mHostContext, mPackageInfo, mNativeLibDir); - } + private void tryToCopyNativeLib(File apk) throws Exception { + PluginUtil.copyNativeLib(apk, mHostContext, mPackageInfo, mNativeLibDir); } public String getLocation() { diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java index 8acea2e..9950d07 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java @@ -23,15 +23,13 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; -import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import android.support.annotation.Keep; import android.text.TextUtils; +import android.util.Log; import android.view.ContextThemeWrapper; import com.didi.virtualapk.PluginManager; @@ -46,10 +44,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -186,68 +180,98 @@ public static IBinder getBinder(Bundle bundle, String key) { return null; } } - - public static void copyNativeLib(File apk, Context context, PackageInfo packageInfo, File nativeLibDir) { + + public static void copyNativeLib(File apk, Context context, PackageInfo packageInfo, File nativeLibDir) throws Exception { + long startTime = System.currentTimeMillis(); + ZipFile zipfile = new ZipFile(apk.getAbsolutePath()); + try { - String cpuArch; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - cpuArch = Build.SUPPORTED_ABIS[0]; + for (String cpuArch : Build.SUPPORTED_ABIS) { + if (findAndCopyNativeLib(zipfile, context, cpuArch, packageInfo, nativeLibDir)) { + return; + } + } + } else { - cpuArch = Build.CPU_ABI; - } - boolean findSo = false; - - ZipFile zipfile = new ZipFile(apk.getAbsolutePath()); - ZipEntry entry; - Enumeration e = zipfile.entries(); - while (e.hasMoreElements()) { - entry = (ZipEntry) e.nextElement(); - if (entry.isDirectory()) - continue; - if(entry.getName().endsWith(".so") && entry.getName().contains("lib/" + cpuArch)){ - findSo = true; - break; + if (findAndCopyNativeLib(zipfile, context, Build.CPU_ABI, packageInfo, nativeLibDir)) { + return; } } - e = zipfile.entries(); - while (e.hasMoreElements()) { - entry = (ZipEntry) e.nextElement(); - if (entry.isDirectory() || !entry.getName().endsWith(".so")) + + findAndCopyNativeLib(zipfile, context, "armeabi", packageInfo, nativeLibDir); + + } finally { + zipfile.close(); + Log.d("NativeLib", "Done! +" + (System.currentTimeMillis() - startTime) + "ms"); + } + } + + private static boolean findAndCopyNativeLib(ZipFile zipfile, Context context, String cpuArch, PackageInfo packageInfo, File nativeLibDir) throws Exception { + Log.d("NativeLib", "Try to copy plugin's cup arch: " + cpuArch); + boolean findLib = false; + boolean findSo = false; + byte buffer[] = null; + String libPrefix = "lib/" + cpuArch + "/"; + ZipEntry entry; + Enumeration e = zipfile.entries(); + + while (e.hasMoreElements()) { + entry = (ZipEntry) e.nextElement(); + String entryName = entry.getName(); + + if (entryName.charAt(0) < 'l') { + continue; + } + if (entryName.charAt(0) > 'l') { + break; + } + if (!findLib && !entryName.startsWith("lib/")) { + continue; + } + findLib = true; + if (!entryName.endsWith(".so") || !entryName.startsWith(libPrefix)) { + continue; + } + + if (buffer == null) { + findSo = true; + Log.d("NativeLib", "Found plugin's cup arch dir: " + cpuArch); + buffer = new byte[8192]; + } + + String libName = entryName.substring(entryName.lastIndexOf('/') + 1); + Log.d("NativeLib", "verify so " + libName); + File libFile = new File(nativeLibDir, libName); + String key = packageInfo.packageName + "_" + libName; + if (libFile.exists()) { + int VersionCode = Settings.getSoVersion(context, key); + if (VersionCode == packageInfo.versionCode) { + Log.d("NativeLib", "skip existing so : " + entry.getName()); continue; - if((findSo && entry.getName().contains("lib/" + cpuArch)) || (!findSo && entry.getName().contains("lib/armeabi/"))){ - String[] temp = entry.getName().split("/"); - String libName = temp[temp.length - 1]; - System.out.println("verify so " + libName); - File libFile = new File(nativeLibDir.getAbsolutePath() + File.separator + libName); - String key = packageInfo.packageName + "_" + libName; - if (libFile.exists()) { - int VersionCode = Settings.getSoVersion(context, key); - if (VersionCode == packageInfo.versionCode) { - System.out.println("skip existing so : " + entry.getName()); - continue; - } - } - FileOutputStream fos = new FileOutputStream(libFile); - System.out.println("copy so " + entry.getName() + " of " + cpuArch); - copySo(zipfile.getInputStream(entry), fos); - Settings.setSoVersion(context, key, packageInfo.versionCode); } - } - - zipfile.close(); - } catch (IOException e) { - e.printStackTrace(); + FileOutputStream fos = new FileOutputStream(libFile); + Log.d("NativeLib", "copy so " + entry.getName() + " of " + cpuArch); + copySo(buffer, zipfile.getInputStream(entry), fos); + Settings.setSoVersion(context, key, packageInfo.versionCode); } + + if (!findLib) { + Log.d("NativeLib", "Fast skip all!"); + return true; + } + + return findSo; } - - private static void copySo(InputStream input, OutputStream output) throws IOException { + + private static void copySo(byte[] buffer, InputStream input, OutputStream output) throws IOException { BufferedInputStream bufferedInput = new BufferedInputStream(input); BufferedOutputStream bufferedOutput = new BufferedOutputStream(output); int count; - byte data[] = new byte[8192]; - while ((count = bufferedInput.read(data, 0, 8192)) != -1) { - bufferedOutput.write(data, 0, count); + + while ((count = bufferedInput.read(buffer)) > 0) { + bufferedOutput.write(buffer, 0, count); } bufferedOutput.flush(); bufferedOutput.close(); diff --git a/CoreLibrary/upload.gradle b/CoreLibrary/upload.gradle index daeac9e..c4148e6 100644 --- a/CoreLibrary/upload.gradle +++ b/CoreLibrary/upload.gradle @@ -11,7 +11,7 @@ def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url group = GROUP_ID archivesBaseName = 'core' -version = "0.9.4-dev" +version = "0.9.5-dev" install { diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 6d17da1..1e652df 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -51,7 +51,7 @@ dependencies { // the following aars are also compiled in host project, so they will be filterd when build plugin apk. // but, wo can still visit their Class and Resources. compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.4-dev' + compile 'com.didi.virtualapk:core:0.9.5-dev' } apply plugin: 'com.didi.virtualapk.plugin' diff --git a/README.md b/README.md index ce76e8d..5996395 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ apply plugin: 'com.didi.virtualapk.host' Compile VirtualAPK in application module of `build.gradle`. ``` java -compile 'com.didi.virtualapk:core:0.9.4-dev' +compile 'com.didi.virtualapk:core:0.9.5-dev' ``` Initialize `PluginManager` in `YourApplication::attachBaseContext()`. diff --git a/app/build.gradle b/app/build.gradle index 1445ae3..b560bcd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.4-dev' + compile 'com.didi.virtualapk:core:0.9.5-dev' // compile project (':CoreLibrary') } \ No newline at end of file From efc966b88efb4e09614d942951bfd224f1931981 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Tue, 22 May 2018 14:54:33 +0800 Subject: [PATCH 28/66] Updated version and change log. --- CoreLibrary/upload.gradle | 2 +- PluginDemo/app/build.gradle | 2 +- PluginDemo/build.gradle | 2 +- README.md | 6 +++--- RELEASE-NOTES.md | 12 ++++++++++-- app/build.gradle | 2 +- build.gradle | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CoreLibrary/upload.gradle b/CoreLibrary/upload.gradle index c4148e6..62152ee 100644 --- a/CoreLibrary/upload.gradle +++ b/CoreLibrary/upload.gradle @@ -11,7 +11,7 @@ def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url group = GROUP_ID archivesBaseName = 'core' -version = "0.9.5-dev" +version = "0.9.5" install { diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 1e652df..78b5041 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -51,7 +51,7 @@ dependencies { // the following aars are also compiled in host project, so they will be filterd when build plugin apk. // but, wo can still visit their Class and Resources. compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.5-dev' + compile 'com.didi.virtualapk:core:0.9.5' } apply plugin: 'com.didi.virtualapk.plugin' diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index c24a8de..ea0b971 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2' } } diff --git a/README.md b/README.md index 5996395..573c074 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2' } ``` @@ -41,7 +41,7 @@ apply plugin: 'com.didi.virtualapk.host' Compile VirtualAPK in application module of `build.gradle`. ``` java -compile 'com.didi.virtualapk:core:0.9.5-dev' +compile 'com.didi.virtualapk:core:0.9.5' ``` Initialize `PluginManager` in `YourApplication::attachBaseContext()`. @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2' } ``` diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 04a8468..d9062a3 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,9 +1,9 @@ # Release Notes -## 0.9.0 +## com.didi.virtualapk:core:0.9.0 开源的第一个版本,支持了几乎所有 Android 特性,目前已经被大家广泛使用。 -## 0.9.1 +## com.didi.virtualapk:core:0.9.1 ##### 1. 最初,为了让程序更加健壮,当我们通过无效的 Intent 来启动插件中的 Activity,这个时候程序不会报错。尽管我们的初衷是好的,但现在我们觉得这种方式太过友好了,不利于bug的排查。 ```改动```:现在,启动无效的 Activity 会直接抛出 ActivityNotFoundException。 @@ -26,4 +26,12 @@ ```改动```:hook Android N 的资源,尽管是多余的。 +## com.didi.virtualapk:core:0.9.5 +1. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。 +2. 与 com.didi.virtualapk:gradle:0.9.8.2 搭配使用,支持官方 Data Binding。 + ## VirtualAPK 的构建部分已经开源了,![点击这里查看](https://github.com/didi/VirtualAPK/tree/master/virtualapk-gradle-plugin) + +## com.didi.virtualapk:gradle:0.9.8.2 +1. 适配android gradle 3.0.0 +2. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。 \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b560bcd..b598e70 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.5-dev' + compile 'com.didi.virtualapk:core:0.9.5' // compile project (':CoreLibrary') } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 35a8f5d..59ef6c0 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.2-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.2' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' From ec88e4ce3d4ae1fd339996f3eee23bef4d8a5da0 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Tue, 22 May 2018 16:32:52 +0800 Subject: [PATCH 29/66] Fixed bug: Cannot add task ':app:assemblePlugin' as a task with that name already exists. --- PluginDemo/build.gradle | 2 +- README.md | 4 ++-- RELEASE-NOTES.md | 6 +++++- build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- .../src/main/groovy/com.didi.virtualapk/BasePlugin.groovy | 3 +++ 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index ea0b971..440894b 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.2' + classpath 'com.didi.virtualapk:gradle:0.9.8.3' } } diff --git a/README.md b/README.md index 573c074..fd6d1dc 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.2' + classpath 'com.didi.virtualapk:gradle:0.9.8.3' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.2' + classpath 'com.didi.virtualapk:gradle:0.9.8.3' } ``` diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index d9062a3..27ea050 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -34,4 +34,8 @@ ## com.didi.virtualapk:gradle:0.9.8.2 1. 适配android gradle 3.0.0 -2. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。 \ No newline at end of file +2. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。 +3. 插件工程需要定义 productFlavors。 + +## com.didi.virtualapk:gradle:0.9.8.3 +1. 兼容不定义 productFlavors 的配置。 diff --git a/build.gradle b/build.gradle index 59ef6c0..f619ade 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.2' + classpath 'com.didi.virtualapk:gradle:0.9.8.3' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index a51ad6b..3e0a6c7 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.2-dev +VERSION=0.9.8.3 diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy index 1e20e00..5122c9d 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy @@ -98,6 +98,9 @@ public abstract class BasePlugin implements Plugin { } String createPluginTaskName(String name) { + if (name == 'assembleReleasePlugin') { + return '_assemblePlugin' + } return name.replace('Release', '') } From 6fd815f6b3ae7be7d2975ae1b7e552b0a684002a Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 23 May 2018 16:45:12 +0800 Subject: [PATCH 30/66] Fixed bug: file not found when compiling a library project. --- .../dependence/AarDependenceInfo.groovy | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy index 9d302d3..ac6706b 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy @@ -1,6 +1,9 @@ package com.didi.virtualapk.collector.dependence +import com.android.SdkConstants +import com.android.build.gradle.internal.TaskManager import com.android.builder.model.AndroidLibrary +import com.android.utils.FileUtils import com.didi.virtualapk.collector.res.ResourceEntry import com.didi.virtualapk.collector.res.StyleableEntry import com.google.common.collect.ArrayListMultimap @@ -65,6 +68,10 @@ class AarDependenceInfo extends DependenceInfo { def resKeys = [] as Set def rSymbol = library.symbolFile + if (!rSymbol.exists()) { + // module library + rSymbol = FileUtils.join(intermediatesDir, TaskManager.DIR_BUNDLES, library.projectVariant, SdkConstants.FN_RESOURCE_TEXT) + } if (rSymbol.exists()) { rSymbol.eachLine { line -> if (!line.empty) { @@ -87,7 +94,24 @@ class AarDependenceInfo extends DependenceInfo { * @return package name of this library */ public String getPackage() { - def xmlManifest = new XmlParser().parse(library.manifest) + File manifest + if (library.manifest.exists()) { + manifest = library.manifest + } else { + // module library + manifest = FileUtils.join(intermediatesDir, 'manifests', 'full', library.projectVariant, SdkConstants.ANDROID_MANIFEST_XML) + } + def xmlManifest = new XmlParser().parse(manifest) return xmlManifest.@package } + + File getIntermediatesDir() { + String path = library.folder.path + return new File(path.substring(0, path.indexOf("${File.separator}intermediates${File.separator}")), 'intermediates') + } + + @Override + String toString() { + return "${super.toString()} -> ${library}" + } } \ No newline at end of file From 03178da2f99006623de8a61169d8347a0b4d94fa Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 23 May 2018 16:58:49 +0800 Subject: [PATCH 31/66] Optimized VAExtention. --- .../com.didi.virtualapk/VAExtention.groovy | 93 ++++++++++++++----- .../com.didi.virtualapk/VAPlugin.groovy | 10 +- .../collector/ResourceCollector.groovy | 8 +- .../hooker/DxTaskHooker.groovy | 10 +- .../hooker/GradleTaskHooker.groovy | 9 +- .../hooker/MergeAssetsHooker.groovy | 2 +- .../hooker/MergeJniLibsHooker.groovy | 2 +- .../hooker/MergeManifestsHooker.groovy | 2 +- .../hooker/PrepareDependenciesHooker.groovy | 6 +- .../hooker/ProcessResourcesHooker.groovy | 12 +-- .../hooker/ProguardHooker.groovy | 2 +- .../tasks/AssemblePlugin.groovy | 4 +- .../StripClassAndResTransform.groovy | 7 +- .../utils/CheckList.groovy | 18 ++-- 14 files changed, 123 insertions(+), 62 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy index f1d8c79..609f4c7 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy @@ -13,44 +13,93 @@ import com.didi.virtualapk.utils.CheckList public class VAExtention { /** Custom defined resource package Id **/ - int packageId + private int packageId /** Local host application directory or Jenkins build number, fetch config files from here **/ - String targetHost + private String targetHost /** Apply Host Proguard Mapping or not**/ - boolean applyHostMapping = true + private boolean applyHostMapping = true /** Exclude dependent aar or jar **/ - Collection excludes = new HashSet<>() - - /** host Symbol file - Host_R.txt */ - File hostSymbolFile + private Collection excludes = new HashSet<>() /** host dependence file - version.txt*/ - File hostDependenceFile + public File hostDependenceFile - Collection stripDependencies = [] - Collection retainedAarLibs = [] + private final Map vaContextMap = [] as HashMap - /** Variant application id */ - String packageName + public VAContext getVaContext(String variantName) { + synchronized (vaContextMap) { + VAContext vaContext = vaContextMap.get(variantName) + if (vaContext == null) { + vaContext = new VAContext(variantName) + vaContextMap.put(variantName, vaContext) + } + return vaContext + } + } - /** Package path for java classes */ - String packagePath + public int getPackageId() { + return packageId + } - /** File of split R.java */ - File splitRJavaFile + public void setPackageId(int packageId) { + this.packageId = packageId + } - final CheckList checkList = new CheckList() + public String getTargetHost() { + return targetHost + } + + public void setTargetHost(String targetHost) { + this.targetHost = targetHost + } + + public boolean getApplyHostMapping() { + return applyHostMapping + } + + public void setApplyHostMapping(boolean applyHostMapping) { + this.applyHostMapping = applyHostMapping + } - public File getBuildDir(VariantScope scope) { - return new File(scope.getGlobalScope().getIntermediatesDir(), - "virtualapk/" + scope.getVariantConfiguration().getDirName()) + Collection getExcludes() { + return excludes } - public void exclude(final String...filters) { + public void setExcludes(final String...filters) { if (null != filters) { for (final String filter :filters) { - this.excludes.add(filter); + this.excludes.add(filter) } } } + + public static class VAContext { + + /** host Symbol file - Host_R.txt */ + public File hostSymbolFile + + public Collection stripDependencies = [] + public Collection retainedAarLibs = [] + + /** Variant application id */ + public String packageName + + /** Package path for java classes */ + public String packagePath + + /** File of split R.java */ + public File splitRJavaFile + + public final CheckList checkList + + VAContext(String variantName) { + checkList = new CheckList(variantName) + } + + public File getBuildDir(VariantScope scope) { + return new File(scope.getGlobalScope().getIntermediatesDir(), + "virtualapk/" + scope.getVariantConfiguration().getDirName()) + } + + } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 66f6ac2..95e7d40 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -80,6 +80,8 @@ class VAPlugin extends BasePlugin { hostDir.mkdirs() } + virtualApk.hostDependenceFile = new File(hostDir, "versions.txt") + project.afterEvaluate { if (!isBuildingPlugin) { return @@ -98,10 +100,10 @@ class VAPlugin extends BasePlugin { checkConfig() virtualApk.with { - packageName = variant.applicationId - packagePath = packageName.replace('.'.charAt(0), File.separatorChar) - hostSymbolFile = new File(hostDir, "Host_R.txt") - hostDependenceFile = new File(hostDir, "versions.txt") + VAExtention.VAContext vaContext = getVaContext(variant.name) + vaContext.packageName = variant.applicationId + vaContext.packagePath = vaContext.packageName.replace('.'.charAt(0), File.separatorChar) + vaContext.hostSymbolFile = new File(hostDir, "Host_R.txt") } } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy index 1369217..09fed28 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/ResourceCollector.groovy @@ -19,6 +19,7 @@ class ResourceCollector { private Project project private VAExtention virtualApk + private VAExtention.VAContext vaContext /** * Gradle task of process resource in Android build system @@ -56,11 +57,12 @@ class ResourceCollector { this.project = project virtualApk = project.virtualApk + vaContext = virtualApk.getVaContext(par.variantName) processResTask = par allRSymbolFile = par.textSymbolOutputFile - hostRSymbolFile = virtualApk.hostSymbolFile + hostRSymbolFile = vaContext.hostSymbolFile } /** @@ -83,7 +85,7 @@ class ResourceCollector { reassignPluginResourceId() //5、Collect all the resources in the retained AARs, to regenerate the R java file that uses the new resource ID - virtualApk.retainedAarLibs.each { + vaContext.retainedAarLibs.each { gatherReservedAarResources(it) } } @@ -263,7 +265,7 @@ class ResourceCollector { } vendorTypeFile.withPrintWriter { pw -> - virtualApk.retainedAarLibs.each { aarLib -> + vaContext.retainedAarLibs.each { aarLib -> pw.println "${aarLib.name}" aarLib.aarResources.values().each { diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy index 817aff2..f82a9b5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy @@ -44,7 +44,7 @@ class DxTaskHooker extends GradleTaskHooker { } void handleFile(File file) { - if (file.directory && file.path.endsWith(virtualApk.packagePath)) { + if (file.directory && file.path.endsWith(vaContext.packagePath)) { if (recompileSplitR(file)) { Log.i 'DxTaskHooker', "Recompiled R.java in dir: ${file.absoluteFile}" @@ -59,11 +59,11 @@ class DxTaskHooker extends GradleTaskHooker { } // VirtualApk Package Dir - File pkgDir = new File(unzipJarDir, virtualApk.packagePath) + File pkgDir = new File(unzipJarDir, vaContext.packagePath) if (pkgDir.exists()) { if (recompileSplitR(pkgDir)) { Log.i 'DxTaskHooker', "Recompiled R.java in jar: ${file.absoluteFile}" - File backupDir = new File(virtualApk.getBuildDir(scope), 'origin/classes') + File backupDir = new File(vaContext.getBuildDir(scope), 'origin/classes') backupDir.deleteDir() project.copy { from file @@ -98,10 +98,10 @@ class DxTaskHooker extends GradleTaskHooker { it.delete() } - String baseDir = pkgDir.path - "${File.separator}${virtualApk.packagePath}" + String baseDir = pkgDir.path - "${File.separator}${vaContext.packagePath}" project.ant.javac( - srcdir: virtualApk.splitRJavaFile.parentFile, + srcdir: vaContext.splitRJavaFile.parentFile, source: apkVariant.javaCompiler.sourceCompatibility, target: apkVariant.javaCompiler.targetCompatibility, destdir: new File(baseDir)) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy index efd89ec..756f79e 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy @@ -5,6 +5,7 @@ import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.scope.VariantScope import com.android.build.gradle.internal.variant.BaseVariantData import com.didi.virtualapk.VAExtention +import com.didi.virtualapk.VAExtention.VAContext import org.gradle.api.Project import org.gradle.api.Task @@ -32,7 +33,7 @@ public abstract class GradleTaskHooker { this.apkVariant = apkVariant this.virtualApk = project.virtualApk - virtualApk.checkList.addCheckPoint(apkVariant.name, taskName) + vaContext.checkList.addCheckPoint(taskName) } public Project getProject() { @@ -55,8 +56,12 @@ public abstract class GradleTaskHooker { return this.virtualApk } + public VAContext getVaContext() { + return this.virtualApk.getVaContext(apkVariant.name) + } + public void mark() { - virtualApk.checkList.mark(apkVariant.name, taskName) + vaContext.checkList.mark(taskName) } public void setTaskHookerManager(TaskHookerManager taskHookerManager) { diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy index c646b23..8b4ef5f 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy @@ -34,7 +34,7 @@ class MergeAssetsHooker extends GradleTaskHooker { @Override void beforeTaskExecute(MergeSourceSetFolders task) { - Set strippedAssetPaths = virtualApk.stripDependencies.collect { + Set strippedAssetPaths = vaContext.stripDependencies.collect { if (it instanceof AarDependenceInfo) { return it.assetsFolder.path } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy index c50047a..4dce896 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy @@ -36,7 +36,7 @@ class MergeJniLibsHooker extends GradleTaskHooker { @Override void beforeTaskExecute(TransformTask task) { - def excludeJniFiles = jniLibsCollector.collect(virtualApk.stripDependencies) + def excludeJniFiles = jniLibsCollector.collect(vaContext.stripDependencies) excludeJniFiles.each { androidConfig.packagingOptions.exclude("/${it}") diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy index 6c77e5a..bcf30e5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeManifestsHooker.groovy @@ -39,7 +39,7 @@ class MergeManifestsHooker extends GradleTaskHooker { @Override void beforeTaskExecute(MergeManifests task) { - def stripAarNames = virtualApk.stripDependencies. + def stripAarNames = vaContext.stripDependencies. findAll { it.dependenceType == DependenceInfo.DependenceType.AAR }. diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index b438531..1437741 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -110,14 +110,14 @@ class PrepareDependenciesHooker extends GradleTaskHooker { } - File hostDir = virtualApk.getBuildDir(scope) + File hostDir = vaContext.getBuildDir(scope) FileUtil.saveFile(hostDir, "${taskName}-stripDependencies", stripDependencies) FileUtil.saveFile(hostDir, "${taskName}-retainedAarLibs", retainedAarLibs) FileUtil.saveFile(hostDir, "${taskName}-retainedJarLib", retainedJarLib) Log.i 'PrepareDependenciesHooker', "Analyzed all dependencis. Get more infomation in dir: ${hostDir.absoluteFile}" - virtualApk.stripDependencies = stripDependencies - virtualApk.retainedAarLibs = retainedAarLibs + vaContext.stripDependencies = stripDependencies + vaContext.retainedAarLibs = retainedAarLibs mark() } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy index 1ca6f57..cec55ca 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProcessResourcesHooker.groovy @@ -116,7 +116,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { //Modify the arsc file, and replace ids of related xml files aapt.filterPackage(retainedTypes, retainedStylealbes, virtualApk.packageId, resIdMap, libRefTable, updatedResources) - File hostDir = virtualApk.getBuildDir(scope) + File hostDir = vaContext.getBuildDir(scope) FileUtil.saveFile(hostDir, "${taskName}-retainedTypes", retainedTypes) FileUtil.saveFile(hostDir, "${taskName}-retainedStylealbes", retainedStylealbes) FileUtil.saveFile(hostDir, "${taskName}-filteredResources", filteredResources) @@ -149,7 +149,7 @@ class ProcessResourcesHooker extends GradleTaskHooker { * */ def updateRJava(Aapt aapt, File sourceOutputDir) { - File vaBuildDir = virtualApk.getBuildDir(scope) + File vaBuildDir = vaContext.getBuildDir(scope) File backupDir = new File(vaBuildDir, "origin/r") project.ant.move(todir: backupDir) { @@ -160,16 +160,16 @@ class ProcessResourcesHooker extends GradleTaskHooker { FileUtil.deleteEmptySubfolders(sourceOutputDir) - def rSourceFile = new File(sourceOutputDir, "${virtualApk.packagePath}${File.separator}R.java") + def rSourceFile = new File(sourceOutputDir, "${vaContext.packagePath}${File.separator}R.java") aapt.generateRJava(rSourceFile, apkVariant.applicationId, resourceCollector.allResources, resourceCollector.allStyleables) Log.i 'ProcessResourcesHooker', "Updated R.java: ${rSourceFile.absoluteFile}" - def splitRSourceFile = new File(vaBuildDir, "source${File.separator}r${File.separator}${virtualApk.packagePath}${File.separator}R.java") + def splitRSourceFile = new File(vaBuildDir, "source${File.separator}r${File.separator}${vaContext.packagePath}${File.separator}R.java") aapt.generateRJava(splitRSourceFile, apkVariant.applicationId, resourceCollector.pluginResources, resourceCollector.pluginStyleables) Log.i 'ProcessResourcesHooker', "Updated R.java: ${splitRSourceFile.absoluteFile}" - virtualApk.splitRJavaFile = splitRSourceFile + vaContext.splitRJavaFile = splitRSourceFile - virtualApk.retainedAarLibs.each { + vaContext.retainedAarLibs.each { def aarPackage = it.package def rJavaFile = new File(sourceOutputDir, "${aarPackage.replace('.'.charAt(0), File.separatorChar)}${File.separator}R.java") aapt.generateRJava(rJavaFile, aarPackage, it.aarResources, it.aarStyleables) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy index 7e2f9bd..d6df4f3 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy @@ -63,7 +63,7 @@ class ProguardHooker extends GradleTaskHooker { proguardTransform.applyTestedMapping(applyMappingFile) } - virtualApk.stripDependencies.each { + vaContext.stripDependencies.each { proguardTransform.libraryJar(it.jarFile) if (it instanceof AarDependenceInfo) { it.localJars.each { diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy index a46b24a..720c1b5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy @@ -2,6 +2,7 @@ package com.didi.virtualapk.tasks import com.android.annotations.NonNull import com.android.build.gradle.api.ApkVariant +import com.didi.virtualapk.VAExtention import com.didi.virtualapk.utils.TaskUtil import com.sun.istack.internal.NotNull import org.gradle.api.Action @@ -40,7 +41,8 @@ public class AssemblePlugin extends DefaultTask { */ @TaskAction public void outputPluginApk() { - project.virtualApk.checkList.check(variantName) + VAExtention virtualApk = project.virtualApk + virtualApk.getVaContext(variantName).checkList.check() getProject().copy { from originApkFile diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy index dac37ea..1d9aeb8 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy @@ -27,7 +27,7 @@ class StripClassAndResTransform extends Transform { void onProjectAfterEvaluate() { project.android.applicationVariants.each { ApplicationVariant variant -> - virtualApk.checkList.addCheckPoint(variant.name, name) + virtualApk.getVaContext(variant.name).checkList.addCheckPoint(name) } } @@ -57,7 +57,8 @@ class StripClassAndResTransform extends Transform { @Override void transform(final TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { - def stripEntries = classAndResCollector.collect(virtualApk.stripDependencies) + VAExtention.VAContext vaContext = virtualApk.getVaContext(transformInvocation.context.variantName) + def stripEntries = classAndResCollector.collect(vaContext.stripDependencies) if (!isIncremental()) { transformInvocation.outputProvider.deleteAll() @@ -97,6 +98,6 @@ class StripClassAndResTransform extends Transform { } } - virtualApk.checkList.mark(transformInvocation.context.variantName, name) + vaContext.checkList.mark(name) } } \ No newline at end of file diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy index d707eb7..57cdd99 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy @@ -2,9 +2,13 @@ package com.didi.virtualapk.utils class CheckList { Map mMap = new LinkedHashMap<>() + String variantName - void addCheckPoint(String variantName, String name) { - String key = "${variantName}:${name}" + CheckList(String variantName) { + this.variantName = variantName + } + + void addCheckPoint(String key) { if (mMap.containsKey(key)) { throw new RuntimeException("[${key}] has already exists.") } @@ -12,18 +16,14 @@ class CheckList { // Log.i('test', "addCheckPoint: ${key}") } - void mark(String variantName, String name) { - String key = "${variantName}:${name}" + void mark(String key) { mMap.put(key, true) // Log.i('test', "mark: ${key}") } - void check(String variantName) { + void check() { boolean check = true - Map matched = mMap.findAll { -// Log.i 'CheckList', "all: ${it.key}: ${it.value}" - it.key.startsWith("${variantName}:") - } + Map matched = mMap matched.each { check &= it.value From e6174132a0e83c49ed8f70561b5a94c59951b195 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 23 May 2018 17:12:24 +0800 Subject: [PATCH 32/66] Optimized log. --- .../com.didi.virtualapk/BasePlugin.groovy | 6 +++--- .../com.didi.virtualapk/VAHostPlugin.groovy | 10 ++++++---- .../groovy/com.didi.virtualapk/VAPlugin.groovy | 2 +- .../collector/HostJniLibsCollector.groovy | 2 +- .../dependence/AarDependenceInfo.groovy | 7 +++++++ .../dependence/JarDependenceInfo.groovy | 2 ++ .../hooker/PrepareDependenciesHooker.groovy | 4 ++++ .../transform/StripClassAndResTransform.groovy | 7 ++++--- .../transform/TransformWrapper.groovy | 9 +++++---- .../com.didi.virtualapk/utils/LogUtil.groovy | 14 -------------- .../ProcessDataBinding.java | 4 +++- .../java/com/didi/virtualapk/utils/Log.java | 18 ++++++++++++++++++ 12 files changed, 54 insertions(+), 31 deletions(-) delete mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy create mode 100644 virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/utils/Log.java diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy index 5122c9d..6fd7a4e 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/BasePlugin.groovy @@ -57,7 +57,7 @@ public abstract class BasePlugin implements Plugin { Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ('preVariantWork' == method.name) { checkVariantFactoryInvoked = true - Log.i 'Plugin', "Evaluating VirtualApk's configurations..." + Log.i 'VAPlugin', "Evaluating VirtualApk's configurations..." boolean isBuildingPlugin = evaluateBuildingPlugin(appPlugin, project) beforeCreateAndroidTasks(isBuildingPlugin) } @@ -127,7 +127,7 @@ public abstract class BasePlugin implements Plugin { } // pluginTasks.each { -// Log.i 'Plugin', "pluginTask: ${it}" +// Log.i 'VAPlugin', "pluginTask: ${it}" // } boolean isBuildingPlugin = false @@ -135,7 +135,7 @@ public abstract class BasePlugin implements Plugin { targetTasks.every { String taskName = nameMatcher.find(it, pluginTasks) if (taskName != null) { -// Log.i 'Plugin', "Found task name '${taskName}' by given name '${it}'" +// Log.i 'VAPlugin', "Found task name '${taskName}' by given name '${it}'" isBuildingPlugin = true return false } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy index 9ea078d..e67c53c 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAHostPlugin.groovy @@ -8,6 +8,7 @@ import com.android.build.gradle.internal.publishing.AndroidArtifacts import com.android.build.gradle.internal.transforms.ProGuardTransform import com.android.build.gradle.tasks.ProcessAndroidResources import com.didi.virtualapk.utils.FileUtil +import com.didi.virtualapk.utils.Log import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.component.ComponentIdentifier @@ -23,8 +24,9 @@ import org.gradle.api.artifacts.component.ProjectComponentIdentifier */ public class VAHostPlugin implements Plugin { + public static final String TAG = 'VAHostPlugin' Project project - File vaHostDir; + File vaHostDir @Override public void apply(Project project) { @@ -33,7 +35,7 @@ public class VAHostPlugin implements Plugin { //The target project must be a android application module if (!project.plugins.hasPlugin('com.android.application')) { - System.err.println("application required!"); + Log.e(TAG, "application required!") return; } @@ -83,9 +85,9 @@ public class VAHostPlugin implements Plugin { FileUtil.saveFile(vaHostDir, "versions", { List deps = new ArrayList() - println "Used compileClasspath: ${applicationVariant.name}" + Log.i TAG, "Used compileClasspath: ${applicationVariant.name}" Set compileArtifacts = ArtifactDependencyGraph.getAllArtifacts( - applicationVariant.variantData.scope, AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH, null); + applicationVariant.variantData.scope, AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH, null) compileArtifacts.each { ArtifactDependencyGraph.HashableResolvedArtifactResult artifact -> ComponentIdentifier id = artifact.id.componentIdentifier diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 95e7d40..8da6902 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -53,7 +53,7 @@ class VAPlugin extends BasePlugin { protected void beforeCreateAndroidTasks(boolean isBuildingPlugin) { this.isBuildingPlugin = isBuildingPlugin if (!isBuildingPlugin) { - Log.i 'Plugin', "Skipped all VirtualApk's configurations!" + Log.i 'VAPlugin', "Skipped all VirtualApk's configurations!" return } stripClassAndResTransform = new StripClassAndResTransform(project) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy index 3049ca7..943e51a 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostJniLibsCollector.groovy @@ -120,7 +120,7 @@ class HostJniLibsCollector { @NonNull File file, @NonNull String path, @NonNull AarDependenceInfo aarDependenceInfo, - @NonNull ListMultimap content) { + @NonNull ListMultimap content) { File[] children = file.listFiles(new FilenameFilter() { @Override public boolean accept(File f, String name) { diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy index ac6706b..b949355 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy @@ -6,6 +6,7 @@ import com.android.builder.model.AndroidLibrary import com.android.utils.FileUtils import com.didi.virtualapk.collector.res.ResourceEntry import com.didi.virtualapk.collector.res.StyleableEntry +import com.didi.virtualapk.utils.Log import com.google.common.collect.ArrayListMultimap import com.google.common.collect.ListMultimap import com.google.common.collect.Lists @@ -39,6 +40,7 @@ class AarDependenceInfo extends DependenceInfo { @Override File getJarFile() { + Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s jar file: ${library.jarFile}" return library.jarFile } @@ -48,14 +50,17 @@ class AarDependenceInfo extends DependenceInfo { } File getAssetsFolder() { + Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s assets folder: ${library.assetsFolder}" return library.assetsFolder } File getJniFolder() { + Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s jni folder: ${library.jniFolder}" return library.jniFolder } Collection getLocalJars() { + Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s local jars: ${library.localJars}" return library.localJars } @@ -73,6 +78,7 @@ class AarDependenceInfo extends DependenceInfo { rSymbol = FileUtils.join(intermediatesDir, TaskManager.DIR_BUNDLES, library.projectVariant, SdkConstants.FN_RESOURCE_TEXT) } if (rSymbol.exists()) { + Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s symbol file: ${rSymbol}" rSymbol.eachLine { line -> if (!line.empty) { def tokenizer = new StringTokenizer(line) @@ -101,6 +107,7 @@ class AarDependenceInfo extends DependenceInfo { // module library manifest = FileUtils.join(intermediatesDir, 'manifests', 'full', library.projectVariant, SdkConstants.ANDROID_MANIFEST_XML) } + Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s manifest file: ${manifest}" def xmlManifest = new XmlParser().parse(manifest) return xmlManifest.@package } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy index cd24e5f..2a4e108 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy @@ -1,6 +1,7 @@ package com.didi.virtualapk.collector.dependence import com.android.builder.model.JavaLibrary +import com.didi.virtualapk.utils.Log /** * Represents a Jar library. This could be the output of a Java project. @@ -18,6 +19,7 @@ class JarDependenceInfo extends DependenceInfo { @Override File getJarFile() { + Log.i 'JarDependenceInfo', "Found [${library.resolvedCoordinates}]'s jar file: ${library.jarFile}" return library.jarFile } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index 1437741..9eec5ff 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -73,6 +73,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { dependencies.libraries.each { def mavenCoordinates = it.resolvedCoordinates if (hostDependencies.contains("${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}")) { + Log.i 'PrepareDependenciesHooker', "Need strip aar: ${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}:${mavenCoordinates.version}" stripDependencies.add( new AarDependenceInfo( mavenCoordinates.groupId, @@ -81,6 +82,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { it)) } else { + Log.i 'PrepareDependenciesHooker', "Need retain aar: ${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}:${mavenCoordinates.version}" retainedAarLibs.add( new AarDependenceInfo( mavenCoordinates.groupId, @@ -93,6 +95,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { dependencies.javaLibraries.each { def mavenCoordinates = it.resolvedCoordinates if (hostDependencies.contains("${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}")) { + Log.i 'PrepareDependenciesHooker', "Need strip jar: ${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}:${mavenCoordinates.version}" stripDependencies.add( new JarDependenceInfo( mavenCoordinates.groupId, @@ -100,6 +103,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { mavenCoordinates.version, it)) } else { + Log.i 'PrepareDependenciesHooker', "Need retain jar: ${mavenCoordinates.groupId}:${mavenCoordinates.artifactId}:${mavenCoordinates.version}" retainedJarLib.add( new JarDependenceInfo( mavenCoordinates.groupId, diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy index 1d9aeb8..411d856 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/StripClassAndResTransform.groovy @@ -66,10 +66,10 @@ class StripClassAndResTransform extends Transform { transformInvocation.inputs.each { it.directoryInputs.each { directoryInput -> -// Log.i 'StripClassAndResTransform', "input dir: ${directoryInput.file.absoluteFile}" + Log.i 'StripClassAndResTransform', "input dir: ${directoryInput.file.absoluteFile}" def destDir = transformInvocation.outputProvider.getContentLocation( directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) -// Log.i 'StripClassAndResTransform', "output dir: ${destDir.absoluteFile}" + Log.i 'StripClassAndResTransform', "output dir: ${destDir.absoluteFile}" directoryInput.file.traverse(type: FileType.FILES) { def entryName = it.path.substring(directoryInput.file.path.length() + 1) // Log.i 'StripClassAndResTransform', "found file: ${it.absoluteFile}" @@ -85,11 +85,12 @@ class StripClassAndResTransform extends Transform { } it.jarInputs.each { jarInput -> -// Log.i 'StripClassAndResTransform', "${name} jar: ${jarInput.file.absoluteFile}" + Log.i 'StripClassAndResTransform', "input jar: ${jarInput.file.absoluteFile}" Set jarEntries = HostClassAndResCollector.unzipJar(jarInput.file) if (!stripEntries.containsAll(jarEntries)){ def dest = transformInvocation.outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR) + Log.i 'StripClassAndResTransform', "output jar: ${dest.absoluteFile}" FileUtils.copyFile(jarInput.file, dest) // Log.i 'StripClassAndResTransform', "Copied to jar: ${dest.absoluteFile}" } else { diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy index 720352e..e78d584 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy @@ -1,6 +1,7 @@ package com.didi.virtualapk.transform import com.android.build.api.transform.* +import com.didi.virtualapk.utils.Log public class TransformWrapper extends Transform { @@ -75,20 +76,20 @@ public class TransformWrapper extends Transform { Collection inputs = invocation.getInputs() for (TransformInput input : inputs) { for (DirectoryInput directoryInput : input.getDirectoryInputs()) { - println "input dir: ${directoryInput.getFile()}" + Log.i "${name}", "input dir: ${directoryInput.getFile()}" } for (JarInput jarInput : input.getJarInputs()) { - println "input jar: ${jarInput.getFile()}" + Log.i "${name}", "input jar: ${jarInput.getFile()}" } } Collection referencedInputs = invocation.getReferencedInputs(); for (TransformInput transformInput : referencedInputs) { for (DirectoryInput directoryInput : transformInput.getDirectoryInputs()) { - println "referenced input dir: ${directoryInput.getFile()}" + Log.i "${name}", "referenced input dir: ${directoryInput.getFile()}" } for (JarInput jarInput : transformInput.getJarInputs()) { - println "referenced input jar: ${jarInput.getFile()}" + Log.i "${name}", "referenced input jar: ${jarInput.getFile()}" } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy deleted file mode 100644 index 4595506..0000000 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/LogUtil.groovy +++ /dev/null @@ -1,14 +0,0 @@ -package com.didi.virtualapk.utils - -public final class Log { - - private Log() { - - } - - public static int i(String tag, String msg) { - println "[INFO][${tag}] ${msg}" - return 0 - } - -} diff --git a/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java b/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java index 58ee521..8e8b674 100644 --- a/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java +++ b/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/databinding/annotationprocessor/ProcessDataBinding.java @@ -16,6 +16,8 @@ package com.didi.virtualapk.databinding.annotationprocessor; +import com.didi.virtualapk.utils.Log; + import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.Closeable; @@ -100,7 +102,7 @@ private void readDataBinderMapper() { writer.newLine(); } - System.out.print("Generated java source file: " + DATA_BINDER_MAPPER_PACKAGE + "." + className); + Log.i("ProcessDataBinding", "Generated java source file: " + DATA_BINDER_MAPPER_PACKAGE + "." + className); } catch (Exception e) { e.printStackTrace(); diff --git a/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/utils/Log.java b/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/utils/Log.java new file mode 100644 index 0000000..b46a0a7 --- /dev/null +++ b/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/utils/Log.java @@ -0,0 +1,18 @@ +package com.didi.virtualapk.utils; + +public final class Log { + + private Log() { + + } + + public static int i(String tag, String msg) { + System.out.println("[INFO][" + tag + "] " + msg); + return 0; + } + + public static int e(String tag, String msg) { + System.err.println("[ERROR][" + tag + "] " + msg); + return 0; + } +} From 0c42de6c86b7ed5368cd9f0a1722a94c06d2f034 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 23 May 2018 17:17:14 +0800 Subject: [PATCH 33/66] Added plugin's configuration check. --- .../com.didi.virtualapk/VAExtention.groovy | 47 +++++++++++++ .../com.didi.virtualapk/VAPlugin.groovy | 66 ++++++++++++++++--- .../hooker/PrepareDependenciesHooker.groovy | 5 +- .../tasks/AssemblePlugin.groovy | 11 ++++ 4 files changed, 115 insertions(+), 14 deletions(-) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy index 609f4c7..57ce6c4 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy @@ -4,6 +4,7 @@ import com.android.build.gradle.internal.scope.VariantScope import com.didi.virtualapk.collector.dependence.AarDependenceInfo import com.didi.virtualapk.collector.dependence.DependenceInfo import com.didi.virtualapk.utils.CheckList +import com.didi.virtualapk.utils.Log /** * VirtualApk extension for plugin projects. @@ -20,9 +21,14 @@ public class VAExtention { private boolean applyHostMapping = true /** Exclude dependent aar or jar **/ private Collection excludes = new HashSet<>() + private boolean forceUseHostDependences + private ArrayList warningList = new ArrayList<>() /** host dependence file - version.txt*/ public File hostDependenceFile + //group:artifact -> group:artifact:version + private Map hostDependencies + private HashSet flagTable = new HashSet<>() private final Map vaContextMap = [] as HashMap @@ -73,6 +79,47 @@ public class VAExtention { } } + public boolean getForceUseHostDependences() { + return forceUseHostDependences + } + + public void setForceUseHostDependences(boolean forceUseHostDependences) { + this.forceUseHostDependences = forceUseHostDependences + } + + public Map getHostDependencies() { + if (hostDependencies == null) { + hostDependencies = [] as LinkedHashMap + hostDependenceFile.splitEachLine('\\s+', { columns -> + final def module = columns[0].split(':') + hostDependencies.put("${module[0]}:${module[1]}", [group: module[0], name: module[1], version: module[2]]) + }) + } + return hostDependencies + } + + public void addWarning(String detail) { + warningList.add(detail) + } + + public void printWarning(String tag) { + warningList.each { + Log.i(tag, it) + } + } + + public void setFlag(String key, boolean value) { + if (value) { + flagTable.add(key) + } else { + flagTable.remove(key) + } + } + + public boolean getFlag(String key) { + return flagTable.contains(key) + } + public static class VAContext { /** host Symbol file - Host_R.txt */ diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 8da6902..22c4a91 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -1,19 +1,15 @@ package com.didi.virtualapk import com.android.build.gradle.internal.api.ApplicationVariantImpl -import com.didi.virtualapk.hooker.DxTaskHooker -import com.didi.virtualapk.hooker.MergeAssetsHooker -import com.didi.virtualapk.hooker.MergeJniLibsHooker -import com.didi.virtualapk.hooker.MergeManifestsHooker -import com.didi.virtualapk.hooker.PrepareDependenciesHooker -import com.didi.virtualapk.hooker.ProcessResourcesHooker -import com.didi.virtualapk.hooker.ProguardHooker -import com.didi.virtualapk.hooker.TaskHookerManager +import com.didi.virtualapk.hooker.* import com.didi.virtualapk.transform.StripClassAndResTransform import com.didi.virtualapk.utils.FileBinaryCategory import com.didi.virtualapk.utils.Log import org.gradle.api.InvalidUserDataException import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.DependencyResolveDetails +import org.gradle.api.artifacts.ResolutionStrategy import org.gradle.internal.reflect.Instantiator import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry @@ -36,6 +32,7 @@ class VAPlugin extends BasePlugin { private def hostDir protected boolean isBuildingPlugin = false + private boolean checked /** * TaskHooker manager, registers hookers when apply invoked @@ -56,10 +53,56 @@ class VAPlugin extends BasePlugin { Log.i 'VAPlugin', "Skipped all VirtualApk's configurations!" return } + + checkConfig() + stripClassAndResTransform = new StripClassAndResTransform(project) android.registerTransform(stripClassAndResTransform) android.defaultConfig.buildConfigField("int", "PACKAGE_ID", "0x" + Integer.toHexString(virtualApk.packageId)) + + // Force using the versions of host dependencies + // check current project was the first subproject +// if (project.rootProject.subprojects.first() != project) { +// throw new RuntimeException("The project ':${project.name}' must be the first subproject when enabled \"virtualApk.forceUseHostDependences\" . " + +// "You should modify the project's configuration explicitly in \"settings.gradle\" like this:\n\n" + +// "include ':_${project.name}'\n" + +// "project(':_${project.name}').projectDir = new File('./${project.name}')") +// } + HashSet replacedSet = [] as HashSet + project.rootProject.subprojects { Project p -> + p.configurations.all { Configuration configuration -> + configuration.resolutionStrategy { ResolutionStrategy resolutionStrategy -> + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (!isBuildingPlugin) { + return + } + + checkConfig() + + def hostDependency = virtualApk.hostDependencies.get("${details.requested.group}:${details.requested.name}") + if (hostDependency != null) { + if ("${details.requested.version}" != "${hostDependency['version']}") { + String key = "${p.name}:${details.requested}" + if (!replacedSet.contains(key)) { + replacedSet.add(key) + if (virtualApk.forceUseHostDependences) { + Log.i 'Dependencies', "ATTENTION: Replaced module [${details.requested}] in project(:${p.name})'s configuration to host version: [${hostDependency['version']}]!" + } else { + virtualApk.addWarning "WARNING: [${details.requested}] in project(:${p.name})'s configuration will be occupied by Host App! Please change it to host version: [${hostDependency['group']}:${hostDependency['name']}:${hostDependency['version']}]." + virtualApk.setFlag('tip.forceUseHostDependences', true) + } + } + + if (virtualApk.forceUseHostDependences) { + details.useVersion(hostDependency['version']) + } + } + } + } + } + } + } } File getJarPath() { @@ -97,8 +140,6 @@ class VAPlugin extends BasePlugin { android.applicationVariants.each { ApplicationVariantImpl variant -> - checkConfig() - virtualApk.with { VAExtention.VAContext vaContext = getVaContext(variant.name) vaContext.packageName = variant.applicationId @@ -113,6 +154,11 @@ class VAPlugin extends BasePlugin { * Check the plugin apk related config infos */ private void checkConfig() { + if (checked) { + return + } + checked = true + int packageId = virtualApk.packageId if (packageId == 0) { def err = new StringBuilder('you should set the packageId in build.gradle,\n ') diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index 9eec5ff..541a5d7 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -45,10 +45,7 @@ class PrepareDependenciesHooker extends GradleTaskHooker { @Override void beforeTaskExecute(AppPreBuildTask task) { - virtualApk.hostDependenceFile.splitEachLine('\\s+', { columns -> - final def module = columns[0].split(':') - hostDependencies.add("${module[0]}:${module[1]}") - }) + hostDependencies.addAll(virtualApk.hostDependencies.keySet()) virtualApk.excludes.each { String artifact -> final def module = artifact.split(':') diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy index 720c1b5..9fdca0b 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy @@ -3,6 +3,7 @@ package com.didi.virtualapk.tasks import com.android.annotations.NonNull import com.android.build.gradle.api.ApkVariant import com.didi.virtualapk.VAExtention +import com.didi.virtualapk.utils.Log import com.didi.virtualapk.utils.TaskUtil import com.sun.istack.internal.NotNull import org.gradle.api.Action @@ -43,6 +44,16 @@ public class AssemblePlugin extends DefaultTask { public void outputPluginApk() { VAExtention virtualApk = project.virtualApk virtualApk.getVaContext(variantName).checkList.check() + virtualApk.printWarning(name) + + if (virtualApk.getFlag('tip.forceUseHostDependences')) { + def tip = new StringBuilder('To avoid configuration WARNINGs, you could set the forceUseHostDependences to be true in build.gradle,\n ') + tip.append('please declare it in application project build.gradle:\n') + tip.append(' virtualApk {\n') + tip.append(' forceUseHostDependences = true \n') + tip.append(' }\n') + Log.i name, tip.toString() + } getProject().copy { from originApkFile From 043c549efac8488915bd7fc48ca9b0d76db6cc03 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 24 May 2018 15:50:41 +0800 Subject: [PATCH 34/66] Optimized some log. --- .../com.didi.virtualapk/VAPlugin.groovy | 6 +-- .../tasks/AssemblePlugin.groovy | 42 ++++++------------- .../com.didi.virtualapk/utils/TaskUtil.groovy | 25 ----------- 3 files changed, 16 insertions(+), 57 deletions(-) delete mode 100644 virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy index 22c4a91..7075013 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAPlugin.groovy @@ -185,7 +185,7 @@ class VAPlugin extends BasePlugin { File hostLocalDir = new File(targetHost) if (!hostLocalDir.exists()) { - def err = "The directory of host application doesn't exist! Dir: ${hostLocalDir.absolutePath}" + def err = "The directory of host application doesn't exist! Dir: ${hostLocalDir.canonicalPath}" throw new InvalidUserDataException(err) } @@ -196,7 +196,7 @@ class VAPlugin extends BasePlugin { dst << hostR } } else { - def err = new StringBuilder("Can't find ${hostR.absolutePath}, please check up your host application\n") + def err = new StringBuilder("Can't find ${hostR.canonicalPath}, please check up your host application\n") err.append(" need apply com.didi.virtualapk.host in build.gradle of host application\n ") throw new InvalidUserDataException(err.toString()) } @@ -208,7 +208,7 @@ class VAPlugin extends BasePlugin { dst << hostVersions } } else { - def err = new StringBuilder("Can't find ${hostVersions.absolutePath}, please check up your host application\n") + def err = new StringBuilder("Can't find ${hostVersions.canonicalPath}, please check up your host application\n") err.append(" need apply com.didi.virtualapk.host in build.gradle of host application \n") throw new InvalidUserDataException(err.toString()) } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy index 9fdca0b..d1b97e5 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy @@ -1,21 +1,17 @@ package com.didi.virtualapk.tasks -import com.android.annotations.NonNull import com.android.build.gradle.api.ApkVariant +import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.didi.virtualapk.VAExtention import com.didi.virtualapk.utils.Log -import com.didi.virtualapk.utils.TaskUtil import com.sun.istack.internal.NotNull import org.gradle.api.Action import org.gradle.api.DefaultTask import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import java.util.concurrent.Callable - /** * Gradle task for assemble plugin apk * @author zhengtao @@ -36,6 +32,8 @@ public class AssemblePlugin extends DefaultTask { String variantName + String buildDir + /** * Copy the plugin apk to out/plugin directory and rename to * the format required for the backend system @@ -55,6 +53,8 @@ public class AssemblePlugin extends DefaultTask { Log.i name, tip.toString() } + Log.i name, "More building infomation could be found in the dir: ${buildDir}." + getProject().copy { from originApkFile into pluginApkDir @@ -68,7 +68,7 @@ public class AssemblePlugin extends DefaultTask { @NotNull Project project @NotNull - ApkVariant variant + ApplicationVariantImpl variant ConfigAction(@NotNull Project project, @NotNull ApkVariant variant) { this.project = project @@ -77,35 +77,19 @@ public class AssemblePlugin extends DefaultTask { @Override void execute(AssemblePlugin assemblePluginTask) { + VAExtention virtualApk = project.virtualApk - map(assemblePluginTask, "appPackageName") { - variant.applicationId - } - - map(assemblePluginTask, "apkTimestamp", { - new Date().format("yyyyMMddHHmmss") - }) - - map(assemblePluginTask, "originApkFile") { - variant.outputs[0].outputFile - } - - map(assemblePluginTask, "pluginApkDir") { - new File(project.buildDir, "/outputs/plugin/${variant.name}") - } - - map(assemblePluginTask, "variantName") { - variant.name - } + assemblePluginTask.appPackageName = variant.applicationId + assemblePluginTask.apkTimestamp = new Date().format("yyyyMMddHHmmss") + assemblePluginTask.originApkFile = variant.outputs[0].outputFile + assemblePluginTask.pluginApkDir = new File(project.buildDir, "/outputs/plugin/${variant.name}") + assemblePluginTask.variantName = variant.name + assemblePluginTask.buildDir = virtualApk.getVaContext(variant.name).getBuildDir(variant.variantData.scope).canonicalPath assemblePluginTask.setGroup("build") assemblePluginTask.setDescription("Build ${variant.name.capitalize()} plugin apk") assemblePluginTask.dependsOn(variant.assemble.name) } - - static void map(@NonNull Task task, @NonNull String key, @NonNull Callable value) { - TaskUtil.map(task, key, value) - } } } diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy deleted file mode 100644 index 75ae193..0000000 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/TaskUtil.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.didi.virtualapk.utils - -import com.android.annotations.NonNull -import org.gradle.api.Task -import org.gradle.api.internal.ConventionMapping - -import java.util.concurrent.Callable - -/** - * Created by qiaopu on 2018/3/19. - */ -public class TaskUtil { - - public static void map(@NonNull Task task, @NonNull String key, @NonNull Callable value) { - if (task instanceof GroovyObject) { - ConventionMapping conventionMapping = - (ConventionMapping) ((GroovyObject) task).getProperty("conventionMapping"); - conventionMapping.map(key, value); - } else { - throw new IllegalArgumentException( - "Don't know how to apply convention mapping to task of type " + task.getClass().getName()); - } - } - -} From 262fbcc38461ad07539da79490b2d9d8fcd485c5 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 24 May 2018 15:51:20 +0800 Subject: [PATCH 35/66] Fixed a null pointer exception. --- .../main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy index 0d94aac..a2ab2c0 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy @@ -57,6 +57,11 @@ public class ArscEditor extends AssetEditor { def retainedEntries = [] def libPackageIds = [] + if (t.typeList.specs.size() == 0) { + println "\t -- There was no res." + return + } + // Ensure there is an `attr' typeSpec if (retainedTypes[0].id != 1) { // attr type id is always as `1' def attrSpec = t.typeList.specs[0] From da7e95c32b320dcb6eece5c913194849c1de3cf3 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 24 May 2018 15:52:23 +0800 Subject: [PATCH 36/66] Check dependencies in plugin. --- .../hooker/PrepareDependenciesHooker.groovy | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy index 541a5d7..557ca39 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/PrepareDependenciesHooker.groovy @@ -115,6 +115,9 @@ class PrepareDependenciesHooker extends GradleTaskHooker { FileUtil.saveFile(hostDir, "${taskName}-stripDependencies", stripDependencies) FileUtil.saveFile(hostDir, "${taskName}-retainedAarLibs", retainedAarLibs) FileUtil.saveFile(hostDir, "${taskName}-retainedJarLib", retainedJarLib) + + checkDependencies() + Log.i 'PrepareDependenciesHooker', "Analyzed all dependencis. Get more infomation in dir: ${hostDir.absoluteFile}" vaContext.stripDependencies = stripDependencies @@ -122,4 +125,36 @@ class PrepareDependenciesHooker extends GradleTaskHooker { mark() } + void checkDependencies() { + ArrayList allRetainedDependencies = new ArrayList<>() + allRetainedDependencies.addAll(retainedAarLibs) + allRetainedDependencies.addAll(retainedJarLib) + + ArrayList checked = new ArrayList<>() + + allRetainedDependencies.each { + String group = it.group + String artifact = it.artifact + String version = it.version + + // com.didi.virtualapk:core + if (group == 'com.didi.virtualapk' && artifact == 'core') { + checked.add("${group}:${artifact}:${version}") + } + + // com.android.support:all + if (group == 'com.android.support' || group.startsWith('com.android.support.')) { + checked.add("${group}:${artifact}:${version}") + } + + // com.android.databinding:all + if (group == 'com.android.databinding' || group.startsWith('com.android.databinding.')) { + checked.add("${group}:${artifact}:${version}") + } + } + + if (!checked.empty) { + throw new Exception("The dependencies [${String.join(', ', checked)}] that will be used in the current plugin must be included in the host app first. Please add it in the host app as well.") + } + } } \ No newline at end of file From 800437b475dc752ae60549830e728444b3ad147d Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 17 May 2018 20:54:35 +0800 Subject: [PATCH 37/66] Fixed Android P crash. --- .../com/didi/virtualapk/internal/LoadedPlugin.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 0d7e508..0a21af8 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -155,9 +155,15 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags this.mPackageInfo = new PackageInfo(); this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo; this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath(); - - if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview - this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures; + + if (Build.VERSION.SDK_INT >= 28 + || (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // Android P Preview + try { + this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures; + } catch (Throwable e) { + PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); + this.mPackageInfo.signatures = info.signatures; + } } else { this.mPackageInfo.signatures = this.mPackage.mSignatures; } From baec217b15b756debed362fd8e52b72c8d0927c5 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 24 May 2018 16:52:59 +0800 Subject: [PATCH 38/66] Updated version. --- CoreLibrary/upload.gradle | 2 +- PluginDemo/app/build.gradle | 2 +- PluginDemo/build.gradle | 2 +- README.md | 6 +++--- app/build.gradle | 2 +- build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CoreLibrary/upload.gradle b/CoreLibrary/upload.gradle index 62152ee..4c48657 100644 --- a/CoreLibrary/upload.gradle +++ b/CoreLibrary/upload.gradle @@ -11,7 +11,7 @@ def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url group = GROUP_ID archivesBaseName = 'core' -version = "0.9.5" +version = "0.9.6-dev" install { diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 78b5041..5ae2dde 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -51,7 +51,7 @@ dependencies { // the following aars are also compiled in host project, so they will be filterd when build plugin apk. // but, wo can still visit their Class and Resources. compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.5' + compile 'com.didi.virtualapk:core:0.9.6-dev' } apply plugin: 'com.didi.virtualapk.plugin' diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index 440894b..7aeeaa6 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.3' + classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' } } diff --git a/README.md b/README.md index fd6d1dc..1b39917 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.3' + classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' } ``` @@ -41,7 +41,7 @@ apply plugin: 'com.didi.virtualapk.host' Compile VirtualAPK in application module of `build.gradle`. ``` java -compile 'com.didi.virtualapk:core:0.9.5' +compile 'com.didi.virtualapk:core:0.9.6-dev' ``` Initialize `PluginManager` in `YourApplication::attachBaseContext()`. @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.3' + classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' } ``` diff --git a/app/build.gradle b/app/build.gradle index b598e70..6cc6ef1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.5' + compile 'com.didi.virtualapk:core:0.9.6-dev' // compile project (':CoreLibrary') } \ No newline at end of file diff --git a/build.gradle b/build.gradle index f619ade..01e226e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.3' + classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 3e0a6c7..6e2252e 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.3 +VERSION=0.9.8.4-dev From 8133a24ff6dfcdda4e0d9e49f8a5dcc4bdb8ff47 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 4 Jun 2018 15:02:57 +0800 Subject: [PATCH 39/66] Optimized process of the host dependencies. --- README.md | 4 ++-- virtualapk-gradle-plugin/gradle.properties | 2 +- .../com.didi.virtualapk/VAExtention.groovy | 23 +++++++++++++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1b39917..67d8f78 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4.1-dev' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4.1-dev' } ``` diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index 6e2252e..ed79f32 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.4-dev +VERSION=0.9.8.4.1-dev diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy index 57ce6c4..4c3e4d7 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/VAExtention.groovy @@ -91,8 +91,27 @@ public class VAExtention { if (hostDependencies == null) { hostDependencies = [] as LinkedHashMap hostDependenceFile.splitEachLine('\\s+', { columns -> - final def module = columns[0].split(':') - hostDependencies.put("${module[0]}:${module[1]}", [group: module[0], name: module[1], version: module[2]]) + String id = columns[0] + int index1 = id.indexOf(':') + int index2 = id.lastIndexOf(':') + def module = [group: 'unspecified', name: 'unspecified', version: 'unspecified'] + + if (index1 < 0 || index2 < 0 || index1 == index2) { + Log.e('Dependencies', "Parsed error: [${id}] -> ${module}") + return + } + + if (index1 > 0) { + module.group = id.substring(0, index1) + } + if (index2 - index1 > 0) { + module.name = id.substring(index1 + 1, index2) + } + if (id.length() - index2 > 1) { + module.version = id.substring(index2 + 1) + } + + hostDependencies.put("${module.group}:${module.name}", module) }) } return hostDependencies From 56d09cd49b24ec4797dc6952ecc5f4977753dddb Mon Sep 17 00:00:00 2001 From: superq_sky Date: Tue, 5 Jun 2018 19:29:06 +0800 Subject: [PATCH 40/66] Fixed the null pointer exception. --- virtualapk-gradle-plugin/gradle.properties | 2 +- .../dependence/AarDependenceInfo.groovy | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index ed79f32..cb5a256 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.4.1-dev +VERSION=0.9.8.4.3-dev diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy index b949355..94ab3f3 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/AarDependenceInfo.groovy @@ -23,6 +23,8 @@ class AarDependenceInfo extends DependenceInfo { */ AndroidLibrary library + File intermediatesFile + /** * All resources(e.g. drawable, layout...) this library can access * include resources of self-project and dependence(direct&transitive) project @@ -72,11 +74,7 @@ class AarDependenceInfo extends DependenceInfo { def resKeys = [] as Set - def rSymbol = library.symbolFile - if (!rSymbol.exists()) { - // module library - rSymbol = FileUtils.join(intermediatesDir, TaskManager.DIR_BUNDLES, library.projectVariant, SdkConstants.FN_RESOURCE_TEXT) - } + def rSymbol = getFile(library.symbolFile, TaskManager.DIR_BUNDLES, library.projectVariant, SdkConstants.FN_RESOURCE_TEXT) if (rSymbol.exists()) { Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s symbol file: ${rSymbol}" rSymbol.eachLine { line -> @@ -100,23 +98,39 @@ class AarDependenceInfo extends DependenceInfo { * @return package name of this library */ public String getPackage() { - File manifest - if (library.manifest.exists()) { - manifest = library.manifest - } else { - // module library - manifest = FileUtils.join(intermediatesDir, 'manifests', 'full', library.projectVariant, SdkConstants.ANDROID_MANIFEST_XML) - } + File manifest = getFile(library.manifest, 'manifests', 'full', library.projectVariant, SdkConstants.ANDROID_MANIFEST_XML) Log.i 'AarDependenceInfo', "Found [${library.resolvedCoordinates}]'s manifest file: ${manifest}" def xmlManifest = new XmlParser().parse(manifest) return xmlManifest.@package } File getIntermediatesDir() { - String path = library.folder.path - return new File(path.substring(0, path.indexOf("${File.separator}intermediates${File.separator}")), 'intermediates') + if (intermediatesFile == null) { + String path = library.folder.path + try { + intermediatesFile = new File(path.substring(0, path.indexOf("${File.separator}intermediates${File.separator}")), 'intermediates') + + } catch (Exception e) { + Log.e('AarDependenceInfo', "Can not find [${library.resolvedCoordinates}]'s intermediates dir from the path: ${path}") + intermediatesFile = library.folder + } + } + return intermediatesFile } + File getFile(File defaultFile, String... paths) { + if (library.projectVariant == null) { + return defaultFile + } + + if (defaultFile.exists()) { + return defaultFile + } + + // module library + return FileUtils.join(intermediatesDir, paths) + } + @Override String toString() { return "${super.toString()} -> ${library}" From 93d286eb26d6367682323204e1274cca01343692 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 6 Jun 2018 16:01:48 +0800 Subject: [PATCH 41/66] Fixed bug: the resources.arsc's type ids was error when the plugin has some attrs value. --- PluginDemo/build.gradle | 2 +- README.md | 4 ++-- build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- .../main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index 7aeeaa6..7ee1b36 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' } } diff --git a/README.md b/README.md index 67d8f78..1014b63 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.4.1-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' } ``` @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.4.1-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' } ``` diff --git a/build.gradle b/build.gradle index 01e226e..da3bf6a 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index cb5a256..d9e99b1 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.4.3-dev +VERSION=0.9.8.4.4-dev diff --git a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy index a2ab2c0..f15e1e3 100644 --- a/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy +++ b/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ArscEditor.groovy @@ -63,7 +63,7 @@ public class ArscEditor extends AssetEditor { } // Ensure there is an `attr' typeSpec - if (retainedTypes[0].id != 1) { // attr type id is always as `1' + if (retainedTypes[0].id == Aapt.ID_NO_ATTR) { // attr type id is always at first def attrSpec = t.typeList.specs[0] attrSpec.entryCount = 0 attrSpec.configs = [] @@ -71,6 +71,7 @@ public class ArscEditor extends AssetEditor { attrSpec.header.size = attrSpec.header.headerSize // id(1) res0(1) res1(2) entryCount(4) retainedTypeIds.add(attrSpec.id - 1) retainedTypeSpecs.add(attrSpec) + println "\t -- There was no attr." } def index = 0 From cee62abdc16b322c6a76ad3722d4d41592fd679c Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 2 Jul 2018 20:58:15 +0800 Subject: [PATCH 42/66] Updated version. --- CoreLibrary/upload.gradle | 2 +- PluginDemo/app/build.gradle | 2 +- PluginDemo/build.gradle | 2 +- README.md | 6 +++--- RELEASE-NOTES.md | 10 +++++++++- app/build.gradle | 2 +- build.gradle | 2 +- virtualapk-gradle-plugin/gradle.properties | 2 +- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CoreLibrary/upload.gradle b/CoreLibrary/upload.gradle index 4c48657..ab0009b 100644 --- a/CoreLibrary/upload.gradle +++ b/CoreLibrary/upload.gradle @@ -11,7 +11,7 @@ def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url group = GROUP_ID archivesBaseName = 'core' -version = "0.9.6-dev" +version = "0.9.6" install { diff --git a/PluginDemo/app/build.gradle b/PluginDemo/app/build.gradle index 5ae2dde..2d9915b 100644 --- a/PluginDemo/app/build.gradle +++ b/PluginDemo/app/build.gradle @@ -51,7 +51,7 @@ dependencies { // the following aars are also compiled in host project, so they will be filterd when build plugin apk. // but, wo can still visit their Class and Resources. compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.6-dev' + compile 'com.didi.virtualapk:core:0.9.6' } apply plugin: 'com.didi.virtualapk.plugin' diff --git a/PluginDemo/build.gradle b/PluginDemo/build.gradle index 7ee1b36..5a16e6a 100644 --- a/PluginDemo/build.gradle +++ b/PluginDemo/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4' } } diff --git a/README.md b/README.md index 1014b63..88593e9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add a dependency in `build.gradle` in root of host project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4' } ``` @@ -41,7 +41,7 @@ apply plugin: 'com.didi.virtualapk.host' Compile VirtualAPK in application module of `build.gradle`. ``` java -compile 'com.didi.virtualapk:core:0.9.6-dev' +compile 'com.didi.virtualapk:core:0.9.6' ``` Initialize `PluginManager` in `YourApplication::attachBaseContext()`. @@ -85,7 +85,7 @@ Add a dependency in `build.gradle` in root of plugin project as following. ``` java dependencies { - classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4' } ``` diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 27ea050..64845e4 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -28,7 +28,10 @@ ## com.didi.virtualapk:core:0.9.5 1. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。 -2. 与 com.didi.virtualapk:gradle:0.9.8.2 搭配使用,支持官方 Data Binding。 +2. 与 com.didi.virtualapk:gradle:0.9.8.2及以上版本 搭配使用,支持官方 Data Binding。 + +## com.didi.virtualapk:core:0.9.6 +1. 修复部分空指针问题。 ## VirtualAPK 的构建部分已经开源了,![点击这里查看](https://github.com/didi/VirtualAPK/tree/master/virtualapk-gradle-plugin) @@ -39,3 +42,8 @@ ## com.didi.virtualapk:gradle:0.9.8.3 1. 兼容不定义 productFlavors 的配置。 + +## com.didi.virtualapk:gradle:0.9.8.4 +1. 修复当插件依赖library module时构建失败的bug。 +2. 修复依赖本地aar时构建失败的bug。 +3. 修复当插件自定义attr属性时id错误的bug。 diff --git a/app/build.gradle b/app/build.gradle index 6cc6ef1..9001173 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.didi.virtualapk:core:0.9.6-dev' + compile 'com.didi.virtualapk:core:0.9.6' // compile project (':CoreLibrary') } \ No newline at end of file diff --git a/build.gradle b/build.gradle index da3bf6a..b034296 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' - classpath 'com.didi.virtualapk:gradle:0.9.8.4.4-dev' + classpath 'com.didi.virtualapk:gradle:0.9.8.4' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' diff --git a/virtualapk-gradle-plugin/gradle.properties b/virtualapk-gradle-plugin/gradle.properties index d9e99b1..0c1dd1d 100644 --- a/virtualapk-gradle-plugin/gradle.properties +++ b/virtualapk-gradle-plugin/gradle.properties @@ -1,3 +1,3 @@ GROUP_ID=com.didi.virtualapk ARTIFACT_ID=gradle -VERSION=0.9.8.4.4-dev +VERSION=0.9.8.4 From 900abe784545168d66e8bee145a94eb8b96abf8f Mon Sep 17 00:00:00 2001 From: superq_sky Date: Tue, 3 Jul 2018 10:52:37 +0800 Subject: [PATCH 43/66] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88593e9..3770550 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # VirtualAPK [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/didi/VirtualAPK/blob/master/LICENSE) -[![Release Version](https://img.shields.io/badge/release-0.9.1-red.svg)](https://github.com/didi/VirtualAPK/releases) +[![Release Version](https://img.shields.io/badge/release-0.9.6-red.svg)](https://github.com/didi/VirtualAPK/releases) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/didi/VirtualAPK/pulls) VirtualAPK is a powerful yet lightweight plugin framework for Android. It can dynamically load and run an APK file (we call it `LoadedPlugin`) seamlessly as an installed application. Developers can use any Class, Resources, Activity, Service, Receiver and Provider in `LoadedPlugin` as if they are registered in app's manifest file. From 832b1756819134cd0241a1eb6aeecae22c5bc337 Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 31 May 2018 13:50:55 +0800 Subject: [PATCH 44/66] Maked all the stub components to be unexported. --- CoreLibrary/src/main/AndroidManifest.xml | 57 ++++++++++++------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/CoreLibrary/src/main/AndroidManifest.xml b/CoreLibrary/src/main/AndroidManifest.xml index 06c85a7..7b634ed 100644 --- a/CoreLibrary/src/main/AndroidManifest.xml +++ b/CoreLibrary/src/main/AndroidManifest.xml @@ -9,51 +9,52 @@ - - + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - + - + From c5e3a88badd4f85543c01ea8eebc7aacf4e7765b Mon Sep 17 00:00:00 2001 From: superq_sky Date: Wed, 25 Apr 2018 21:00:37 +0800 Subject: [PATCH 45/66] Optimized reflection APIs. --- .../aidl/android/app/IServiceConnection.aidl | 4 +- .../java/android/app/Instrumentation.java | 68 +++ .../java/android/app/ResourcesManager.java | 11 + .../java/android/content/ContentResolver.java | 40 ++ CoreLibrary/build.gradle | 25 +- .../content/ContentResolverWrapper.java | 58 +++ .../com/didi/virtualapk/PluginManager.java | 58 +-- .../java/com/didi/virtualapk/Systems.java | 41 -- .../virtualapk/delegate/LocalService.java | 12 +- .../virtualapk/internal/LoadedPlugin.java | 118 +++-- .../internal/PluginContentResolver.java | 88 ++-- .../virtualapk/internal/ResourcesManager.java | 71 +-- .../internal/VAInstrumentation.java | 157 ++---- .../com/didi/virtualapk/utils/DexUtil.java | 29 +- .../virtualapk/utils/PackageParserCompat.java | 15 +- .../com/didi/virtualapk/utils/PluginUtil.java | 23 +- .../didi/virtualapk/utils/ReflectUtil.java | 173 ------- .../com/didi/virtualapk/utils/Reflector.java | 447 ++++++++++++++++++ 18 files changed, 884 insertions(+), 554 deletions(-) create mode 100644 AndroidStub/src/main/java/android/app/Instrumentation.java create mode 100644 AndroidStub/src/main/java/android/app/ResourcesManager.java create mode 100644 AndroidStub/src/main/java/android/content/ContentResolver.java create mode 100644 CoreLibrary/src/main/java/android/content/ContentResolverWrapper.java delete mode 100644 CoreLibrary/src/main/java/com/didi/virtualapk/Systems.java delete mode 100644 CoreLibrary/src/main/java/com/didi/virtualapk/utils/ReflectUtil.java create mode 100644 CoreLibrary/src/main/java/com/didi/virtualapk/utils/Reflector.java diff --git a/AndroidStub/src/main/aidl/android/app/IServiceConnection.aidl b/AndroidStub/src/main/aidl/android/app/IServiceConnection.aidl index fb19b0b..cd5b86f 100644 --- a/AndroidStub/src/main/aidl/android/app/IServiceConnection.aidl +++ b/AndroidStub/src/main/aidl/android/app/IServiceConnection.aidl @@ -21,8 +21,8 @@ import android.content.ComponentName; /** @hide */ oneway interface IServiceConnection { - void connected(in ComponentName name, IBinder service); +// void connected(in ComponentName name, IBinder service); /** Added in Android O */ - //void connected(in ComponentName name, IBinder service, boolean dead); + void connected(in ComponentName name, IBinder service, boolean dead); } diff --git a/AndroidStub/src/main/java/android/app/Instrumentation.java b/AndroidStub/src/main/java/android/app/Instrumentation.java new file mode 100644 index 0000000..0b73401 --- /dev/null +++ b/AndroidStub/src/main/java/android/app/Instrumentation.java @@ -0,0 +1,68 @@ +package android.app; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PersistableBundle; + +/** + * Created by qiaopu on 2018/5/7. + */ +public class Instrumentation { + + public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + throw new RuntimeException("Stub!"); + } + + public void callApplicationOnCreate(Application app) { + throw new RuntimeException("Stub!"); + } + + public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + throw new RuntimeException("Stub!"); + } + + public void callActivityOnCreate(Activity activity, Bundle icicle) { + throw new RuntimeException("Stub!"); + } + + public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { + throw new RuntimeException("Stub!"); + } + + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) { + throw new RuntimeException("Stub!"); + } + + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { + throw new RuntimeException("Stub!"); + } + + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options) { + throw new RuntimeException("Stub!"); + } + + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) { + throw new RuntimeException("Stub!"); + } + + public Context getContext() { + throw new RuntimeException("Stub!"); + } + + public Context getTargetContext() { + throw new RuntimeException("Stub!"); + } + + public ComponentName getComponentName() { + throw new RuntimeException("Stub!"); + } + + public static final class ActivityResult { + public ActivityResult(int resultCode, Intent resultData) { + throw new RuntimeException("Stub!"); + } + } +} diff --git a/AndroidStub/src/main/java/android/app/ResourcesManager.java b/AndroidStub/src/main/java/android/app/ResourcesManager.java new file mode 100644 index 0000000..518118c --- /dev/null +++ b/AndroidStub/src/main/java/android/app/ResourcesManager.java @@ -0,0 +1,11 @@ +package android.app; + +/** + * Created by qiaopu on 2018/4/25. + */ +public class ResourcesManager { + + public static ResourcesManager getInstance() { + throw new RuntimeException("Stub!"); + } +} \ No newline at end of file diff --git a/AndroidStub/src/main/java/android/content/ContentResolver.java b/AndroidStub/src/main/java/android/content/ContentResolver.java new file mode 100644 index 0000000..f3c3ed3 --- /dev/null +++ b/AndroidStub/src/main/java/android/content/ContentResolver.java @@ -0,0 +1,40 @@ +package android.content; + +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +/** + * Created by qiaopu on 2018/5/7. + */ +public abstract class ContentResolver { + + public ContentResolver(Context context) { + throw new RuntimeException("Stub!"); + } + + public final @Nullable + Bundle call(@NonNull Uri uri, @NonNull String method, + @Nullable String arg, @Nullable Bundle extras) { + throw new RuntimeException("Stub!"); + } + + protected abstract IContentProvider acquireProvider(Context c, String name); + + protected IContentProvider acquireExistingProvider(Context c, String name) { + throw new RuntimeException("Stub!"); + } + + public abstract boolean releaseProvider(IContentProvider icp); + + protected abstract IContentProvider acquireUnstableProvider(Context c, String name); + + public abstract boolean releaseUnstableProvider(IContentProvider icp); + + public abstract void unstableProviderDied(IContentProvider icp); + + public void appNotRespondingViaProvider(IContentProvider icp) { + throw new RuntimeException("Stub!"); + } +} diff --git a/CoreLibrary/build.gradle b/CoreLibrary/build.gradle index 2656e8a..840f587 100644 --- a/CoreLibrary/build.gradle +++ b/CoreLibrary/build.gradle @@ -1,3 +1,7 @@ +import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.api.LibraryVariant +import com.google.common.base.Joiner + apply plugin: 'com.android.library' android { @@ -29,12 +33,31 @@ repositories { jcenter() } +final String projectAndroidStub = ':AndroidStub' + dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - provided project(':AndroidStub') + provided project(projectAndroidStub) testCompile 'junit:junit:4.12' } +// Using Stub classes first when compiling. +afterEvaluate { + project.android.libraryVariants.each { LibraryVariant variant -> + variant.javaCompile.doFirst { JavaCompile javaCompile -> + String projectAndroidStubPath = project.project(projectAndroidStub).projectDir.canonicalPath +// println "projectAndroidStubPath: ${projectAndroidStubPath}" + File stubPath = javaCompile.classpath.find { + it.canonicalPath.startsWith(projectAndroidStubPath) + } + if (stubPath == null) { + throw new RuntimeException("reset bootclasspath error.") + } + javaCompile.options.setBootClasspath(Joiner.on(File.pathSeparator).join(stubPath, javaCompile.options.bootClasspath)) + } + } +} + apply from: 'upload.gradle' diff --git a/CoreLibrary/src/main/java/android/content/ContentResolverWrapper.java b/CoreLibrary/src/main/java/android/content/ContentResolverWrapper.java new file mode 100644 index 0000000..47aa31d --- /dev/null +++ b/CoreLibrary/src/main/java/android/content/ContentResolverWrapper.java @@ -0,0 +1,58 @@ +package android.content; + +import android.annotation.TargetApi; +import android.os.Build; + +/** + * Wrapper of {@link ContentResolver} + * Created by qiaopu on 2018/5/7. + */ +public abstract class ContentResolverWrapper extends ContentResolver { + + ContentResolver mBase; + + public ContentResolverWrapper(Context context) { + super(context); + mBase = context.getContentResolver(); + } + + @Override + protected IContentProvider acquireProvider(Context context, String auth) { + return mBase.acquireProvider(context, auth); + } + + @Override + protected IContentProvider acquireExistingProvider(Context context, String auth) { + return mBase.acquireExistingProvider(context, auth); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + protected IContentProvider acquireUnstableProvider(Context context, String auth) { + return mBase.acquireUnstableProvider(context, auth); + } + + @Override + public boolean releaseProvider(IContentProvider icp) { + return mBase.releaseProvider(icp); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public boolean releaseUnstableProvider(IContentProvider icp) { + return mBase.releaseUnstableProvider(icp); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public void unstableProviderDied(IContentProvider icp) { + mBase.unstableProviderDied(icp); + } + + @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override + public void appNotRespondingViaProvider(IContentProvider icp) { + mBase.appNotRespondingViaProvider(icp); + } + +} diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java index 9c83914..669fec3 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java @@ -28,24 +28,25 @@ import android.content.Intent; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; -import android.databinding.DataBinderMapperProxy; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.util.Log; import android.util.Singleton; -import com.didi.virtualapk.delegate.IContentProviderProxy; -import com.didi.virtualapk.internal.PluginContentResolver; import com.didi.virtualapk.delegate.ActivityManagerProxy; +import com.didi.virtualapk.delegate.IContentProviderProxy; import com.didi.virtualapk.internal.ComponentsHandler; import com.didi.virtualapk.internal.LoadedPlugin; +import com.didi.virtualapk.internal.PluginContentResolver; import com.didi.virtualapk.internal.VAInstrumentation; import com.didi.virtualapk.utils.PluginUtil; -import com.didi.virtualapk.utils.ReflectUtil; +import com.didi.virtualapk.utils.Reflector; import com.didi.virtualapk.utils.RunUtil; import java.io.File; -import java.io.FileNotFoundException; +import java.io.FileInputStream; +import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; @@ -94,7 +95,6 @@ private PluginManager(Context context) { } private void prepare() { - Systems.sHostContext = getHostContext(); this.hookInstrumentationAndHandler(); this.hookSystemServices(); hookDataBindingUtil(); @@ -114,16 +114,16 @@ private void doInWorkThread() { } private void hookDataBindingUtil() { - try { - Class cls = Class.forName("android.databinding.DataBindingUtil"); - Object old = ReflectUtil.getField(cls, null, "sMapper"); - Callback callback = new DataBinderMapperProxy(old); - ReflectUtil.setField(cls, null, "sMapper", callback); - - addCallback(callback); - - } catch (Exception e) { - e.printStackTrace(); + Reflector.QuietReflector reflector = Reflector.QuietReflector.on("android.databinding.DataBindingUtil").field("sMapper"); + Object old = reflector.get(); + if (old != null) { + try { + Callback callback = Reflector.on("android.databinding.DataBinderMapperProxy").constructor().newInstance(); + reflector.set(callback); + addCallback(callback); + } catch (Reflector.ReflectedException e) { + e.printStackTrace(); + } } } @@ -144,14 +144,14 @@ private void hookSystemServices() { Singleton defaultSingleton; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManager.class, null, "IActivityManagerSingleton"); + defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get(); } else { - defaultSingleton = (Singleton) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault"); + defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get(); } IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get()); // Hook IActivityManager from ActivityManagerNative - ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy); + Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); if (defaultSingleton.get() == activityManagerProxy) { this.mActivityManager = activityManagerProxy; @@ -163,16 +163,18 @@ private void hookSystemServices() { private void hookInstrumentationAndHandler() { try { - Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext); + ActivityThread activityThread = ActivityThread.currentActivityThread(); + Instrumentation baseInstrumentation = activityThread.getInstrumentation(); if (baseInstrumentation.getClass().getName().contains("lbe")) { // reject executing in paralell space, for example, lbe. System.exit(0); } - + final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation); - Object activityThread = ReflectUtil.getActivityThread(this.mContext); - ReflectUtil.setInstrumentation(activityThread, instrumentation); - ReflectUtil.setHandlerCallback(this.mContext, instrumentation); + + Reflector.with(activityThread).field("mInstrumentation").set(instrumentation); + Handler mainHandler = Reflector.with(activityThread).method("getHandler").call(); + Reflector.with(mainHandler).field("mCallback").set(instrumentation); this.mInstrumentation = instrumentation; } catch (Exception e) { e.printStackTrace(); @@ -185,8 +187,8 @@ private void hookIContentProviderAsNeeded() { try { Field authority = null; Field mProvider = null; - ActivityThread activityThread = (ActivityThread) ReflectUtil.getActivityThread(mContext); - Map mProviderMap = (Map) ReflectUtil.getField(activityThread.getClass(), activityThread, "mProviderMap"); + ActivityThread activityThread = ActivityThread.currentActivityThread(); + Map mProviderMap = Reflector.with(activityThread).field("mProviderMap").get(); Iterator iter = mProviderMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); @@ -230,7 +232,9 @@ public void loadPlugin(File apk) throws Exception { } if (!apk.exists()) { - throw new FileNotFoundException(apk.getAbsolutePath()); + // throw the FileNotFoundException by opening a stream. + InputStream in = new FileInputStream(apk); + in.close(); } LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/Systems.java b/CoreLibrary/src/main/java/com/didi/virtualapk/Systems.java deleted file mode 100644 index e913573..0000000 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/Systems.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.didi.virtualapk; - -import android.content.Context; - - -/** - * Created by renyugang on 16/12/22. - */ - -/** - * This is God class, you should not know where it's from and any details. - */ -public class Systems { - - static Context sHostContext; - - /** - * get a Context object anywhere you want. - * @return a Context object - */ - public static Context getContext() { - return sHostContext; - } - -} diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/delegate/LocalService.java b/CoreLibrary/src/main/java/com/didi/virtualapk/delegate/LocalService.java index b544422..de64890 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/delegate/LocalService.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/delegate/LocalService.java @@ -33,7 +33,7 @@ import com.didi.virtualapk.PluginManager; import com.didi.virtualapk.internal.LoadedPlugin; import com.didi.virtualapk.utils.PluginUtil; -import com.didi.virtualapk.utils.ReflectUtil; +import com.didi.virtualapk.utils.Reflector; import java.lang.reflect.Method; @@ -87,7 +87,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { target.setExtrasClassLoader(plugin.getClassLoader()); switch (command) { case EXTRA_COMMAND_START_SERVICE: { - ActivityThread mainThread = (ActivityThread)ReflectUtil.getActivityThread(getBaseContext()); + ActivityThread mainThread = ActivityThread.currentActivityThread(); IApplicationThread appThread = mainThread.getApplicationThread(); Service service; @@ -114,7 +114,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { break; } case EXTRA_COMMAND_BIND_SERVICE: { - ActivityThread mainThread = (ActivityThread)ReflectUtil.getActivityThread(getBaseContext()); + ActivityThread mainThread = ActivityThread.currentActivityThread(); IApplicationThread appThread = mainThread.getApplicationThread(); Service service = null; @@ -141,11 +141,9 @@ public int onStartCommand(Intent intent, int flags, int startId) { IBinder serviceConnection = PluginUtil.getBinder(intent.getExtras(), "sc"); IServiceConnection iServiceConnection = IServiceConnection.Stub.asInterface(serviceConnection); if (Build.VERSION.SDK_INT >= 26) { - ReflectUtil.invokeNoException(IServiceConnection.class, iServiceConnection, "connected", - new Class[]{ComponentName.class, IBinder.class, boolean.class}, - new Object[]{component, binder, false}); + iServiceConnection.connected(component, binder, false); } else { - iServiceConnection.connected(component, binder); + Reflector.QuietReflector.with(iServiceConnection).method("connected", ComponentName.class, IBinder.class).call(component, binder); } } catch (Exception e) { e.printStackTrace(); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 0a21af8..6425c0b 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -57,11 +57,10 @@ import com.didi.virtualapk.utils.DexUtil; import com.didi.virtualapk.utils.PackageParserCompat; import com.didi.virtualapk.utils.PluginUtil; -import com.didi.virtualapk.utils.ReflectUtil; +import com.didi.virtualapk.utils.Reflector; import com.didi.virtualapk.utils.RunUtil; import java.io.File; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -101,7 +100,7 @@ private static ClassLoader createClassLoader(Context context, File apk, File lib private static AssetManager createAssetManager(Context context, File apk) { try { AssetManager am = AssetManager.class.newInstance(); - ReflectUtil.invoke(AssetManager.class, am, "addAssetPath", apk.getAbsolutePath()); + Reflector.with(am).method("addAssetPath", String.class).call(apk.getAbsolutePath()); return am; } catch (Exception e) { e.printStackTrace(); @@ -167,6 +166,7 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags } else { this.mPackageInfo.signatures = this.mPackage.mSignatures; } + this.mPackageInfo.packageName = this.mPackage.packageName; if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) { throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName); @@ -367,11 +367,7 @@ public Resources.Theme getTheme() { } public void setTheme(int resid) { - try { - ReflectUtil.setField(Resources.class, this.mResources, "mThemeResId", resid); - } catch (Exception e) { - e.printStackTrace(); - } + Reflector.QuietReflector.with(this.mResources).field("mThemeResId").set(resid); } private Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { @@ -549,7 +545,7 @@ public String[] canonicalToCurrentPackageNames(String[] names) { } @Override - public Intent getLaunchIntentForPackage(String packageName) { + public Intent getLaunchIntentForPackage(@NonNull String packageName) { LoadedPlugin plugin = mPluginManager.getLoadedPlugin(packageName); if (null != plugin) { return plugin.getLaunchIntent(); @@ -558,9 +554,9 @@ public Intent getLaunchIntentForPackage(String packageName) { return this.mHostPackageManager.getLaunchIntentForPackage(packageName); } - @Override @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public Intent getLeanbackLaunchIntentForPackage(String packageName) { + @Override + public Intent getLeanbackLaunchIntentForPackage(@NonNull String packageName) { LoadedPlugin plugin = mPluginManager.getLoadedPlugin(packageName); if (null != plugin) { return plugin.getLeanbackLaunchIntent(); @@ -570,9 +566,21 @@ public Intent getLeanbackLaunchIntentForPackage(String packageName) { } @Override - public int[] getPackageGids(String packageName) throws NameNotFoundException { + public int[] getPackageGids(@NonNull String packageName) throws NameNotFoundException { return this.mHostPackageManager.getPackageGids(packageName); } + + @TargetApi(Build.VERSION_CODES.N) + @Override + public int[] getPackageGids(String packageName, int flags) throws NameNotFoundException { + return this.mHostPackageManager.getPackageGids(packageName, flags); + } + + @TargetApi(Build.VERSION_CODES.N) + @Override + public int getPackageUid(String packageName, int flags) throws NameNotFoundException { + return this.mHostPackageManager.getPackageUid(packageName, flags); + } @Override public PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException { @@ -649,8 +657,8 @@ public List getInstalledPackages(int flags) { return this.mHostPackageManager.getInstalledPackages(flags); } - @Override @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + @Override public List getPackagesHoldingPermissions(String[] permissions, int flags) { return this.mHostPackageManager.getPackagesHoldingPermissions(permissions, flags); } @@ -660,6 +668,12 @@ public int checkPermission(String permName, String pkgName) { return this.mHostPackageManager.checkPermission(permName, pkgName); } + @TargetApi(Build.VERSION_CODES.M) + @Override + public boolean isPermissionRevokedByPolicy(@NonNull String permName, @NonNull String pkgName) { + return this.mHostPackageManager.isPermissionRevokedByPolicy(permName, pkgName); + } + @Override public boolean addPermission(PermissionInfo info) { return this.mHostPackageManager.addPermission(info); @@ -708,8 +722,8 @@ public boolean isInstantApp() { @TargetApi(Build.VERSION_CODES.O) @Override - public boolean isInstantApp(String s) { - return this.mHostPackageManager.isInstantApp(s); + public boolean isInstantApp(String packageName) { + return this.mHostPackageManager.isInstantApp(packageName); } @TargetApi(Build.VERSION_CODES.O) @@ -733,8 +747,8 @@ public void clearInstantAppCookie() { @TargetApi(Build.VERSION_CODES.O) @Override - public void updateInstantAppCookie(@Nullable byte[] bytes) { - this.mHostPackageManager.updateInstantAppCookie(bytes); + public void updateInstantAppCookie(@Nullable byte[] cookie) { + this.mHostPackageManager.updateInstantAppCookie(cookie); } @Override @@ -745,15 +759,15 @@ public String[] getSystemSharedLibraryNames() { @TargetApi(Build.VERSION_CODES.O) @NonNull @Override - public List getSharedLibraries(int i) { - return this.mHostPackageManager.getSharedLibraries(i); + public List getSharedLibraries(int flags) { + return this.mHostPackageManager.getSharedLibraries(flags); } @TargetApi(Build.VERSION_CODES.O) @Nullable @Override - public ChangedPackages getChangedPackages(int i) { - return this.mHostPackageManager.getChangedPackages(i); + public ChangedPackages getChangedPackages(int sequenceNumber) { + return this.mHostPackageManager.getChangedPackages(sequenceNumber); } @Override @@ -766,6 +780,12 @@ public boolean hasSystemFeature(String name) { return this.mHostPackageManager.hasSystemFeature(name); } + @TargetApi(Build.VERSION_CODES.N) + @Override + public boolean hasSystemFeature(String name, int version) { + return this.mHostPackageManager.hasSystemFeature(name, version); + } + @Override public ResolveInfo resolveActivity(Intent intent, int flags) { ResolveInfo resolveInfo = mPluginManager.resolveActivity(intent, flags); @@ -840,7 +860,7 @@ public List queryBroadcastReceivers(Intent intent, int flags) { } } - List all = new ArrayList(); + List all = new ArrayList<>(); List pluginResolveInfos = mPluginManager.queryBroadcastReceivers(intent, flags); if (null != pluginResolveInfos && pluginResolveInfos.size() > 0) { @@ -969,8 +989,8 @@ public Drawable getActivityIcon(Intent intent) throws NameNotFoundException { return this.mHostPackageManager.getActivityIcon(intent); } - @Override @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override public Drawable getActivityBanner(ComponentName component) throws NameNotFoundException { LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component); if (null != plugin) { @@ -980,8 +1000,8 @@ public Drawable getActivityBanner(ComponentName component) throws NameNotFoundEx return this.mHostPackageManager.getActivityBanner(component); } - @Override @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override public Drawable getActivityBanner(Intent intent) throws NameNotFoundException { ResolveInfo ri = mPluginManager.resolveActivity(intent); if (null != ri) { @@ -1017,8 +1037,8 @@ public Drawable getApplicationIcon(String packageName) throws NameNotFoundExcept return this.mHostPackageManager.getApplicationIcon(packageName); } - @Override @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override public Drawable getApplicationBanner(ApplicationInfo info) { LoadedPlugin plugin = mPluginManager.getLoadedPlugin(info.packageName); if (null != plugin) { @@ -1028,8 +1048,8 @@ public Drawable getApplicationBanner(ApplicationInfo info) { return this.mHostPackageManager.getApplicationBanner(info); } - @Override @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override public Drawable getApplicationBanner(String packageName) throws NameNotFoundException { LoadedPlugin plugin = mPluginManager.getLoadedPlugin(packageName); if (null != plugin) { @@ -1080,8 +1100,8 @@ public Drawable getApplicationLogo(String packageName) throws NameNotFoundExcept return this.mHostPackageManager.getApplicationLogo(packageName); } - @Override @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { return this.mHostPackageManager.getUserBadgedIcon(icon, user); } @@ -1089,21 +1109,22 @@ public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public Drawable getUserBadgeForDensity(UserHandle user, int density) { try { - Method method = PackageManager.class.getMethod("getUserBadgeForDensity", UserHandle.class, int.class); - return (Drawable) method.invoke(this.mHostPackageManager, user, density); + return Reflector.with(this.mHostPackageManager) + .method("getUserBadgeForDensity", UserHandle.class, int.class) + .call(user, density); } catch (Exception e) { throw new RuntimeException(e); } } - @Override @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override public Drawable getUserBadgedDrawableForDensity(Drawable drawable, UserHandle user, Rect badgeLocation, int badgeDensity) { return this.mHostPackageManager.getUserBadgedDrawableForDensity(drawable, user, badgeLocation, badgeDensity); } - @Override @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) { return this.mHostPackageManager.getUserBadgedLabel(label, user); } @@ -1177,8 +1198,8 @@ public void verifyPendingInstall(int id, int verificationCode) { this.mHostPackageManager.verifyPendingInstall(id, verificationCode); } - @Override @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + @Override public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay) { this.mHostPackageManager.extendVerificationTimeout(id, verificationCodeAtTimeout, millisecondsToDelay); } @@ -1229,7 +1250,7 @@ public void clearPackagePreferredActivities(String packageName) { } @Override - public int getPreferredActivities(List outFilters, List outActivities, String packageName) { + public int getPreferredActivities(@NonNull List outFilters, @NonNull List outActivities, String packageName) { return this.mHostPackageManager.getPreferredActivities(outFilters, outActivities, packageName); } @@ -1260,13 +1281,13 @@ public boolean isSafeMode() { @TargetApi(Build.VERSION_CODES.O) @Override - public void setApplicationCategoryHint(@NonNull String s, int i) { - this.mHostPackageManager.setApplicationCategoryHint(s, i); + public void setApplicationCategoryHint(@NonNull String packageName, int categoryHint) { + this.mHostPackageManager.setApplicationCategoryHint(packageName, categoryHint); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override - public PackageInstaller getPackageInstaller() { + public @NonNull PackageInstaller getPackageInstaller() { return this.mHostPackageManager.getPackageInstaller(); } @@ -1275,31 +1296,6 @@ public PackageInstaller getPackageInstaller() { public boolean canRequestPackageInstalls() { return this.mHostPackageManager.canRequestPackageInstalls(); } - - @TargetApi(24) - public int[] getPackageGids(String s, int i) throws NameNotFoundException { - return mHostPackageManager.getPackageGids(s); - } - - public int getPackageUid(String s, int i) throws NameNotFoundException { - Object uid = ReflectUtil.invokeNoException(PackageManager.class, mHostPackageManager, "getPackageUid", - new Class[]{String.class, int.class}, s, i); - if (uid != null) { - return (int) uid; - } else { - throw new NameNotFoundException(s); - } - } - - @TargetApi(23) - public boolean isPermissionRevokedByPolicy(String s, String s1) { - return false; - } - - @TargetApi(24) - public boolean hasSystemFeature(String s, int i) { - return mHostPackageManager.hasSystemFeature(s); - } } } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContentResolver.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContentResolver.java index 6f5248c..6d6c738 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContentResolver.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContentResolver.java @@ -16,108 +16,76 @@ package com.didi.virtualapk.internal; -import android.content.ContentResolver; +import android.annotation.TargetApi; +import android.content.ContentResolverWrapper; import android.content.Context; import android.content.IContentProvider; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.support.annotation.Keep; import com.didi.virtualapk.PluginManager; import com.didi.virtualapk.delegate.RemoteContentProvider; -import java.lang.reflect.Method; - /** * Created by renyugang on 16/12/7. */ -public class PluginContentResolver extends ContentResolver { - private ContentResolver mBase; +public class PluginContentResolver extends ContentResolverWrapper { private PluginManager mPluginManager; - private static Method sAcquireProvider; - private static Method sAcquireExistingProvider; - private static Method sAcquireUnstableProvider; - - static { - try { - sAcquireProvider = ContentResolver.class.getDeclaredMethod("acquireProvider", - new Class[]{Context.class, String.class}); - sAcquireProvider.setAccessible(true); - sAcquireExistingProvider = ContentResolver.class.getDeclaredMethod("acquireExistingProvider", - new Class[]{Context.class, String.class}); - sAcquireExistingProvider.setAccessible(true); - sAcquireUnstableProvider = ContentResolver.class.getDeclaredMethod("acquireUnstableProvider", - new Class[]{Context.class, String.class}); - sAcquireUnstableProvider.setAccessible(true); - } catch (Exception e) { - //ignored - } - } public PluginContentResolver(Context context) { super(context); - mBase = context.getContentResolver(); mPluginManager = PluginManager.getInstance(context); } - + + @Override protected IContentProvider acquireProvider(Context context, String auth) { - try { - if (mPluginManager.resolveContentProvider(auth, 0) != null) { - return mPluginManager.getIContentProvider(); - } - - return (IContentProvider) sAcquireProvider.invoke(mBase, context, auth); - } catch (Exception e) { - e.printStackTrace(); + if (mPluginManager.resolveContentProvider(auth, 0) != null) { + return mPluginManager.getIContentProvider(); } - - return null; + return super.acquireProvider(context, auth); } + @Override protected IContentProvider acquireExistingProvider(Context context, String auth) { - try { - if (mPluginManager.resolveContentProvider(auth, 0) != null) { - return mPluginManager.getIContentProvider(); - } - - return (IContentProvider) sAcquireExistingProvider.invoke(mBase, context, auth); - } catch (Exception e) { - e.printStackTrace(); + if (mPluginManager.resolveContentProvider(auth, 0) != null) { + return mPluginManager.getIContentProvider(); } - - return null; + return super.acquireExistingProvider(context, auth); } - + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override protected IContentProvider acquireUnstableProvider(Context context, String auth) { - try { - if (mPluginManager.resolveContentProvider(auth, 0) != null) { - return mPluginManager.getIContentProvider(); - } - - return (IContentProvider) sAcquireUnstableProvider.invoke(mBase, context, auth); - } catch (Exception e) { - e.printStackTrace(); + if (mPluginManager.resolveContentProvider(auth, 0) != null) { + return mPluginManager.getIContentProvider(); } - - return null; + return super.acquireUnstableProvider(context, auth); } + @Override public boolean releaseProvider(IContentProvider provider) { return true; } - + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override public boolean releaseUnstableProvider(IContentProvider icp) { return true; } - + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override public void unstableProviderDied(IContentProvider icp) { } + @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override public void appNotRespondingViaProvider(IContentProvider icp) { } - /** @hide */ protected int resolveUserIdFromAuthority(String auth) { return 0; } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java index b54858a..682246b 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java @@ -16,6 +16,7 @@ package com.didi.virtualapk.internal; +import android.app.ActivityThread; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Configuration; @@ -24,7 +25,7 @@ import android.util.DisplayMetrics; import com.didi.virtualapk.PluginManager; -import com.didi.virtualapk.utils.ReflectUtil; +import com.didi.virtualapk.utils.Reflector; import java.lang.ref.WeakReference; import java.util.List; @@ -40,16 +41,19 @@ public static synchronized Resources createResources(Context hostContext, String Resources newResources = null; AssetManager assetManager; try { + Reflector reflector = Reflector.on(AssetManager.class).method("addAssetPath", String.class); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { assetManager = AssetManager.class.newInstance(); - ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", hostContext.getApplicationInfo().sourceDir); + reflector.bind(assetManager); + reflector.call(hostContext.getApplicationInfo().sourceDir); } else { assetManager = hostResources.getAssets(); + reflector.bind(assetManager); } - ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", apk); + reflector.call(apk); List pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins(); for (LoadedPlugin plugin : pluginList) { - ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", plugin.getLocation()); + reflector.call(plugin.getLocation()); } if (isMiUi(hostResources)) { newResources = MiUiResourcesCompat.createResources(hostResources, assetManager); @@ -77,21 +81,27 @@ public static synchronized Resources createResources(Context hostContext, String public static void hookResources(Context base, Resources resources) { try { - ReflectUtil.setField(base.getClass(), base, "mResources", resources); - Object loadedApk = ReflectUtil.getPackageInfo(base); - ReflectUtil.setField(loadedApk.getClass(), loadedApk, "mResources", resources); - - Object activityThread = ReflectUtil.getActivityThread(base); - Object resManager = ReflectUtil.getField(activityThread.getClass(), activityThread, "mResourcesManager"); + Reflector reflector = Reflector.with(base); + reflector.field("mResources").set(resources); + Object loadedApk = reflector.field("mPackageInfo").get(); + Reflector.with(loadedApk).field("mResources").set(resources); + + Object activityThread = ActivityThread.currentActivityThread(); + Object resManager; + if (Build.VERSION.SDK_INT >= 19) { + resManager = android.app.ResourcesManager.getInstance(); + } else { + resManager = Reflector.with(activityThread).field("mResourcesManager").get(); + } if (Build.VERSION.SDK_INT < 24) { - Map> map = (Map>) ReflectUtil.getField(resManager.getClass(), resManager, "mActiveResources"); + Map> map = Reflector.with(resManager).field("mActiveResources").get(); Object key = map.keySet().iterator().next(); map.put(key, new WeakReference<>(resources)); } else { // still hook Android N Resources, even though it's unnecessary, then nobody will be strange. - Map map = (Map) ReflectUtil.getFieldNoException(resManager.getClass(), resManager, "mResourceImpls"); + Map map = Reflector.QuietReflector.with(resManager).field("mResourceImpls").get(); Object key = map.keySet().iterator().next(); - Object resourcesImpl = ReflectUtil.getFieldNoException(Resources.class, resources, "mResourcesImpl"); + Object resourcesImpl = Reflector.QuietReflector.with(resources).field("mResourcesImpl").get(); map.put(key, new WeakReference<>(resourcesImpl)); } } catch (Exception e) { @@ -117,34 +127,30 @@ private static boolean isNotRawResources(Resources resources) { private static final class MiUiResourcesCompat { private static Resources createResources(Resources hostResources, AssetManager assetManager) throws Exception { - Class resourcesClazz = Class.forName("android.content.res.MiuiResources"); - Resources newResources = (Resources) ReflectUtil.invokeConstructor(resourcesClazz, - new Class[]{AssetManager.class, DisplayMetrics.class, Configuration.class}, - new Object[]{assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()}); + Reflector reflector = Reflector.on("android.content.res.MiuiResources"); + Resources newResources = reflector.constructor(AssetManager.class, DisplayMetrics.class, Configuration.class) + .newInstance(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); return newResources; } } private static final class VivoResourcesCompat { private static Resources createResources(Context hostContext, Resources hostResources, AssetManager assetManager) throws Exception { - Class resourcesClazz = Class.forName("android.content.res.VivoResources"); - Resources newResources = (Resources) ReflectUtil.invokeConstructor(resourcesClazz, - new Class[]{AssetManager.class, DisplayMetrics.class, Configuration.class}, - new Object[]{assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()}); - ReflectUtil.invokeNoException(resourcesClazz, newResources, "init", - new Class[]{String.class}, hostContext.getPackageName()); - Object themeValues = ReflectUtil.getFieldNoException(resourcesClazz, hostResources, "mThemeValues"); - ReflectUtil.setFieldNoException(resourcesClazz, newResources, "mThemeValues", themeValues); + Reflector reflector = Reflector.on("android.content.res.VivoResources"); + Resources newResources = reflector.constructor(AssetManager.class, DisplayMetrics.class, Configuration.class) + .newInstance(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); + reflector.method("init", String.class).callByCaller(newResources, hostContext.getPackageName()); + reflector.field("mThemeValues"); + reflector.set(newResources, reflector.get(hostResources)); return newResources; } } private static final class NubiaResourcesCompat { private static Resources createResources(Resources hostResources, AssetManager assetManager) throws Exception { - Class resourcesClazz = Class.forName("android.content.res.NubiaResources"); - Resources newResources = (Resources) ReflectUtil.invokeConstructor(resourcesClazz, - new Class[]{AssetManager.class, DisplayMetrics.class, Configuration.class}, - new Object[]{assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()}); + Reflector reflector = Reflector.on("android.content.res.NubiaResources"); + Resources newResources = reflector.constructor(AssetManager.class, DisplayMetrics.class, Configuration.class) + .newInstance(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); return newResources; } } @@ -153,10 +159,9 @@ private static final class AdaptationResourcesCompat { private static Resources createResources(Resources hostResources, AssetManager assetManager) throws Exception { Resources newResources; try { - Class resourcesClazz = hostResources.getClass(); - newResources = (Resources) ReflectUtil.invokeConstructor(resourcesClazz, - new Class[]{AssetManager.class, DisplayMetrics.class, Configuration.class}, - new Object[]{assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()}); + Reflector reflector = Reflector.with(hostResources); + newResources = reflector.constructor(AssetManager.class, DisplayMetrics.class, Configuration.class) + .newInstance(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); } catch (Exception e) { newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 72aa53c..0759f49 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -16,25 +16,25 @@ package com.didi.virtualapk.internal; +import android.annotation.TargetApi; import android.app.Activity; import android.app.Fragment; import android.app.Instrumentation; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.PersistableBundle; import android.util.Log; -import android.view.ContextThemeWrapper; import com.didi.virtualapk.PluginManager; import com.didi.virtualapk.utils.PluginUtil; -import com.didi.virtualapk.utils.ReflectUtil; +import com.didi.virtualapk.utils.Reflector; /** @@ -53,119 +53,38 @@ public VAInstrumentation(PluginManager pluginManager, Instrumentation base) { this.mBase = base; } - public ActivityResult execStartActivity( - Context who, IBinder contextThread, IBinder token, Activity target, - Intent intent, int requestCode, Bundle options) { - mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent); - // null component is an implicitly intent - if (intent.getComponent() != null) { - Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), - intent.getComponent().getClassName())); - // resolve intent with Stub Activity if needed - this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); - } - - ActivityResult result = realExecStartActivity(who, contextThread, token, target, - intent, requestCode, options); - - return result; - - } - - private ActivityResult realExecStartActivity( - Context who, IBinder contextThread, IBinder token, Activity target, - Intent intent, int requestCode, Bundle options) { - ActivityResult result = null; - try { - Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, - int.class, Bundle.class}; - result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase, - "execStartActivity", parameterTypes, - who, contextThread, token, target, intent, requestCode, options); - } catch (Exception e) { - if (e.getCause() instanceof ActivityNotFoundException) { - throw (ActivityNotFoundException) e.getCause(); - } - e.printStackTrace(); - } - - return result; + @Override + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) { + injectIntent(intent); + return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode); } - public ActivityResult execStartActivity( - Context who, IBinder contextThread, IBinder token, Fragment target, - Intent intent, int requestCode, Bundle options) { - mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent); - // null component is an implicitly intent - if (intent.getComponent() != null) { - Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), - intent.getComponent().getClassName())); - // resolve intent with Stub Activity if needed - this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); - } - - ActivityResult result = realExecStartActivity(who, contextThread, token, target, - intent, requestCode, options); - - return result; - + @Override + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { + injectIntent(intent); + return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options); } - private ActivityResult realExecStartActivity( - Context who, IBinder contextThread, IBinder token, Fragment target, - Intent intent, int requestCode, Bundle options) { - ActivityResult result = null; - try { - Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Fragment.class, Intent.class, - int.class, Bundle.class}; - result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase, - "execStartActivity", parameterTypes, - who, contextThread, token, target, intent, requestCode, options); - } catch (Exception e) { - if (e.getCause() instanceof ActivityNotFoundException) { - throw (ActivityNotFoundException) e.getCause(); - } - e.printStackTrace(); - } - - return result; + @Override + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options) { + injectIntent(intent); + return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options); } - private ActivityResult realExecStartActivity( - Context who, IBinder contextThread, IBinder token, String target, - Intent intent, int requestCode, Bundle options) { - ActivityResult result = null; - try { - Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, String.class, Intent.class, - int.class, Bundle.class}; - result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase, - "execStartActivity", parameterTypes, - who, contextThread, token, target, intent, requestCode, options); - } catch (Exception e) { - if (e.getCause() instanceof ActivityNotFoundException) { - throw (ActivityNotFoundException) e.getCause(); - } - e.printStackTrace(); - } - - return result; + @Override + public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) { + injectIntent(intent); + return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options); } - - public ActivityResult execStartActivity( - Context who, IBinder contextThread, IBinder token, String target, - Intent intent, int requestCode, Bundle options) { + + private void injectIntent(Intent intent) { mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent); // null component is an implicitly intent if (intent.getComponent() != null) { - Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), - intent.getComponent().getClassName())); + Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), intent.getComponent().getClassName())); // resolve intent with Stub Activity if needed this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); } - - ActivityResult result = realExecStartActivity(who, contextThread, token, target, - intent, requestCode, options); - return null; } @Override @@ -185,7 +104,7 @@ public Activity newActivity(ClassLoader cl, String className, Intent intent) thr try { // for 4.1+ - ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources()); + Reflector.with(activity).field("mResources").set(plugin.getResources()); } catch (Exception ignored) { // ignored. } @@ -199,15 +118,27 @@ public Activity newActivity(ClassLoader cl, String className, Intent intent) thr @Override public void callActivityOnCreate(Activity activity, Bundle icicle) { + injectActivity(activity); + mBase.callActivityOnCreate(activity, icicle); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { + injectActivity(activity); + mBase.callActivityOnCreate(activity, icicle, persistentState); + } + + private void injectActivity(Activity activity) { final Intent intent = activity.getIntent(); if (PluginUtil.isIntentFromPlugin(intent)) { Context base = activity.getBaseContext(); try { LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent); - ReflectUtil.setField(base.getClass(), base, "mResources", plugin.getResources()); - ReflectUtil.setField(ContextWrapper.class, activity, "mBase", plugin.getPluginContext()); - ReflectUtil.setField(Activity.class, activity, "mApplication", plugin.getApplication()); - ReflectUtil.setFieldNoException(ContextThemeWrapper.class, activity, "mBase", plugin.getPluginContext()); + Reflector.with(base).field("mResources").set(plugin.getResources()); + Reflector reflector = Reflector.with(activity); + reflector.field("mBase").set(plugin.getPluginContext()); + reflector.field("mApplication").set(plugin.getApplication()); // set screenOrientation ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent)); @@ -217,10 +148,7 @@ public void callActivityOnCreate(Activity activity, Bundle icicle) { } catch (Exception e) { e.printStackTrace(); } - } - - mBase.callActivityOnCreate(activity, icicle); } @Override @@ -229,9 +157,10 @@ public boolean handleMessage(Message msg) { // ActivityClientRecord r Object r = msg.obj; try { - Intent intent = (Intent) ReflectUtil.getField(r.getClass(), r, "intent"); + Reflector reflector = Reflector.with(r); + Intent intent = reflector.field("intent").get(); intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader()); - ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo"); + ActivityInfo activityInfo = reflector.field("activityInfo").get(); if (PluginUtil.isIntentFromPlugin(intent)) { int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java index a9e8e31..4316718 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java @@ -16,10 +16,10 @@ package com.didi.virtualapk.utils; +import android.app.ActivityThread; import android.content.Context; import android.os.Build; -import com.didi.virtualapk.Systems; import com.didi.virtualapk.internal.Constants; import java.io.File; @@ -38,7 +38,7 @@ public static void insertDex(DexClassLoader dexClassLoader) throws Exception { Object newDexElements = getDexElements(getPathList(dexClassLoader)); Object allDexElements = combineArray(baseDexElements, newDexElements); Object pathList = getPathList(getPathClassLoader()); - ReflectUtil.setField(pathList.getClass(), pathList, "dexElements", allDexElements); + Reflector.with(pathList).field("dexElements").set(allDexElements); insertNativeLibrary(dexClassLoader); @@ -50,11 +50,11 @@ private static PathClassLoader getPathClassLoader() { } private static Object getDexElements(Object pathList) throws Exception { - return ReflectUtil.getField(pathList.getClass(), pathList, "dexElements"); + return Reflector.with(pathList).field("dexElements").get(); } private static Object getPathList(Object baseDexClassLoader) throws Exception { - return ReflectUtil.getField(Class.forName("dalvik.system.BaseDexClassLoader"), baseDexClassLoader, "pathList"); + return Reflector.with(baseDexClassLoader).field("pathList").get(); } private static Object combineArray(Object firstArray, Object secondArray) { @@ -78,17 +78,18 @@ private static synchronized void insertNativeLibrary(DexClassLoader dexClassLoad } sHasInsertedNativeLibrary = true; + Context context = ActivityThread.currentApplication(); Object basePathList = getPathList(getPathClassLoader()); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { - List nativeLibraryDirectories = (List) ReflectUtil.getField(basePathList.getClass(), - basePathList, "nativeLibraryDirectories"); - nativeLibraryDirectories.add(Systems.getContext().getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE)); + Reflector reflector = Reflector.with(basePathList); + List nativeLibraryDirectories = reflector.field("nativeLibraryDirectories").get(); + nativeLibraryDirectories.add(context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE)); - Object baseNativeLibraryPathElements = ReflectUtil.getField(basePathList.getClass(), basePathList, "nativeLibraryPathElements"); + Object baseNativeLibraryPathElements = reflector.field("nativeLibraryPathElements").get(); final int baseArrayLength = Array.getLength(baseNativeLibraryPathElements); Object newPathList = getPathList(dexClassLoader); - Object newNativeLibraryPathElements = ReflectUtil.getField(newPathList.getClass(), newPathList, "nativeLibraryPathElements"); + Object newNativeLibraryPathElements = reflector.get(newPathList); Class elementClass = newNativeLibraryPathElements.getClass().getComponentType(); Object allNativeLibraryPathElements = Array.newInstance(elementClass, baseArrayLength + 1); System.arraycopy(baseNativeLibraryPathElements, 0, allNativeLibraryPathElements, 0, baseArrayLength); @@ -110,15 +111,15 @@ private static synchronized void insertNativeLibrary(DexClassLoader dexClassLoad } } - ReflectUtil.setField(basePathList.getClass(), basePathList, "nativeLibraryPathElements", allNativeLibraryPathElements); + reflector.set(allNativeLibraryPathElements); } else { - File[] nativeLibraryDirectories = (File[]) ReflectUtil.getFieldNoException(basePathList.getClass(), - basePathList, "nativeLibraryDirectories"); + Reflector reflector = Reflector.with(basePathList).field("nativeLibraryDirectories"); + File[] nativeLibraryDirectories = reflector.get(); final int N = nativeLibraryDirectories.length; File[] newNativeLibraryDirectories = new File[N + 1]; System.arraycopy(nativeLibraryDirectories, 0, newNativeLibraryDirectories, 0, N); - newNativeLibraryDirectories[N] = Systems.getContext().getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE); - ReflectUtil.setField(basePathList.getClass(), basePathList, "nativeLibraryDirectories", newNativeLibraryDirectories); + newNativeLibraryDirectories[N] = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE); + reflector.set(newNativeLibraryDirectories); } } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java index ba7c133..9c5230a 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PackageParserCompat.java @@ -46,8 +46,9 @@ private static final class PackageParserPPreview { static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException { PackageParser parser = new PackageParser(); PackageParser.Package pkg = parser.parsePackage(apk, flags); - ReflectUtil.invokeNoException(PackageParser.class, null, "collectCertificates", - new Class[]{PackageParser.Package.class, boolean.class}, pkg, false); + Reflector.QuietReflector.with(parser) + .method("collectCertificates", PackageParser.Package.class, boolean.class) + .call(pkg, false); return pkg; } } @@ -57,8 +58,9 @@ private static final class PackageParserV24 { static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException { PackageParser parser = new PackageParser(); PackageParser.Package pkg = parser.parsePackage(apk, flags); - ReflectUtil.invokeNoException(PackageParser.class, null, "collectCertificates", - new Class[]{PackageParser.Package.class, int.class}, pkg, flags); + Reflector.QuietReflector.with(parser) + .method("collectCertificates", PackageParser.Package.class, int.class) + .call(pkg, flags); return pkg; } } @@ -83,8 +85,9 @@ private static final class PackageParserLegacy { static final PackageParser.Package parsePackage(Context context, File apk, int flags) { PackageParser parser = new PackageParser(apk.getAbsolutePath()); PackageParser.Package pkg = parser.parsePackage(apk, apk.getAbsolutePath(), context.getResources().getDisplayMetrics(), flags); - ReflectUtil.invokeNoException(PackageParser.class, parser, "collectCertificates", - new Class[]{PackageParser.Package.class, int.class}, pkg, flags); + Reflector.QuietReflector.with(parser) + .method("collectCertificates", PackageParser.Package.class, int.class) + .call(pkg, flags); return pkg; } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java index 9950d07..5d7b5a5 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java @@ -30,7 +30,6 @@ import android.os.IBinder; import android.text.TextUtils; import android.util.Log; -import android.view.ContextThemeWrapper; import com.didi.virtualapk.PluginManager; import com.didi.virtualapk.internal.Constants; @@ -133,16 +132,17 @@ public static void hookActivityResources(Activity activity, String packageName) final LoadedPlugin plugin = PluginManager.getInstance(activity).getLoadedPlugin(packageName); final Resources resources = plugin.getResources(); if (resources != null) { - ReflectUtil.setField(base.getClass(), base, "mResources", resources); + Reflector.with(base).field("mResources").set(resources); // copy theme Resources.Theme theme = resources.newTheme(); theme.setTo(activity.getTheme()); - int themeResource = (int)ReflectUtil.getField(ContextThemeWrapper.class, activity, "mThemeResource"); + Reflector reflector = Reflector.with(activity); + int themeResource = reflector.field("mThemeResource").get(); theme.applyStyle(themeResource, true); - ReflectUtil.setField(ContextThemeWrapper.class, activity, "mTheme", theme); + reflector.field("mTheme").set(theme); - ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", resources); + reflector.field("mResources").set(resources); } } catch (Exception e) { e.printStackTrace(); @@ -161,10 +161,7 @@ public static void putBinder(Bundle bundle, String key, IBinder value) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { bundle.putBinder(key, value); } else { - try { - ReflectUtil.invoke(Bundle.class, bundle, "putIBinder", new Class[]{String.class, IBinder.class}, key, value); - } catch (Exception e) { - } + Reflector.QuietReflector.with(bundle).method("putIBinder", String.class, IBinder.class).call(key, value); } } @@ -172,12 +169,8 @@ public static IBinder getBinder(Bundle bundle, String key) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return bundle.getBinder(key); } else { - try { - return (IBinder) ReflectUtil.invoke(Bundle.class, bundle, "getIBinder", key); - } catch (Exception e) { - } - - return null; + return (IBinder) Reflector.QuietReflector.with(bundle) + .method("getIBinder", String.class).call(key); } } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/ReflectUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/ReflectUtil.java deleted file mode 100644 index 1633985..0000000 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/ReflectUtil.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.didi.virtualapk.utils; - -import android.app.Instrumentation; -import android.content.Context; -import android.os.Handler; -import android.support.annotation.UiThread; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -@SuppressWarnings("rawtypes") -public class ReflectUtil { - - public static Object sActivityThread; - public static Object sLoadedApk; - public static Instrumentation sInstrumentation; - - public static Object getField(Class clazz, Object target, String name) throws Exception { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - return field.get(target); - } - - public static Object getFieldNoException(Class clazz, Object target, String name) { - try { - return ReflectUtil.getField(clazz, target, name); - } catch (Exception e) { - //ignored. - } - - return null; - } - - public static void setField(Class clazz, Object target, String name, Object value) throws Exception { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - field.set(target, value); - } - - public static void setFieldNoException(Class clazz, Object target, String name, Object value) { - try { - ReflectUtil.setField(clazz, target, name, value); - } catch (Exception e) { - //ignored. - } - } - - @SuppressWarnings("unchecked") - public static Object invoke(Class clazz, Object target, String name, Object... args) - throws Exception { - Class[] parameterTypes = null; - if (args != null) { - parameterTypes = new Class[args.length]; - for (int i = 0; i < args.length; i++) { - parameterTypes[i] = args[i].getClass(); - } - } - - Method method = clazz.getDeclaredMethod(name, parameterTypes); - method.setAccessible(true); - return method.invoke(target, args); - } - - @SuppressWarnings("unchecked") - public static Object invoke(Class clazz, Object target, String name, Class[] parameterTypes, Object... args) - throws Exception { - Method method = clazz.getDeclaredMethod(name, parameterTypes); - method.setAccessible(true); - return method.invoke(target, args); - } - - @SuppressWarnings("unchecked") - public static Object invokeNoException(Class clazz, Object target, String name, Class[] parameterTypes, Object... args) { - try { - return invoke(clazz, target, name, parameterTypes, args); - } catch (Exception e) { - } - - return null; - } - - @SuppressWarnings("unchecked") - public static Object invokeConstructor(Class clazz, Class[] parameterTypes, Object... args) - throws Exception { - Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); - constructor.setAccessible(true); - return constructor.newInstance(args); - } - - @UiThread - public static Object getActivityThread(Context base) { - if (sActivityThread == null) { - try { - Class activityThreadClazz = Class.forName("android.app.ActivityThread"); - Object activityThread = null; - try { - activityThread = ReflectUtil.getField(activityThreadClazz, null, "sCurrentActivityThread"); - } catch (Exception e) { - // ignored - } - if (activityThread == null) { - activityThread = ((ThreadLocal) ReflectUtil.getField(activityThreadClazz, null, "sThreadLocal")).get(); - } - sActivityThread = activityThread; - } catch (Exception e) { - e.printStackTrace(); - } - } - - return sActivityThread; - } - - public static Instrumentation getInstrumentation(Context base) { - if (getActivityThread(base) != null) { - try { - sInstrumentation = (Instrumentation) ReflectUtil.invoke( - sActivityThread.getClass(), sActivityThread, "getInstrumentation"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - return sInstrumentation; - } - - public static void setInstrumentation(Object activityThread, Instrumentation instrumentation) { - try { - ReflectUtil.setField(activityThread.getClass(), activityThread, "mInstrumentation", instrumentation); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static Object getPackageInfo(Context base) { - if (sLoadedApk == null) { - try { - sLoadedApk = ReflectUtil.getField(base.getClass(), base, "mPackageInfo"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - return sLoadedApk; - } - - public static void setHandlerCallback(Context base, Handler.Callback callback) { - try { - Object activityThread = getActivityThread(base); - Handler mainHandler = (Handler) ReflectUtil.invoke(activityThread.getClass(), activityThread, "getHandler", (Object[])null); - ReflectUtil.setField(Handler.class, mainHandler, "mCallback", callback); - } catch (Exception e) { - e.printStackTrace(); - } - } - -} diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/Reflector.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/Reflector.java new file mode 100644 index 0000000..339a0b4 --- /dev/null +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/Reflector.java @@ -0,0 +1,447 @@ +package com.didi.virtualapk.utils; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Created by qiaopu on 2018/4/26. + */ +public class Reflector { + + public static final String LOG_TAG = "VA.Reflector"; + + protected Class mType; + protected Object mCaller; + protected Constructor mConstructor; + protected Field mField; + protected Method mMethod; + + public static class ReflectedException extends Exception { + + public ReflectedException(String message) { + super(message); + } + public ReflectedException(String message, Throwable cause) { + super(message, cause); + } + + } + public static Reflector on(@NonNull String name) throws ReflectedException { + return on(name, true, Reflector.class.getClassLoader()); + } + + public static Reflector on(@NonNull String name, boolean initialize) throws ReflectedException { + return on(name, initialize, Reflector.class.getClassLoader()); + } + + public static Reflector on(@NonNull String name, boolean initialize, @Nullable ClassLoader loader) throws ReflectedException { + try { + return on(Class.forName(name, initialize, loader)); + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + public static Reflector on(@NonNull Class type) { + Reflector reflector = new Reflector(); + reflector.mType = type; + return reflector; + } + + public static Reflector with(@NonNull Object caller) throws ReflectedException { + return on(caller.getClass()).bind(caller); + } + + protected Reflector() { + + } + + public Reflector constructor(@Nullable Class... parameterTypes) throws ReflectedException { + try { + mConstructor = mType.getDeclaredConstructor(parameterTypes); + mConstructor.setAccessible(true); + mField = null; + mMethod = null; + return this; + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + @SuppressWarnings("unchecked") + public R newInstance(@Nullable Object ... initargs) throws ReflectedException { + if (mConstructor == null) { + throw new ReflectedException("Constructor was null!"); + } + try { + return (R) mConstructor.newInstance(initargs); + } catch (InvocationTargetException e) { + throw new ReflectedException("Oops!", e.getTargetException()); + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + protected Object checked(@Nullable Object caller) throws ReflectedException { + if (caller == null || mType.isInstance(caller)) { + return caller; + } + throw new ReflectedException("Caller [" + caller + "] is not a instance of type [" + mType + "]!"); + } + + protected void check(@Nullable Object caller, @Nullable Member member, @NonNull String name) throws ReflectedException { + if (member == null) { + throw new ReflectedException(name + " was null!"); + } + if (caller == null && !Modifier.isStatic(member.getModifiers())) { + throw new ReflectedException("Need a caller!"); + } + checked(caller); + } + + public Reflector bind(@Nullable Object caller) throws ReflectedException { + mCaller = checked(caller); + return this; + } + + public Reflector unbind() { + mCaller = null; + return this; + } + + public Reflector field(@NonNull String name) throws ReflectedException { + try { + mField = findField(name); + mField.setAccessible(true); + mConstructor = null; + mMethod = null; + return this; + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + protected Field findField(@NonNull String name) throws NoSuchFieldException { + try { + return mType.getField(name); + } catch (NoSuchFieldException e) { + for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { + try { + return cls.getDeclaredField(name); + } catch (NoSuchFieldException ex) { + // Ignored + } + } + throw e; + } + } + + @SuppressWarnings("unchecked") + public R get() throws ReflectedException { + return get(mCaller); + } + + @SuppressWarnings("unchecked") + public R get(@Nullable Object caller) throws ReflectedException { + check(caller, mField, "Field"); + try { + return (R) mField.get(caller); + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + public Reflector set(@Nullable Object value) throws ReflectedException { + return set(mCaller, value); + } + + public Reflector set(@Nullable Object caller, @Nullable Object value) throws ReflectedException { + check(caller, mField, "Field"); + try { + mField.set(caller, value); + return this; + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + public Reflector method(@NonNull String name, @Nullable Class... parameterTypes) throws ReflectedException { + try { + mMethod = findMethod(name, parameterTypes); + mMethod.setAccessible(true); + mConstructor = null; + mField = null; + return this; + } catch (NoSuchMethodException e) { + throw new ReflectedException("Oops!", e); + } + } + + protected Method findMethod(@NonNull String name, @Nullable Class... parameterTypes) throws NoSuchMethodException { + try { + return mType.getMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { + try { + return cls.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException ex) { + // Ignored + } + } + throw e; + } + } + + public R call(@Nullable Object... args) throws ReflectedException { + return callByCaller(mCaller, args); + } + + @SuppressWarnings("unchecked") + public R callByCaller(@Nullable Object caller, @Nullable Object... args) throws ReflectedException { + check(caller, mMethod, "Method"); + try { + return (R) mMethod.invoke(caller, args); + } catch (InvocationTargetException e) { + throw new ReflectedException("Oops!", e.getTargetException()); + } catch (Throwable e) { + throw new ReflectedException("Oops!", e); + } + } + + public static class QuietReflector extends Reflector { + + protected Throwable mIgnored; + + public static QuietReflector on(@NonNull String name) { + return on(name, true, QuietReflector.class.getClassLoader()); + } + + public static QuietReflector on(@NonNull String name, boolean initialize) { + return on(name, initialize, QuietReflector.class.getClassLoader()); + } + + public static QuietReflector on(@NonNull String name, boolean initialize, @Nullable ClassLoader loader) { + Class cls = null; + try { + cls = Class.forName(name, initialize, loader); + return on(cls, null); + } catch (Throwable e) { +// Log.w(LOG_TAG, "Oops!", e); + return on(cls, e); + } + } + + public static QuietReflector on(@Nullable Class type) { + return on(type, (type == null) ? new ReflectedException("Type was null!") : null); + } + + private static QuietReflector on(@Nullable Class type, @Nullable Throwable ignored) { + QuietReflector reflector = new QuietReflector(); + reflector.mType = type; + reflector.mIgnored = ignored; + return reflector; + } + + public static QuietReflector with(@Nullable Object caller) { + if (caller == null) { + return on((Class) null); + } + return on(caller.getClass()).bind(caller); + } + + protected QuietReflector() { + + } + + public Throwable getIgnored() { + return mIgnored; + } + + protected boolean skip() { + return skipAlways() || mIgnored != null; + } + + protected boolean skipAlways() { + return mType == null; + } + + @Override + public QuietReflector constructor(@Nullable Class... parameterTypes) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.constructor(parameterTypes); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R newInstance(@Nullable Object... initargs) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.newInstance(initargs); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public QuietReflector bind(@Nullable Object obj) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.bind(obj); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector unbind() { + super.unbind(); + return this; + } + + @Override + public QuietReflector field(@NonNull String name) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.field(name); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R get() { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.get(); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public R get(@Nullable Object caller) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.get(caller); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public QuietReflector set(@Nullable Object value) { + if (skip()) { + return this; + } + try { + mIgnored = null; + super.set(value); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector set(@Nullable Object caller, @Nullable Object value) { + if (skip()) { + return this; + } + try { + mIgnored = null; + super.set(caller, value); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector method(@NonNull String name, @Nullable Class... parameterTypes) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.method(name, parameterTypes); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R call(@Nullable Object... args) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.call(args); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public R callByCaller(@Nullable Object caller, @Nullable Object... args) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.callByCaller(caller, args); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + } +} From 7857a51c69a2fc582b24f5afd2492680c963685a Mon Sep 17 00:00:00 2001 From: superq_sky Date: Thu, 31 May 2018 13:24:18 +0800 Subject: [PATCH 46/66] Fixed some null pointer exception. --- .../com/didi/virtualapk/PluginManager.java | 6 ++- .../virtualapk/internal/LoadedPlugin.java | 2 +- .../internal/VAInstrumentation.java | 47 ++++++++++++------- .../com/didi/virtualapk/utils/DexUtil.java | 34 +++++--------- .../com/didi/virtualapk/utils/PluginUtil.java | 19 ++++++-- .../com/didi/virtualapk/utils/RunUtil.java | 22 +++++---- 6 files changed, 74 insertions(+), 56 deletions(-) diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java index 669fec3..63dcf75 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java @@ -253,11 +253,13 @@ public void loadPlugin(File apk) throws Exception { } public LoadedPlugin getLoadedPlugin(Intent intent) { - ComponentName component = PluginUtil.getComponent(intent); - return getLoadedPlugin(component.getPackageName()); + return getLoadedPlugin(PluginUtil.getComponent(intent)); } public LoadedPlugin getLoadedPlugin(ComponentName component) { + if (component == null) { + return null; + } return this.getLoadedPlugin(component.getPackageName()); } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index 6425c0b..a248a73 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -88,7 +88,7 @@ private static ClassLoader createClassLoader(Context context, File apk, File lib if (Constants.COMBINE_CLASSLOADER) { try { - DexUtil.insertDex(loader); + DexUtil.insertDex(loader, parent); } catch (Exception e) { e.printStackTrace(); } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 0759f49..1f7450a 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -18,6 +18,7 @@ import android.annotation.TargetApi; import android.app.Activity; +import android.app.Application; import android.app.Fragment; import android.app.Instrumentation; import android.content.ComponentName; @@ -91,30 +92,44 @@ private void injectIntent(Intent intent) { public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try { cl.loadClass(className); + Log.i(TAG, String.format("newActivity[%s]", className)); + } catch (ClassNotFoundException e) { ComponentName component = PluginUtil.getComponent(intent); - LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component); + + if (component == null) { + return mBase.newActivity(cl, className, intent); + } + String targetClassName = component.getClassName(); - Log.i(TAG, String.format("newActivity[%s : %s/%s]", className, component.getPackageName(), targetClassName)); - - if (plugin != null) { - Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent); - activity.setIntent(intent); - - try { - // for 4.1+ - Reflector.with(activity).field("mResources").set(plugin.getResources()); - } catch (Exception ignored) { - // ignored. - } - - return activity; + + LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component); + + if (plugin == null) { + return mBase.newActivity(cl, className, intent); + } + + Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent); + activity.setIntent(intent); + + try { + // for 4.1+ + Reflector.with(activity).field("mResources").set(plugin.getResources()); + } catch (Exception ignored) { + // ignored. } + + return activity; } return mBase.newActivity(cl, className, intent); } + + @Override + public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + return mBase.newApplication(cl, className, context); + } @Override public void callActivityOnCreate(Activity activity, Bundle icicle) { @@ -159,7 +174,7 @@ public boolean handleMessage(Message msg) { try { Reflector reflector = Reflector.with(r); Intent intent = reflector.field("intent").get(); - intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader()); + intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader()); ActivityInfo activityInfo = reflector.field("activityInfo").get(); if (PluginUtil.isIntentFromPlugin(intent)) { diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java index 4316718..f2bb1b6 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/DexUtil.java @@ -28,58 +28,46 @@ import java.util.List; import dalvik.system.DexClassLoader; -import dalvik.system.PathClassLoader; public class DexUtil { private static boolean sHasInsertedNativeLibrary = false; - public static void insertDex(DexClassLoader dexClassLoader) throws Exception { - Object baseDexElements = getDexElements(getPathList(getPathClassLoader())); + public static void insertDex(DexClassLoader dexClassLoader, ClassLoader baseClassLoader) throws Exception { + Object baseDexElements = getDexElements(getPathList(baseClassLoader)); Object newDexElements = getDexElements(getPathList(dexClassLoader)); Object allDexElements = combineArray(baseDexElements, newDexElements); - Object pathList = getPathList(getPathClassLoader()); + Object pathList = getPathList(baseClassLoader); Reflector.with(pathList).field("dexElements").set(allDexElements); - insertNativeLibrary(dexClassLoader); - - } - - private static PathClassLoader getPathClassLoader() { - PathClassLoader pathClassLoader = (PathClassLoader) DexUtil.class.getClassLoader(); - return pathClassLoader; + insertNativeLibrary(dexClassLoader, baseClassLoader); } private static Object getDexElements(Object pathList) throws Exception { return Reflector.with(pathList).field("dexElements").get(); } - private static Object getPathList(Object baseDexClassLoader) throws Exception { + private static Object getPathList(ClassLoader baseDexClassLoader) throws Exception { return Reflector.with(baseDexClassLoader).field("pathList").get(); } private static Object combineArray(Object firstArray, Object secondArray) { Class localClass = firstArray.getClass().getComponentType(); int firstArrayLength = Array.getLength(firstArray); - int allLength = firstArrayLength + Array.getLength(secondArray); - Object result = Array.newInstance(localClass, allLength); - for (int k = 0; k < allLength; ++k) { - if (k < firstArrayLength) { - Array.set(result, k, Array.get(firstArray, k)); - } else { - Array.set(result, k, Array.get(secondArray, k - firstArrayLength)); - } - } + int secondArrayLength = Array.getLength(secondArray); + Object result = Array.newInstance(localClass, firstArrayLength + secondArrayLength); + System.arraycopy(firstArray, 0, result, 0, firstArrayLength); + System.arraycopy(secondArray, 0, result, firstArrayLength, secondArrayLength); return result; } - private static synchronized void insertNativeLibrary(DexClassLoader dexClassLoader) throws Exception { + private static synchronized void insertNativeLibrary(DexClassLoader dexClassLoader, ClassLoader baseClassLoader) throws Exception { if (sHasInsertedNativeLibrary) { return; } sHasInsertedNativeLibrary = true; Context context = ActivityThread.currentApplication(); - Object basePathList = getPathList(getPathClassLoader()); + Object basePathList = getPathList(baseClassLoader); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { Reflector reflector = Reflector.with(basePathList); List nativeLibraryDirectories = reflector.field("nativeLibraryDirectories").get(); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java index 5d7b5a5..e4bef95 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/PluginUtil.java @@ -51,16 +51,22 @@ */ public class PluginUtil { - public static String getTargetActivity(Intent intent) { - return intent.getStringExtra(Constants.KEY_TARGET_ACTIVITY); - } - public static ComponentName getComponent(Intent intent) { - return new ComponentName(intent.getStringExtra(Constants.KEY_TARGET_PACKAGE), + if (intent == null) { + return null; + } + if (isIntentFromPlugin(intent)) { + return new ComponentName(intent.getStringExtra(Constants.KEY_TARGET_PACKAGE), intent.getStringExtra(Constants.KEY_TARGET_ACTIVITY)); + } + + return intent.getComponent(); } public static boolean isIntentFromPlugin(Intent intent) { + if (intent == null) { + return false; + } return intent.getBooleanExtra(Constants.KEY_IS_PLUGIN, false); } @@ -166,6 +172,9 @@ public static void putBinder(Bundle bundle, String key, IBinder value) { } public static IBinder getBinder(Bundle bundle, String key) { + if (bundle == null) { + return null; + } if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return bundle.getBinder(key); } else { diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/RunUtil.java b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/RunUtil.java index 2a1dd56..a5ea97b 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/utils/RunUtil.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/utils/RunUtil.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.concurrent.ThreadPoolExecutor; /** * Created by renyugang on 16/11/10. @@ -77,17 +76,22 @@ public static Executor getThreadPool() { return AsyncTask.THREAD_POOL_EXECUTOR; } - public static String getProcessNameByPid(Context context, int pid) { - ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - List appProcessList = manager.getRunningAppProcesses(); - if (appProcessList != null) { - for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessList) { - if (pid == appProcessInfo.pid) { - return appProcessInfo.processName; + private static String getProcessNameByPid(Context context, int pid) { + try { + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List appProcessList = manager.getRunningAppProcesses(); + if (appProcessList != null) { + for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessList) { + if (pid == appProcessInfo.pid) { + return appProcessInfo.processName; + } } } + + } catch (Throwable e) { + e.printStackTrace(); } - + return null; } From 0ae4d69f12dfe028fdb8666d7cd88d3eb6a314ba Mon Sep 17 00:00:00 2001 From: superq_sky Date: Mon, 7 May 2018 20:31:48 +0800 Subject: [PATCH 47/66] Optimized process of creating resources. --- .../java/android/app/ResourcesManager.java | 5 + .../content/res/CompatibilityInfo.java | 41 +++ .../java/android/content/res/Resources.java | 67 +++++ .../android/content/res/ResourcesImpl.java | 8 + .../android/content/res/ResourcesKey.java | 41 +++ .../com/didi/virtualapk/PluginManager.java | 4 +- .../virtualapk/internal/LoadedPlugin.java | 23 +- .../virtualapk/internal/PluginContext.java | 5 + .../virtualapk/internal/ResourcesManager.java | 244 +++++++++++++++--- .../internal/VAInstrumentation.java | 33 ++- .../com/didi/virtualapk/MainActivity.java | 25 +- app/src/main/res/layout/activity_main.xml | 31 ++- app/src/main/res/values-en/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 14 files changed, 454 insertions(+), 75 deletions(-) create mode 100644 AndroidStub/src/main/java/android/content/res/CompatibilityInfo.java create mode 100644 AndroidStub/src/main/java/android/content/res/Resources.java create mode 100644 AndroidStub/src/main/java/android/content/res/ResourcesImpl.java create mode 100644 AndroidStub/src/main/java/android/content/res/ResourcesKey.java diff --git a/AndroidStub/src/main/java/android/app/ResourcesManager.java b/AndroidStub/src/main/java/android/app/ResourcesManager.java index 518118c..20ee690 100644 --- a/AndroidStub/src/main/java/android/app/ResourcesManager.java +++ b/AndroidStub/src/main/java/android/app/ResourcesManager.java @@ -8,4 +8,9 @@ public class ResourcesManager { public static ResourcesManager getInstance() { throw new RuntimeException("Stub!"); } + + public void appendLibAssetForMainAssetPath(String assetPath, String libAsset) { + throw new RuntimeException("Stub!"); + } + } \ No newline at end of file diff --git a/AndroidStub/src/main/java/android/content/res/CompatibilityInfo.java b/AndroidStub/src/main/java/android/content/res/CompatibilityInfo.java new file mode 100644 index 0000000..92fc45d --- /dev/null +++ b/AndroidStub/src/main/java/android/content/res/CompatibilityInfo.java @@ -0,0 +1,41 @@ +package android.content.res; + +import android.content.pm.ApplicationInfo; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by qiaopu on 2018/5/3. + */ +public class CompatibilityInfo implements Parcelable { + + public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, + boolean forceCompat) { + throw new RuntimeException("Stub!"); + } + + @Override + public int describeContents() { + throw new RuntimeException("Stub!"); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + throw new RuntimeException("Stub!"); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public CompatibilityInfo createFromParcel(Parcel source) { + throw new RuntimeException("Stub!"); + } + + @Override + public CompatibilityInfo[] newArray(int size) { + throw new RuntimeException("Stub!"); + } + }; + +} + diff --git a/AndroidStub/src/main/java/android/content/res/Resources.java b/AndroidStub/src/main/java/android/content/res/Resources.java new file mode 100644 index 0000000..58692ef --- /dev/null +++ b/AndroidStub/src/main/java/android/content/res/Resources.java @@ -0,0 +1,67 @@ +package android.content.res; + +import android.graphics.drawable.Drawable; +import android.util.DisplayMetrics; + +/** + * Created by qiaopu on 2018/5/18. + */ +public class Resources { + + public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { + throw new RuntimeException("Stub!"); + } + + public final AssetManager getAssets() { + throw new RuntimeException("Stub!"); + } + + public Configuration getConfiguration() { + throw new RuntimeException("Stub!"); + } + + public DisplayMetrics getDisplayMetrics() { + throw new RuntimeException("Stub!"); + } + + public Drawable getDrawable(int id) throws NotFoundException { + throw new RuntimeException("Stub!"); + } + + public CharSequence getText(int id) throws NotFoundException { + throw new RuntimeException("Stub!"); + } + + public XmlResourceParser getXml(int id) throws NotFoundException { + throw new RuntimeException("Stub!"); + } + + public ResourcesImpl getImpl() { + throw new RuntimeException("Stub!"); + } + + public final Theme newTheme() { + throw new RuntimeException("Stub!"); + } + + public final class Theme { + + public void applyStyle(int resId, boolean force) { + throw new RuntimeException("Stub!"); + } + + public TypedArray obtainStyledAttributes(int[] attrs) { + throw new RuntimeException("Stub!"); + } + + public void setTo(Theme other) { + throw new RuntimeException("Stub!"); + } + + } + + public static class NotFoundException extends RuntimeException { + + } + +} diff --git a/AndroidStub/src/main/java/android/content/res/ResourcesImpl.java b/AndroidStub/src/main/java/android/content/res/ResourcesImpl.java new file mode 100644 index 0000000..c25947f --- /dev/null +++ b/AndroidStub/src/main/java/android/content/res/ResourcesImpl.java @@ -0,0 +1,8 @@ +package android.content.res; + +/** + * Created by qiaopu on 2018/5/18. + */ +public class ResourcesImpl { + +} diff --git a/AndroidStub/src/main/java/android/content/res/ResourcesKey.java b/AndroidStub/src/main/java/android/content/res/ResourcesKey.java new file mode 100644 index 0000000..b3cecd8 --- /dev/null +++ b/AndroidStub/src/main/java/android/content/res/ResourcesKey.java @@ -0,0 +1,41 @@ +package android.content.res; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +/** + * Created by qiaopu on 2018/5/3. + */ +public final class ResourcesKey { + @Nullable + public final String mResDir; + + @Nullable + public final String[] mSplitResDirs; + + @Nullable + public final String[] mOverlayDirs; + + @Nullable + public final String[] mLibDirs; + + public final int mDisplayId; + + @NonNull + public final Configuration mOverrideConfiguration; + + @NonNull + public final CompatibilityInfo mCompatInfo; + + public ResourcesKey(@Nullable String resDir, + @Nullable String[] splitResDirs, + @Nullable String[] overlayDirs, + @Nullable String[] libDirs, + int displayId, + @Nullable Configuration overrideConfig, + @Nullable CompatibilityInfo compatInfo) { + throw new RuntimeException("Stub!"); + } + +} + diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java index 63dcf75..ff62f6a 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/PluginManager.java @@ -69,7 +69,7 @@ public class PluginManager { private Map mPlugins = new ConcurrentHashMap<>(); private final List mCallbacks = new ArrayList<>(); - private Instrumentation mInstrumentation; // Hooked instrumentation + private VAInstrumentation mInstrumentation; // Hooked instrumentation private IActivityManager mActivityManager; // Hooked IActivityManager binder private IContentProvider mIContentProvider; // Hooked IContentProvider binder @@ -277,7 +277,7 @@ public Context getHostContext() { return this.mContext; } - public Instrumentation getInstrumentation() { + public VAInstrumentation getInstrumentation() { return this.mInstrumentation; } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java index a248a73..92c5b31 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/LoadedPlugin.java @@ -47,11 +47,12 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.WorkerThread; +import android.support.annotation.UiThread; import com.didi.virtualapk.PluginManager; import com.didi.virtualapk.utils.DexUtil; @@ -108,12 +109,9 @@ private static AssetManager createAssetManager(Context context, File apk) { } } - @WorkerThread - private static Resources createResources(Context context, File apk) { + private static Resources createResources(Context context, String packageName, File apk) throws Exception { if (Constants.COMBINE_RESOURCES) { - Resources resources = ResourcesManager.createResources(context, apk.getAbsolutePath()); - ResourcesManager.hookResources(context, resources); - return resources; + return ResourcesManager.createResources(context, packageName, apk); } else { Resources hostResources = context.getResources(); AssetManager assetManager = createAssetManager(context, apk); @@ -145,7 +143,11 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags private Application mApplication; + @UiThread LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception { + if (Thread.currentThread() != Looper.getMainLooper().getThread()) { + throw new RuntimeException("plugin mast be created by UI thread."); + } this.mPluginManager = pluginManager; this.mHostContext = context; this.mLocation = apk.getAbsolutePath(); @@ -177,7 +179,7 @@ private static ResolveInfo chooseBestActivity(Intent intent, String s, int flags this.mPackageManager = new PluginPackageManager(); this.mPluginContext = new PluginContext(this); this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE); - this.mResources = createResources(context, apk); + this.mResources = createResources(context, getPackageName(), apk); this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader()); tryToCopyNativeLib(apk); @@ -280,14 +282,13 @@ public Application getApplication() { } public void invokeApplication() { - if (mApplication != null) { - return; - } - // make sure application's callback is run on ui thread. RunUtil.runOnUiThread(new Runnable() { @Override public void run() { + if (mApplication != null) { + return; + } mApplication = makeApplication(false, mPluginManager.getInstrumentation()); } }, true); diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java index 337fb4c..462bd42 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java @@ -36,6 +36,11 @@ public PluginContext(LoadedPlugin plugin) { super(plugin.getPluginManager().getHostContext()); this.mPlugin = plugin; } + + public PluginContext(LoadedPlugin plugin, Context base) { + super(base); + this.mPlugin = plugin; + } @Override public Context getApplicationContext() { diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java index 682246b..3432ffa 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ResourcesManager.java @@ -16,70 +16,93 @@ package com.didi.virtualapk.internal; +import android.annotation.TargetApi; +import android.app.Activity; import android.app.ActivityThread; +import android.app.LoadedApk; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.ResourcesImpl; +import android.content.res.ResourcesKey; import android.os.Build; +import android.util.ArrayMap; import android.util.DisplayMetrics; +import android.util.Log; import com.didi.virtualapk.PluginManager; import com.didi.virtualapk.utils.Reflector; +import java.io.File; import java.lang.ref.WeakReference; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Created by renyugang on 16/8/9. */ class ResourcesManager { + + public static final String TAG = "LoadedPlugin"; - public static synchronized Resources createResources(Context hostContext, String apk) { + private static Configuration mDefaultConfiguration; + + public static synchronized Resources createResources(Context hostContext, String packageName, File apk) throws Exception { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return createResourcesForN(hostContext, packageName, apk); + } + + Resources resources = ResourcesManager.createResourcesSimple(hostContext, apk.getAbsolutePath()); + ResourcesManager.hookResources(hostContext, resources); + return resources; + } + + private static synchronized Resources createResourcesSimple(Context hostContext, String apk) throws Exception { Resources hostResources = hostContext.getResources(); Resources newResources = null; AssetManager assetManager; - try { - Reflector reflector = Reflector.on(AssetManager.class).method("addAssetPath", String.class); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - assetManager = AssetManager.class.newInstance(); - reflector.bind(assetManager); - reflector.call(hostContext.getApplicationInfo().sourceDir); - } else { - assetManager = hostResources.getAssets(); - reflector.bind(assetManager); - } - reflector.call(apk); - List pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins(); - for (LoadedPlugin plugin : pluginList) { - reflector.call(plugin.getLocation()); - } - if (isMiUi(hostResources)) { - newResources = MiUiResourcesCompat.createResources(hostResources, assetManager); - } else if (isVivo(hostResources)) { - newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager); - } else if (isNubia(hostResources)) { - newResources = NubiaResourcesCompat.createResources(hostResources, assetManager); - } else if (isNotRawResources(hostResources)) { - newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager); - } else { - // is raw android resources - newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); - } - // lastly, sync all LoadedPlugin to newResources - for (LoadedPlugin plugin : pluginList) { - plugin.updateResources(newResources); - } - } catch (Exception e) { - e.printStackTrace(); + Reflector reflector = Reflector.on(AssetManager.class).method("addAssetPath", String.class); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + assetManager = AssetManager.class.newInstance(); + reflector.bind(assetManager); + reflector.call(hostContext.getApplicationInfo().sourceDir); + } else { + assetManager = hostResources.getAssets(); + reflector.bind(assetManager); } - + reflector.call(apk); + List pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins(); + for (LoadedPlugin plugin : pluginList) { + reflector.call(plugin.getLocation()); + } + if (isMiUi(hostResources)) { + newResources = MiUiResourcesCompat.createResources(hostResources, assetManager); + } else if (isVivo(hostResources)) { + newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager); + } else if (isNubia(hostResources)) { + newResources = NubiaResourcesCompat.createResources(hostResources, assetManager); + } else if (isNotRawResources(hostResources)) { + newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager); + } else { + // is raw android resources + newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); + } + // lastly, sync all LoadedPlugin to newResources + for (LoadedPlugin plugin : pluginList) { + plugin.updateResources(newResources); + } + return newResources; - } public static void hookResources(Context base, Resources resources) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return; + } try { Reflector reflector = Reflector.with(base); reflector.field("mResources").set(resources); @@ -108,6 +131,90 @@ public static void hookResources(Context base, Resources resources) { e.printStackTrace(); } } + + /** + * Use System Apis to update all existing resources. + *
+ * 1. Update ApplicationInfo.splitSourceDirs and LoadedApk.mSplitResDirs + *
+ * 2. Replace all keys of ResourcesManager.mResourceImpls to new ResourcesKey + *
+ * 3. Use ResourcesManager.appendLibAssetForMainAssetPath(appInfo.publicSourceDir, "${packageName}.vastub") to update all existing resources. + *
+ * + * see android.webkit.WebViewDelegate.addWebViewAssetPath(Context) + */ + @TargetApi(Build.VERSION_CODES.N) + private static Resources createResourcesForN(Context context, String packageName, File apk) throws Exception { + long startTime = System.currentTimeMillis(); + String newAssetPath = apk.getAbsolutePath(); + ApplicationInfo info = context.getApplicationInfo(); + String baseResDir = info.publicSourceDir; + + info.splitSourceDirs = append(info.splitSourceDirs, newAssetPath); + LoadedApk loadedApk = Reflector.with(context).field("mPackageInfo").get(); + + Reflector rLoadedApk = Reflector.with(loadedApk).field("mSplitResDirs"); + String[] splitResDirs = rLoadedApk.get(); + rLoadedApk.set(append(splitResDirs, newAssetPath)); + + final android.app.ResourcesManager resourcesManager = android.app.ResourcesManager.getInstance(); + ArrayMap> originalMap = Reflector.with(resourcesManager).field("mResourceImpls").get(); + + synchronized (resourcesManager) { + HashMap> resolvedMap = new HashMap<>(); + + if (Build.VERSION.SDK_INT >= 28 + || (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // P Preview + ResourcesManagerCompatForP.resolveResourcesImplMap(originalMap, resolvedMap, context, loadedApk); + + } else { + ResourcesManagerCompatForN.resolveResourcesImplMap(originalMap, resolvedMap, baseResDir, newAssetPath); + } + + originalMap.clear(); + originalMap.putAll(resolvedMap); + } + + android.app.ResourcesManager.getInstance().appendLibAssetForMainAssetPath(baseResDir, packageName + ".vastub"); + + Resources newResources = context.getResources(); + + // lastly, sync all LoadedPlugin to newResources + for (LoadedPlugin plugin : PluginManager.getInstance(context).getAllLoadedPlugins()) { + plugin.updateResources(newResources); + } + + Log.d(TAG, "createResourcesForN cost time: +" + (System.currentTimeMillis() - startTime) + "ms"); + return newResources; + } + + private static String[] append(String[] paths, String newPath) { + if (contains(paths, newPath)) { + return paths; + } + + final int newPathsCount = 1 + (paths != null ? paths.length : 0); + final String[] newPaths = new String[newPathsCount]; + if (paths != null) { + System.arraycopy(paths, 0, newPaths, 0, paths.length); + } + newPaths[newPathsCount - 1] = newPath; + return newPaths; + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + private static boolean contains(String[] array, String value) { + if (array == null) { + return false; + } + for (int i = 0; i < array.length; i++) { + if (Objects.equals(array[i], value)) { + return true; + } + } + return false; + } private static boolean isMiUi(Resources resources) { return resources.getClass().getName().equals("android.content.res.MiuiResources"); @@ -170,4 +277,69 @@ private static Resources createResources(Resources hostResources, AssetManager a } } + private static final class ResourcesManagerCompatForN { + + public static void resolveResourcesImplMap(Map> originalMap, Map> resolvedMap, String baseResDir, String newAssetPath) throws Exception { + for (Map.Entry> entry : originalMap.entrySet()) { + ResourcesKey key = entry.getKey(); + if (Objects.equals(key.mResDir, baseResDir)) { + resolvedMap.put(new ResourcesKey(key.mResDir, + append(key.mSplitResDirs, newAssetPath), + key.mOverlayDirs, + key.mLibDirs, + key.mDisplayId, + key.mOverrideConfiguration, + key.mCompatInfo), entry.getValue()); + } else { + resolvedMap.put(key, entry.getValue()); + } + } + } + } + + private static final class ResourcesManagerCompatForP { + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static void resolveResourcesImplMap(Map> originalMap, Map> resolvedMap, Context context, LoadedApk loadedApk) throws Exception { + HashMap newResImplMap = new HashMap<>(); + Map resKeyMap = new HashMap<>(); + Resources newRes; + + // Recreate the resImpl of the context + + // See LoadedApk.getResources() + if (mDefaultConfiguration == null) { + mDefaultConfiguration = new Configuration(); + } + newRes = context.createConfigurationContext(mDefaultConfiguration).getResources(); + newResImplMap.put(newRes.getImpl(), context); + + // Recreate the ResImpl of the activity + for (WeakReference ref : PluginManager.getInstance(context).getInstrumentation().getActivities()) { + Activity activity = ref.get(); + if (activity != null) { + newRes = activity.createConfigurationContext(activity.getResources().getConfiguration()).getResources(); + newResImplMap.put(newRes.getImpl(), activity); + } + } + + // Mapping all resKey and resImpl + for (Map.Entry> entry : originalMap.entrySet()) { + ResourcesImpl resImpl = entry.getValue().get(); + if (resImpl != null) { + resKeyMap.put(resImpl, entry.getKey()); + } + resolvedMap.put(entry.getKey(), entry.getValue()); + } + + // Replace the resImpl to the new resKey and remove the origin resKey + for (Map.Entry entry : newResImplMap.entrySet()) { + ResourcesKey newKey = resKeyMap.get(entry.getKey()); + ResourcesImpl originResImpl = entry.getValue().getResources().getImpl(); + + resolvedMap.put(newKey, new WeakReference<>(originResImpl)); + resolvedMap.remove(resKeyMap.get(originResImpl)); + } + } + } } diff --git a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java index 1f7450a..76377d8 100644 --- a/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java +++ b/CoreLibrary/src/main/java/com/didi/virtualapk/internal/VAInstrumentation.java @@ -37,6 +37,10 @@ import com.didi.virtualapk.utils.PluginUtil; import com.didi.virtualapk.utils.Reflector; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + /** * Created by renyugang on 16/8/10. @@ -46,6 +50,8 @@ public class VAInstrumentation extends Instrumentation implements Handler.Callba public static final int LAUNCH_ACTIVITY = 100; private Instrumentation mBase; + + private final ArrayList> mActivities = new ArrayList<>(); PluginManager mPluginManager; @@ -98,7 +104,7 @@ public Activity newActivity(ClassLoader cl, String className, Intent intent) thr ComponentName component = PluginUtil.getComponent(intent); if (component == null) { - return mBase.newActivity(cl, className, intent); + return newActivity(mBase.newActivity(cl, className, intent)); } String targetClassName = component.getClassName(); @@ -107,7 +113,7 @@ public Activity newActivity(ClassLoader cl, String className, Intent intent) thr LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component); if (plugin == null) { - return mBase.newActivity(cl, className, intent); + return newActivity(mBase.newActivity(cl, className, intent)); } Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent); @@ -120,10 +126,10 @@ public Activity newActivity(ClassLoader cl, String className, Intent intent) thr // ignored. } - return activity; + return newActivity(activity); } - return mBase.newActivity(cl, className, intent); + return newActivity(mBase.newActivity(cl, className, intent)); } @Override @@ -152,7 +158,7 @@ private void injectActivity(Activity activity) { LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent); Reflector.with(base).field("mResources").set(plugin.getResources()); Reflector reflector = Reflector.with(activity); - reflector.field("mBase").set(plugin.getPluginContext()); + reflector.field("mBase").set(new PluginContext(plugin, activity.getBaseContext())); reflector.field("mApplication").set(plugin.getApplication()); // set screenOrientation @@ -207,4 +213,21 @@ public ComponentName getComponentName() { return mBase.getComponentName(); } + private Activity newActivity(Activity activity) { + synchronized (mActivities) { + for (int i = mActivities.size() - 1; i >= 0; i--) { + if (mActivities.get(i).get() == null) { + mActivities.remove(i); + } + } + mActivities.add(new WeakReference<>(activity)); + } + return activity; + } + + List> getActivities() { + synchronized (mActivities) { + return new ArrayList<>(mActivities); + } + } } diff --git a/app/src/main/java/com/didi/virtualapk/MainActivity.java b/app/src/main/java/com/didi/virtualapk/MainActivity.java index 65a31bd..d355264 100644 --- a/app/src/main/java/com/didi/virtualapk/MainActivity.java +++ b/app/src/main/java/com/didi/virtualapk/MainActivity.java @@ -14,15 +14,18 @@ import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; -import com.didi.virtualapk.internal.PluginContentResolver; import com.didi.virtualapk.internal.LoadedPlugin; +import com.didi.virtualapk.internal.PluginContentResolver; import java.io.File; - public class MainActivity extends AppCompatActivity { private static final int PERMISSION_REQUEST_CODE_STORAGE = 20171222; @@ -105,14 +108,22 @@ public void onButtonClick(View v) { bookUri = PluginContentResolver.wrapperUri(plugin, bookUri); Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null); - while (bookCursor.moveToNext()) { - int bookId = bookCursor.getInt(0); - String bookName = bookCursor.getString(1); - Log.d("ryg", "query book:" + bookId + ", " + bookName); + if (bookCursor != null) { + while (bookCursor.moveToNext()) { + int bookId = bookCursor.getInt(0); + String bookName = bookCursor.getString(1); + Log.d("ryg", "query book:" + bookId + ", " + bookName); + } + bookCursor.close(); } - bookCursor.close(); } else if (v.getId() == R.id.about) { showAbout(); + } else if (v.getId() == R.id.webview) { + LinearLayout linearLayout = (LinearLayout) v.getParent(); + WebView webView = new WebView(this); + linearLayout.addView(webView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1)); + webView.setWebViewClient(new WebViewClient()); + webView.loadUrl("http://github.com/didi/VirtualAPK"); } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 79b54dd..b092c85 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,8 +1,9 @@ -