├── .gitignore ├── Introduction.md ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── easydone │ │ └── componentizationapp │ │ └── MainActivity.java │ └── res │ ├── layout │ ├── activity_main.xml │ └── content_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 │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── componentizationlibrary ├── .gitignore ├── build.gradle ├── outputs │ └── jar │ │ └── sharedDependency.jar ├── proguard-rules.pro └── src │ └── main │ ├── debug │ └── AndroidManifest.xml │ ├── java │ └── cn │ │ └── easydone │ │ └── componentizationlibrary │ │ └── LibraryActivity.java │ ├── release │ └── AndroidManifest.xml │ └── res │ ├── layout │ └── activity_library.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /Introduction.md: -------------------------------------------------------------------------------- 1 | Android 插件化技术发展到现在其实已经很成熟了,但是相应的问题,如果没有真正地去实践过,根本不了解其中有多少问题,会牵涉到多少技术细节,多少被外人膜拜的外表光鲜的技术大牛都被『插件化』这三个字折磨地死去活来,这对于 Android 整个生态的损害也让人无法忽视。 2 | 3 | 9月24日的 MDCC ,冯森林老师提出了一个很有意思的思路『组件化』。 4 | 5 | 我们首先要想一下,我们做插件化的目的是什么? 6 | 7 | - 为了满足产品随时上线的需求? 8 | - 为了修复因为我们对自己要求不严格而写出来的 bug ? 9 | - 为了向人炫耀自己的技术实力? 10 | 11 | 很抱歉,如果是为了这些目的,那就真的太对不起自己是『开发者』这个如此高逼格的身份了。 12 | 13 | 做插件化真正的目的:是为了去适应并行开发,是为了解耦各个模块,是为了避免模块之间的交叉依赖,是为了加快编译速度,从而提高并行开发效率。 14 | 15 | 明确了这些,我们再来看插件化的结果,每个模块都支持独立运行测试,分为稳定的 release 版本和不稳定的 snapshot 版本,每个模块都高度解耦,没有交叉依赖,不会出现一个模块依赖了另一个模块,其中一个人改了这个模块的代码,对另一个模块造成影响。这时候,我们再看冯老师的『组件化』思想。 16 | 17 | 那么这个『组件化』是什么意思呢?我说下我自己的理解,可能不对,还请指教: 18 | > 通过 gradle 配置的方式,将打 debug 包和 release 包分开。这样会有一个好处,开发一个模块,在 debug 的时候,可以打成一个 apk ,独立运行测试,可以完全独立于整个宿主 APP 的其他所有组件;待到要打 release 包的时候,再把这个模块作为一个 library ,打成 aar ,作为整个宿主 APP 的一部分。而 debug 和 release 的切换都是通过 gradle 配置,可以做到无缝切换。至于模块之间的跳转,可以用别名的方式,而不是用 Activity 和 Fragment 类名。这样所有的模块和宿主 APP 都是完全解耦的,彻底解决了并行开发的可能造成的交叉依赖等问题。 19 | 20 | 按照这个思路,我们再来看看一些其他的细节: 21 | 22 | 1. 在 Android 里有一个比较爽的一点是,作为 library 的时候,aar 里的引用依赖,在宿主 Application 里也有同样的引用依赖,并不会打包两份到宿主 Application 里; 23 | 2. 模块之间的跳转,除了使用别名的方式,我能想到的还有另外一种方式,同样是通过 gradle 脚本,将跳转用到的类打成一个 jar ,作为一个 API 服务提供给其他模块作为编译期依赖(provided)引入; 24 | 3. 各个 library 在 debug 的时候作为 apk ,要独立打包运行测试,这时就需要有一个启动 Activity ,而 library 是不需要的,我的想法是放置两个 AndroidManifest.xml ,使用 sourceSets 分别在 debug 和 release 的时候加载不同的 AndroidManifest.xml 。 25 | 26 | 怎么样?看上去是不是很像插件化 Atlas ?然而这个方案没有任何『黑科技』,不牵涉任何 hook ,跟 Atlas 的区别就是无需关心不同的 Context ,无需再关心类、资源怎么去加载,无需关心 Context 的安全问题,无需关心不同机型的兼容适配...技术成本可能连 Atlas 的十分之一都不到! 27 | 28 | 感兴趣的话,可以看看这个 slide 分享,地址在这儿[from-containerization-to-modularity](http://www.slideshare.net/oasisfeng/from-containerization-to-modularity),我也写了个小 sample 去实现这个想法[ComponentizationApp](GitHub - liangzhitao/ComponentizationApp)。 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Liangzhitao is pleased to support the open source community by making android modularization app available. 2 | 3 | If you have downloaded a copy of the this project, please note that the this repo 4 | is licensed under the BSD 3-Clause License. 5 | 6 | 7 | If you have downloaded a copy of the ComponentizationApp source code from Tencent, please note that ComponentizationApp 8 | source code is licensed under the BSD 3-Clause License, except for the third-party components 9 | listed below which are subject to different license terms. Your integration of ComponentizationApp into your own 10 | projects may require compliance with the BSD 3-Clause License, as well as the other licenses 11 | applicable to the third-party components included within ComponentizationApp. 12 | 13 | A copy of the BSD 3-Clause License is included in this file. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What's this ? 2 | This is a simple sample to display `Componentization App` that proposed by [oasisfeng](https://github.com/oasisfeng) 3 | 4 | Want to know more? See [Here](https://github.com/MDCC2016/Android-Session-Slides/blob/master/02-From.Containerization.To.Modularity.pdf) I have written an article to introduce this sample,you can see this [Introduction](https://github.com/liangzhitao/ComponentizationApp/blob/master/Introduction.md) and this [issue](https://github.com/liangzhitao/ComponentizationApp/issues/1) 5 | 6 | Practice in business project, See here [组件化实践](http://blog.zhaiyifan.cn/2016/10/20/android-new-project-from-0-p11/) 7 | 8 | ## Thanks 9 | [oasisfeng](https://github.com/oasisfeng) 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "24.0.1" 6 | defaultConfig { 7 | applicationId "cn.easydone.componentizationapp" 8 | minSdkVersion 14 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | println isDebug.toBoolean() 23 | provided fileTree(dir: project(':componentizationlibrary').projectDir.absolutePath + '/outputs/jar/', include: 'sharedDependency.jar') 24 | if (!isDebug.toBoolean()) { 25 | compile project(':componentizationlibrary') 26 | } else { 27 | compile 'com.android.support:appcompat-v7:24.2.1' 28 | compile 'com.android.support:design:24.2.1' 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 /Users/liang/Documents/android-sdk-macosx/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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/cn/easydone/componentizationapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cn.easydone.componentizationapp; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.design.widget.Snackbar; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.View; 10 | import android.widget.Button; 11 | 12 | import cn.easydone.componentizationlibrary.LibraryActivity; 13 | 14 | public class MainActivity extends AppCompatActivity { 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 21 | setSupportActionBar(toolbar); 22 | 23 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 24 | fab.setOnClickListener(new View.OnClickListener() { 25 | @Override 26 | public void onClick(View view) { 27 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 28 | .setAction("Action", null).show(); 29 | } 30 | }); 31 | Button button = (Button) findViewById(R.id.button); 32 | button.setOnClickListener(new View.OnClickListener() { 33 | @Override 34 | public void onClick(View v) { 35 | Intent intent = new Intent(MainActivity.this, LibraryActivity.class); 36 | startActivity(intent); 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 |