本文所講的組件化案例是基於本身開源的組件化框架項目
github上地址github.com/HelloChenJi…
其中即時通信(Chat)模塊是單獨的項目
github上地址github.com/HelloChenJi…java
項目發展到必定階段時,隨着需求的增長以及頻繁地變動,項目會愈來愈大,代碼變得愈來愈臃腫,耦合會愈來愈多,開發效率也會下降,這個時候咱們就須要對舊項目進行重構即模塊的拆分,官方的說法就是組件化。react
一、 如今Android項目中代碼量達到必定程度,編譯將是一件很是痛苦的事情,通常都須要變異5到6分鐘。Android studio推出instant run因爲各類缺陷和限制條件(好比採用熱修復tinker)通常狀況下是被關閉的。而組件化框架可使模塊單獨編譯調試,能夠有效地減小編譯的時間。
二、經過組件化能夠更好的進行並行開發,由於咱們能夠爲每個模塊進行單獨的版本控制,甚至每個模塊的負責人能夠選擇本身的設計架構而不影響其餘模塊的開發,與此同時組件化還能夠避免模塊之間的交叉依賴,每個模塊的開發人員能夠對本身的模塊進行獨立測試,獨立編譯和運行,甚至能夠實現單獨的部署。從而極大的提升了並行開發效率。android
music組件下的build.gradle文件,其餘組件相似。git
//控制組件模式和集成模式
if (rootProject.ext.isAlone) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
if (rootProject.ext.isAlone) {
// 組件模式下設置applicationId
applicationId "com.example.cootek.music"
}
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
if (!rootProject.ext.isAlone) {
// 集成模式下Arouter的配置,用於組件間通訊的實現
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
sourceSets {
main {
//控制兩種模式下的資源和代碼配置狀況
if (rootProject.ext.isAlone) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/module/java']
res.srcDirs = ['src/main/res', 'src/main/module/res']
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
// 依賴基類庫
compile project(':commonlibrary')
//用做顏色選擇器
compile 'com.afollestad.material-dialogs:commons:0.9.1.0'
apt rootProject.ext.dependencies.dagger2_compiler
if (!rootProject.ext.isAlone) {
// 集成模式下須要編譯器生成路由通訊的代碼
apt rootProject.ext.dependencies.arouter_compiler
}
testCompile 'junit:junit:4.12'
}複製代碼
一、首先須要在config,gradle文件中設置isAlone=falsegithub
ext {
isAlone = false;//false:做爲Lib組件存在, true:做爲application存在複製代碼
二、而後Sync 下。
三、最後選擇app運行便可。
數據庫
一、首先須要在config,gradle文件中設置isAlone=trueapi
ext {
isAlone = true;//false:做爲Lib組件存在, true:做爲application存在複製代碼
二、而後Sync 下。
三、最後相應的模塊(new、chat、live、music、app)進行運行便可。bash
config.gradle文件的配置狀況網絡
ext {
isAlone = false;//false:做爲集成模式存在, true:做爲組件模式存在
// 各個組件版本號的統一管理
android = [
compileSdkVersion: 24,
buildToolsVersion: "25.0.2",
minSdkVersion : 16,
targetSdkVersion : 22,
versionCode : 1,
versionName : '1.0.0',
]
libsVersion = [
// 第三方庫版本號的管理
supportLibraryVersion = "25.3.0",
retrofitVersion = "2.1.0",
glideVersion = "3.7.0",
loggerVersion = "1.15",
// eventbusVersion = "3.0.0",
gsonVersion = "2.8.0",
butterknife = "8.8.0",
retrofit = "2.3.0",
rxjava = "2.1.1",
rxjava_android = "2.0.1",
rxlifecycle = "2.1.0",
rxlifecycle_components = "2.1.0",
dagger_compiler = "2.11",
dagger = "2.11",
greenDao = "3.2.2",
arouter_api = "1.2.2",
arouter_compiler = "1.1.3",
transformations = "2.0.2",
rxjava_adapter = "2.3.0",
gson_converter = "2.3.0",
scalars_converter = "2.3.0",
rxpermission = "0.9.4",
eventbus="3.0.0",
support_v4="25.4.0",
okhttp3="3.8.1"
]
// 依賴庫管理
dependencies = [
appcompatV7 : "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion",
design : "com.android.support:design:$rootProject.supportLibraryVersion",
cardview : "com.android.support:cardview-v7:$rootProject.supportLibraryVersion",
palette : "com.android.support:palette-v7:$rootProject.supportLibraryVersion",
recycleview : "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion",
support_v4 : "com.android.support:support-v4:$rootProject.support_v4",
annotations : "com.android.support:support-annotations:$rootProject.supportLibraryVersion",
eventBus : "org.greenrobot:eventbus:$rootProject.eventbus",
glide : "com.github.bumptech.glide:glide:$rootProject.glideVersion",
gson : "com.google.code.gson:gson:$rootProject.gsonVersion",
logger : "com.orhanobut:logger:$rootProject.loggerVersion",
butterknife : "com.jakewharton:butterknife:$rootProject.butterknife",
butterknife_compiler : "com.jakewharton:butterknife-compiler:$rootProject.butterknife",
retrofit : "com.squareup.retrofit2:retrofit:$rootProject.retrofit",
okhttp3 : "com.squareup.okhttp3:okhttp:$rootProject.retrofit",
retrofit_adapter_rxjava2 : "com.squareup.retrofit2:adapter-rxjava2:$rootProject.rxjava_adapter",
retrofit_converter_gson : "com.squareup.retrofit2:converter-gson:$rootProject.gson_converter",
retrofit_converter_scalars: "com.squareup.retrofit2:converter-scalars:$rootProject.scalars_converter",
rxpermission : "com.tbruyelle.rxpermissions2:rxpermissions:$rootProject.rxpermission@aar",
rxjava2 : "io.reactivex.rxjava2:rxjava:$rootProject.rxjava",
rxjava2_android : "io.reactivex.rxjava2:rxandroid:$rootProject.rxjava_android",
rxlifecycle2 : "com.trello.rxlifecycle2:rxlifecycle:$rootProject.rxlifecycle",
rxlifecycle2_components : "com.trello.rxlifecycle2:rxlifecycle-components:$rootProject.rxlifecycle_components",
dagger2_compiler : "com.google.dagger:dagger-compiler:$rootProject.dagger_compiler",
dagger2 : "com.google.dagger:dagger:$rootProject.dagger",
greenDao : "org.greenrobot:greendao:$rootProject.greenDao",
transformations : "jp.wasabeef:glide-transformations:$rootProject.transformations",
//路由通信
arouter_api : "com.alibaba:arouter-api:$rootProject.arouter_api",
arouter_compiler : "com.alibaba:arouter-compiler:$rootProject.arouter_compiler"
]
}複製代碼
組件間通訊的實現是採用阿里開源的Arouter路由通訊。
github地址:github.com/alibaba/ARo…
在App工程中,初始化組件通訊數據架構
private List<MainItemBean> getDefaultData() {
List<MainItemBean> result=new ArrayList<>();
MainItemBean mainItemBean=new MainItemBean();
mainItemBean.setName("校園");
mainItemBean.setPath("/news/main");
mainItemBean.setResId(R.mipmap.ic_launcher);
MainItemBean music=new MainItemBean();
music.setName("音樂");
music.setResId(R.mipmap.ic_launcher);
music.setPath("/music/main");
MainItemBean live=new MainItemBean();
live.setName("直播");
live.setResId(R.mipmap.ic_launcher);
live.setPath("/live/main");
MainItemBean chat=new MainItemBean();
chat.setName("聊天");
chat.setPath("/chat/splash");
chat.setResId(R.mipmap.ic_launcher);
result.add(mainItemBean);
result.add(music);
result.add(live);
result.add(chat);
return result;
}複製代碼
而後在設置每一個item的點擊事件時,啓動組件界面跳轉。
@Override
public void onItemClick(int position, View view) {
MainItemBean item=mainAdapter.getData(position);
ARouter.getInstance().build(item.getPath()).navigation();
}複製代碼
每一個組件入口界面的設置(好比直播Live組件,其它組件相似)
@Route(path = "/live/main")
public class MainActivity extends BaseActivity<List<CategoryLiveBean>, MainPresenter> implements View.OnClickListener {複製代碼
咱們經過判斷組件處於哪一種模式來動態設置項目res資源和Manifest、以及代碼的位置。以直播組件爲例,其它組件相似。
sourceSets {
main {
if (rootProject.ext.isAlone) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/module/java']
res.srcDirs = ['src/main/res', 'src/main/module/res']
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}複製代碼
採用相似於Glide在Manifest初始化配置的方式來初始化各個組件的Application,以直播組件爲例,其它相似。
在BaseApplication中,初始化ApplicationDelegate代理類
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
applicationDelegate = new ApplicationDelegate();
applicationDelegate.attachBaseContext(base);
MultiDex.install(this);
}複製代碼
ApplicationDelegate內部是怎樣的呢?繼續看下去
public class ApplicationDelegate implements IAppLife {
private List<IModuleConfig> list;
private List<IAppLife> appLifes;
private List<Application.ActivityLifecycleCallbacks> liferecycleCallbacks;
public ApplicationDelegate() {
appLifes = new ArrayList<>();
liferecycleCallbacks = new ArrayList<>();
}
@Override
public void attachBaseContext(Context base) {
// 初始化Manifest文件解析器,用於解析組件在本身的Manifest文件配置的Application
ManifestParser manifestParser = new ManifestParser(base);
list = manifestParser.parse();
//解析獲得的組件Application列表以後,給每一個組件Application注入
context,和Application的生命週期的回調,用於實現application的同步
if (list != null && list.size() > 0) {
for (IModuleConfig configModule :
list) {
configModule.injectAppLifecycle(base, appLifes);
configModule.injectActivityLifecycle(base, liferecycleCallbacks);
}
}
if (appLifes != null && appLifes.size() > 0) {
for (IAppLife life :
appLifes) {
life.attachBaseContext(base);
}
}
}
@Override
public void onCreate(Application application) {
// 相應調用組件Application代理類的onCreate方法
if (appLifes != null && appLifes.size() > 0) {
for (IAppLife life :
appLifes) {
life.onCreate(application);
}
}
if (liferecycleCallbacks != null && liferecycleCallbacks.size() > 0) {
for (Application.ActivityLifecycleCallbacks life :
liferecycleCallbacks) {
application.registerActivityLifecycleCallbacks(life);
}
}
}
@Override
public void onTerminate(Application application) {
// 相應調用組件Application代理類的onTerminate方法
if (appLifes != null && appLifes.size() > 0) {
for (IAppLife life :
appLifes) {
life.onTerminate(application);
}
}
if (liferecycleCallbacks != null && liferecycleCallbacks.size() > 0) {
for (Application.ActivityLifecycleCallbacks life :
liferecycleCallbacks) {
application.unregisterActivityLifecycleCallbacks(life);
}
}
}
}複製代碼
組件Manifest中application的全局配置
<meta-data
android:name="com.example.live.LiveApplication"
android:value="IModuleConfig" />複製代碼
ManifestParser會對其中value爲IModuleConfig的meta-data進行解析,並經過反射生成實例。
public final class ManifestParser {
private static final String MODULE_VALUE = "IModuleConfig";
private final Context context;
public ManifestParser(Context context) {
this.context = context;
}
public List<IModuleConfig> parse() {
List<IModuleConfig> modules = new ArrayList<>();
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
context.getPackageName(), PackageManager.GET_META_DATA);
if (appInfo.metaData != null) {
for (String key : appInfo.metaData.keySet()) {
//會對其中value爲IModuleConfig的meta-data進行解析,並經過反射生成實例
if (MODULE_VALUE.equals(appInfo.metaData.get(key))) {
modules.add(parseModule(key));
}
}
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Unable to find metadata to parse IModuleConfig", e);
}
return modules;
}
//經過類名生成實例
private static IModuleConfig parseModule(String className) {
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to find IModuleConfig implementation", e);
}
Object module;
try {
module = clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Unable to instantiate IModuleConfig implementation for " + clazz, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate IModuleConfig implementation for " + clazz, e);
}
if (!(module instanceof IModuleConfig)) {
throw new RuntimeException("Expected instanceof IModuleConfig, but found: " + module);
}
return (IModuleConfig) module;
}複製代碼
這樣經過以上步驟就能夠在Manifest文件中配置本身組件的Application,用於初始化組件內的數據,好比在直播組件中初始化Dagger的全局配置
public class LiveApplication implements IModuleConfig,IAppLife {
private static MainComponent mainComponent;
@Override
public void injectAppLifecycle(Context context, List<IAppLife> iAppLifes) {
// 這裏須要把本引用添加到Application的生命週期的回調中,以便實現回調
iAppLifes.add(this);
}
@Override
public void injectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> lifecycleCallbackses) {
}
@Override
public void attachBaseContext(Context base) {
}
@Override
public void onCreate(Application application) {
// 在onCreate方法中對Dagger進行初始化
mainComponent= DaggerMainComponent.builder().mainModule(new MainModule()).appComponent(BaseApplication.getAppComponent()).build();
}
@Override
public void onTerminate(Application application) {
if (mainComponent != null) {
mainComponent = null;
}
}
public static MainComponent getMainComponent() {
return mainComponent;
}
}複製代碼
因爲每一個組件的BaseUrl和網絡配置等可能不同,因此每一個組件能夠在本身配置的dagger中的 MainConponent實現本身的網絡請求和攔截器。
以直播組件爲例,其它相似。
MainComponent
@PerApplication
@Component(dependencies = AppComponent.class, modules = MainModule.class)
public interface MainComponent {
public DaoSession getDaoSession();
public MainRepositoryManager getMainRepositoryManager();
}複製代碼
MainModule代碼
@Module
public class MainModule {
@Provides
@PerApplication
public MainRepositoryManager provideRepositoryManager(@Named("live") Retrofit retrofit, DaoSession daoSession) {
return new MainRepositoryManager(retrofit, daoSession);
}
@Provides
@Named("live")
@PerApplication
public Retrofit provideRetrofit(@Named("live") OkHttpClient okHttpClient,@Nullable Gson gson){
Retrofit.Builder builder=new Retrofit.Builder().baseUrl(LiveUtil.BASE_URL).addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient);
return builder.build();
}
@Provides
@Named("live")
@PerApplication
public OkHttpClient provideOkHttpClient(@Named("live")LiveInterceptor interceptor){
OkHttpClient.Builder builder=new OkHttpClient.Builder();
builder.connectTimeout(10, TimeUnit.SECONDS).readTimeout(10,TimeUnit.SECONDS);
builder.addInterceptor(interceptor);
return builder.build();
}
@Provides
@Named("live")
@PerApplication
public LiveInterceptor provideNewsInterceptor(){
return new LiveInterceptor();
}
}複製代碼
greendao數據庫初始化代碼,在基類庫的NetClientModule.java中
public DaoSession provideDaoSession(Application application) {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(application, "common_library_db", null);
Database database = devOpenHelper.getWritableDb();
DaoMaster master = new DaoMaster(database);
return master.newSession();
}複製代碼
其中的DaoMaster是經過APT生成的,因爲DaoMaster給全局的組件使用,因此只能將greendao 數據庫放在基類庫中,而且各個組件的實體類bean的建立也只能在基類庫中進行,以分包命名進行區分,以下圖。由於若是在組件內建立bean 會從新生成另外一個副本DaoMaster而且不能操控其餘組件的數據庫實體,有很大的侷限性。
官方說法是在每一個module的build.gradle文件中配置資源文件名前綴
這種方法缺點就是,全部的資源名必需要以指定的字符串(moudle_prefix)作前綴,不然會異常報錯,並且這方法只限定xml裏面的資源,對圖片資源並不起做用,因此圖片資源仍然須要手動去修改資源名。
因此不是很推薦使用這種方法來解決資源名衝突。因此只能本身注意點,在建立資源的時候,儘可能不讓其重複。
resourcePrefix "moudle_prefix"複製代碼
雖然Butterknife支持在lib中使用,可是條件是用 R2 代替 R ,在組件模式和集成模式的切換中,R2<->R之間的切換是沒法完成轉換的,切換一次要改動全身,是很是麻煩的!因此不推薦在組件化中使用Butterknife。
一、可能你們會認爲,每一個組件都依賴基類庫,基類庫library次不是重複依賴了?其實並不會存在這樣的問題,由於在構建APP的過程當中Gradle會自動將重複的arr包排除,也就不會存在重複依賴基類庫的狀況。
二、可是第三方開源庫依賴的包可能會與咱們本身引用的包重複,因此咱們須要將多餘的包給排除出去。
基類庫(CommonLibrary)中build.gradle
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile(rootProject.ext.dependencies.appcompatV7) {
exclude module: "support-v4"
exclude module: "support-annotations"
}
compile rootProject.ext.dependencies.recycleview
compile rootProject.ext.dependencies.design
compile(rootProject.ext.dependencies.support_v4) {
exclude module: "support-annotations"
}
compile rootProject.ext.dependencies.annotations
compile(rootProject.ext.dependencies.butterknife) {
exclude module: 'support-annotations'
}
compile rootProject.ext.dependencies.rxjava2
compile(rootProject.ext.dependencies.rxjava2_android) {
exclude module: "rxjava"
}
compile(rootProject.ext.dependencies.rxlifecycle2) {
exclude module: 'rxjava'
exclude module: 'jsr305'
}
compile(rootProject.ext.dependencies.rxlifecycle2_components) {
exclude module: 'support-v4'
exclude module: 'appcompat-v7'
exclude module: 'support-annotations'
exclude module: 'rxjava'
exclude module: 'rxandroid'
exclude module: 'rxlifecycle'
}
compile(rootProject.ext.dependencies.retrofit) {
exclude module: 'okhttp'
exclude module: 'okio'
}
compile(rootProject.ext.dependencies.retrofit_converter_gson) {
exclude module: 'gson'
exclude module: 'okhttp'
exclude module: 'okio'
exclude module: 'retrofit'
}
compile(rootProject.ext.dependencies.retrofit_adapter_rxjava2) {
exclude module: 'rxjava'
exclude module: 'okhttp'
exclude module: 'retrofit'
exclude module: 'okio'
}
compile rootProject.ext.dependencies.greenDao
compile rootProject.ext.dependencies.okhttp3
compile rootProject.ext.dependencies.gson
compile rootProject.ext.dependencies.glide
compile rootProject.ext.dependencies.eventBus
compile rootProject.ext.dependencies.dagger2
compile(rootProject.ext.dependencies.rxpermission) {
exclude module: 'rxjava'
}
compile rootProject.ext.dependencies.retrofit_converter_scalars
annotationProcessor rootProject.ext.dependencies.dagger2_compiler
annotationProcessor rootProject.ext.dependencies.butterknife_compiler
compile rootProject.ext.dependencies.butterknife
compile rootProject.ext.dependencies.transformations
compile rootProject.ext.dependencies.arouter_api
}複製代碼
本開源項目是基於騰訊的bugly平臺,用於監控異常信息、熱修復和應用升級。
具體實現:
一、在工程的根目錄build.gradle配置
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.tencent.bugly:tinker-support:1.0.8"
}
}複製代碼
而後在App 的build.gradle進行如下配置
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
if (!rootProject.ext.isAlone) {
compile project(':chat')
compile project(':music')
compile project(':news')
compile project(':live')
apt rootProject.ext.dependencies.arouter_compiler
} else {
compile project(':commonlibrary')
}
testCompile 'junit:junit:4.12'
// 依賴bugly相關SDK
compile 'com.tencent.bugly:crashreport_upgrade:1.3.1'
compile 'com.tencent.bugly:nativecrashreport:latest.release'
}
apply from: 'tinker-support.gradle'複製代碼
而後依賴其中的插件腳本
apply from: 'tinker-support.gradle'複製代碼
其中的tinker-support.gradle文件以下:
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此處填寫每次構建生成的基準包目錄
*/
def baseApkDir = "app-0831-17-50-44"
/**
* 對於插件各參數的詳細解析請參考
*/
tinkerSupport {
// 開啓tinker-support插件,默認值true
enable = true
// 自動生成tinkerId, 你無須關注tinkerId,默認爲false
autoGenerateTinkerId = true
// 指定歸檔目錄,默認值當前module的子目錄tinker
autoBackupApkDir = "${bakPath}"
// 是否啓用覆蓋tinkerPatch配置功能,默認值false
// 開啓後tinkerPatch配置不生效,即無需添加tinkerPatch
overrideTinkerPatchConfiguration = true
// 編譯補丁包時,必需指定基線版本的apk,默認值爲空
// 若是爲空,則表示不是進行補丁包的編譯
// @{link tinkerPatch.oldApk }
baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
// 對應tinker插件applyMapping
baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
// 對應tinker插件applyResourceMapping
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"
// 構建基準包跟補丁包都要修改tinkerId,主要用於區分
tinkerId = "1.0.5-base_patch"
// 打多渠道補丁時指定目錄
// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
// 是否使用加固模式,默認爲false
// isProtectedApp = true
// 是否採用反射Application的方式集成,無須改造Application
enableProxyApplication = true
}
/**
* 通常來講,咱們無需對下面的參數作任何的修改
* 對於各參數的詳細介紹請參考:
* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
tinkerPatch {
tinkerEnable = true
ignoreWarning = false
useSign = true
dex {
dexMode = "jar"
pattern = ["classes*.dex"]
loader = []
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = []
largeModSize = 100
}
packageConfig {
}
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
// path = "/usr/local/bin/7za"
}
buildConfig {
keepDexApply = false
// tinkerId = "base-2.0.1"
}
}複製代碼
而後須要在Manifest配置文件配置以下
<activity
android:name="com.tencent.bugly.beta.ui.BetaActivity"
android:configChanges="keyboardHidden|orientation|screenSize|locale"
android:theme="@android:style/Theme.Translucent" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>複製代碼
最後在Application中初始化bugly
public class App extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
setStrictMode();
// 設置是否開啓熱更新能力,默認爲true
Beta.enableHotfix = true;
// 設置是否自動下載補丁
Beta.canAutoDownloadPatch = true;
// 設置是否提示用戶重啓
Beta.canNotifyUserRestart = true;
// 設置是否自動合成補丁
Beta.canAutoPatch = true;
/**
* 全量升級狀態回調
*/
Beta.upgradeStateListener = new UpgradeStateListener() {
@Override
public void onUpgradeFailed(boolean b) {
}
@Override
public void onUpgradeSuccess(boolean b) {
}
@Override
public void onUpgradeNoVersion(boolean b) {
Toast.makeText(getApplicationContext(), "最新版本", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrading(boolean b) {
Toast.makeText(getApplicationContext(), "onUpgrading", Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadCompleted(boolean b) {
}
};
/**
* 補丁回調接口,能夠監聽補丁接收、下載、合成的回調
*/
Beta.betaPatchListener = new BetaPatchListener() {
@Override
public void onPatchReceived(String patchFileUrl) {
Toast.makeText(getApplicationContext(), patchFileUrl, Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadReceived(long savedLength, long totalLength) {
Toast.makeText(getApplicationContext(), String.format(Locale.getDefault(),
"%s %d%%",
Beta.strNotificationDownloading,
(int) (totalLength == 0 ? 0 : savedLength * 100 / totalLength)), Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadSuccess(String patchFilePath) {
Toast.makeText(getApplicationContext(), patchFilePath, Toast.LENGTH_SHORT).show();
// Beta.applyDownloadedPatch();
}
@Override
public void onDownloadFailure(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onApplySuccess(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onApplyFailure(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onPatchRollback() {
Toast.makeText(getApplicationContext(), "onPatchRollback", Toast.LENGTH_SHORT).show();
}
};
long start = System.currentTimeMillis();
// 這裏實現SDK初始化,appId替換成你的在Bugly平臺申請的appId,調試時將第三個參數設置爲true
Bugly.init(this, "2e5309db50", true);
long end = System.currentTimeMillis();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// you must install multiDex whatever tinker is installed!
MultiDex.install(base);
// 安裝tinker
Beta.installTinker();
}
@TargetApi(9)
protected void setStrictMode() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
}複製代碼
MVPArms
github.com/JessYanCodi…
全民直播
github.com/jenly1314/K…
音樂項目
github.com/hefuyicoder…
github.com/aa112901/re…
大象:PHPHub客戶端
github.com/Freelander/…
MvpApp
github.com/Rukey7/MvpA…
CloudReader
github.com/youlookwhat…
很是感謝以上開源項目的做者!謝謝!
該組件框架是本身在暑假實習期間作的,因爲實習公司的項目過於龐大和複雜,每次編譯都須要花費10幾分鐘,心都碎了,因此纔想嘗試下組件化框架,摸索了很長時間,最後仍是作出來了,大概花費2個多月的時間,因爲最近項目上比較忙,因此沒什麼時間來完善,界面有點簡陋,但邏輯基本實現了。歡迎fork and star。
有對組件化框架興趣的同窗能夠加本人QQ1981367757,一塊兒探討技術。
github上地址: github.com/HelloChenJi…