├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── EasyDemo.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── net │ │ └── goeasyway │ │ └── easydemo │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── bundles │ │ ├── AmapDemo.apk │ │ └── bundle1-debug.apk │ ├── java │ └── net │ │ └── goeasyway │ │ └── easydemo │ │ ├── App.java │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── bundle1 ├── .gitignore ├── build.gradle ├── bundle1.iml ├── libs │ └── classes.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── net │ │ └── goeasyway │ │ └── bundle1 │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── net │ │ │ └── goeasyway │ │ │ └── bundle1 │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── net │ └── goeasyway │ └── bundle1 │ └── ExampleUnitTest.java ├── easyand ├── .gitignore ├── build.gradle ├── easyand.iml ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── net │ │ └── goeasyway │ │ └── easyand │ │ ├── bundle │ │ ├── Bundle.java │ │ ├── BundleClassLoader.java │ │ ├── BundleContextThemeWrapper.java │ │ ├── BundleContextWrapper.java │ │ ├── BundleException.java │ │ ├── BundleFrameworkApp.java │ │ ├── BundleInfo.java │ │ ├── BundleModule.java │ │ ├── BundlePackageInfo.java │ │ ├── container │ │ │ ├── ActivityStub.java │ │ │ └── BundleServiceContainer.java │ │ └── hook │ │ │ ├── ActivityThreadHandlerCallback.java │ │ │ ├── FrameworkHookHelper.java │ │ │ ├── IActivityManagerHandler.java │ │ │ └── IPackageManagerHandler.java │ │ ├── bundlemananger │ │ ├── BundleManager.java │ │ └── BundleUtils.java │ │ └── utils │ │ ├── LogUtils.java │ │ ├── ParcelableUtils.java │ │ └── ReflectUtils.java │ └── res │ └── values │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── projectFilesBackup └── .idea │ └── workspace.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | EasyDemo -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 32 | 33 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | Android API 23 Platform 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EasyDemo.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyPlug--最简单的Android开源插件框架 2 | 3 | 2015年Android的插件开发热情空前高涨,开源的插件框架也如雨后春笋,初步了解对比了一下各个开源框架,感觉各有千秋。因为面对不同的业务需求,所以在针对性和实现方式上都略有不同。 4 | 5 | 在没有开源框架的时候,世界很安静,大家只能低头研究android机制和framework的代码自己闭门造车,一直怨恨无人同行且不能踩在前辈的肩膀向上。而今面对扑面而来的开源插件时,大家又陷入选择的泥潭而不能自拔,这真是幸福的烦恼。每个插件框架都各有各的优缺点,当你花时间去熟悉对比完各个插件框架后,你也不一定能百分百确定某某框架一定适合你用。你终于在绕了一圈后又回到起点,是自己写一个框架呢还是用开源框架呢?因为你始终不能确认开源的框架是否能经得起实际项目的检测,害怕还有N多不知道的坑在等着你。 6 | 7 | 这就是我推出EasyPlug的目的,无他就是要easy!大道至简,对系统HOOK太多也就会依赖太多,GOOGLE或手机产商任何一点小改动都可能会让你的HOOK失效。太多的功能就会面对太多的不稳定性,太多的类和设计模式会加剧开发者学习曲线,所以EasyPlug要在实现既定目标的前提下,用尽可能少的代码来实现一个基本的插件框架,并用实际中的经验帮大家把兼容性的坑填平。 8 | 9 | ### 需求 10 | 首先明确一下EasyPlug要解决的问题: 11 | 12 | 模块化开发:模块可以独立开发,甚至在Android设备上安装运行和调式;各个模块应相互解耦,也可以有效的解决DEX方法数65535上限的问题。 13 | 不改变原生开发规范:插件的开发仍然按原生开发APK应用的规范,不需要额外学习成本;插件不需要添加需置文件。 14 | 动态部署:可以动态安装、卸载和更新插件,安装和更新完成后插件可以直接运行,不需要宿主进程重启。 15 | 支持插件调用宿主的类:这样很多基础功能(OKHttp, Retrofit, RxJava等)可以编译打包在宿主的APK中,插件无需将这个包打入自己的APK,在运行时用到会从宿主的类加载器中加载。可以极大的减小插件包的大小,并可以对基础库进行统一处理。 16 | 支持常用功能:Activity, Service,自定义View, so库等; 17 | 18 | ### Demo 19 | 如何确认EasyPlug是否合适你的项目使用,请直接看如下的例子,直接不做修改将百度和高德地图的DEMO做为插件。 20 | 21 | 我们写了三个应用: 22 | 23 | >1. 宿主APK:运行EasyPlug框架,负责安装卸载插件,插件的跳转入口。 24 | 2. 两个个插件:高德地图Demo,一个简单的APK应用(调用地图DEMO中的自定义MapView)。 25 | 26 | 当你看完上面的应用,发现适合你的需求,那么最重要的来了:兼容性问题。所以第二步,你需要做的是把上面的DEMO运行在你需要支持的各个手机平台,看是否可个插件都能正常运行。如果有兼容性问题,可以参看源码进行修改,或者发送相关的问题报告给我,我很乐意为大家解决(在下班空闲时间)。 27 | 28 | ### 总结 29 | 1. EasyPlug很多地方参考了其他的开源插件框架,有些代码直接从360的DroidPlugin项目复制过来。在此鸣谢。 30 | 2. EasyPlug有别于其他的开源框架,大而全不是我们追求目标,我们的目的是用尽可能少的代码实现基础的框架能力,去除一些对插件核心无关的代码,不使用复杂的设计模块,降低刚接触插件开发的朋友入门的难度。当你掌握这些核心的知识并能灵活运用时,在框架中添加对其他的组件支持并不是难事。 31 | 3. 不要迷信插件框架,插件框架虽然解决了很多现实的业务问题,可以实现模块化开发,极大的提高了开发协同效率。但所有的Android插件框架都或多或少的使用反射(或者HOOK),如果过于依赖它,把所有Android功能都希望在插件中实现,那么势必要承担更大的风险,Android SDK的每次更新都可能威胁到你的框架。这个度的取舍问题需要各自去把握了。 32 | 4. 如果大家在使用中遇到问题,可以在Github上发起讨论。 33 | 34 | 35 | >对Android开发和面试感兴趣的话可以关注我的微信公众号:Android面试启示录 36 | 37 | ![](https://github.com/goeasyway/SimpleCoverflow/blob/master/image/goeasyway.jpg) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "net.goeasyway.easydemo" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:cardview-v7:23.1.1' 25 | compile 'com.android.support:recyclerview-v7:23.1.1' 26 | compile 'com.android.support:design:23.1.1' 27 | 28 | compile project(':easyand') 29 | } 30 | -------------------------------------------------------------------------------- /app/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 E:\work\sdk_20150801\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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/net/goeasyway/easydemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package net.goeasyway.easydemo; 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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/assets/bundles/AmapDemo.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goeasyway/EasyPlug/de76a47116cd7525e825ee3368c92c676ad18471/app/src/main/assets/bundles/AmapDemo.apk -------------------------------------------------------------------------------- /app/src/main/assets/bundles/bundle1-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goeasyway/EasyPlug/de76a47116cd7525e825ee3368c92c676ad18471/app/src/main/assets/bundles/bundle1-debug.apk -------------------------------------------------------------------------------- /app/src/main/java/net/goeasyway/easydemo/App.java: -------------------------------------------------------------------------------- 1 | package net.goeasyway.easydemo; 2 | 3 | import net.goeasyway.easyand.bundle.BundleFrameworkApp; 4 | import net.goeasyway.easyand.bundlemananger.BundleManager; 5 | 6 | /** 7 | * Created by goeasyway.net on 2016/2/5. 8 | */ 9 | public class App extends BundleFrameworkApp { 10 | 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | BundleManager.getInstance().installOrUpgradeAssetsBundles(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/net/goeasyway/easydemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package net.goeasyway.easydemo; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | 10 | import net.goeasyway.easyand.bundle.hook.FrameworkHookHelper; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | Button button = (Button) findViewById(R.id.button); 20 | button.setOnClickListener(new View.OnClickListener() { 21 | @Override 22 | public void onClick(View v) { 23 | Intent intent = new Intent(); 24 | intent.setClassName("com.amap.map3d.demo", "com.amap.map3d.demo.MainActivity"); 25 | startActivity(intent); 26 | } 27 | }); 28 | 29 | Button button2 = (Button) findViewById(R.id.button2); 30 | button2.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | Intent intent = new Intent(); 34 | intent.setClassName("net.goeasyway.bundle1", "net.goeasyway.bundle1.MainActivity"); 35 | startActivity(intent); 36 | } 37 | }); 38 | } 39 | 40 | @Override 41 | protected void attachBaseContext(Context newBase) { 42 | super.attachBaseContext(newBase); 43 | try { 44 | FrameworkHookHelper.hookActivityManagerNative(); 45 | FrameworkHookHelper.hookActivityThreadHandler(); 46 | FrameworkHookHelper.hookPackageManager(); 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 |