() {
29 | @Override
30 | public CompatibilityInfo createFromParcel(Parcel source) {
31 | throw new RuntimeException("Stub!");
32 | }
33 |
34 | @Override
35 | public CompatibilityInfo[] newArray(int size) {
36 | throw new RuntimeException("Stub!");
37 | }
38 | };
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/content/res/Resources.java:
--------------------------------------------------------------------------------
1 | package android.content.res;
2 |
3 | import android.graphics.drawable.Drawable;
4 | import android.util.DisplayMetrics;
5 |
6 | /**
7 | * Created by qiaopu on 2018/5/18.
8 | */
9 | public class Resources {
10 |
11 | public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
12 | throw new RuntimeException("Stub!");
13 | }
14 |
15 | public final AssetManager getAssets() {
16 | throw new RuntimeException("Stub!");
17 | }
18 |
19 | public int getColor(int id) throws NotFoundException {
20 | throw new RuntimeException("Stub!");
21 | }
22 |
23 | public Configuration getConfiguration() {
24 | throw new RuntimeException("Stub!");
25 | }
26 |
27 | public DisplayMetrics getDisplayMetrics() {
28 | throw new RuntimeException("Stub!");
29 | }
30 |
31 | public Drawable getDrawable(int id) throws NotFoundException {
32 | throw new RuntimeException("Stub!");
33 | }
34 |
35 | public String getString(int id) throws NotFoundException {
36 | throw new RuntimeException("Stub!");
37 | }
38 |
39 | public CharSequence getText(int id) throws NotFoundException {
40 | throw new RuntimeException("Stub!");
41 | }
42 |
43 | public XmlResourceParser getXml(int id) throws NotFoundException {
44 | throw new RuntimeException("Stub!");
45 | }
46 |
47 | public ResourcesImpl getImpl() {
48 | throw new RuntimeException("Stub!");
49 | }
50 |
51 | public final Theme newTheme() {
52 | throw new RuntimeException("Stub!");
53 | }
54 |
55 | public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
56 | throw new RuntimeException("Stub!");
57 | }
58 |
59 | public final class Theme {
60 |
61 | public void applyStyle(int resId, boolean force) {
62 | throw new RuntimeException("Stub!");
63 | }
64 |
65 | public TypedArray obtainStyledAttributes(int[] attrs) {
66 | throw new RuntimeException("Stub!");
67 | }
68 |
69 | public void setTo(Theme other) {
70 | throw new RuntimeException("Stub!");
71 | }
72 |
73 | }
74 |
75 | public static class NotFoundException extends RuntimeException {
76 |
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/content/res/ResourcesImpl.java:
--------------------------------------------------------------------------------
1 | package android.content.res;
2 |
3 | /**
4 | * Created by qiaopu on 2018/5/18.
5 | */
6 | public class ResourcesImpl {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/content/res/ResourcesKey.java:
--------------------------------------------------------------------------------
1 | package android.content.res;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | /**
7 | * Created by qiaopu on 2018/5/3.
8 | */
9 | public final class ResourcesKey {
10 | @Nullable
11 | public final String mResDir;
12 |
13 | @Nullable
14 | public final String[] mSplitResDirs;
15 |
16 | @Nullable
17 | public final String[] mOverlayDirs;
18 |
19 | @Nullable
20 | public final String[] mLibDirs;
21 |
22 | public final int mDisplayId;
23 |
24 | @NonNull
25 | public final Configuration mOverrideConfiguration;
26 |
27 | @NonNull
28 | public final CompatibilityInfo mCompatInfo;
29 |
30 | public ResourcesKey(@Nullable String resDir,
31 | @Nullable String[] splitResDirs,
32 | @Nullable String[] overlayDirs,
33 | @Nullable String[] libDirs,
34 | int displayId,
35 | @Nullable Configuration overrideConfig,
36 | @Nullable CompatibilityInfo compatInfo) {
37 | throw new RuntimeException("Stub!");
38 | }
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/databinding/DataBinderMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package android.databinding;
17 | import android.view.View;
18 | /**
19 | * This class will be stripped from the jar and then replaced by the annotation processor
20 | * as part of the code generation step. This class's existence is just to ensure that
21 | * compile works and no reflection is needed to access the generated class.
22 | */
23 | class DataBinderMapper {
24 | public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view,
25 | int layoutId) {
26 | return null;
27 | }
28 | ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View[] view, int layoutId) {
29 | return null;
30 | }
31 | public int getLayoutId(String tag) { return 0; }
32 | public String convertBrIdToString(int id) {
33 | return null;
34 | }
35 | public static int TARGET_MIN_SDK = 0;
36 | }
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/databinding/DataBindingComponent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package android.databinding;
17 | /**
18 | * This interface is generated during compilation to contain getters for all used instance
19 | * BindingAdapters. When a BindingAdapter is an instance method, an instance of the class
20 | * implementing the method must be instantiated. This interface will be generated with a getter
21 | * for each class with the name get* where * is simple class name of the declaring BindingAdapter
22 | * class/interface. Name collisions will be resolved by adding a numeric suffix to the getter.
23 | *
24 | * An instance of this class may also be passed into static or instance BindingAdapters as the
25 | * first parameter.
26 | *
27 | * If you are using Dagger 2, you should extend this interface and annotate the extended interface
28 | * as a Component.
29 | */
30 | public interface DataBindingComponent {
31 | }
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/os/ServiceManager.java:
--------------------------------------------------------------------------------
1 | package android.os;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * @author johnsonlee
8 | */
9 | public final class ServiceManager {
10 |
11 | private static HashMap sCache = new HashMap();
12 |
13 | public static IBinder getService(final String name) {
14 | throw new RuntimeException("Stub!");
15 | }
16 |
17 | public static void addService(final String name, final IBinder service) {
18 | throw new RuntimeException("Stub!");
19 | }
20 |
21 | public static void addService(final String name, final IBinder service, final boolean allowIsolated) {
22 | throw new RuntimeException("Stub!");
23 | }
24 |
25 | public static IBinder checkService(final String name) {
26 | throw new RuntimeException("Stub!");
27 | }
28 |
29 | public static String[] listServices() throws RemoteException {
30 | throw new RuntimeException("Stub!");
31 | }
32 |
33 | public static void initServiceCache(final Map cache) {
34 | throw new RuntimeException("Stub!");
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/os/SystemProperties.java:
--------------------------------------------------------------------------------
1 | package android.os;
2 |
3 | /**
4 | * @author johnsonlee
5 | */
6 | public class SystemProperties {
7 |
8 | public static String get(final String key) {
9 | throw new RuntimeException("Stub!");
10 | }
11 |
12 | public static String get(final String key, final String def) {
13 | throw new RuntimeException("Stub!");
14 | }
15 |
16 | public static int getInt(final String key, final int def) {
17 | throw new RuntimeException("Stub!");
18 | }
19 |
20 | public static long getLong(final String key, final long def) {
21 | throw new RuntimeException("Stub!");
22 | }
23 |
24 | public static boolean getBoolean(final String key, final boolean def) {
25 | throw new RuntimeException("Stub!");
26 | }
27 |
28 | public static void set(final String key, final String val) {
29 | throw new RuntimeException("Stub!");
30 | }
31 |
32 | public static void addChangeCallback(final Runnable callback) {
33 | throw new RuntimeException("Stub!");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/android/util/Singleton.java:
--------------------------------------------------------------------------------
1 | package android.util;
2 |
3 | /**
4 | * @author johnsonlee
5 | */
6 | public abstract class Singleton {
7 | public Singleton() {
8 | throw new RuntimeException("Stub!");
9 | }
10 |
11 | protected abstract T create();
12 |
13 | public T get() {
14 | throw new RuntimeException("Stub!");
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/AndroidStub/src/main/java/com/android/internal/R.java:
--------------------------------------------------------------------------------
1 | package com.android.internal;
2 |
3 | /**
4 | * @author johnsonlee
5 | */
6 | public final class R {
7 |
8 | public static final class id {
9 |
10 | public static int action0 = 0;
11 |
12 | public static int actions = 0;
13 |
14 | public static int action_divider = 0;
15 |
16 | public static int big_picture = 0;
17 |
18 | public static int big_text = 0;
19 |
20 | public static int big_text2 = 0;
21 |
22 | public static int chronometer = 0;
23 |
24 | public static int icon = 0;
25 |
26 | public static int inbox_text0 = 0;
27 |
28 | public static int inbox_text1 = 0;
29 |
30 | public static int inbox_text2 = 0;
31 |
32 | public static int inbox_text3 = 0;
33 |
34 | public static int inbox_text4 = 0;
35 |
36 | public static int inbox_text5 = 0;
37 |
38 | public static int inbox_text6 = 0;
39 |
40 | public static int inbox_end_pad = 0;
41 |
42 | public static int info = 0;
43 |
44 | public static int line1 = 0;
45 |
46 | public static int line3 = 0;
47 |
48 | public static int overflow_divider = 0;
49 |
50 | public static int progress = 0;
51 |
52 | public static int title = 0;
53 |
54 | public static int text = 0;
55 |
56 | public static int text2 = 0;
57 |
58 | public static int time = 0;
59 |
60 | public static int right_icon = 0;
61 |
62 | public static int fillInIntent = 0;
63 |
64 | public static int status_bar_latest_event_content = 0;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guideline
2 |
3 | Thanks for considering to contribute this project. All issues and pull requests are highly appreciated.
4 |
5 | ## Pull Requests
6 |
7 | Before sending pull request to this project, please read and follow guidelines below.
8 |
9 | 1. Branch: We only accept pull request on `dev` branch.
10 | 2. Coding style: Follow the coding style used in VirtualAPK.
11 | 3. Commit message: Use English and be aware of your spell.
12 | 4. Test: Make sure to test your code.
13 |
14 | Add device mode, API version, related log, screenshots and other related information in your pull request if possible.
15 |
16 | NOTE: We assume all your contribution can be licensed under the [Apache License 2.0](https://github.com/didi/VirtualAPK/blob/master/LICENSE).
17 |
18 | ## Issues
19 |
20 | We love clearly described issues. :)
21 |
22 | Following information can help us to resolve the issue faster.
23 |
24 | * Device mode and hardware information.
25 | * API version.
26 | * Logs.
27 | * Screenshots.
28 | * Steps to reproduce the issue.
29 |
--------------------------------------------------------------------------------
/CoreLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/CoreLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.api.ApplicationVariant
2 | import com.android.build.gradle.api.LibraryVariant
3 | import com.google.common.base.Joiner
4 |
5 | apply plugin: 'com.android.library'
6 |
7 | android {
8 | compileSdkVersion VERSION_COMPILE_SDK
9 | buildToolsVersion VERSION_BUILD_TOOLS
10 |
11 | defaultConfig {
12 | minSdkVersion VERSION_MIN_SDK
13 | targetSdkVersion VERSION_TARGET_SDK
14 | versionCode 1
15 | versionName "1.0.0"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility SOURCE_COMPATIBILITY
25 | }
26 | lintOptions {
27 | abortOnError false
28 | }
29 | }
30 |
31 | repositories {
32 | mavenCentral()
33 | jcenter()
34 | }
35 |
36 | final String projectAndroidStub = ':AndroidStub'
37 |
38 | dependencies {
39 | compile fileTree(dir: 'libs', include: ['*.jar'])
40 |
41 | provided project(projectAndroidStub)
42 |
43 | testCompile 'junit:junit:4.12'
44 | }
45 |
46 | // Using Stub classes first when compiling.
47 | afterEvaluate {
48 | project.android.libraryVariants.each { LibraryVariant variant ->
49 | variant.javaCompile.doFirst { JavaCompile javaCompile ->
50 | String projectAndroidStubPath = project.project(projectAndroidStub).projectDir.canonicalPath
51 | // println "projectAndroidStubPath: ${projectAndroidStubPath}"
52 | File stubPath = javaCompile.classpath.find {
53 | it.canonicalPath.startsWith(projectAndroidStubPath)
54 | }
55 | if (stubPath == null) {
56 | throw new RuntimeException("reset bootclasspath error.")
57 | }
58 | javaCompile.options.setBootClasspath(Joiner.on(File.pathSeparator).join(stubPath, javaCompile.options.bootClasspath))
59 | }
60 | }
61 | }
62 |
63 | apply from: 'upload.gradle'
64 |
--------------------------------------------------------------------------------
/CoreLibrary/gradle.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/CoreLibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/didi/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/CoreLibrary/src/androidTest/java/com/didi/virtualapk/core/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.core;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/CoreLibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/android/content/ContentResolverWrapper.java:
--------------------------------------------------------------------------------
1 | package android.content;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Build;
5 |
6 | /**
7 | * Wrapper of {@link ContentResolver}
8 | * Created by qiaopu on 2018/5/7.
9 | */
10 | public abstract class ContentResolverWrapper extends ContentResolver {
11 |
12 | ContentResolver mBase;
13 |
14 | public ContentResolverWrapper(Context context) {
15 | super(context);
16 | mBase = context.getContentResolver();
17 | }
18 |
19 | @Override
20 | protected IContentProvider acquireProvider(Context context, String auth) {
21 | return mBase.acquireProvider(context, auth);
22 | }
23 |
24 | @Override
25 | protected IContentProvider acquireExistingProvider(Context context, String auth) {
26 | return mBase.acquireExistingProvider(context, auth);
27 | }
28 |
29 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
30 | @Override
31 | protected IContentProvider acquireUnstableProvider(Context context, String auth) {
32 | return mBase.acquireUnstableProvider(context, auth);
33 | }
34 |
35 | @Override
36 | public boolean releaseProvider(IContentProvider icp) {
37 | return mBase.releaseProvider(icp);
38 | }
39 |
40 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
41 | @Override
42 | public boolean releaseUnstableProvider(IContentProvider icp) {
43 | return mBase.releaseUnstableProvider(icp);
44 | }
45 |
46 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
47 | @Override
48 | public void unstableProviderDied(IContentProvider icp) {
49 | mBase.unstableProviderDied(icp);
50 | }
51 |
52 | @TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
53 | @Override
54 | public void appNotRespondingViaProvider(IContentProvider icp) {
55 | // dark greylist in Android P
56 | // mBase.appNotRespondingViaProvider(icp);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/android/databinding/DataBinderMapperProxy.java:
--------------------------------------------------------------------------------
1 | package android.databinding;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | import com.didi.virtualapk.PluginManager;
8 | import com.didi.virtualapk.internal.Constants;
9 | import com.didi.virtualapk.internal.LoadedPlugin;
10 |
11 | import java.util.LinkedList;
12 |
13 | /**
14 | * Replace {@link DataBindingUtil#sMapper}.
15 | * Created by qiaopu on 2018/4/11.
16 | */
17 | public class DataBinderMapperProxy extends DataBinderMapper implements PluginManager.Callback {
18 | public static final String TAG = Constants.TAG_PREFIX + "DataBinderMapperProxy";
19 |
20 | private final LinkedList mMappers;
21 | private DataBinderMapper[] mCache;
22 |
23 | public DataBinderMapperProxy(@NonNull Object source) {
24 | mMappers = new LinkedList<>();
25 |
26 | addMapper((DataBinderMapper) source);
27 | }
28 |
29 | @Override
30 | public ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view, int layoutId) {
31 | ViewDataBinding viewDataBinding;
32 |
33 | for (DataBinderMapper mapper : getCache()) {
34 | viewDataBinding = mapper.getDataBinder(bindingComponent, view, layoutId);
35 | if (viewDataBinding != null) {
36 | // Log.d(TAG, "Found by mapper: " + mapper);
37 | return viewDataBinding;
38 | }
39 | }
40 |
41 | return null;
42 | }
43 |
44 | @Override
45 | ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View[] view, int layoutId) {
46 | ViewDataBinding viewDataBinding;
47 |
48 | for (DataBinderMapper mapper : getCache()) {
49 | viewDataBinding = mapper.getDataBinder(bindingComponent, view, layoutId);
50 | if (viewDataBinding != null) {
51 | // Log.d(TAG, "Found by mapper: " + mapper);
52 | return viewDataBinding;
53 | }
54 | }
55 |
56 | return null;
57 | }
58 |
59 | @Override
60 | public int getLayoutId(String tag) {
61 | int layoutId;
62 |
63 | for (DataBinderMapper mapper : getCache()) {
64 | layoutId = mapper.getLayoutId(tag);
65 | if (layoutId != 0) {
66 | // Log.d(TAG, "Found by mapper: " + mapper);
67 | return layoutId;
68 | }
69 | }
70 |
71 | return 0;
72 | }
73 |
74 | @Override
75 | public String convertBrIdToString(int id) {
76 | String brId;
77 |
78 | for (DataBinderMapper mapper : getCache()) {
79 | brId = mapper.convertBrIdToString(id);
80 | if (brId != null) {
81 | // Log.d(TAG, "Found by mapper: " + mapper);
82 | return brId;
83 | }
84 | }
85 |
86 | return null;
87 | }
88 |
89 | @Override
90 | public void onAddedLoadedPlugin(LoadedPlugin plugin) {
91 | try {
92 | String clsName = "android.databinding.DataBinderMapper_" + plugin.getPackageName().replace('.', '_');
93 | Log.d(TAG, "Try to find the class: " + clsName);
94 | Class cls = Class.forName(clsName, true, plugin.getClassLoader());
95 | Object obj = cls.newInstance();
96 |
97 | addMapper((DataBinderMapper) obj);
98 |
99 | } catch (Exception e) {
100 | Log.w(TAG, e);
101 | }
102 | }
103 |
104 | private void addMapper(DataBinderMapper mapper) {
105 | int size = 0;
106 | synchronized (mMappers) {
107 | mMappers.add(mapper);
108 | mCache = null;
109 | size = mMappers.size();
110 | }
111 |
112 | Log.d(TAG, "Added mapper: " + mapper + ", size: " + size);
113 | }
114 |
115 | private DataBinderMapper[] getCache() {
116 | synchronized (mMappers) {
117 | if (mCache == null) {
118 | mCache = mMappers.toArray(new DataBinderMapper[mMappers.size()]);
119 | }
120 | return mCache;
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/delegate/RemoteService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.didi.virtualapk.delegate;
18 |
19 | import android.content.ComponentName;
20 | import android.content.Intent;
21 | import android.os.IBinder;
22 | import android.util.Log;
23 |
24 | import com.didi.virtualapk.PluginManager;
25 | import com.didi.virtualapk.internal.Constants;
26 | import com.didi.virtualapk.internal.LoadedPlugin;
27 |
28 | import java.io.File;
29 |
30 | /**
31 | * @author johnsonlee
32 | */
33 | public class RemoteService extends LocalService {
34 |
35 | private static final String TAG = Constants.TAG_PREFIX + "RemoteService";
36 |
37 | @Override
38 | public IBinder onBind(Intent intent) {
39 | return null;
40 | }
41 |
42 | @Override
43 | public int onStartCommand(Intent intent, int flags, int startId) {
44 | if (intent == null) {
45 | return super.onStartCommand(intent, flags, startId);
46 | }
47 |
48 | Intent target = intent.getParcelableExtra(EXTRA_TARGET);
49 | if (target != null) {
50 | String pluginLocation = intent.getStringExtra(EXTRA_PLUGIN_LOCATION);
51 | ComponentName component = target.getComponent();
52 | LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(component);
53 | if (plugin == null && pluginLocation != null) {
54 | try {
55 | PluginManager.getInstance(this).loadPlugin(new File(pluginLocation));
56 | } catch (Exception e) {
57 | Log.w(TAG, e);
58 | }
59 | }
60 | }
61 |
62 | return super.onStartCommand(intent, flags, startId);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/delegate/StubActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.delegate;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.content.pm.ResolveInfo;
6 | import android.os.Bundle;
7 | import android.support.annotation.Nullable;
8 |
9 | /**
10 | * Created by qiaopu on 2018/6/13.
11 | */
12 | public class StubActivity extends Activity {
13 | @Override
14 | protected void onCreate(@Nullable Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 |
17 | // Go to the main activity
18 | Intent mainIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
19 |
20 | if (mainIntent == null) {
21 | mainIntent = new Intent(Intent.ACTION_MAIN);
22 | mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
23 | mainIntent.setPackage(getPackageName());
24 |
25 | ResolveInfo resolveInfo = getPackageManager().resolveActivity(mainIntent, 0);
26 |
27 | if (resolveInfo != null) {
28 | mainIntent.setClassName(this, resolveInfo.activityInfo.name);
29 | }
30 | }
31 |
32 | startActivity(mainIntent);
33 |
34 | finish();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/internal/ActivityLifecycleCallbacksProxy.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.internal;
2 |
3 | import android.app.Activity;
4 | import android.app.ActivityThread;
5 | import android.app.Application;
6 | import android.os.Bundle;
7 |
8 | import com.didi.virtualapk.utils.Reflector;
9 |
10 | import java.util.ArrayList;
11 |
12 | /**
13 | * Created by qiaopu on 2017/8/9.
14 | */
15 | class ActivityLifecycleCallbacksProxy implements Application.ActivityLifecycleCallbacks {
16 |
17 | final ArrayList mActivityLifecycleCallbacks =
18 | Reflector.QuietReflector.with(ActivityThread.currentApplication()).field("mActivityLifecycleCallbacks").get();
19 |
20 | Object[] collectActivityLifecycleCallbacks() {
21 | if (mActivityLifecycleCallbacks == null) {
22 | return null;
23 | }
24 | Object[] callbacks = null;
25 | synchronized (mActivityLifecycleCallbacks) {
26 | if (mActivityLifecycleCallbacks.size() > 0) {
27 | callbacks = mActivityLifecycleCallbacks.toArray();
28 | }
29 | }
30 | return callbacks;
31 | }
32 |
33 | @Override
34 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
35 | Object[] callbacks = collectActivityLifecycleCallbacks();
36 | if (callbacks != null) {
37 | for (int i = 0; i < callbacks.length; i++) {
38 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(activity,
39 | savedInstanceState);
40 | }
41 | }
42 | }
43 |
44 | @Override
45 | public void onActivityStarted(Activity activity) {
46 | Object[] callbacks = collectActivityLifecycleCallbacks();
47 | if (callbacks != null) {
48 | for (int i = 0; i < callbacks.length; i++) {
49 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(activity);
50 | }
51 | }
52 | }
53 |
54 | @Override
55 | public void onActivityResumed(Activity activity) {
56 | Object[] callbacks = collectActivityLifecycleCallbacks();
57 | if (callbacks != null) {
58 | for (int i = 0; i < callbacks.length; i++) {
59 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(activity);
60 | }
61 | }
62 | }
63 |
64 | @Override
65 | public void onActivityPaused(Activity activity) {
66 | Object[] callbacks = collectActivityLifecycleCallbacks();
67 | if (callbacks != null) {
68 | for (int i = 0; i < callbacks.length; i++) {
69 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(activity);
70 | }
71 | }
72 | }
73 |
74 | @Override
75 | public void onActivityStopped(Activity activity) {
76 | Object[] callbacks = collectActivityLifecycleCallbacks();
77 | if (callbacks != null) {
78 | for (int i = 0; i < callbacks.length; i++) {
79 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(activity);
80 | }
81 | }
82 | }
83 |
84 | @Override
85 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
86 | Object[] callbacks = collectActivityLifecycleCallbacks();
87 | if (callbacks != null) {
88 | for (int i = 0; i < callbacks.length; i++) {
89 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivitySaveInstanceState(activity,
90 | outState);
91 | }
92 | }
93 | }
94 |
95 | @Override
96 | public void onActivityDestroyed(Activity activity) {
97 | Object[] callbacks = collectActivityLifecycleCallbacks();
98 | if (callbacks != null) {
99 | for (int i = 0; i < callbacks.length; i++) {
100 | ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(activity);
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/internal/Constants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.didi.virtualapk.internal;
18 |
19 | /**
20 | * Created by renyugang on 16/8/15.
21 | */
22 | public class Constants {
23 | public static final String KEY_IS_PLUGIN = "isPlugin";
24 | public static final String KEY_TARGET_PACKAGE = "target.package";
25 | public static final String KEY_TARGET_ACTIVITY = "target.activity";
26 |
27 | public static final String OPTIMIZE_DIR = "dex";
28 | public static final String NATIVE_DIR = "valibs";
29 |
30 | public static final boolean COMBINE_RESOURCES = true;
31 | public static final boolean COMBINE_CLASSLOADER = true;
32 | public static final boolean DEBUG = true;
33 |
34 | public static final String TAG = "VA";
35 | public static final String TAG_PREFIX = TAG + ".";
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/internal/PluginContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.didi.virtualapk.internal;
18 |
19 | import android.content.ContentResolver;
20 | import android.content.Context;
21 | import android.content.ContextWrapper;
22 | import android.content.Intent;
23 | import android.content.pm.ApplicationInfo;
24 | import android.content.pm.PackageManager;
25 | import android.content.res.AssetManager;
26 | import android.content.res.Resources;
27 |
28 | /**
29 | * Created by renyugang on 16/8/12.
30 | */
31 | class PluginContext extends ContextWrapper {
32 |
33 | private final LoadedPlugin mPlugin;
34 |
35 | public PluginContext(LoadedPlugin plugin) {
36 | super(plugin.getPluginManager().getHostContext());
37 | this.mPlugin = plugin;
38 | }
39 |
40 | public PluginContext(LoadedPlugin plugin, Context base) {
41 | super(base);
42 | this.mPlugin = plugin;
43 | }
44 |
45 | @Override
46 | public Context getApplicationContext() {
47 | return this.mPlugin.getApplication();
48 | }
49 |
50 | // @Override
51 | // public ApplicationInfo getApplicationInfo() {
52 | // return this.mPlugin.getApplicationInfo();
53 | // }
54 |
55 | private Context getHostContext() {
56 | return getBaseContext();
57 | }
58 |
59 | @Override
60 | public ContentResolver getContentResolver() {
61 | return new PluginContentResolver(getHostContext());
62 | }
63 |
64 | @Override
65 | public ClassLoader getClassLoader() {
66 | return this.mPlugin.getClassLoader();
67 | }
68 |
69 | // @Override
70 | // public String getPackageName() {
71 | // return this.mPlugin.getPackageName();
72 | // }
73 |
74 | // @Override
75 | // public String getPackageResourcePath() {
76 | // return this.mPlugin.getPackageResourcePath();
77 | // }
78 |
79 | // @Override
80 | // public String getPackageCodePath() {
81 | // return this.mPlugin.getCodePath();
82 | // }
83 |
84 | @Override
85 | public PackageManager getPackageManager() {
86 | return this.mPlugin.getPackageManager();
87 | }
88 |
89 | @Override
90 | public Object getSystemService(String name) {
91 | // intercept CLIPBOARD_SERVICE,NOTIFICATION_SERVICE
92 | if (name.equals(Context.CLIPBOARD_SERVICE)) {
93 | return getHostContext().getSystemService(name);
94 | } else if (name.equals(Context.NOTIFICATION_SERVICE)) {
95 | return getHostContext().getSystemService(name);
96 | }
97 |
98 | return super.getSystemService(name);
99 | }
100 |
101 | @Override
102 | public Resources getResources() {
103 | return this.mPlugin.getResources();
104 | }
105 |
106 | @Override
107 | public AssetManager getAssets() {
108 | return this.mPlugin.getAssets();
109 | }
110 |
111 | @Override
112 | public Resources.Theme getTheme() {
113 | return this.mPlugin.getTheme();
114 | }
115 |
116 | @Override
117 | public void startActivity(Intent intent) {
118 | ComponentsHandler componentsHandler = mPlugin.getPluginManager().getComponentsHandler();
119 | componentsHandler.transformIntentToExplicitAsNeeded(intent);
120 | super.startActivity(intent);
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/internal/utils/PackageParserCompat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.didi.virtualapk.internal.utils;
18 |
19 | import android.content.Context;
20 | import android.content.pm.PackageParser;
21 | import android.os.Build;
22 |
23 | import com.didi.virtualapk.utils.Reflector;
24 |
25 | import java.io.File;
26 |
27 | /**
28 | * @author johnsonlee
29 | */
30 | public final class PackageParserCompat {
31 |
32 | public static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) {
33 | try {
34 | if (Build.VERSION.SDK_INT >= 28
35 | || (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // Android P Preview
36 | return PackageParserPPreview.parsePackage(context, apk, flags);
37 | } else if (Build.VERSION.SDK_INT >= 24) {
38 | return PackageParserV24.parsePackage(context, apk, flags);
39 | } else if (Build.VERSION.SDK_INT >= 21) {
40 | return PackageParserLollipop.parsePackage(context, apk, flags);
41 | } else {
42 | return PackageParserLegacy.parsePackage(context, apk, flags);
43 | }
44 |
45 | } catch (Throwable e) {
46 | throw new RuntimeException("error", e);
47 | }
48 | }
49 |
50 | private static final class PackageParserPPreview {
51 |
52 | static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws Throwable {
53 | PackageParser parser = new PackageParser();
54 | PackageParser.Package pkg = parser.parsePackage(apk, flags);
55 | Reflector.with(parser)
56 | .method("collectCertificates", PackageParser.Package.class, boolean.class)
57 | .call(pkg, false);
58 | return pkg;
59 | }
60 | }
61 |
62 | private static final class PackageParserV24 {
63 |
64 | static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws Throwable {
65 | PackageParser parser = new PackageParser();
66 | PackageParser.Package pkg = parser.parsePackage(apk, flags);
67 | Reflector.with(parser)
68 | .method("collectCertificates", PackageParser.Package.class, int.class)
69 | .call(pkg, flags);
70 | return pkg;
71 | }
72 | }
73 |
74 | private static final class PackageParserLollipop {
75 |
76 | static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) throws Throwable {
77 | PackageParser parser = new PackageParser();
78 | PackageParser.Package pkg = parser.parsePackage(apk, flags);
79 | parser.collectCertificates(pkg, flags);
80 | return pkg;
81 | }
82 |
83 | }
84 |
85 | private static final class PackageParserLegacy {
86 |
87 | static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws Throwable {
88 | PackageParser parser = new PackageParser(apk.getAbsolutePath());
89 | PackageParser.Package pkg = parser.parsePackage(apk, apk.getAbsolutePath(), context.getResources().getDisplayMetrics(), flags);
90 | Reflector.with(parser)
91 | .method("collectCertificates", PackageParser.Package.class, int.class)
92 | .call(pkg, flags);
93 | return pkg;
94 | }
95 |
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/CoreLibrary/src/main/java/com/didi/virtualapk/internal/utils/Settings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.didi.virtualapk.internal.utils;
18 |
19 | import android.content.Context;
20 | import android.content.SharedPreferences;
21 |
22 | /**
23 | * Created by renyugang on 16/8/23.
24 | */
25 | public class Settings {
26 |
27 | private static final String FILE_NAME = "VirtualAPK_Settings";
28 |
29 | public static void setSoVersion(Context context, String name, int version) {
30 | SharedPreferences preferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
31 | SharedPreferences.Editor editor = preferences.edit();
32 | editor.putInt(name, version);
33 | editor.commit();
34 | }
35 |
36 | public static int getSoVersion(Context context, String name) {
37 | SharedPreferences preferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
38 | return preferences.getInt(name, 0);
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/CoreLibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CoreLibrary
3 |
4 |
--------------------------------------------------------------------------------
/CoreLibrary/src/test/java/com/didi/virtualapk/core/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.core;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/CoreLibrary/upload.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 | def GROUP_ID = 'com.didi.virtualapk'
5 | def ARTIFACT_ID = 'core'
6 |
7 |
8 | def siteUrl = 'https://github.com/didi/VirtualAPK' // 项目的主页
9 | def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url
10 |
11 | group = GROUP_ID
12 | archivesBaseName = 'core'
13 |
14 | version = "0.9.8"
15 |
16 |
17 | install {
18 | repositories.mavenInstaller {
19 | // This generates POM.xml with proper parameters
20 | pom {
21 | artifactId = ARTIFACT_ID
22 |
23 | project {
24 | packaging 'aar'
25 | // Add your description here
26 | name 'A powerful but lightweight plugin framework for Android' //项目描述
27 | url siteUrl
28 | // Set your license
29 | licenses {
30 | license {
31 | name 'Apache License 2.0'
32 | url 'http://www.apache.org/licenses/LICENSE-2.0'
33 | }
34 | }
35 | developers {
36 | developer {
37 | id 'singwhatiwanna' //填写的一些基本信息
38 | name 'DiDi'
39 | email 'singwhatiwanna@gmail.com'
40 | }
41 | }
42 | scm {
43 | connection gitUrl
44 | developerConnection gitUrl
45 | url siteUrl
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | task sourcesJar(type: Jar) {
53 | from android.sourceSets.main.java.srcDirs
54 | classifier = 'sources'
55 | }
56 | task javadoc(type: Javadoc) {
57 | source = android.sourceSets.main.java.srcDirs
58 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
59 | }
60 | task javadocJar(type: Jar, dependsOn: javadoc) {
61 | classifier = 'javadoc'
62 | from javadoc.destinationDir
63 | }
64 | artifacts {
65 | //archives javadocJar
66 | archives sourcesJar
67 | }
68 |
69 | Properties properties = new Properties()
70 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
71 | bintray {
72 | user = properties.getProperty("bintray.user")
73 | key = properties.getProperty("bintray.apikey")
74 | configurations = ['archives']
75 | pkg {
76 | repo = "maven"
77 | name = "${GROUP_ID}:${ARTIFACT_ID}" //发布到JCenter上的项目名字
78 | websiteUrl = siteUrl
79 | vcsUrl = gitUrl
80 | licenses = ["Apache-2.0"]
81 | publish = true
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/PluginDemo/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion VERSION_COMPILE_SDK
5 | buildToolsVersion VERSION_BUILD_TOOLS
6 |
7 | defaultConfig {
8 | applicationId "com.didi.virtualapk.demo"
9 | minSdkVersion VERSION_MIN_SDK
10 | targetSdkVersion VERSION_TARGET_SDK
11 | versionName "1.0.0"
12 | versionCode 1
13 | }
14 | compileOptions {
15 | sourceCompatibility SOURCE_COMPATIBILITY
16 | }
17 |
18 | flavorDimensions "demo"
19 | productFlavors {
20 | beijing {
21 | dimension "demo"
22 | applicationId 'com.didi.virtualapk.demo'
23 | }
24 | shanghai {
25 | dimension "demo"
26 | applicationId 'com.didi.virtualapk.demo'
27 | }
28 | }
29 |
30 | signingConfigs {
31 | release {
32 | storeFile file("../../keystore/test.keystore")
33 | storePassword "test123456"
34 | keyAlias "test"
35 | keyPassword "test123456"
36 | }
37 | }
38 |
39 | buildTypes {
40 | debug {
41 | minifyEnabled false
42 | shrinkResources false
43 | }
44 | release {
45 | minifyEnabled true
46 | shrinkResources true
47 | signingConfig signingConfigs.release
48 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
49 | }
50 | }
51 | }
52 |
53 | dependencies {
54 | // the following aars are also compiled in host project, so they will be filterd when build plugin apk.
55 | // but, wo can still visit their Class and Resources.
56 | implementation 'com.android.support:appcompat-v7:23.4.0'
57 | // implementation 'com.didi.virtualapk:core:0.9.8'
58 | }
59 |
60 | apply plugin: 'com.didi.virtualapk.plugin'
61 |
62 | virtualApk {
63 | packageId = 0x6f // the package id of Resources.
64 | targetHost = '../../VirtualAPK/app' // the path of application module in host project.
65 | applyHostMapping = true //optional, default value: true.
66 | }
--------------------------------------------------------------------------------
/PluginDemo/app/gradle.properties:
--------------------------------------------------------------------------------
1 | #android.enableD8=false
2 | android.useDexArchive=false
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/aidl/Book.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.aidl;
2 |
3 | parcelable Book;
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/aidl/IBookManager.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.aidl;
2 |
3 | import com.didi.virtualapk.demo.aidl.Book;
4 | import com.didi.virtualapk.demo.aidl.IOnNewBookArrivedListener;
5 |
6 | interface IBookManager {
7 | List getBookList();
8 | void addBook(in Book book);
9 | void registerListener(IOnNewBookArrivedListener listener);
10 | void unregisterListener(IOnNewBookArrivedListener listener);
11 | }
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/aidl/IOnNewBookArrivedListener.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.aidl;
2 |
3 | import com.didi.virtualapk.demo.aidl.Book;
4 |
5 | interface IOnNewBookArrivedListener {
6 | void onNewBookArrived(in Book newBook);
7 | }
8 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/binderpool/IBinderPool.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | interface IBinderPool {
4 |
5 | /**
6 | * @param binderCode, the unique token of specific Binder
7 | * @return specific Binder who's token is binderCode.
8 | */
9 | IBinder queryBinder(int binderCode);
10 | }
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/binderpool/ICompute.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | interface ICompute {
4 | int add(int a, int b);
5 | }
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/binderpool/ISecurityCenter.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | interface ISecurityCenter {
4 | String encrypt(String content);
5 | String decrypt(String password);
6 | }
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/aidl/com/didi/virtualapk/demo/manualbinder/Book.aidl:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.manualbinder;
2 |
3 | parcelable Book;
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.ObjectOutputStream;
7 | import java.io.Serializable;
8 |
9 | import com.didi.virtualapk.demo.R;
10 | import com.didi.virtualapk.demo.aidl.Book;
11 | import com.didi.virtualapk.demo.manager.UserManager;
12 | import com.didi.virtualapk.demo.model.User;
13 | import com.didi.virtualapk.demo.utils.MyConstants;
14 | import com.didi.virtualapk.demo.utils.MyUtils;
15 |
16 | import android.app.Activity;
17 | import android.content.Intent;
18 | import android.os.Bundle;
19 | import android.util.Log;
20 | import android.view.View;
21 | import android.view.View.OnClickListener;
22 |
23 | public class MainActivity extends Activity {
24 |
25 | private static final String TAG = "MainActivity";
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | super.onCreate(savedInstanceState);
30 | setContentView(R.layout.activity_main);
31 | UserManager.sUserId = 2;
32 | findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
33 |
34 | @Override
35 | public void onClick(View v) {
36 | Intent intent = new Intent();
37 | intent.setClass(MainActivity.this, SecondActivity.class);
38 | User user = new User(0, "jake", true);
39 | user.book = new Book();
40 | intent.putExtra("extra_user", (Serializable) user);
41 | startActivity(intent);
42 | }
43 | });
44 | }
45 |
46 | @Override
47 | protected void onResume() {
48 | Log.d(TAG, "UserManage.sUserId=" + UserManager.sUserId);
49 | persistToFile();
50 |
51 | super.onResume();
52 | }
53 |
54 | private void persistToFile() {
55 | new Thread(new Runnable() {
56 |
57 | @Override
58 | public void run() {
59 | User user = new User(1, "hello world", false);
60 | File dir = new File(MyConstants.CHAPTER_2_PATH);
61 | if (!dir.exists()) {
62 | dir.mkdirs();
63 | }
64 | File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
65 | ObjectOutputStream objectOutputStream = null;
66 | try {
67 | objectOutputStream = new ObjectOutputStream(
68 | new FileOutputStream(cachedFile));
69 | objectOutputStream.writeObject(user);
70 | Log.d(TAG, "persist user:" + user);
71 | } catch (IOException e) {
72 | e.printStackTrace();
73 | } finally {
74 | MyUtils.close(objectOutputStream);
75 | }
76 | }
77 | }).start();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo;
2 |
3 | import com.didi.virtualapk.demo.utils.MyUtils;
4 |
5 | import android.app.Application;
6 | import android.os.Process;
7 | import android.util.Log;
8 |
9 | public class MyApplication extends Application {
10 |
11 | private static final String TAG = "MyApplication";
12 |
13 | @Override
14 | public void onCreate() {
15 | super.onCreate();
16 | String processName = MyUtils.getProcessName(getApplicationContext(),
17 | Process.myPid());
18 | Log.d(TAG, "application start, process name:" + processName);
19 | new Thread(new Runnable() {
20 |
21 | @Override
22 | public void run() {
23 | doWorkInBackground();
24 | }
25 | }).start();
26 | }
27 |
28 | private void doWorkInBackground() {
29 | // init binder pool
30 | //BinderPool.getInsance(getApplicationContext());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/SecondActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.IOException;
6 | import java.io.ObjectInputStream;
7 |
8 | import com.didi.virtualapk.demo.R;
9 | import com.didi.virtualapk.demo.model.User;
10 | import com.didi.virtualapk.demo.utils.MyConstants;
11 | import com.didi.virtualapk.demo.utils.MyUtils;
12 |
13 | import android.app.Activity;
14 | import android.content.Intent;
15 | import android.os.Bundle;
16 | import android.util.Log;
17 | import android.view.View;
18 | import android.view.View.OnClickListener;
19 |
20 | public class SecondActivity extends Activity {
21 | private static final String TAG = "SecondActivity";
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_second);
27 | findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
28 |
29 | @Override
30 | public void onClick(View v) {
31 | Intent intent = new Intent();
32 | intent.setClass(SecondActivity.this, ThirdActivity.class);
33 | intent.putExtra("time", System.currentTimeMillis());
34 | startActivity(intent);
35 | }
36 | });
37 | Log.d(TAG, "onCreate");
38 | }
39 |
40 | @Override
41 | protected void onNewIntent(Intent intent) {
42 | super.onNewIntent(intent);
43 | Log.d(TAG, "onNewIntent");
44 | }
45 |
46 | @Override
47 | protected void onResume() {
48 | super.onResume();
49 | //User user = (User) getIntent().getSerializableExtra("extra_user");
50 | //Log.d(TAG, "user:" + user.toString());
51 | // Log.d(TAG, "UserManage.sUserId=" + UserManager.sUserId);
52 | //recoverFromFile();
53 | }
54 |
55 | private void recoverFromFile() {
56 | new Thread(new Runnable() {
57 |
58 | @Override
59 | public void run() {
60 | User user = null;
61 | File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
62 | if (cachedFile.exists()) {
63 | ObjectInputStream objectInputStream = null;
64 | try {
65 | objectInputStream = new ObjectInputStream(
66 | new FileInputStream(cachedFile));
67 | user = (User) objectInputStream.readObject();
68 | Log.d(TAG, "recover user:" + user);
69 | } catch (IOException e) {
70 | e.printStackTrace();
71 | } catch (ClassNotFoundException e) {
72 | e.printStackTrace();
73 | } finally {
74 | MyUtils.close(objectInputStream);
75 | }
76 | }
77 | }
78 | }).start();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/ThirdActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo;
2 |
3 | import com.didi.virtualapk.demo.R;
4 |
5 | import android.app.Activity;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.util.Log;
9 | import android.view.View;
10 | import android.view.View.OnClickListener;
11 |
12 | public class ThirdActivity extends Activity {
13 | private static final String TAG = "ThirdActivity";
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | // TODO Auto-generated method stub
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_third);
20 | findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
21 |
22 | @Override
23 | public void onClick(View v) {
24 | Intent intent = new Intent();
25 | intent.setClass(ThirdActivity.this, SecondActivity.class);
26 | intent.putExtra("time", System.currentTimeMillis());
27 | startActivity(intent);
28 | }
29 | });
30 | Log.d(TAG, "onCreate");
31 | }
32 |
33 | @Override
34 | protected void onStart() {
35 | // TODO Auto-generated method stub
36 | super.onStart();
37 | Log.d(TAG, "onStart");
38 | }
39 |
40 | @Override
41 | protected void onResume() {
42 | // TODO Auto-generated method stub
43 | super.onResume();
44 | Log.d(TAG, "onResume");
45 | }
46 |
47 | @Override
48 | protected void onSaveInstanceState(Bundle outState) {
49 | super.onSaveInstanceState(outState);
50 | Log.d(TAG, "onSaveInstanceState");
51 | }
52 |
53 | @Override
54 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
55 | super.onRestoreInstanceState(savedInstanceState);
56 | Log.d(TAG, "onRestoreInstanceState");
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/aidl/Book.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.aidl;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class Book implements Parcelable {
7 |
8 | public int bookId;
9 | public String bookName;
10 |
11 | public Book() {
12 |
13 | }
14 |
15 | public Book(int bookId, String bookName) {
16 | this.bookId = bookId;
17 | this.bookName = bookName;
18 | }
19 |
20 | public int describeContents() {
21 | return 0;
22 | }
23 |
24 | public void writeToParcel(Parcel out, int flags) {
25 | out.writeInt(bookId);
26 | out.writeString(bookName);
27 | }
28 |
29 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
30 | public Book createFromParcel(Parcel in) {
31 | return new Book(in);
32 | }
33 |
34 | public Book[] newArray(int size) {
35 | return new Book[size];
36 | }
37 | };
38 |
39 | private Book(Parcel in) {
40 | bookId = in.readInt();
41 | bookName = in.readString();
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/binderpool/BinderPoolActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | import com.didi.virtualapk.demo.R;
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.os.IBinder;
7 | import android.os.RemoteException;
8 | import android.util.Log;
9 |
10 | public class BinderPoolActivity extends Activity {
11 | private static final String TAG = "BinderPoolActivity";
12 |
13 | private ISecurityCenter mSecurityCenter;
14 | private ICompute mCompute;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_binder_pool);
20 | new Thread(new Runnable() {
21 |
22 | @Override
23 | public void run() {
24 | doWork();
25 | }
26 | }).start();
27 | }
28 |
29 | private void doWork() {
30 | BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);
31 | IBinder securityBinder = binderPool
32 | .queryBinder(BinderPool.BINDER_SECURITY_CENTER);
33 | ;
34 | mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
35 | .asInterface(securityBinder);
36 | Log.d(TAG, "visit ISecurityCenter");
37 | String msg = "helloworld-安卓";
38 | System.out.println("content:" + msg);
39 | try {
40 | String password = mSecurityCenter.encrypt(msg);
41 | System.out.println("encrypt:" + password);
42 | System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
43 | } catch (RemoteException e) {
44 | e.printStackTrace();
45 | }
46 |
47 | Log.d(TAG, "visit ICompute");
48 | IBinder computeBinder = binderPool
49 | .queryBinder(BinderPool.BINDER_COMPUTE);
50 | ;
51 | mCompute = ComputeImpl.asInterface(computeBinder);
52 | try {
53 | System.out.println("3+5=" + mCompute.add(3, 5));
54 | } catch (RemoteException e) {
55 | e.printStackTrace();
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/binderpool/BinderPoolService.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.Binder;
6 | import android.os.IBinder;
7 | import android.util.Log;
8 |
9 | public class BinderPoolService extends Service {
10 |
11 | private static final String TAG = "BinderPoolService";
12 |
13 | private Binder mBinderPool = new BinderPool.BinderPoolImpl();
14 |
15 | @Override
16 | public void onCreate() {
17 | super.onCreate();
18 | }
19 |
20 | @Override
21 | public IBinder onBind(Intent intent) {
22 | Log.d(TAG, "onBind");
23 | return mBinderPool;
24 | }
25 |
26 | @Override
27 | public void onDestroy() {
28 | super.onDestroy();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/binderpool/ComputeImpl.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | import android.os.RemoteException;
4 |
5 | public class ComputeImpl extends ICompute.Stub {
6 |
7 | @Override
8 | public int add(int a, int b) throws RemoteException {
9 | return a + b;
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/binderpool/SecurityCenterImpl.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.binderpool;
2 |
3 | import android.os.RemoteException;
4 |
5 | public class SecurityCenterImpl extends ISecurityCenter.Stub {
6 |
7 | private static final char SECRET_CODE = '^';
8 |
9 | @Override
10 | public String encrypt(String content) throws RemoteException {
11 | char[] chars = content.toCharArray();
12 | for (int i = 0; i < chars.length; i++) {
13 | chars[i] ^= SECRET_CODE;
14 | }
15 | return new String(chars);
16 | }
17 |
18 | @Override
19 | public String decrypt(String password) throws RemoteException {
20 | return encrypt(password);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/manager/BookManager.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.manager;
2 |
3 | import com.didi.virtualapk.demo.aidl.IBookManager;
4 |
5 | import android.os.IBinder;
6 |
7 | public class BookManager {
8 |
9 | private IBookManager mBookManager;
10 |
11 | private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
12 | @Override
13 | public void binderDied() {
14 | if (mBookManager == null)
15 | return;
16 | mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
17 | mBookManager = null;
18 | // TODO:这里重新绑定远程Service
19 | }
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/manager/UserManager.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.manager;
2 |
3 | public class UserManager {
4 |
5 | public static int sUserId = 1;
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/manualbinder/Book.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.manualbinder;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class Book implements Parcelable {
7 |
8 | public int bookId;
9 | public String bookName;
10 |
11 | public Book() {
12 |
13 | }
14 |
15 | public Book(int bookId, String bookName) {
16 | this.bookId = bookId;
17 | this.bookName = bookName;
18 | }
19 |
20 | public int describeContents() {
21 | return 0;
22 | }
23 |
24 | public void writeToParcel(Parcel out, int flags) {
25 | out.writeInt(bookId);
26 | out.writeString(bookName);
27 | }
28 |
29 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
30 | public Book createFromParcel(Parcel in) {
31 | return new Book(in);
32 | }
33 |
34 | public Book[] newArray(int size) {
35 | return new Book[size];
36 | }
37 | };
38 |
39 | private Book(Parcel in) {
40 | bookId = in.readInt();
41 | bookName = in.readString();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/manualbinder/IBookManager.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.manualbinder;
2 |
3 | import java.util.List;
4 |
5 | import android.os.IBinder;
6 | import android.os.IInterface;
7 | import android.os.RemoteException;
8 |
9 | public interface IBookManager extends IInterface {
10 |
11 | static final String DESCRIPTOR = "com.ryg.chapter_2.manualbinder.IBookManager";
12 |
13 | static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
14 | static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);
15 |
16 | public List getBookList() throws RemoteException;
17 |
18 | public void addBook(Book book) throws RemoteException;
19 | }
20 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/messenger/MessengerActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.messenger;
2 |
3 | import com.didi.virtualapk.demo.R;
4 | import com.didi.virtualapk.demo.R.layout;
5 | import com.didi.virtualapk.demo.utils.MyConstants;
6 |
7 | import android.app.Activity;
8 | import android.content.ComponentName;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.content.ServiceConnection;
12 | import android.os.Bundle;
13 | import android.os.Handler;
14 | import android.os.IBinder;
15 | import android.os.Message;
16 | import android.os.Messenger;
17 | import android.os.RemoteException;
18 | import android.util.Log;
19 |
20 | public class MessengerActivity extends Activity {
21 |
22 | private static final String TAG = "MessengerActivity";
23 |
24 | private Messenger mService;
25 | private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
26 |
27 | private static class MessengerHandler extends Handler {
28 | @Override
29 | public void handleMessage(Message msg) {
30 | switch (msg.what) {
31 | case MyConstants.MSG_FROM_SERVICE:
32 | Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
33 | break;
34 | default:
35 | super.handleMessage(msg);
36 | }
37 | }
38 | }
39 |
40 | private ServiceConnection mConnection = new ServiceConnection() {
41 | public void onServiceConnected(ComponentName className, IBinder service) {
42 | mService = new Messenger(service);
43 | Log.d(TAG, "bind service");
44 | Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
45 | Bundle data = new Bundle();
46 | data.putString("msg", "hello, this is client.");
47 | msg.setData(data);
48 | msg.replyTo = mGetReplyMessenger;
49 | try {
50 | mService.send(msg);
51 | } catch (RemoteException e) {
52 | e.printStackTrace();
53 | }
54 | }
55 |
56 | public void onServiceDisconnected(ComponentName className) {
57 | }
58 | };
59 |
60 | @Override
61 | protected void onCreate(Bundle savedInstanceState) {
62 | super.onCreate(savedInstanceState);
63 | setContentView(R.layout.activity_messenger);
64 | Intent intent = new Intent("com.ryg.MessengerService.launch");
65 | bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
66 | }
67 |
68 | @Override
69 | protected void onDestroy() {
70 | unbindService(mConnection);
71 | super.onDestroy();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/messenger/MessengerService.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.messenger;
2 |
3 | import com.didi.virtualapk.demo.utils.MyConstants;
4 |
5 | import android.app.Service;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.os.IBinder;
10 | import android.os.Message;
11 | import android.os.Messenger;
12 | import android.os.RemoteException;
13 | import android.util.Log;
14 |
15 | public class MessengerService extends Service {
16 |
17 | private static final String TAG = "MessengerService";
18 |
19 | private static class MessengerHandler extends Handler {
20 | @Override
21 | public void handleMessage(Message msg) {
22 | switch (msg.what) {
23 | case MyConstants.MSG_FROM_CLIENT:
24 | Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
25 | Messenger client = msg.replyTo;
26 | Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
27 | Bundle bundle = new Bundle();
28 | bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。");
29 | relpyMessage.setData(bundle);
30 | try {
31 | client.send(relpyMessage);
32 | } catch (RemoteException e) {
33 | e.printStackTrace();
34 | }
35 | break;
36 | default:
37 | super.handleMessage(msg);
38 | }
39 | }
40 | }
41 |
42 | private final Messenger mMessenger = new Messenger(new MessengerHandler());
43 |
44 | @Override
45 | public IBinder onBind(Intent intent) {
46 | return mMessenger.getBinder();
47 | }
48 |
49 | @Override
50 | public void onCreate() {
51 | super.onCreate();
52 | }
53 |
54 | @Override
55 | public int onStartCommand(Intent intent, int flags, int startId) {
56 | return super.onStartCommand(intent, flags, startId);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/model/User.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.model;
2 |
3 | import java.io.Serializable;
4 |
5 | import com.didi.virtualapk.demo.aidl.Book;
6 |
7 | import android.os.Parcel;
8 | import android.os.Parcelable;
9 |
10 | public class User implements Parcelable, Serializable {
11 | private static final long serialVersionUID = 519067123721295773L;
12 |
13 | public int userId;
14 | public String userName;
15 | public boolean isMale;
16 |
17 | public Book book;
18 |
19 | public User() {
20 | }
21 |
22 | public User(int userId, String userName, boolean isMale) {
23 | this.userId = userId;
24 | this.userName = userName;
25 | this.isMale = isMale;
26 | }
27 |
28 | public int describeContents() {
29 | return 0;
30 | }
31 |
32 | public void writeToParcel(Parcel out, int flags) {
33 | out.writeInt(userId);
34 | out.writeString(userName);
35 | out.writeInt(isMale ? 1 : 0);
36 | out.writeParcelable(book, 0);
37 | }
38 |
39 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
40 | public User createFromParcel(Parcel in) {
41 | return new User(in);
42 | }
43 |
44 | public User[] newArray(int size) {
45 | return new User[size];
46 | }
47 | };
48 |
49 | private User(Parcel in) {
50 | userId = in.readInt();
51 | userName = in.readString();
52 | isMale = in.readInt() == 1;
53 | book = in
54 | .readParcelable(Thread.currentThread().getContextClassLoader());
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return String.format(
60 | "User:{userId:%s, userName:%s, isMale:%s}, with child:{%s}",
61 | userId, userName, isMale, book);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/provider/DbOpenHelper.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.provider;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 |
7 | public class DbOpenHelper extends SQLiteOpenHelper {
8 |
9 | private static final String DB_NAME = "book_provider.db";
10 | public static final String BOOK_TABLE_NAME = "book";
11 | public static final String USER_TALBE_NAME = "user";
12 |
13 | private static final int DB_VERSION = 3;
14 |
15 | private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
16 | + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
17 |
18 | private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "
19 | + USER_TALBE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"
20 | + "sex INT)";
21 |
22 | public DbOpenHelper(Context context) {
23 | super(context, DB_NAME, null, DB_VERSION);
24 | }
25 |
26 | @Override
27 | public void onCreate(SQLiteDatabase db) {
28 | db.execSQL(CREATE_BOOK_TABLE);
29 | db.execSQL(CREATE_USER_TABLE);
30 | }
31 |
32 | @Override
33 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
34 | // TODO ignored
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/provider/ProviderActivity.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.provider;
2 |
3 | import com.didi.virtualapk.demo.R;
4 | import com.didi.virtualapk.demo.aidl.Book;
5 | import com.didi.virtualapk.demo.model.User;
6 |
7 | import android.app.Activity;
8 | import android.content.ContentValues;
9 | import android.database.Cursor;
10 | import android.net.Uri;
11 | import android.os.Bundle;
12 | import android.util.Log;
13 |
14 | public class ProviderActivity extends Activity {
15 | private static final String TAG = "ProviderActivity";
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_provider);
21 | // Uri uri = Uri.parse("content://com.ryg.chapter_2.book.provider");
22 | // getContentResolver().query(uri, null, null, null, null);
23 | // getContentResolver().query(uri, null, null, null, null);
24 | // getContentResolver().query(uri, null, null, null, null);
25 |
26 | Uri bookUri = Uri.parse("content://com.ryg.chapter_2.book.provider/book");
27 | ContentValues values = new ContentValues();
28 | values.put("_id", 6);
29 | values.put("name", "程序设计的艺术");
30 | getContentResolver().insert(bookUri, values);
31 | Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
32 | while (bookCursor.moveToNext()) {
33 | Book book = new Book();
34 | book.bookId = bookCursor.getInt(0);
35 | book.bookName = bookCursor.getString(1);
36 | Log.d(TAG, "query book:" + book.toString());
37 | }
38 | bookCursor.close();
39 |
40 | Uri userUri = Uri.parse("content://com.ryg.chapter_2.book.provider/user");
41 | Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "name", "sex"}, null, null, null);
42 | while (userCursor.moveToNext()) {
43 | User user = new User();
44 | user.userId = userCursor.getInt(0);
45 | user.userName = userCursor.getString(1);
46 | user.isMale = userCursor.getInt(2) == 1;
47 | Log.d(TAG, "query user:" + user.toString());
48 | }
49 | userCursor.close();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/socket/TCPServerService.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.socket;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.BufferedWriter;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 | import java.io.OutputStreamWriter;
8 | import java.io.PrintWriter;
9 | import java.net.ServerSocket;
10 | import java.net.Socket;
11 | import java.util.Random;
12 |
13 | import com.didi.virtualapk.demo.utils.MyUtils;
14 |
15 | import android.app.Service;
16 | import android.content.Intent;
17 | import android.os.IBinder;
18 |
19 | public class TCPServerService extends Service {
20 |
21 | private boolean mIsServiceDestoryed = false;
22 | private String[] mDefinedMessages = new String[] {
23 | "你好啊,哈哈",
24 | "请问你叫什么名字呀?",
25 | "今天北京天气不错啊,shy",
26 | "你知道吗?我可是可以和多个人同时聊天的哦",
27 | "给你讲个笑话吧:据说爱笑的人运气不会太差,不知道真假。"
28 | };
29 |
30 | @Override
31 | public void onCreate() {
32 | new Thread(new TcpServer()).start();
33 | super.onCreate();
34 | }
35 |
36 | @Override
37 | public IBinder onBind(Intent intent) {
38 | return null;
39 | }
40 |
41 | @Override
42 | public void onDestroy() {
43 | mIsServiceDestoryed = true;
44 | super.onDestroy();
45 | }
46 |
47 | private class TcpServer implements Runnable {
48 |
49 | @SuppressWarnings("resource")
50 | @Override
51 | public void run() {
52 | ServerSocket serverSocket = null;
53 | try {
54 | serverSocket = new ServerSocket(8688);
55 | } catch (IOException e) {
56 | System.err.println("establish tcp server failed, port:8688");
57 | e.printStackTrace();
58 | return;
59 | }
60 |
61 | while (!mIsServiceDestoryed) {
62 | try {
63 | // 接受客户端请求
64 | final Socket client = serverSocket.accept();
65 | System.out.println("accept");
66 | new Thread() {
67 | @Override
68 | public void run() {
69 | try {
70 | responseClient(client);
71 | } catch (IOException e) {
72 | e.printStackTrace();
73 | }
74 | };
75 | }.start();
76 |
77 | } catch (IOException e) {
78 | e.printStackTrace();
79 | }
80 | }
81 | }
82 | }
83 |
84 | private void responseClient(Socket client) throws IOException {
85 | // 用于接收客户端消息
86 | BufferedReader in = new BufferedReader(new InputStreamReader(
87 | client.getInputStream()));
88 | // 用于向客户端发送消息
89 | PrintWriter out = new PrintWriter(new BufferedWriter(
90 | new OutputStreamWriter(client.getOutputStream())), true);
91 | out.println("欢迎来到聊天室!");
92 | while (!mIsServiceDestoryed) {
93 | String str = in.readLine();
94 | System.out.println("msg from client:" + str);
95 | if (str == null) {
96 | break;
97 | }
98 | int i = new Random().nextInt(mDefinedMessages.length);
99 | String msg = mDefinedMessages[i];
100 | out.println(msg);
101 | System.out.println("send :" + msg);
102 | }
103 | System.out.println("client quit.");
104 | // 关闭流
105 | MyUtils.close(out);
106 | MyUtils.close(in);
107 | client.close();
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/utils/MyConstants.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.utils;
2 |
3 | import android.os.Environment;
4 |
5 | public class MyConstants {
6 | public static final String CHAPTER_2_PATH = Environment
7 | .getExternalStorageDirectory().getPath()
8 | + "/singwhatiwanna/chapter_2/";
9 |
10 | public static final String CACHE_FILE_PATH = CHAPTER_2_PATH + "usercache";
11 |
12 | public static final int MSG_FROM_CLIENT = 0;
13 | public static final int MSG_FROM_SERVICE = 1;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/java/com/didi/virtualapk/demo/utils/MyUtils.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.demo.utils;
2 |
3 | import java.io.Closeable;
4 | import java.io.IOException;
5 | import java.util.List;
6 |
7 | import android.app.ActivityManager;
8 | import android.app.ActivityManager.RunningAppProcessInfo;
9 | import android.content.Context;
10 |
11 | public class MyUtils {
12 |
13 | public static String getProcessName(Context cxt, int pid) {
14 | ActivityManager am = (ActivityManager) cxt
15 | .getSystemService(Context.ACTIVITY_SERVICE);
16 | List runningApps = am.getRunningAppProcesses();
17 | if (runningApps == null) {
18 | return null;
19 | }
20 | for (RunningAppProcessInfo procInfo : runningApps) {
21 | if (procInfo.pid == pid) {
22 | return procInfo.processName;
23 | }
24 | }
25 | return null;
26 | }
27 |
28 | public static void close(Closeable closeable) {
29 | try {
30 | if (closeable != null) {
31 | closeable.close();
32 | }
33 | } catch (IOException e) {
34 | e.printStackTrace();
35 | }
36 | }
37 |
38 | public static void executeInThread(Runnable runnable) {
39 | new Thread(runnable).start();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/PluginDemo/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/PluginDemo/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/PluginDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/PluginDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/drawable/edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 | -
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_binder_pool.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_book_manager.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
21 |
22 |
27 |
28 |
32 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_messenger.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_provider.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_second.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_tcpclient.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
26 |
27 |
30 |
31 |
39 |
40 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/layout/activity_third.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #1b000000
5 |
6 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Chapter_2
5 | Hello world!
6 | MessengerActivity
7 | BookManagerActivity
8 | ProviderActivity
9 | TCPServerActivity
10 | TCPClientActivity
11 | BinderPoolActivity
12 |
13 |
14 |
--------------------------------------------------------------------------------
/PluginDemo/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
26 |
27 |
31 |
32 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/PluginDemo/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.0.0'
9 | classpath 'com.didi.virtualapk:gradle:0.9.8.6'
10 | }
11 | }
12 |
13 | ext {
14 | VERSION_COMPILE_SDK = 27
15 | VERSION_BUILD_TOOLS = '26.0.2'
16 |
17 | VERSION_MIN_SDK = 15
18 | VERSION_TARGET_SDK = 25
19 |
20 | SOURCE_COMPATIBILITY = JavaVersion.VERSION_1_7
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | google()
26 | jcenter()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/PluginDemo/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/PluginDemo/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/PluginDemo/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/PluginDemo/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/PluginDemo/make.sh:
--------------------------------------------------------------------------------
1 | ./gradlew clean assemblePlugin
2 | adb push app/build/outputs/apk/app-beijing-release-unsigned.apk /sdcard/Test.apk
3 | adb shell am force-stop com.didi.virtualapk
4 | adb shell am start -n com.didi.virtualapk/com.didi.virtualapk.MainActivity
5 |
--------------------------------------------------------------------------------
/PluginDemo/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/RELEASE-NOTES.md:
--------------------------------------------------------------------------------
1 | # Release Notes
2 |
3 | ## com.didi.virtualapk:core:0.9.0
4 | 开源的第一个版本,支持了几乎所有 Android 特性,目前已经被大家广泛使用。
5 |
6 | ## com.didi.virtualapk:core:0.9.1
7 | ##### 1. 最初,为了让程序更加健壮,当我们通过无效的 Intent 来启动插件中的 Activity,这个时候程序不会报错。尽管我们的初衷是好的,但现在我们觉得这种方式太过友好了,不利于bug的排查。
8 |
9 | ```改动```:现在,启动无效的 Activity 会直接抛出 ActivityNotFoundException。
10 |
11 | ##### 2. 最初,为了性能考虑,当启动插件中的四大组件时,要求 Intent 中的包名必须和目标插件一致,否则就会抛出 RuntimeException。从反馈来看,这貌似给大家造成了一些困扰,所以我们优化了这一块,采用全局搜索策略,牺牲一点点效率来解决这个问题。
12 |
13 | ```改动```:现在,启动插件中的组件将不再依赖 Intent 中的包名。
14 |
15 | ```新增```:现在,如果一个组件(Activity/Service/Receiver)在宿主和插件中同时存在,那么只有插件中的组件会被唤起。
16 |
17 | ##### 3. 现在 VirtualAPK 将全面支持 Android O。
18 |
19 | ```改动```:兼容 Android O。
20 |
21 | ##### 4. 修复了一个 bug,该bug曾经导致:如果先后加载了A和B两个插件,并且存在通过A的Resources对象来访问B中资源这种情形,这个时候资源访问就会失败,造成 Crash。
22 |
23 | ```改动```:我们修复了这个bug,使得通过任何 Resources 对象均可以访问所有插件以及宿主的资源。
24 |
25 | ##### 5. 现在 VirtualAPK 将开始 hook Android N 的 Resources 对象,尽管这个改动是多余的。事实上,从Android L开始,仅仅替换ContextImpl和PluginContext中的资源就可以满足绝大多数使用场景,为了避免开发者心存疑问,我们统一了这一行为,任何版本都hook资源。
26 |
27 | ```改动```:hook Android N 的资源,尽管是多余的。
28 |
29 | ## com.didi.virtualapk:core:0.9.5
30 | 1. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。
31 | 2. 与 com.didi.virtualapk:gradle:0.9.8.2及以上版本 搭配使用,支持官方 Data Binding。
32 |
33 | ## com.didi.virtualapk:core:0.9.6
34 | 1. 修复部分空指针问题。
35 |
36 | ## com.didi.virtualapk:core:0.9.6
37 | 1. 代码重构。
38 | 2. 修复部分空指针bug。
39 | 3. 适配Android P。
40 | 4. 修复webview初始化后插件资源不可用的bug。
41 |
42 | ## VirtualAPK 的构建部分已经开源了,
43 |
44 | ## com.didi.virtualapk:gradle:0.9.8.2
45 | 1. 适配android gradle 3.0.0
46 | 2. 修复多个bug,强烈建议升级至此版本,以前版本不再维护。
47 | 3. 插件工程需要定义 productFlavors。
48 |
49 | ## com.didi.virtualapk:gradle:0.9.8.3
50 | 1. 兼容不定义 productFlavors 的配置。
51 |
52 | ## com.didi.virtualapk:gradle:0.9.8.4
53 | 1. 修复当插件依赖library module时构建失败的bug。
54 | 2. 修复依赖本地aar时构建失败的bug。
55 | 3. 修复当插件自定义attr属性时id错误的bug。
56 |
57 | ## com.didi.virtualapk:gradle:0.9.8.6
58 | 1. 适配com.android.tools.build:gradle:3.1.0。
59 | 2. 修复当插件未定义attr资源时anim资源找不到的bug。
60 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'com.didi.virtualapk.host'
3 |
4 | android {
5 | compileSdkVersion VERSION_COMPILE_SDK
6 | buildToolsVersion VERSION_BUILD_TOOLS
7 |
8 | defaultConfig {
9 | applicationId "com.didi.virtualapk"
10 | minSdkVersion VERSION_MIN_SDK
11 | targetSdkVersion VERSION_TARGET_SDK
12 | versionCode 3
13 | versionName "1.0.0"
14 | }
15 | compileOptions {
16 | sourceCompatibility SOURCE_COMPATIBILITY
17 | }
18 |
19 | signingConfigs {
20 | release {
21 | storeFile file("../keystore/test.keystore")
22 | storePassword "test123456"
23 | keyAlias "test"
24 | keyPassword "test123456"
25 | }
26 | }
27 |
28 | buildTypes {
29 | debug {
30 | minifyEnabled false
31 | shrinkResources false
32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
33 | }
34 | release {
35 | minifyEnabled true
36 | shrinkResources true
37 | signingConfig signingConfigs.release
38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
39 | }
40 | }
41 |
42 | lintOptions {
43 | abortOnError false
44 | }
45 | }
46 |
47 | dependencies {
48 | implementation fileTree(dir: 'libs', include: ['*.jar'])
49 | testImplementation 'junit:junit:4.12'
50 |
51 | implementation 'com.android.support:appcompat-v7:23.4.0'
52 | implementation 'com.didi.virtualapk:core:0.9.8'
53 | // implementation project (':CoreLibrary')
54 |
55 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/didi/virtualapk/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/didi/virtualapk/VAApplication.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.util.Log;
6 |
7 | /**
8 | * Created by renyugang on 16/8/10.
9 | */
10 | public class VAApplication extends Application {
11 |
12 | @Override
13 | protected void attachBaseContext(Context base) {
14 | super.attachBaseContext(base);
15 | long start = System.currentTimeMillis();
16 | PluginManager.getInstance(base).init();
17 | Log.d("ryg", "use time:" + (System.currentTimeMillis() - start));
18 | }
19 |
20 | @Override
21 | public void onCreate() {
22 | super.onCreate();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi/libaa.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/app/src/main/jniLibs/armeabi/libaa.so
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
26 |
27 |
34 |
35 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-en/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VirtualAPK-EN
3 | open plugin
4 | about
5 | Append WebView
6 | VirtualAPK is a plugin framework powered by DiDi company for Android,see the source code : https://github.com/didi/VirtualAPK
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VirtualAPK
3 | 加载插件
4 | 关于
5 | Append WebView
6 | VirtualAPK 是一款由滴滴出行研发的 Android 插件化框架,项目地址:https://github.com/didi/VirtualAPK
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/didi/virtualapk/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | System.properties['com.android.build.gradle.overrideVersionCheck'] = 'true'
3 |
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.0.0'
10 | classpath 'com.didi.virtualapk:gradle:0.9.8.6'
11 |
12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
13 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
14 | }
15 | }
16 |
17 | ext {
18 | VERSION_COMPILE_SDK = 27
19 | VERSION_BUILD_TOOLS = '26.0.2'
20 |
21 | VERSION_MIN_SDK = 15
22 | VERSION_TARGET_SDK = 25
23 |
24 | SOURCE_COMPATIBILITY = JavaVersion.VERSION_1_7
25 | }
26 |
27 | allprojects {
28 | repositories {
29 | mavenCentral()
30 | jcenter()
31 | google()
32 | }
33 | }
34 |
35 | task clean(type: Delete) {
36 | delete rootProject.buildDir
37 | }
38 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Mon Jun 12 18:17:16 CST 2017
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 05 15:48:51 CST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/imgs/demo-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/demo-1.png
--------------------------------------------------------------------------------
/imgs/demo-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/demo-2.png
--------------------------------------------------------------------------------
/imgs/demo-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/demo-3.png
--------------------------------------------------------------------------------
/imgs/didi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/didi.png
--------------------------------------------------------------------------------
/imgs/uber-china.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/uber-china.png
--------------------------------------------------------------------------------
/imgs/va-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/va-logo.png
--------------------------------------------------------------------------------
/imgs/va.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/va.png
--------------------------------------------------------------------------------
/imgs/va1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/imgs/va1.png
--------------------------------------------------------------------------------
/keystore/test.cer:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDXTCCAkWgAwIBAgIEWWZdhTANBgkqhkiG9w0BAQsFADBfMQ4wDAYDVQQGDAV6aF9jbjEQMA4G
3 | A1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzENMAsGA1UEChMEZGlkaTEMMAoGA1UECxMD
4 | YXBwMQwwCgYDVQQDEwNyeWcwHhcNMTcwNTA4MDkxOTM0WhcNNDQwOTIzMDkxOTM0WjBfMQ4wDAYD
5 | VQQGDAV6aF9jbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzENMAsGA1UEChME
6 | ZGlkaTEMMAoGA1UECxMDYXBwMQwwCgYDVQQDEwNyeWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
7 | ggEKAoIBAQC6d9Pq0+qedUEuHgYhIuROidDWVMSECAq2+MTlX1kn1gGgAKiAt7P6c864YlFCB8Mq
8 | 4Q8R8HR9SMprTD4NPUt7nsf3jQiFSOH0f9PvOI3fWMLNCpUq64GthcwHd3bVqOy6r5h7f3Jd9Pi3
9 | PmkhFs6FwCNaFoPkQioDdnE+8bNKOC53rsx5gqHFzs0xpYYZLz7+tP7OSm9KQe19VBwPOI3MxgPD
10 | 6dbEWRNI4FPk6yNC3k9Fq8HrtCzlfapmEsdxpBjrQ3DUeegvtfQhqZtLujj9X894cuoCR05eowoi
11 | 6mamKRdRErQ7JMhkUYMwJQvKYLuLr1JElEYHJ5iT46Ph8FVzAgMBAAGjITAfMB0GA1UdDgQWBBQ2
12 | yNaLVuMnjEKQiMSz+/FjbjPYHTANBgkqhkiG9w0BAQsFAAOCAQEAnFXbS07Zu8vphohhZ1nHPeNN
13 | 8GGcbSllO7xwbONPng+uTFGldIUhPP9lmKkw/5bA1T/57tjbfmNph9li6MfuwO4CECkG2WTQNpr9
14 | Y8ZcvM0sh3unx9wHkPJQ43Cvp6vCxsaor/ALT4UbTTHtWRpwcZUBxsgdNQDpwM1u0ye5uMd5E/P6
15 | hlsdI0GdtnMdyZrCInwhQbBEHYpSerVyRpCRq9pa72kP865fDB9zCyLSekctvfZBYkEKEMX9QVfs
16 | d9tJ7vxqABxUjNPYWlAxLlRPf/Dz4NDyeYHuTjEZW46NErqJ8GQGqvS+MG4OehqV1SCIQoHkCJQ2
17 | o7w3hv2XVmdozw==
18 | -----END CERTIFICATE-----
19 |
--------------------------------------------------------------------------------
/keystore/test.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/keystore/test.keystore
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':CoreLibrary'
3 | include ':AndroidStub'
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenLocal()
4 | google()
5 | mavenCentral()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
11 | }
12 | }
13 |
14 | apply plugin: 'java'
15 | apply plugin: 'groovy'
16 |
17 | tasks.withType(GroovyCompile) {
18 | sourceCompatibility = '1.7'
19 | targetCompatibility = '1.7'
20 | }
21 |
22 | repositories {
23 | mavenLocal()
24 | google()
25 | mavenCentral()
26 | jcenter()
27 | }
28 |
29 | configurations {
30 | provided
31 | }
32 |
33 | sourceSets {
34 | main {
35 | compileClasspath += configurations.provided
36 | }
37 | }
38 |
39 | dependencies {
40 | compile gradleApi()
41 | compile localGroovy()
42 | compile 'com.google.guava:guava:19.0'
43 | compile 'commons-io:commons-io:1.4'
44 | compile 'commons-codec:commons-codec:1.6'
45 | compile 'org.ow2.asm:asm:4.0'
46 | compile 'org.javassist:javassist:3.18.2-GA'
47 | compile 'com.android.tools.build:gradle:3.0.0'
48 | }
49 |
50 |
51 |
52 | apply from: 'upload.gradle'
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/gradle.properties:
--------------------------------------------------------------------------------
1 | GROUP_ID=com.didi.virtualapk
2 | ARTIFACT_ID=gradle
3 | VERSION=0.9.8.6
4 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/didi/VirtualAPK/b1a778a06688f89f574ec8608f0764eadb9009b7/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Sep 27 15:30:56 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/Constants.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk
2 |
3 | public final class Constants {
4 | public static final String GRADLE_3_1_0 = 'va.gradle.3.1.0'
5 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResAttr.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from libs/androidfw/Command.cpp
5 | */
6 | public final class ResAttr {
7 |
8 | public static final int LABEL_ATTR = 0x01010001
9 |
10 | public static final int ICON_ATTR = 0x01010002
11 |
12 | public static final int NAME_ATTR = 0x01010003
13 |
14 | public static final int PERMISSION_ATTR = 0x01010006
15 |
16 | public static final int EXPORTED_ATTR = 0x01010010
17 |
18 | public static final int GRANT_URI_PERMISSIONS_ATTR = 0x0101001b
19 |
20 | public static final int RESOURCE_ATTR = 0x01010025
21 |
22 | public static final int DEBUGGABLE_ATTR = 0x0101000f
23 |
24 | public static final int VALUE_ATTR = 0x01010024
25 |
26 | public static final int VERSION_CODE_ATTR = 0x0101021b
27 |
28 | public static final int VERSION_NAME_ATTR = 0x0101021c
29 |
30 | public static final int SCREEN_ORIENTATION_ATTR = 0x0101001e
31 |
32 | public static final int MIN_SDK_VERSION_ATTR = 0x0101020c
33 |
34 | public static final int MAX_SDK_VERSION_ATTR = 0x01010271
35 |
36 | public static final int REQ_TOUCH_SCREEN_ATTR = 0x01010227
37 |
38 | public static final int REQ_KEYBOARD_TYPE_ATTR = 0x01010228
39 |
40 | public static final int REQ_HARD_KEYBOARD_ATTR = 0x01010229
41 |
42 | public static final int REQ_NAVIGATION_ATTR = 0x0101022a
43 |
44 | public static final int REQ_FIVE_WAY_NAV_ATTR = 0x01010232
45 |
46 | public static final int TARGET_SDK_VERSION_ATTR = 0x01010270
47 |
48 | public static final int TEST_ONLY_ATTR = 0x01010272
49 |
50 | public static final int ANY_DENSITY_ATTR = 0x0101026c
51 |
52 | public static final int GL_ES_VERSION_ATTR = 0x01010281
53 |
54 | public static final int SMALL_SCREEN_ATTR = 0x01010284
55 |
56 | public static final int NORMAL_SCREEN_ATTR = 0x01010285
57 |
58 | public static final int LARGE_SCREEN_ATTR = 0x01010286
59 |
60 | public static final int XLARGE_SCREEN_ATTR = 0x010102bf
61 |
62 | public static final int REQUIRED_ATTR = 0x0101028e
63 |
64 | public static final int INSTALL_LOCATION_ATTR = 0x010102b7
65 |
66 | public static final int SCREEN_SIZE_ATTR = 0x010102ca
67 |
68 | public static final int SCREEN_DENSITY_ATTR = 0x010102cb
69 |
70 | public static final int REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364
71 |
72 | public static final int COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365
73 |
74 | public static final int LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366
75 |
76 | public static final int PUBLIC_KEY_ATTR = 0x010103a6
77 |
78 | public static final int CATEGORY_ATTR = 0x010103e8
79 |
80 | public static final int BANNER_ATTR = 0x10103f2
81 |
82 | public static final int ISGAME_ATTR = 0x10103f4
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResStringFlag.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from include/androidfw/ResourceTypes.h
5 | */
6 | public final class ResStringFlag {
7 |
8 | public static final int SORTED_FLAG = 1 << 0
9 |
10 | public static final int UTF8_FLAG = 1 << 8
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResStringPoolSpan.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from include/androidfw/ResourceTypes.h
5 | */
6 | public final class ResStringPoolSpan {
7 |
8 | public static final int END = 0xFFFFFFFF
9 |
10 | public static final byte[] END_SPAN = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
11 |
12 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResTableEntry.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from include/androidfw/ResourceTypes.h
5 | */
6 | public final class ResTableEntry {
7 |
8 | public static final int FLAG_COMPLEX = 0X0001
9 |
10 | public static final int FLAG_PUBLIC = 0X0002
11 |
12 | public static final int FLAG_WEAK = 0X0004
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResTableType.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from include/androidfw/ResourceTypes.h
5 | */
6 | public final class ResTableType {
7 |
8 | public static final int NO_ENTRY = -1;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResType.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from include/androidfw/ResourceTypes.h
5 | */
6 | public final class ResType {
7 | public static final int RES_NULL_TYPE = 0x0000
8 | public static final int RES_STRING_POOL_TYPE = 0x0001
9 | public static final int RES_TABLE_TYPE = 0x0002
10 | public static final int RES_XML_TYPE = 0x0003
11 |
12 | // Chunk types in RES_XML_TYPE
13 | public static final int RES_XML_FIRST_CHUNK_TYPE = 0x0100
14 | public static final int RES_XML_START_NAMESPACE_TYPE = 0x0100
15 | public static final int RES_XML_END_NAMESPACE_TYPE = 0x0101
16 | public static final int RES_XML_START_ELEMENT_TYPE = 0x0102
17 | public static final int RES_XML_END_ELEMENT_TYPE = 0x0103
18 | public static final int RES_XML_CDATA_TYPE = 0x0104
19 | public static final int RES_XML_LAST_CHUNK_TYPE = 0x017
20 | // This contains a uint32_t array mapping strings in the string
21 | // pool back to resource identifiers. It is optional.
22 | public static final int RES_XML_RESOURCE_MAP_TYPE = 0x0180
23 |
24 | // Chunk types in RES_TABLE_TYPE
25 | public static final int RES_TABLE_PACKAGE_TYPE = 0x0200
26 | public static final int RES_TABLE_TYPE_TYPE = 0x0201
27 | public static final int RES_TABLE_TYPE_SPEC_TYPE = 0x0202
28 | public static final int RES_TABLE_LIBRARY_TYPE = 0x0203
29 | }
30 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/ResValueDataType.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.aapt
2 |
3 | /**
4 | * enum from include/androidfw/ResourceTypes.h
5 | */
6 | public final class ResValueDataType {
7 |
8 | public static final int TYPE_REFERENCE = 0x01
9 |
10 | public static final int TYPE_STRING = 0x03
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/aapt/packageinfo:
--------------------------------------------------------------------------------
1 | Editor of ARSC file, forked from https://github.com/wequick/Small and made some improvements. Thanks to the Small team.
2 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/HostClassAndResCollector.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.collector
2 |
3 | import com.didi.virtualapk.collector.dependence.AarDependenceInfo
4 | import com.didi.virtualapk.collector.dependence.DependenceInfo
5 |
6 | import java.util.zip.ZipFile
7 |
8 | /**
9 | * Collector of Class and Java Resource(no-class files in jar) in host apk
10 | *
11 | * @author zhengtao
12 | */
13 |
14 | class HostClassAndResCollector {
15 |
16 | private def hostJarFiles = [] as LinkedList
17 | private def hostClassesAndResources = [] as LinkedHashSet
18 |
19 | /**
20 | * Collect jar entries that already exist in the host apk
21 | *
22 | * @param stripDependencies DependencyInfos that exists in the host apk, including AAR and JAR
23 | * @return set of classes and java resources
24 | */
25 | public Set collect(Collection stripDependencies) {
26 | flatToJarFiles(stripDependencies, hostJarFiles)
27 | hostJarFiles.each {
28 | hostClassesAndResources.addAll(unzipJar(it))
29 | }
30 | hostClassesAndResources
31 | }
32 |
33 | /**
34 | * Collect the jar files that are held by the DependenceInfo, including local jars of the DependenceInfo
35 | * @param stripDependencies Collection of DependenceInfo
36 | * @param jarFiles Collection used to store jar files
37 | */
38 | def flatToJarFiles(Collection stripDependencies, Collection jarFiles) {
39 | stripDependencies.each {
40 | jarFiles.add(it.jarFile)
41 | if (it instanceof AarDependenceInfo) {
42 | it.localJars.each {
43 | jarFiles.add(it)
44 | }
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * Unzip the entries of Jar
51 | *
52 | * @return Set of entries in the JarFile
53 | */
54 | public static Set unzipJar(File jarFile) {
55 |
56 | def jarEntries = [] as Set
57 |
58 | ZipFile zipFile = new ZipFile(jarFile)
59 | try {
60 | zipFile.entries().each {
61 | jarEntries.add(it.name)
62 | }
63 | } finally {
64 | zipFile.close();
65 | }
66 |
67 | return jarEntries
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/DependenceInfo.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.collector.dependence
2 |
3 | /**
4 | * Represents a library in Android Project
5 | *
6 | * @author zhengtao
7 | */
8 | public abstract class DependenceInfo {
9 |
10 | /**
11 | * The type of of the DependenceInfo.
12 | */
13 | public enum DependenceType{
14 |
15 | /**
16 | * Type of Android library
17 | */
18 | AAR(0x01),
19 |
20 | /**
21 | * Type of Java library
22 | */
23 | JAR(0x02)
24 |
25 | private final int value;
26 |
27 | DependenceType(int value) {
28 | this.value = value;
29 | }
30 |
31 | public int getValue() {
32 | return value;
33 | }
34 | }
35 |
36 | /**
37 | * Group name of dependence in a Maven repository
38 | */
39 | private String group
40 | /**
41 | * Module name of dependence in a Maven repository
42 | */
43 | private String artifact
44 | /**
45 | * Version of dependence in a Maven repository
46 | */
47 | private String version
48 |
49 |
50 | DependenceInfo(String group, String artifact, String version) {
51 | this.group = group
52 | this.artifact = artifact
53 | this.version = version
54 | }
55 |
56 |
57 | String getGroup() {
58 | return group
59 | }
60 |
61 | String getArtifact() {
62 | return artifact
63 | }
64 |
65 | String getVersion() {
66 | return version
67 | }
68 |
69 | abstract File getJarFile()
70 | abstract DependenceType getDependenceType()
71 |
72 | @Override
73 | String toString() {
74 | return "${group}:${artifact}:${version} -> ${jarFile} -> ${super.toString()}"
75 | }
76 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/dependence/JarDependenceInfo.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.collector.dependence
2 |
3 | import com.android.builder.model.JavaLibrary
4 | import com.didi.virtualapk.utils.Log
5 |
6 | /**
7 | * Represents a Jar library. This could be the output of a Java project.
8 | *
9 | * @author zhengtao
10 | */
11 | class JarDependenceInfo extends DependenceInfo {
12 |
13 | JavaLibrary library
14 |
15 | JarDependenceInfo(String group, String artifact, String version, JavaLibrary library) {
16 | super(group, artifact, version)
17 | this.library = library
18 | }
19 |
20 | @Override
21 | File getJarFile() {
22 | Log.i 'JarDependenceInfo', "Found [${library.resolvedCoordinates}]'s jar file: ${library.jarFile}"
23 | return library.jarFile
24 | }
25 |
26 | @Override
27 | DependenceType getDependenceType() {
28 | return DependenceType.JAR
29 | }
30 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/res/ResourceEntry.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.collector.res
2 | /**
3 | * Represent a resource entry(e.g. drawable, anim, attr, layout...) in android project,
4 | * the information in this class will be recorded in the arsc file.
5 | *
6 | * @author zhengtao
7 | */
8 | class ResourceEntry {
9 |
10 | /**
11 | * Type of a resource entry, e.g. drawable
12 | */
13 | String resourceType
14 | /**
15 | * Name of a resource entry, e.g. abc_btn_check_material is name of a drawable
16 | */
17 | String resourceName
18 | /**
19 | * Original id value of a resource entry, generated by aapt
20 | */
21 | int resourceId
22 |
23 | /**
24 | * New assigned id value of resource entry, pid is modified
25 | */
26 | int newResourceId
27 |
28 | public ResourceEntry(resType, resName, resId) {
29 | resourceType = resType
30 | resourceName = resName
31 | resourceId = resId
32 | }
33 |
34 | public void setNewResourceId(id) {
35 | this.newResourceId = id
36 | }
37 |
38 | /**
39 | * Generate a new resource id from packageId, typeId, entryId.
40 | *
41 | * For example: 0x7f02000e is a id of resource entry
42 | *
43 | * @param packageId 7f
44 | * @param typeId 02
45 | * @param entryId 0000e
46 | */
47 | public void setNewResourceId(packageId, typeId, entryId) {
48 | newResourceId = packageId << 24 | typeId << 16 | entryId
49 | }
50 |
51 | /**
52 | * @return the hexadecimal resource id
53 | */
54 | public String getHexResourceId() {
55 | return "0x${Integer.toHexString(resourceId)}"
56 | }
57 |
58 | /**
59 | * @return the hexadecimal new resource id
60 | */
61 | public String getHexNewResourceId() {
62 | return "0x${Integer.toHexString(newResourceId)}"
63 | }
64 |
65 |
66 | boolean equals(o) {
67 | if (this.is(o)) return true
68 | if (getClass() != o.class) return false
69 |
70 | ResourceEntry that = (ResourceEntry) o
71 |
72 | if (resourceName != that.resourceName) return false
73 | if (resourceType != that.resourceType) return false
74 |
75 | return true
76 | }
77 |
78 |
79 | int hashCode() {
80 | int result
81 | result = resourceType.hashCode()
82 | result = 31 * result + resourceName.hashCode()
83 | return result
84 | }
85 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/collector/res/StyleableEntry.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.collector.res
2 |
3 | /**
4 | * Represent styleable item in R symbol file
5 | * e.g.
6 | * int[] styleable TagLayout { 0x010100af, 0x7f0102b5, 0x7f0102b6 }
7 | * int styleable TagLayout_android_gravity 0
8 | *
9 | *
10 | * Styleable will not be recorded to the arsc file, and the representation
11 | * in the R file is different from other resource types, so separate representation
12 | *
13 | * @author zhengtao
14 | */
15 | class StyleableEntry {
16 |
17 | /**
18 | * Name of a styleable entry, e.g. TagLayout or TagLayout_android_gravity
19 | */
20 | String name
21 | /**
22 | * Value of a styleable entry represent in R file, e.g. { 0x010100af, 0x7f0102b5, 0x7f0102b6 } or 0
23 | */
24 | String value
25 |
26 | /**
27 | * Type of a styleable entry value , int or int[]
28 | */
29 | String valueType
30 |
31 | public StyleableEntry(name, value, valueType) {
32 | this.name = name
33 | this.value = value
34 | this.valueType = valueType
35 | }
36 |
37 | /**
38 | * e.g.
39 | * when value of the entry is: { 0x010100af, 0x7f0102b5, 0x7f0102b6 }
40 | * this method return [0x010100af, 0x7f0102b5, 0x7f0102b6]
41 | *
42 | * @return value of styleable entry as list
43 | */
44 | def getValueAsList() {
45 | value.trim()[1..-2].split(',')*.trim()
46 | }
47 |
48 | /**
49 | * Generate the value of int[] type styleable entry by list of value element
50 | * @param values list of
51 | */
52 | def setValue(List values) {
53 | value = "{ ${values.join(', ')} }"
54 | }
55 |
56 | def setValue(String value) {
57 | this.value = value
58 | }
59 |
60 |
61 | boolean equals(o) {
62 | if (this.is(o)) return true
63 | if (getClass() != o.class) return false
64 |
65 | StyleableEntry that = (StyleableEntry) o
66 |
67 | if (name != that.name) return false
68 | if (valueType != that.valueType) return false
69 |
70 | return true
71 | }
72 |
73 | int hashCode() {
74 | int result
75 | result = name.hashCode()
76 | result = 31 * result + valueType.hashCode()
77 | return result
78 | }
79 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/DxTaskHooker.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.gradle.api.ApkVariant
4 | import com.android.build.gradle.internal.pipeline.TransformTask
5 | import com.didi.virtualapk.utils.Log
6 | import org.apache.commons.io.FilenameUtils
7 | import org.gradle.api.Project
8 |
9 | /**
10 | * Minify R class file under the applicationId namespace before dx task
11 | *
12 | * @author zhengtao
13 | */
14 | class DxTaskHooker extends GradleTaskHooker {
15 |
16 |
17 | public DxTaskHooker(Project project, ApkVariant apkVariant) {
18 | super(project, apkVariant)
19 | }
20 |
21 | @Override
22 | String getTransformName() {
23 | return "dex"
24 | }
25 |
26 | /**
27 | * Replace the R class files record all resources with the stripped R only record plugin resources.
28 | * Input file may be a directory or jar file
29 | *
30 | * @param task Gradle transform task for dex
31 | */
32 | @Override
33 | void beforeTaskExecute(TransformTask task) {
34 | task.inputs.files.each { input ->
35 | // Log.i 'DxTaskHooker', "${task.name}: ${input.absoluteFile}"
36 | if(input.directory) {
37 | input.eachFileRecurse { file ->
38 | handleFile(file)
39 | }
40 | } else {
41 | handleFile(input)
42 | }
43 | }
44 | }
45 |
46 | void handleFile(File file) {
47 | if (file.directory && file.path.endsWith(vaContext.packagePath)) {
48 |
49 | if (recompileSplitR(file)) {
50 | Log.i 'DxTaskHooker', "Recompiled R.java in dir: ${file.absoluteFile}"
51 | }
52 |
53 | } else if (file.file && file.name.endsWith('.jar')) {
54 | // Decompress jar file
55 | File unzipJarDir = new File(file.parentFile, FilenameUtils.getBaseName(file.name))
56 | project.copy {
57 | from project.zipTree(file)
58 | into unzipJarDir
59 | }
60 |
61 | // VirtualApk Package Dir
62 | File pkgDir = new File(unzipJarDir, vaContext.packagePath)
63 | if (pkgDir.exists()) {
64 | if (recompileSplitR(pkgDir)) {
65 | Log.i 'DxTaskHooker', "Recompiled R.java in jar: ${file.absoluteFile}"
66 | File backupDir = new File(vaContext.getBuildDir(scope), 'origin/classes')
67 | backupDir.deleteDir()
68 | project.copy {
69 | from file
70 | into backupDir
71 | }
72 |
73 | project.ant.zip(baseDir: unzipJarDir, destFile: file)
74 | }
75 | }
76 | }
77 | }
78 |
79 | /**
80 | * Delete the large R class file under the applicationId namespace, then
81 | * compile the splitRJavaFile to generate the R class file only records
82 | * plugin resources
83 | *
84 | * @param pkgDir The path to storing the R class file
85 | * @return true if the search&delete&compile actions succeed
86 | */
87 | boolean recompileSplitR(File pkgDir) {
88 |
89 | File[] RClassFiles = pkgDir.listFiles(new FilenameFilter() {
90 | @Override
91 | boolean accept(File dir, String name) {
92 | return name.startsWith('R$') && name.endsWith('.class')
93 | }
94 | })
95 |
96 | if(RClassFiles?.length) {
97 | RClassFiles.each {
98 | it.delete()
99 | }
100 |
101 | String baseDir = pkgDir.path - "${File.separator}${vaContext.packagePath}"
102 |
103 | project.ant.javac(
104 | srcdir: vaContext.splitRJavaFile.parentFile,
105 | source: apkVariant.javaCompiler.sourceCompatibility,
106 | target: apkVariant.javaCompiler.targetCompatibility,
107 | destdir: new File(baseDir))
108 |
109 | mark()
110 | return true
111 | }
112 |
113 | return false
114 | }
115 |
116 |
117 | @Override
118 | void afterTaskExecute(TransformTask task) { }
119 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/GradleTaskHooker.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.gradle.api.ApkVariant
4 | import com.android.build.gradle.internal.api.ApplicationVariantImpl
5 | import com.android.build.gradle.internal.scope.VariantScope
6 | import com.android.build.gradle.internal.variant.BaseVariantData
7 | import com.didi.virtualapk.VAExtention
8 | import com.didi.virtualapk.VAExtention.VAContext
9 | import org.gradle.api.Project
10 | import org.gradle.api.Task
11 |
12 | /**
13 | * Base class of gradle task hooker, provides some common field used by hookers
14 | * @param Type of hooked task
15 | *
16 | * @author zhengtao
17 | */
18 | public abstract class GradleTaskHooker {
19 |
20 | private Project project
21 |
22 | /**
23 | * A Build variant when build a apk and all its public data.
24 | */
25 | private ApkVariant apkVariant
26 |
27 | private VAExtention virtualApk
28 |
29 | private TaskHookerManager taskHookerManager
30 |
31 | public GradleTaskHooker(Project project, ApkVariant apkVariant) {
32 | this.project = project
33 | this.apkVariant = apkVariant
34 | this.virtualApk = project.virtualApk
35 |
36 | vaContext.checkList.addCheckPoint(taskName)
37 | }
38 |
39 | public Project getProject() {
40 | return this.project
41 | }
42 |
43 | public ApkVariant getApkVariant() {
44 | return this.apkVariant
45 | }
46 |
47 | public BaseVariantData getVariantData() {
48 | return ((ApplicationVariantImpl) this.apkVariant).variantData
49 | }
50 |
51 | public VariantScope getScope() {
52 | return variantData.scope
53 | }
54 |
55 | public VAExtention getVirtualApk() {
56 | return this.virtualApk
57 | }
58 |
59 | public VAContext getVaContext() {
60 | return this.virtualApk.getVaContext(apkVariant.name)
61 | }
62 |
63 | public void mark() {
64 | vaContext.checkList.mark(taskName)
65 | }
66 |
67 | public void setTaskHookerManager(TaskHookerManager taskHookerManager) {
68 | this.taskHookerManager = taskHookerManager
69 | }
70 |
71 | public TaskHookerManager getTaskHookerManager() {
72 | return this.taskHookerManager
73 | }
74 |
75 | public T getTask() {
76 |
77 | }
78 |
79 | /**
80 | * Return the transform name of the hooked task(transform task)
81 | */
82 | public String getTransformName() {
83 | return ""
84 | }
85 |
86 | /**
87 | * Return the task name(exclude transform task)
88 | */
89 | public String getTaskName() {
90 | return "${transformName}For${apkVariant.name.capitalize()}"
91 | }
92 |
93 | /**
94 | * Callback function before the hooked task executes
95 | * @param task Hooked task
96 | */
97 | public abstract void beforeTaskExecute(T task)
98 | /**
99 | * Callback function after the hooked task executes
100 | * @param task Hooked task
101 | */
102 | public abstract void afterTaskExecute(T task)
103 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeAssetsHooker.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.gradle.api.ApkVariant
4 | import com.android.build.gradle.tasks.MergeSourceSetFolders
5 | import com.android.ide.common.res2.AssetSet
6 | import com.didi.virtualapk.collector.dependence.AarDependenceInfo
7 | import com.didi.virtualapk.utils.Log
8 | import com.didi.virtualapk.utils.Reflect
9 | import org.gradle.api.Project
10 |
11 | import java.util.function.Predicate
12 | import java.util.function.Supplier
13 |
14 | /**
15 | * Remove the asset directory included in the excluded library before mergeAssets task
16 | *
17 | * @author zhengtao
18 | */
19 | class MergeAssetsHooker extends GradleTaskHooker {
20 |
21 | public MergeAssetsHooker(Project project, ApkVariant apkVariant) {
22 | super(project, apkVariant)
23 | }
24 |
25 | @Override
26 | String getTaskName() {
27 | return scope.getTaskName('merge', 'Assets')
28 | }
29 |
30 | /**
31 | * Remove the element(AssetSet) generated by the stripped dependenceInfos in the task inputDirectorySets
32 | * @param task Gradle task of mergeAssets
33 | */
34 | @Override
35 | void beforeTaskExecute(MergeSourceSetFolders task) {
36 |
37 | Set strippedAssetPaths = vaContext.stripDependencies.collect {
38 | if (it instanceof AarDependenceInfo) {
39 | return it.assetsFolder.path
40 | }
41 | return ''
42 | }
43 |
44 | Reflect reflect = Reflect.on(task)
45 | reflect.set('assetSetSupplier', new FixedSupplier(this, reflect.get('assetSetSupplier'), strippedAssetPaths))
46 | }
47 |
48 | @Override
49 | void afterTaskExecute(MergeSourceSetFolders task) {
50 | }
51 |
52 | static class FixedSupplier implements Supplier> {
53 |
54 | MergeAssetsHooker hooker
55 | Supplier> origin
56 | Set strippedAssetPaths
57 |
58 | FixedSupplier(MergeAssetsHooker hooker, Supplier> origin, Set strippedAssetPaths) {
59 | this.hooker = hooker
60 | this.origin = origin
61 | this.strippedAssetPaths = strippedAssetPaths
62 | }
63 |
64 | @Override
65 | List get() {
66 | List assetSets = origin.get()
67 | assetSets.removeIf(new Predicate() {
68 | @Override
69 | boolean test(AssetSet assetSet) {
70 | boolean ret = strippedAssetPaths.contains(assetSet.sourceFiles.get(0).path)
71 | if (ret) {
72 | Log.i 'MergeAssetsHooker', "Stripped asset of artifact: ${assetSet} -> ${assetSet.sourceFiles.get(0).path}"
73 | }
74 | return ret
75 | }
76 | })
77 | hooker.mark()
78 | return assetSets
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/MergeJniLibsHooker.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.gradle.AndroidConfig
4 | import com.android.build.gradle.AppExtension
5 | import com.android.build.gradle.api.ApkVariant
6 | import com.android.build.gradle.internal.pipeline.TransformTask
7 | import com.didi.virtualapk.collector.HostJniLibsCollector
8 | import com.didi.virtualapk.utils.Log
9 | import org.gradle.api.Project
10 |
11 | /**
12 | * Remove the Native libs(.so) in stripped dependencies before mergeJniLibs task
13 | *
14 | * @author zhengtao
15 | */
16 | class MergeJniLibsHooker extends GradleTaskHooker {
17 |
18 | HostJniLibsCollector jniLibsCollector
19 | AndroidConfig androidConfig
20 |
21 | public MergeJniLibsHooker(Project project, ApkVariant apkVariant) {
22 | super(project, apkVariant)
23 | jniLibsCollector = new HostJniLibsCollector()
24 | androidConfig = project.extensions.findByType(AppExtension)
25 | }
26 |
27 | @Override
28 | String getTransformName() {
29 | return "mergeJniLibs"
30 | }
31 |
32 | /**
33 | * Prevent .so files from packaging into apk via the PackagingOptions exclude configuration
34 | * @param task Gradle task of mergeJniLibs
35 | */
36 | @Override
37 | void beforeTaskExecute(TransformTask task) {
38 |
39 | def excludeJniFiles = jniLibsCollector.collect(vaContext.stripDependencies)
40 |
41 | excludeJniFiles.each {
42 | androidConfig.packagingOptions.exclude("/${it}")
43 | Log.i 'MergeJniLibsHooker', "Stripped jni file: ${it}"
44 | }
45 |
46 | mark()
47 | // Reflect.on(task.transform)
48 | // .set('packagingOptions', new ParsedPackagingOptions(androidConfig.packagingOptions))
49 | }
50 |
51 | @Override
52 | void afterTaskExecute(TransformTask task) {}
53 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ProguardHooker.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.gradle.api.ApkVariant
4 | import com.android.build.gradle.internal.pipeline.TransformTask
5 | import com.android.build.gradle.internal.transforms.ProGuardTransform
6 | import com.didi.virtualapk.collector.dependence.AarDependenceInfo
7 | import org.gradle.api.InvalidUserDataException
8 | import org.gradle.api.Project
9 |
10 | /**
11 | * Apply host mapping file to maintain compatibility between the plugin and host apk.
12 | * And input the stripped jar files as library, to prevent proguard errors
13 | *
14 | * @author zhengtao
15 | */
16 | class ProguardHooker extends GradleTaskHooker {
17 |
18 | public static final String MAPPING_KEY = "applyMapping"
19 |
20 | ProguardHooker(Project project, ApkVariant apkVariant) {
21 | super(project, apkVariant)
22 | }
23 |
24 | @Override
25 | String getTransformName() {
26 | return "proguard"
27 | }
28 |
29 | /**
30 | * Before the run of proguard, specifies the mapping file to reuse when obfuscating,
31 | * if classes in host apk is obfuscated, we need to ensure compatibility by specifying
32 | * the mapping file.
33 | *
34 | * In addition, we need to input the stripped jars into the Proguard program in the
35 | * form of libarary, via the libraryJar(protected) method
36 | *
37 | * @param task Gradle task of proguard
38 | */
39 | @Override
40 | void beforeTaskExecute(TransformTask task) {
41 |
42 | def proguardTransform = task.transform as ProGuardTransform
43 |
44 | File applyMappingFile;
45 |
46 | //Specifies the proguard mapping file through ${ MAPPING_KEY }
47 | if (project.hasProperty(MAPPING_KEY)) {
48 | applyMappingFile = new File(project.properties[MAPPING_KEY])
49 | if (!applyMappingFile.exists()) {
50 | throw new InvalidUserDataException("${project.properties[MAPPING_KEY]} does not exist")
51 | }
52 | if (!applyMappingFile.isFile()) {
53 | throw new InvalidUserDataException("${project.properties[MAPPING_KEY]} is not a file")
54 | }
55 | }
56 |
57 | //Default to use the mapping file generated by host apk
58 | if (virtualApk.applyHostMapping && applyMappingFile == null) {
59 | applyMappingFile = new File(project.rootProject.projectDir, "host/mapping.txt")
60 | }
61 |
62 | if (applyMappingFile?.exists()) {
63 | proguardTransform.applyTestedMapping(applyMappingFile)
64 | }
65 |
66 | vaContext.stripDependencies.each {
67 | proguardTransform.libraryJar(it.jarFile)
68 | if (it instanceof AarDependenceInfo) {
69 | it.localJars.each {
70 | proguardTransform.libraryJar(it)
71 | }
72 | }
73 | }
74 | mark()
75 | }
76 |
77 | @Override
78 | void afterTaskExecute(TransformTask task) { }
79 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/ShrinkResourcesHooker.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.api.transform.TransformException
4 | import com.android.build.api.transform.TransformInvocation
5 | import com.android.build.gradle.api.ApkVariant
6 | import com.android.build.gradle.internal.pipeline.TransformTask
7 | import com.android.build.gradle.internal.transforms.ShrinkResourcesTransform
8 | import com.didi.virtualapk.transform.TransformWrapper
9 | import com.didi.virtualapk.utils.Log
10 | import com.didi.virtualapk.utils.Reflect
11 | import org.gradle.api.Project
12 |
13 | class ShrinkResourcesHooker extends GradleTaskHooker {
14 |
15 | ShrinkResourcesHooker(Project project, ApkVariant apkVariant) {
16 | super(project, apkVariant)
17 | }
18 |
19 | @Override
20 | String getTransformName() {
21 | return "shrinkRes"
22 | }
23 |
24 | @Override
25 | void beforeTaskExecute(TransformTask task) {
26 | def shrinkResourcesTransform = task.transform as ShrinkResourcesTransform
27 | Reflect.on(task).set('transform', new TransformWrapper(shrinkResourcesTransform) {
28 | @Override
29 | void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException {
30 | Log.i 'ShrinkResourcesHooker', "sourceDir: ${Reflect.on(origin).get('sourceDir')}"
31 | super.transform(invocation)
32 | mark()
33 | }
34 | })
35 | }
36 |
37 | @Override
38 | void afterTaskExecute(TransformTask task) {
39 |
40 | }
41 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/hooker/TaskHookerManager.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.hooker
2 |
3 | import com.android.build.gradle.AppExtension
4 | import com.android.build.gradle.internal.api.ApplicationVariantImpl
5 | import com.android.build.gradle.internal.pipeline.TransformTask
6 | import com.didi.virtualapk.utils.Log
7 | import org.gradle.api.Project
8 | import org.gradle.api.Task
9 | import org.gradle.api.execution.TaskExecutionListener
10 | import org.gradle.api.tasks.TaskState
11 | import org.gradle.internal.reflect.Instantiator
12 |
13 | /**
14 | * Manager of hookers, responsible for registration and scheduling execution
15 | *
16 | * @author zhengtao
17 | */
18 | public abstract class TaskHookerManager {
19 |
20 | protected Map taskHookerMap = new HashMap<>()
21 |
22 | protected Project project
23 | protected AppExtension android
24 | protected Instantiator instantiator
25 |
26 | public TaskHookerManager(Project project, Instantiator instantiator) {
27 | this.project = project
28 | this.instantiator = instantiator
29 | android = project.extensions.findByType(AppExtension)
30 | project.gradle.addListener(new VirtualApkTaskListener())
31 | }
32 |
33 | public abstract void registerTaskHookers()
34 |
35 | protected void registerTaskHooker(GradleTaskHooker taskHooker) {
36 | taskHooker.setTaskHookerManager(this)
37 | taskHookerMap.put(taskHooker.taskName, taskHooker)
38 | }
39 |
40 |
41 | public T findHookerByName(String taskName) {
42 | return taskHookerMap[taskName] as T
43 | }
44 |
45 | private class VirtualApkTaskListener implements TaskExecutionListener {
46 |
47 | @Override
48 | void beforeExecute(Task task) {
49 | // Log.i 'TaskHookerManager', "beforeExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}"
50 | if (task.project == project) {
51 | if (task in TransformTask) {
52 | taskHookerMap["${task.transform.name}For${task.variantName.capitalize()}".toString()]?.beforeTaskExecute(task)
53 | } else {
54 | taskHookerMap[task.name]?.beforeTaskExecute(task)
55 | }
56 | }
57 | }
58 |
59 | @Override
60 | void afterExecute(Task task, TaskState taskState) {
61 | // Log.i 'TaskHookerManager', "afterExecute ${task.name} tid: ${Thread.currentThread().id} t: ${Thread.currentThread().name}"
62 | if (task.project == project) {
63 | if (task in TransformTask) {
64 | taskHookerMap["${task.transform.name}For${task.variantName.capitalize()}".toString()]?.afterTaskExecute(task)
65 | } else {
66 | taskHookerMap[task.name]?.afterTaskExecute(task)
67 | }
68 | }
69 | }
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/tasks/AssemblePlugin.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.tasks
2 |
3 | import com.android.build.gradle.api.ApkVariant
4 | import com.android.build.gradle.internal.api.ApplicationVariantImpl
5 | import com.didi.virtualapk.VAExtention
6 | import com.didi.virtualapk.utils.Log
7 | import com.sun.istack.internal.NotNull
8 | import org.gradle.api.Action
9 | import org.gradle.api.DefaultTask
10 | import org.gradle.api.Project
11 | import org.gradle.api.tasks.Input
12 | import org.gradle.api.tasks.OutputDirectory
13 | import org.gradle.api.tasks.TaskAction
14 |
15 | /**
16 | * Gradle task for assemble plugin apk
17 | * @author zhengtao
18 | */
19 | public class AssemblePlugin extends DefaultTask {
20 |
21 | @OutputDirectory
22 | File pluginApkDir
23 |
24 | @Input
25 | String appPackageName
26 |
27 | @Input
28 | String apkTimestamp
29 |
30 | @Input
31 | File originApkFile
32 |
33 | String variantName
34 |
35 | String buildDir
36 |
37 | /**
38 | * Copy the plugin apk to out/plugin directory and rename to
39 | * the format required for the backend system
40 | */
41 | @TaskAction
42 | public void outputPluginApk() {
43 | VAExtention virtualApk = project.virtualApk
44 | virtualApk.getVaContext(variantName).checkList.check()
45 | virtualApk.printWarning(name)
46 |
47 | if (virtualApk.getFlag('tip.forceUseHostDependences')) {
48 | def tip = new StringBuilder('To avoid configuration WARNINGs, you could set the forceUseHostDependences to be true in build.gradle,\n ')
49 | tip.append('please declare it in application project build.gradle:\n')
50 | tip.append(' virtualApk {\n')
51 | tip.append(' forceUseHostDependences = true \n')
52 | tip.append(' }\n')
53 | Log.i name, tip.toString()
54 | }
55 |
56 | Log.i name, "More building infomation could be found in the dir: ${buildDir}."
57 |
58 | getProject().copy {
59 | from originApkFile
60 | into pluginApkDir
61 | rename { "${appPackageName}_${apkTimestamp}.apk" }
62 | }
63 | }
64 |
65 |
66 | public static class ConfigAction implements Action {
67 |
68 | @NotNull
69 | Project project
70 | @NotNull
71 | ApplicationVariantImpl variant
72 |
73 | ConfigAction(@NotNull Project project, @NotNull ApkVariant variant) {
74 | this.project = project
75 | this.variant = variant
76 | }
77 |
78 | @Override
79 | void execute(AssemblePlugin assemblePluginTask) {
80 | VAExtention virtualApk = project.virtualApk
81 |
82 | assemblePluginTask.appPackageName = variant.applicationId
83 | assemblePluginTask.apkTimestamp = new Date().format("yyyyMMddHHmmss")
84 | assemblePluginTask.originApkFile = variant.outputs[0].outputFile
85 | assemblePluginTask.pluginApkDir = new File(project.buildDir, "/outputs/plugin/${variant.name}")
86 | assemblePluginTask.variantName = variant.name
87 | assemblePluginTask.buildDir = virtualApk.getVaContext(variant.name).getBuildDir(variant.variantData.scope).canonicalPath
88 |
89 | assemblePluginTask.setGroup("build")
90 | assemblePluginTask.setDescription("Build ${variant.name.capitalize()} plugin apk")
91 | assemblePluginTask.dependsOn(variant.assemble.name)
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/transform/TransformWrapper.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.transform
2 |
3 | import com.android.build.api.transform.*
4 | import com.didi.virtualapk.utils.Log
5 |
6 | public class TransformWrapper extends Transform {
7 |
8 | Transform origin
9 |
10 | TransformWrapper(Transform transform) {
11 | origin = transform
12 | }
13 |
14 | @Override
15 | String getName() {
16 | return origin.getName()
17 | }
18 |
19 | @Override
20 | Set getInputTypes() {
21 | return origin.getInputTypes()
22 | }
23 |
24 | @Override
25 | Set getOutputTypes() {
26 | return origin.getOutputTypes()
27 | }
28 |
29 | @Override
30 | Set super QualifiedContent.Scope> getScopes() {
31 | return origin.getScopes()
32 | }
33 |
34 | @Override
35 | Set super QualifiedContent.Scope> getReferencedScopes() {
36 | return origin.getReferencedScopes()
37 | }
38 |
39 | @Override
40 | Collection getSecondaryFileInputs() {
41 | return origin.getSecondaryFileInputs()
42 | }
43 |
44 | @Override
45 | Collection getSecondaryFiles() {
46 | return origin.getSecondaryFiles()
47 | }
48 |
49 | @Override
50 | Collection getSecondaryFileOutputs() {
51 | return origin.getSecondaryFileOutputs()
52 | }
53 |
54 | @Override
55 | Collection getSecondaryDirectoryOutputs() {
56 | return origin.getSecondaryDirectoryOutputs()
57 | }
58 |
59 | @Override
60 | Map getParameterInputs() {
61 | return origin.getParameterInputs()
62 | }
63 |
64 | @Override
65 | boolean isIncremental() {
66 | return origin.isIncremental()
67 | }
68 |
69 | @Override
70 | void transform(Context context, Collection inputs, Collection referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
71 | origin.transform(context, inputs, referencedInputs, outputProvider, isIncremental)
72 | }
73 |
74 | @Override
75 | void transform(TransformInvocation invocation) throws TransformException, InterruptedException, IOException {
76 | Collection inputs = invocation.getInputs()
77 | for (TransformInput input : inputs) {
78 | for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
79 | Log.i "${name}", "input dir: ${directoryInput.getFile()}"
80 | }
81 | for (JarInput jarInput : input.getJarInputs()) {
82 | Log.i "${name}", "input jar: ${jarInput.getFile()}"
83 | }
84 | }
85 |
86 | Collection referencedInputs = invocation.getReferencedInputs();
87 | for (TransformInput transformInput : referencedInputs) {
88 | for (DirectoryInput directoryInput : transformInput.getDirectoryInputs()) {
89 | Log.i "${name}", "referenced input dir: ${directoryInput.getFile()}"
90 | }
91 | for (JarInput jarInput : transformInput.getJarInputs()) {
92 | Log.i "${name}", "referenced input jar: ${jarInput.getFile()}"
93 | }
94 | }
95 |
96 | origin.transform(invocation)
97 | }
98 |
99 | @Override
100 | boolean isCacheable() {
101 | return origin.isCacheable()
102 | }
103 |
104 | @Override
105 | String toString() {
106 | return origin.toString()
107 | }
108 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/CheckList.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.utils
2 |
3 | class CheckList {
4 | Map mMap = new LinkedHashMap<>()
5 | String variantName
6 |
7 | CheckList(String variantName) {
8 | this.variantName = variantName
9 | }
10 |
11 | void addCheckPoint(String key) {
12 | if (mMap.containsKey(key)) {
13 | throw new RuntimeException("[${key}] has already exists.")
14 | }
15 | mMap.put(key, false)
16 | // Log.i('test', "addCheckPoint: ${key}")
17 | }
18 |
19 | void mark(String key) {
20 | mMap.put(key, true)
21 | // Log.i('test', "mark: ${key}")
22 | }
23 |
24 | void check() {
25 | boolean check = true
26 | Map matched = mMap
27 |
28 | matched.each {
29 | check &= it.value
30 | }
31 |
32 | if (check) {
33 | Log.i 'CheckList', "All checked ok."
34 | return
35 | }
36 |
37 | Log.i 'CheckList', "Checked WARNING:"
38 | matched.each {
39 | Log.i 'CheckList', "${it.key}: ${it.value}"
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/FileBinaryCategory.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.utils
2 |
3 | /**
4 | * Extend << operator to copy file
5 | * @author zhengtao
6 | */
7 | class FileBinaryCategory {
8 |
9 |
10 | //Write content from a url to a file
11 | def static leftShift(File file, URL url) {
12 | def conn = url.openConnection()
13 | conn.with {
14 | def is = conn.getInputStream()
15 | file.withOutputStream { os->
16 | def bs = new BufferedOutputStream(os)
17 | bs << is
18 | }
19 | }
20 | }
21 |
22 |
23 | //Write content from a src file to a dst file
24 | def static leftShift(File dst, File src) {
25 | src.withInputStream { is->
26 | dst.withOutputStream { os->
27 | def bs = new BufferedOutputStream(os)
28 | bs << is
29 | }
30 | }
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/FileUtil.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.utils
2 | /**
3 | * Created by qiaopu on 2017/9/4.
4 | */
5 | public class FileUtil {
6 |
7 | public static void saveFile(File dir, String fileName, Closure> action) {
8 | List> list = action.call();
9 | saveFile(dir, fileName, list)
10 | }
11 |
12 | public static void saveFile(File dir, String fileName, Collection> collection) {
13 | saveFile(dir, fileName, false, collection)
14 | }
15 |
16 | public static void saveFile(File dir, String fileName, boolean sort, Collection> collection) {
17 | dir.mkdirs()
18 | def file = new File(dir, "${fileName}.txt")
19 | ArrayList> list = new ArrayList<>(collection)
20 | if (sort) {
21 | Collections.sort(list)
22 | }
23 | list.add('')
24 | file.write(list.join('\r\n'))
25 | }
26 |
27 | public static boolean deleteEmptySubfolders(File dir) {
28 | if(dir == null || !dir.exists()) {
29 | return true;
30 | }
31 | if(!dir.isDirectory()) {
32 | return false;
33 | }
34 | File[] files = dir.listFiles();
35 |
36 | if (files == null || files.length == 0) {
37 | return dir.delete();
38 | }
39 |
40 | boolean result = true;
41 | int len = files.length;
42 |
43 | for(int i = 0; i < len; ++i) {
44 | File file = files[i];
45 | if(file.isDirectory()) {
46 | if(!deleteEmptySubfolders(file)) {
47 | result = false;
48 | }
49 | }
50 | }
51 |
52 | File[] updatedFiles = dir.listFiles();
53 | if (updatedFiles == null || updatedFiles.length == 0) {
54 | if (!dir.delete()) {
55 | result = false;
56 | }
57 | }
58 |
59 | return result;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/groovy/com.didi.virtualapk/utils/ZipUtil.groovy:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.utils
2 |
3 | import java.util.jar.JarFile
4 | import java.util.zip.ZipEntry
5 | import java.util.zip.ZipFile
6 | import java.util.zip.ZipOutputStream
7 |
8 |
9 | /**
10 | * operate on jar/zip file
11 | */
12 | public class ZipUtil {
13 |
14 | private byte[] buffer = new byte[1024]
15 |
16 | private final File file
17 |
18 | public static ZipUtil with(final File file) {
19 | return new ZipUtil(file)
20 | }
21 |
22 | /**
23 | * unzip jar to class names
24 | * @param jarFile
25 | * @return class name set
26 | */
27 | public static Set jarFileToClasses(final File jarFile) {
28 | if (jarFile == null || !jarFile.exists() || !jarFile.name.endsWith("jar")) {
29 | return Collections.EMPTY_SET;
30 | }
31 |
32 | Set entryNames = new LinkedHashSet<>();
33 | JarFile jar = new JarFile(jarFile)
34 | jar.entries().each { entry ->
35 | entryNames.add(entry.name)
36 | }
37 |
38 | return entryNames
39 | }
40 |
41 | private ZipUtil(final File file) {
42 | this.file = file
43 | }
44 |
45 | public ZipUtil deleteAll(final Set deletes) {
46 | // deletes.each {
47 | // println "Deleting [${it}]..."
48 | // }
49 | ZipFile zf = new ZipFile(this.file)
50 | File temp = new File(this.file.parentFile, "${this.file.name}~")
51 | ZipOutputStream os = new ZipOutputStream(new FileOutputStream(temp))
52 |
53 | def entries = zf.entries()
54 | while (entries.hasMoreElements()) {
55 | ZipEntry ze = entries.nextElement()
56 | //print "#### [${ze.name}] "
57 | if (!deletes.find { it == ze.name }) {
58 | writeEntry(zf, os, ze)
59 | //println " +"
60 | } else {
61 | //println " -"
62 | }
63 | }
64 |
65 | zf.close()
66 | os.flush()
67 | os.close()
68 |
69 | this.file.delete() // delete first to avoid `renameTo' failed on Windows
70 | temp.renameTo(file)
71 | return this
72 | }
73 |
74 | private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze) throws IOException {
75 | ZipEntry ze2 = new ZipEntry(ze.getName());
76 | ze2.setMethod(ze.getMethod());
77 | ze2.setTime(ze.getTime());
78 | ze2.setComment(ze.getComment());
79 | ze2.setExtra(ze.getExtra());
80 | if (ze.getMethod() == ZipEntry.STORED) {
81 | ze2.setSize(ze.getSize());
82 | ze2.setCrc(ze.getCrc());
83 | }
84 | os.putNextEntry(ze2);
85 | writeBytes(zf, ze, os);
86 | }
87 |
88 | private synchronized void writeBytes(ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
89 | int n;
90 |
91 | InputStream is = null;
92 | try {
93 | is = zf.getInputStream(ze);
94 | long left = ze.getSize();
95 |
96 | while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
97 | os.write(buffer, 0, n);
98 | left -= n;
99 | }
100 | } finally {
101 | if (is != null) {
102 | is.close();
103 | }
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/java/com/didi/virtualapk/utils/Log.java:
--------------------------------------------------------------------------------
1 | package com.didi.virtualapk.utils;
2 |
3 | public final class Log {
4 |
5 | private Log() {
6 |
7 | }
8 |
9 | public static int i(String tag, String msg) {
10 | System.out.println("[INFO][" + tag + "] " + msg);
11 | return 0;
12 | }
13 |
14 | public static int e(String tag, String msg) {
15 | System.err.println("[ERROR][" + tag + "] " + msg);
16 | return 0;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.didi.virtualapk.host.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.didi.virtualapk.VAHostPlugin
2 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.didi.virtualapk.plugin.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.didi.virtualapk.VAPlugin
2 |
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor:
--------------------------------------------------------------------------------
1 | com.didi.virtualapk.databinding.annotationprocessor.ProcessDataBinding
--------------------------------------------------------------------------------
/virtualapk-gradle-plugin/upload.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 |
5 | def siteUrl = 'https://github.com/didi/VirtualAPK' // 项目的主页
6 | def gitUrl = 'https://github.com/didi/VirtualAPK' // Git仓库的url
7 |
8 | group = GROUP_ID
9 | archivesBaseName = 'gradle'
10 | version = VERSION
11 |
12 | install {
13 | repositories.mavenInstaller {
14 | // This generates POM.xml with proper parameters
15 | pom {
16 | artifactId = ARTIFACT_ID
17 |
18 | project {
19 | packaging 'aar'
20 | // Add your description here
21 | name 'A powerful but lightweight plugin framework for Android' //项目描述
22 | url siteUrl
23 | // Set your license
24 | licenses {
25 | license {
26 | name 'Apache License 2.0'
27 | url 'http://www.apache.org/licenses/LICENSE-2.0'
28 | }
29 | }
30 | developers {
31 | developer {
32 | id 'zhengtao' //填写的一些基本信息
33 | name 'DiDi'
34 | email 'zhengtao620@gmail.com'
35 | }
36 | }
37 | scm {
38 | connection gitUrl
39 | developerConnection gitUrl
40 | url siteUrl
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 | task sourcesJar(type: Jar) {
48 | from sourceSets.main.allJava
49 | classifier = 'sources'
50 | }
51 | task javadocJar(type: Jar, dependsOn: javadoc) {
52 | classifier = 'javadoc'
53 | from groovydoc.destinationDir
54 | }
55 | artifacts {
56 | //archives javadocJar
57 | archives sourcesJar
58 | }
59 |
60 | Properties properties = new Properties()
61 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
62 | bintray {
63 | user = properties.getProperty("bintray.user")
64 | key = properties.getProperty("bintray.apikey")
65 | configurations = ['archives']
66 | pkg {
67 | repo = "maven"
68 | name = "${GROUP_ID}:${ARTIFACT_ID}" //发布到JCenter上的项目名字
69 | websiteUrl = siteUrl
70 | vcsUrl = gitUrl
71 | licenses = ["Apache-2.0"]
72 | publish = true
73 | }
74 |
75 | }
--------------------------------------------------------------------------------