厦门Android培训
达内厦门Android培训中心

0592-5903858

热门课程

终极组件化框架项目方案详解

  • 时间:2018-12-27 16:30
  • 发布:转载
  • 来源:网络

基本介绍

什么是组件化?

项目发展到一定阶段时,随着需求的增加以及频繁地变更,项目会越来越大,代码变得越来越臃肿,耦合会越来越多,开发效率也会降低,这个时候我们就需要对旧项目进行重构即模块的拆分,官方的说法就是组件化。

为什么需要组件化和组件化带来的好处?

1、现在Android项目中代码量达到一定程度,编译将是一件非常痛苦的事情,一般都需要编译5到6分钟。Android Studio 推出 instant run 由于各种缺陷和限制条件(比如采用热修复tinker)一般情况下是被关闭的。而组件化框架可以使模块单独编译调试,可以有效地减少编译的时间。

2、通过组件化可以更好的进行并行开发,因为我们可以为每一个模块进行单独的版本控制,甚至每一个模块的负责人可以选择自己的设计架构而不影响其他模块的开发,与此同时组件化还可以避免模块之间的交叉依赖,每一个模块的开发人员可以对自己的模块进行独立测试,独立编译和运行,甚至可以实现单独的部署。从而极大的提高了并行开发效率。

组件化的基本框架

基类库的封装

基类库中主要包括开发常用的一些框架。

1、网络请求(多任务下载和上传,采用 Retrofit+RxJava 框架)

2、图片加载(策略模式,Glide 与 Picasso 之间可以切换)

3、通信机制(RxBus)

4、基类 adapter 的封装(支持 item动画、多布局item、下拉和加载更多、item点击事件)

5、基类 RecyclerView 的封装(支持原生风格的下拉加载,item侧滑等)

6、mvp 框架

7、各组件的数据库实体类

8、通用的工具类

9、自定义view(包括对话框,ToolBar布局,圆形图片等view的自定义)

10、dagger 的封装(用于初始化全局的变量和网络请求等配置)

11、其他等等

组件模式和集成模式切换的实现

music组件 下的 build.gradle 文件,其他组件类似。

//控制组件模式和集成模式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 {             minifyEnabledfalse            proguardFilesgetDefaultProguardFile('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 {     compilefileTree(dir:'libs', include: ['*.jar'])    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {         exclude group:'com.android.support', module:'support-annotations'    })    //依赖基类库   compileproject(':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'}

集成模式

1、首先需要在 config.gradle 文件中设置 isAlone = false

ext {     isAlone =false;//false:作为Lib组件存在,true:作为application存在

2、然后 Sync 下。

3、最后选择 app 运行即可。

组件模式

1、首先需要在 config.gradle 文件中设置 isAlone = true

2、然后 Sync 下。

3、最后相应的模块(new、chat、live、music、app)进行运行即可。

第三方开源库和组件版本号的管理

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 路由通信:

https://github.com/alibaba/ARouter

在App工程中,初始化组件通信数据:

privateList<MainItemBean>getDefaultData(){     List<MainItemBean> result =newArrayList<>();     MainItemBean mainItemBean =newMainItemBean();     mainItemBean.setName("校园");     mainItemBean.setPath("/news/main");     mainItemBean.setResId(R.mipmap.ic_launcher);     MainItemBean music=newMainItemBean();     music.setName("音乐");     music.setResId(R.mipmap.ic_launcher);     music.setPath("/music/main");     MainItemBean live =newMainItemBean();     live.setName("直播");     live.setResId(R.mipmap.ic_launcher);     live.setPath("/live/main");     MainItemBean chat =newMainItemBean();     chat.setName("聊天");     chat.setPath("/chat/splash");     chat.setResId(R.mipmap.ic_launcher);     result.add(mainItemBean);     result.add(music);     result.add(live);     result.add(chat);     returnresult; }

然后在设置每个 item 的点击事件时,启动组件界面跳转。

@OverridepublicvoidonItemClick(intposition, View view){     MainItemBean item=mainAdapter.getData(position);     ARouter.getInstance().build(item.getPath()).navigation(); }

每个组件入口界面的设置(比如直播 Live 组件,其它组件类似)

@Route(path ="/live/main")publicclassMainActivityextendsBaseActivity<List<CategoryLiveBean>,MainPresenter>implementsView.OnClickListener{

组件合并时res资源和AndroidManifest配置的问题

我们通过判断组件处于哪种模式来动态设置项目res资源和Manifest、以及代码的位置。以直播组件为例,其它组件类似。

直播组件框架

直播组件的 build.gradle 文件对代码资源等位置的配置

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'        }     } }

组件全局application的实现和数据的初始化

采用类似于 Glide 在 Manifest 初始化配置的方式来初始化各个组件的 Application,以直播组件为例,其它类似。

在 BaseApplication 中,初始化 ApplicationDelegate 代理类

@OverrideprotectedvoidattachBaseContext(Context base){     super.attachBaseContext(base);     applicationDelegate =newApplicationDelegate();     applicationDelegate.attachBaseContext(base);     MultiDex.install(this); }

ApplicationDelegate 内部是怎样的呢?继续看下去

publicclassApplicationDelegateimplementsIAppLife{     privateList<IModuleConfig> list;     privateList<IAppLife> appLifes;     privateList<Application.ActivityLifecycleCallbacks> liferecycleCallbacks;     publicApplicationDelegate(){         appLifes =newArrayList<>();         liferecycleCallbacks =newArrayList<>();     }     @Override    publicvoidattachBaseContext(Context base){         //初始化Manifest文件解析器,用于解析组件在自己的Manifest文件配置的Application       ManifestParser manifestParser =newManifestParser(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    publicvoidonCreate(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    publicvoidonTerminate(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 进行解析,并通过反射生成实例。

publicfinalclassManifestParser{     privatestaticfinalString MODULE_VALUE ="IModuleConfig";     privatefinalContext context;     publicManifestParser(Context context){         this.context = context;     }     publicList<IModuleConfig>parse(){         List<IModuleConfig> modules =newArrayList<>();         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) {             thrownewRuntimeException("Unable to find metadata to parse IModuleConfig", e);         }         returnmodules;     }     //通过类名生成实例   privatestaticIModuleConfigparseModule(String className){         Class<?> clazz;         try{             clazz = Class.forName(className);         }catch(ClassNotFoundException e) {             thrownewIllegalArgumentException("Unable to find IModuleConfig implementation", e);         }         Object module;         try{             module = clazz.newInstance();         }catch(InstantiationException e) {             thrownewRuntimeException("Unable to instantiate IModuleConfig implementation for "+ clazz, e);         }catch(IllegalAccessException e) {             thrownewRuntimeException("Unable to instantiate IModuleConfig implementation for "+ clazz, e);         }         if(!(moduleinstanceofIModuleConfig)) {             thrownewRuntimeException("Expected instanceof IModuleConfig, but found: "+ module);         }         return(IModuleConfig) module;     } }

这样通过以上步骤就可以在 Manifest 文件中配置自己组件的 Application,用于初始化组件内的数据,比如在直播组件中初始化 Dagger 的全局配置

publicclassLiveApplicationimplementsIModuleConfig,IAppLife{     privatestaticMainComponent mainComponent;     @Override    publicvoidinjectAppLifecycle(Context context, List<IAppLife> iAppLifes){         //这里需要把本引用添加到Application的生命周期的回调中,以便实现回调       iAppLifes.add(this);     }     @Override    publicvoidinjectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> lifecycleCallbackses){     }     @Override    publicvoidattachBaseContext(Context base){     }     @Override    publicvoidonCreate(Application application){         //在onCreate方法中对Dagger进行初始化       mainComponent = DaggerMainComponent.builder().mainModule(newMainModule())                               .appComponent(BaseApplication.getAppComponent()).build();     }     @Override    publicvoidonTerminate(Application application){         if(mainComponent !=null) {             mainComponent =null;         }     }     publicstaticMainComponentgetMainComponent(){         returnmainComponent;     } }

组件内网络请求和拦截器的实现

由于每个组件的 BaseUrl 和网络配置等可能不一样,所以每个组件可以在自己配置的 dagger 中的 MainConponent 实现自己的网络请求和拦截器。以直播组件为例,其它类似。

MainComponent

@PerApplication @Component(dependencies = AppComponent.class, modules = MainModule.class)publicinterfaceMainComponent{     publicDaoSessiongetDaoSession();     publicMainRepositoryManagergetMainRepositoryManager(); }

MainModule 代码

publicclassMainModule{     @Provides    @PerApplication    publicMainRepositoryManagerprovideRepositoryManager(@Named("live")Retrofit retrofit, DaoSession daoSession){         returnnewMainRepositoryManager(retrofit, daoSession);     }     @Provides    @Named("live")     @PerApplication    publicRetrofitprovideRetrofit(@Named("live")OkHttpClient okHttpClient,@Nullable Gson gson){         Retrofit.Builder builder=newRetrofit.Builder().baseUrl(LiveUtil.BASE_URL).addCallAdapterFactory(RxJava2CallAdapterFactory.create())                 .addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient);         returnbuilder.build();     }     @Provides    @Named("live")     @PerApplication    publicOkHttpClientprovideOkHttpClient(@Named("live")LiveInterceptor interceptor){         OkHttpClient.Builder builder=newOkHttpClient.Builder();         builder.connectTimeout(10, TimeUnit.SECONDS).readTimeout(10,TimeUnit.SECONDS);         builder.addInterceptor(interceptor);         returnbuilder.build();     }     @Provides    @Named("live")     @PerApplication    publicLiveInterceptorprovideNewsInterceptor(){         returnnewLiveInterceptor();     } }

组件化技术难点

greendao数据库的实现

greendao 数据库初始化代码,在基类库的 NetClientModule.java 中

publicDaoSessionprovideDaoSession(Application application){     DaoMaster.DevOpenHelper devOpenHelper =newDaoMaster.DevOpenHelper(application,"common_library_db",null);     Database database = devOpenHelper.getWritableDb();     DaoMaster master =newDaoMaster(database);     returnmaster.newSession(); }

其中的 DaoMaster 是通过APT生成的,由于 DaoMaster 给全局的组件使用,所以只能将 greendao 数据库放在基类库中,并且各个组件的实体类 bean 的创建也只能在基类库中进行,以分包命名进行区分,如下图。因为如果在组件内创建 bean 会重新生成另一个副本 DaoMaster 并且不能操控其他组件的数据库实体,有很大的局限性。

资源命名冲突

官方说法是在每个 module 的 build.gradle 文件中配置资源文件名前缀

这种方法缺点就是,所有的资源名必须要以指定的字符串(moudle_prefix)做前缀,否则会异常报错,而且这方法只限定xml里面的资源,对图片资源并不起作用,所以图片资源仍然需要手动去修改资源名。

所以不是很推荐使用这种方法来解决资源名冲突。所以只能自己注意点,在创建资源的时候,尽量不让其重复。

resourcePrefix  "moudle_prefix"

butterKnife不能使用的原因

虽然 Butterknife 支持在 lib 中使用,但是条件是用 R2 代替 R ,在组件模式和集成模式的切换中,R2<->R 之间的切换是无法完成转换的,切换一次要改动全身,是非常麻烦的!所以不推荐在组件化中使用 Butterknife。

library重复依赖问题

1、可能大家会认为,每个组件都依赖基类库,基类库 library 不是重复依赖了?其实并不会存在这样的问题,因为在构建APP的过程中Gradle会自动将重复的arr包排除,也就不会存在重复依赖基类库的情况。

2、但是第三方开源库依赖的包可能会与我们自己引用的包重复,所以我们需要将多余的包给排除出去。

基类库(CommonLibrary)中 build.gradle

dependencies {     compilefileTree(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 平台,用于监控异常信息、热修复和应用升级。具体实现:

1、在工程的根目录 build.gradle 配置

buildscript {     repositories {         jcenter()     }     dependencies {         classpath "com.tencent.bugly:tinker-support:1.0.8"     } }

然后在 App 的 build.gradle 进行以下配置

dependencies {     compilefileTree(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){         compileproject(':chat')        compileproject(':music')        compileproject(':news')        compileproject(':live')        apt rootProject.ext.dependencies.arouter_compiler     }else{         compileproject(':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 文件如下:

ddd

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

publicclassAppextendsBaseApplication{     @Override    publicvoidonCreate(){         super.onCreate();         setStrictMode();         // 设置是否开启热更新能力,默认为true       Beta.enableHotfix =true;         // 设置是否自动下载补丁       Beta.canAutoDownloadPatch =true;         // 设置是否提示用户重启       Beta.canNotifyUserRestart =true;         // 设置是否自动合成补丁       Beta.canAutoPatch =true;         /**          *  全量升级状态回调          */        Beta.upgradeStateListener =newUpgradeStateListener() {             @Override            publicvoidonUpgradeFailed(booleanb){             }             @Override            publicvoidonUpgradeSuccess(booleanb){             }             @Override            publicvoidonUpgradeNoVersion(booleanb){                 Toast.makeText(getApplicationContext(),"最新版本", Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonUpgrading(booleanb){                 Toast.makeText(getApplicationContext(),"onUpgrading", Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonDownloadCompleted(booleanb){             }         };         /**          * 补丁回调接口,可以监听补丁接收、下载、合成的回调          */        Beta.betaPatchListener =newBetaPatchListener() {             @Override            publicvoidonPatchReceived(String patchFileUrl){                 Toast.makeText(getApplicationContext(), patchFileUrl, Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonDownloadReceived(longsavedLength,longtotalLength){                 Toast.makeText(getApplicationContext(), String.format(Locale.getDefault(),                         "%s %d%%",                         Beta.strNotificationDownloading,                         (int) (totalLength ==0?0: savedLength *100/ totalLength)), Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonDownloadSuccess(String patchFilePath){                 Toast.makeText(getApplicationContext(), patchFilePath, Toast.LENGTH_SHORT).show();                 // Beta.applyDownloadedPatch();           }             @Override            publicvoidonDownloadFailure(String msg){                 Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonApplySuccess(String msg){                 Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonApplyFailure(String msg){                 Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();             }             @Override            publicvoidonPatchRollback(){                 Toast.makeText(getApplicationContext(),"onPatchRollback", Toast.LENGTH_SHORT).show();             }         };         longstart = System.currentTimeMillis();         // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId,调试时将第三个参数设置为true       Bugly.init(this,"2e5309db50",true);         longend = System.currentTimeMillis();     }     @Override    protectedvoidattachBaseContext(Context base){         super.attachBaseContext(base);         // you must install multiDex whatever tinker is installed!       MultiDex.install(base);         // 安装tinker       Beta.installTinker();     }     @TargetApi(9)     protectedvoidsetStrictMode(){         StrictMode.setThreadPolicy(newStrictMode.ThreadPolicy.Builder().permitAll().build());         StrictMode.setVmPolicy(newStrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());     } }

上一篇:Android 工程师级别划分
下一篇:没有下一篇了

终极组件化框架项目方案详解

Android 工程师级别划分

Android网络框架如何选择

Android Studio引用第三方库总结

选择城市和中心
贵州省

广西省

海南省