热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

前言 本文所讲的组件化案例是基于自己开源的组件化框架项目 github上地址github.com/HelloChenJi… 其中即时通讯(Chat)模块是单独的项目 github上地址github

前言

本文所讲的组件化案例是基于自己开源的组件化框架项目
github上地址github.com/HelloChenJi…
其中即时通讯(Chat)模块是单独的项目
github上地址github.com/HelloChenJi…

1.什么是组件化?

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

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

1、 现在Android项目中代码量达到一定程度,编译将是一件非常痛苦的事情,一般都需要变异5到6分钟。Android studio推出instant run由于各种缺陷和限制条件(比如采用热修复tinker)一般情况下是被关闭的。而组件化框架可以使模块单独编译调试,可以有效地减少编译的时间。
2、通过组件化可以更好的进行并行开发,因为我们可以为每一个模块进行单独的版本控制,甚至每一个模块的负责人可以选择自己的设计架构而不影响其他模块的开发,与此同时组件化还可以避免模块之间的交叉依赖,每一个模块的开发人员可以对自己的模块进行独立测试,独立编译和运行,甚至可以实现单独的部署。从而极大的提高了并行开发效率。

3.组件化的基本框架

3.1组件框架图运行.png

组件模式

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

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

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

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

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"]
}

4.4、组件间通信实现

组件间通信的实现是采用阿里开源的Arouter路由通信。
github地址:github.com/alibaba/ARo…
在App工程中,初始化组件通信数据

private List getDefaultData() {List result&#61;new ArrayList<>();MainItemBean mainItemBean&#61;new MainItemBean();mainItemBean.setName("校园");mainItemBean.setPath("/news/main");mainItemBean.setResId(R.mipmap.ic_launcher);MainItemBean music&#61;new MainItemBean();music.setName("音乐");music.setResId(R.mipmap.ic_launcher);music.setPath("/music/main");MainItemBean live&#61;new MainItemBean();live.setName("直播");live.setResId(R.mipmap.ic_launcher);live.setPath("/live/main");MainItemBean chat&#61;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的点击事件时,启动组件界面跳转。

&#64;Overridepublic void onItemClick(int position, View view) {MainItemBean item&#61;mainAdapter.getData(position);ARouter.getInstance().build(item.getPath()).navigation();}

每个组件入口界面的设置&#xff08;比如直播Live组件&#xff0c;其它组件类似&#xff09;

&#64;Route(path &#61; "/live/main")
public class MainActivity extends BaseActivity, MainPresenter> implements View.OnClickListener {

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

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

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

sourceSets {main {if (rootProject.ext.isAlone) {manifest.srcFile &#39;src/main/module/AndroidManifest.xml&#39;java.srcDirs &#61; [&#39;src/main/java&#39;, &#39;src/main/module/java&#39;]res.srcDirs &#61; [&#39;src/main/res&#39;, &#39;src/main/module/res&#39;]} else {manifest.srcFile &#39;src/main/AndroidManifest.xml&#39;}}}

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

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

在BaseApplication中&#xff0c;初始化ApplicationDelegate代理类

&#64;Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);applicationDelegate &#61; new ApplicationDelegate();applicationDelegate.attachBaseContext(base);MultiDex.install(this);}

ApplicationDelegate内部是怎样的呢&#xff1f;继续看下去

public class ApplicationDelegate implements IAppLife {private List list;private List appLifes;private List liferecycleCallbacks;public ApplicationDelegate() {appLifes &#61; new ArrayList<>();liferecycleCallbacks &#61; new ArrayList<>();}&#64;Overridepublic void attachBaseContext(Context base) {
// 初始化Manifest文件解析器&#xff0c;用于解析组件在自己的Manifest文件配置的ApplicationManifestParser manifestParser &#61; new ManifestParser(base);list &#61; manifestParser.parse();
//解析得到的组件Application列表之后&#xff0c;给每个组件Application注入
context&#xff0c;和Application的生命周期的回调&#xff0c;用于实现application的同步if (list !&#61; null && list.size() > 0) {for (IModuleConfig configModule :list) {configModule.injectAppLifecycle(base, appLifes);configModule.injectActivityLifecycle(base, liferecycleCallbacks);}}if (appLifes !&#61; null && appLifes.size() > 0) {for (IAppLife life :appLifes) {life.attachBaseContext(base);}}}&#64;Overridepublic void onCreate(Application application) {
// 相应调用组件Application代理类的onCreate方法if (appLifes !&#61; null && appLifes.size() > 0) {for (IAppLife life :appLifes) {life.onCreate(application);}}if (liferecycleCallbacks !&#61; null && liferecycleCallbacks.size() > 0) {for (Application.ActivityLifecycleCallbacks life :liferecycleCallbacks) {application.registerActivityLifecycleCallbacks(life);}}}&#64;Overridepublic void onTerminate(Application application) {
// 相应调用组件Application代理类的onTerminate方法if (appLifes !&#61; null && appLifes.size() > 0) {for (IAppLife life :appLifes) {life.onTerminate(application);}}if (liferecycleCallbacks !&#61; null && liferecycleCallbacks.size() > 0) {for (Application.ActivityLifecycleCallbacks life :liferecycleCallbacks) {application.unregisterActivityLifecycleCallbacks(life);}}}
}

组件Manifest中application的全局配置

"com.example.live.LiveApplication"android:value&#61;"IModuleConfig" />

ManifestParser会对其中value为IModuleConfig的meta-data进行解析&#xff0c;并通过反射生成实例。

public final class ManifestParser {private static final String MODULE_VALUE &#61; "IModuleConfig";private final Context context;public ManifestParser(Context context) {this.context &#61; context;}public List parse() {List modules &#61; new ArrayList<>();try {ApplicationInfo appInfo &#61; context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);if (appInfo.metaData !&#61; null) {for (String key : appInfo.metaData.keySet()) {
//会对其中value为IModuleConfig的meta-data进行解析&#xff0c;并通过反射生成实例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 &#61; Class.forName(className);} catch (ClassNotFoundException e) {throw new IllegalArgumentException("Unable to find IModuleConfig implementation", e);}Object module;try {module &#61; clazz.newInstance();} catch (InstantiationException e) {throw new RuntimeException("Unable to instantiate IModuleConfig implementation for " &#43; clazz, e);} catch (IllegalAccessException e) {throw new RuntimeException("Unable to instantiate IModuleConfig implementation for " &#43; clazz, e);}if (!(module instanceof IModuleConfig)) {throw new RuntimeException("Expected instanceof IModuleConfig, but found: " &#43; module);}return (IModuleConfig) module;}

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

public class LiveApplication implements IModuleConfig,IAppLife {private static MainComponent mainComponent;&#64;Overridepublic void injectAppLifecycle(Context context, List iAppLifes) {
// 这里需要把本引用添加到Application的生命周期的回调中&#xff0c;以便实现回调iAppLifes.add(this);}&#64;Overridepublic void injectActivityLifecycle(Context context, List lifecycleCallbackses) {}&#64;Overridepublic void attachBaseContext(Context base) {}&#64;Overridepublic void onCreate(Application application) {
// 在onCreate方法中对Dagger进行初始化mainComponent&#61; DaggerMainComponent.builder().mainModule(new MainModule()).appComponent(BaseApplication.getAppComponent()).build();}&#64;Overridepublic void onTerminate(Application application) {if (mainComponent !&#61; null) {mainComponent &#61; null;}}public static MainComponent getMainComponent() {return mainComponent;}
}

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

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

&#64;PerApplication
&#64;Component(dependencies &#61; AppComponent.class, modules &#61; MainModule.class)
public interface MainComponent {public DaoSession getDaoSession();public MainRepositoryManager getMainRepositoryManager();
}

MainModule代码

&#64;Module
public class MainModule {&#64;Provides&#64;PerApplicationpublic MainRepositoryManager provideRepositoryManager(&#64;Named("live") Retrofit retrofit, DaoSession daoSession) {return new MainRepositoryManager(retrofit, daoSession);}&#64;Provides&#64;Named("live")&#64;PerApplicationpublic Retrofit provideRetrofit(&#64;Named("live") OkHttpClient okHttpClient,&#64;Nullable Gson gson){Retrofit.Builder builder&#61;new Retrofit.Builder().baseUrl(LiveUtil.BASE_URL).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient);return builder.build();}&#64;Provides&#64;Named("live")&#64;PerApplicationpublic OkHttpClient provideOkHttpClient(&#64;Named("live")LiveInterceptor interceptor){OkHttpClient.Builder builder&#61;new OkHttpClient.Builder();builder.connectTimeout(10, TimeUnit.SECONDS).readTimeout(10,TimeUnit.SECONDS);builder.addInterceptor(interceptor);return builder.build();}&#64;Provides&#64;Named("live")&#64;PerApplicationpublic LiveInterceptor provideNewsInterceptor(){return new LiveInterceptor();}
}

8.组件化实现的技术难点

8.1.greendao数据库的实现

greendao数据库初始化代码&#xff0c;在基类库的NetClientModule.java中

public DaoSession provideDaoSession(Application application) {DaoMaster.DevOpenHelper devOpenHelper &#61; new DaoMaster.DevOpenHelper(application, "common_library_db", null);Database database &#61; devOpenHelper.getWritableDb();DaoMaster master &#61; new DaoMaster(database);return master.newSession();}

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

基类库组件实体分包图基类库组件实体分包图

8.2.资源命名冲突

官方说法是在每个module的build.gradle文件中配置资源文件名前缀
这种方法缺点就是&#xff0c;所有的资源名必须要以指定的字符串(moudle_prefix)做前缀&#xff0c;否则会异常报错&#xff0c;而且这方法只限定xml里面的资源&#xff0c;对图片资源并不起作用&#xff0c;所以图片资源仍然需要手动去修改资源名。
所以不是很推荐使用这种方法来解决资源名冲突。所以只能自己注意点&#xff0c;在创建资源的时候&#xff0c;尽量不让其重复。

resourcePrefix "moudle_prefix"

8.3.butterKnife不能使用的原因

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

8.4.library重复依赖问题

1、可能大家会认为&#xff0c;每个组件都依赖基类库&#xff0c;基类库library次不是重复依赖了&#xff1f;其实并不会存在这样的问题&#xff0c;因为在构建APP的过程中Gradle会自动将重复的arr包排除&#xff0c;也就不会存在重复依赖基类库的情况。
2、但是第三方开源库依赖的包可能会与我们自己引用的包重复&#xff0c;所以我们需要将多余的包给排除出去。
基类库(CommonLibrary)中build.gradle

dependencies {compile fileTree(dir: &#39;libs&#39;, include: [&#39;*.jar&#39;])testCompile &#39;junit:junit:4.12&#39;androidTestCompile(&#39;com.android.support.test.espresso:espresso-core:2.2.2&#39;, {exclude group: &#39;com.android.support&#39;, module: &#39;support-annotations&#39;})compile(rootProject.ext.dependencies.appcompatV7) {exclude module: "support-v4"exclude module: "support-annotations"}compile rootProject.ext.dependencies.recycleviewcompile rootProject.ext.dependencies.designcompile(rootProject.ext.dependencies.support_v4) {exclude module: "support-annotations"}compile rootProject.ext.dependencies.annotationscompile(rootProject.ext.dependencies.butterknife) {exclude module: &#39;support-annotations&#39;}compile rootProject.ext.dependencies.rxjava2compile(rootProject.ext.dependencies.rxjava2_android) {exclude module: "rxjava"}compile(rootProject.ext.dependencies.rxlifecycle2) {exclude module: &#39;rxjava&#39;exclude module: &#39;jsr305&#39;}compile(rootProject.ext.dependencies.rxlifecycle2_components) {exclude module: &#39;support-v4&#39;exclude module: &#39;appcompat-v7&#39;exclude module: &#39;support-annotations&#39;exclude module: &#39;rxjava&#39;exclude module: &#39;rxandroid&#39;exclude module: &#39;rxlifecycle&#39;}compile(rootProject.ext.dependencies.retrofit) {exclude module: &#39;okhttp&#39;exclude module: &#39;okio&#39;}compile(rootProject.ext.dependencies.retrofit_converter_gson) {exclude module: &#39;gson&#39;exclude module: &#39;okhttp&#39;exclude module: &#39;okio&#39;exclude module: &#39;retrofit&#39;}compile(rootProject.ext.dependencies.retrofit_adapter_rxjava2) {exclude module: &#39;rxjava&#39;exclude module: &#39;okhttp&#39;exclude module: &#39;retrofit&#39;exclude module: &#39;okio&#39;}compile rootProject.ext.dependencies.greenDaocompile rootProject.ext.dependencies.okhttp3compile rootProject.ext.dependencies.gsoncompile rootProject.ext.dependencies.glidecompile rootProject.ext.dependencies.eventBuscompile rootProject.ext.dependencies.dagger2compile(rootProject.ext.dependencies.rxpermission) {exclude module: &#39;rxjava&#39;}compile rootProject.ext.dependencies.retrofit_converter_scalarsannotationProcessor rootProject.ext.dependencies.dagger2_compilerannotationProcessor rootProject.ext.dependencies.butterknife_compilercompile rootProject.ext.dependencies.butterknifecompile rootProject.ext.dependencies.transformationscompile rootProject.ext.dependencies.arouter_api
}

9.组件化与热修复的无缝连接

本开源项目是基于腾讯的bugly平台&#xff0c;用于监控异常信息、热修复和应用升级。
具体实现&#xff1a;
1、在工程的根目录build.gradle配置

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

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

dependencies {compile fileTree(include: [&#39;*.jar&#39;], dir: &#39;libs&#39;)androidTestCompile(&#39;com.android.support.test.espresso:espresso-core:2.2.2&#39;, {exclude group: &#39;com.android.support&#39;, module: &#39;support-annotations&#39;})if (!rootProject.ext.isAlone) {compile project(&#39;:chat&#39;)compile project(&#39;:music&#39;)compile project(&#39;:news&#39;)compile project(&#39;:live&#39;)apt rootProject.ext.dependencies.arouter_compiler} else {compile project(&#39;:commonlibrary&#39;)}testCompile &#39;junit:junit:4.12&#39;
// 依赖bugly相关SDKcompile &#39;com.tencent.bugly:crashreport_upgrade:1.3.1&#39;compile &#39;com.tencent.bugly:nativecrashreport:latest.release&#39;
}
apply from: &#39;tinker-support.gradle&#39;

然后依赖其中的插件脚本

apply from: &#39;tinker-support.gradle&#39;

其中的tinker-support.gradle文件如下&#xff1a;

apply plugin: &#39;com.tencent.bugly.tinker-support&#39;
def bakPath &#61; file("${buildDir}/bakApk/")
/*** 此处填写每次构建生成的基准包目录*/
def baseApkDir &#61; "app-0831-17-50-44"
/*** 对于插件各参数的详细解析请参考*/
tinkerSupport {// 开启tinker-support插件&#xff0c;默认值trueenable &#61; true// 自动生成tinkerId, 你无须关注tinkerId&#xff0c;默认为falseautoGenerateTinkerId &#61; true// 指定归档目录&#xff0c;默认值当前module的子目录tinkerautoBackupApkDir &#61; "${bakPath}"// 是否启用覆盖tinkerPatch配置功能&#xff0c;默认值false// 开启后tinkerPatch配置不生效&#xff0c;即无需添加tinkerPatchoverrideTinkerPatchConfiguration &#61; true// 编译补丁包时&#xff0c;必需指定基线版本的apk&#xff0c;默认值为空// 如果为空&#xff0c;则表示不是进行补丁包的编译// &#64;{link tinkerPatch.oldApk }baseApk &#61; "${bakPath}/${baseApkDir}/app-release.apk"// 对应tinker插件applyMappingbaseApkProguardMapping &#61; "${bakPath}/${baseApkDir}/app-release-mapping.txt"// 对应tinker插件applyResourceMappingbaseApkResourceMapping &#61; "${bakPath}/${baseApkDir}/app-release-R.txt"// 构建基准包跟补丁包都要修改tinkerId&#xff0c;主要用于区分tinkerId &#61; "1.0.5-base_patch"// 打多渠道补丁时指定目录// buildAllFlavorsDir &#61; "${bakPath}/${baseApkDir}"// 是否使用加固模式&#xff0c;默认为false// isProtectedApp &#61; true// 是否采用反射Application的方式集成&#xff0c;无须改造ApplicationenableProxyApplication &#61; true
}
/*** 一般来说,我们无需对下面的参数做任何的修改* 对于各参数的详细介绍请参考:* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97*/
tinkerPatch {tinkerEnable &#61; trueignoreWarning &#61; falseuseSign &#61; truedex {dexMode &#61; "jar"pattern &#61; ["classes*.dex"]loader &#61; []}lib {pattern &#61; ["lib/*/*.so"]}res {pattern &#61; ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]ignoreChange &#61; []largeModSize &#61; 100}packageConfig {}sevenZip {zipArtifact &#61; "com.tencent.mm:SevenZip:1.1.10"
// path &#61; "/usr/local/bin/7za"}buildConfig {keepDexApply &#61; false
// tinkerId &#61; "base-2.0.1"}
}

然后需要在Manifest配置文件配置如下

"com.tencent.bugly.beta.ui.BetaActivity" android:configChanges&#61;"keyboardHidden|orientation|screenSize|locale"android:theme&#61;"&#64;android:style/Theme.Translucent" />"android.support.v4.content.FileProvider"android:authorities&#61;"${applicationId}.fileProvider"android:exported&#61;"false"android:grantUriPermissions&#61;"true">"android.support.FILE_PROVIDER_PATHS"android:resource&#61;"&#64;xml/provider_paths"/>

最后在Application中初始化bugly

public class App extends BaseApplication {&#64;Overridepublic void onCreate() {super.onCreate();setStrictMode();// 设置是否开启热更新能力&#xff0c;默认为trueBeta.enableHotfix &#61; true;// 设置是否自动下载补丁Beta.canAutoDownloadPatch &#61; true;// 设置是否提示用户重启Beta.canNotifyUserRestart &#61; true;// 设置是否自动合成补丁Beta.canAutoPatch &#61; true;/*** 全量升级状态回调*/Beta.upgradeStateListener &#61; new UpgradeStateListener() {&#64;Overridepublic void onUpgradeFailed(boolean b) {}&#64;Overridepublic void onUpgradeSuccess(boolean b) {}&#64;Overridepublic void onUpgradeNoVersion(boolean b) {Toast.makeText(getApplicationContext(), "最新版本", Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onUpgrading(boolean b) {Toast.makeText(getApplicationContext(), "onUpgrading", Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onDownloadCompleted(boolean b) {}};/*** 补丁回调接口&#xff0c;可以监听补丁接收、下载、合成的回调*/Beta.betaPatchListener &#61; new BetaPatchListener() {&#64;Overridepublic void onPatchReceived(String patchFileUrl) {Toast.makeText(getApplicationContext(), patchFileUrl, Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onDownloadReceived(long savedLength, long totalLength) {Toast.makeText(getApplicationContext(), String.format(Locale.getDefault(),"%s %d%%",Beta.strNotificationDownloading,(int) (totalLength &#61;&#61; 0 ? 0 : savedLength * 100 / totalLength)), Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onDownloadSuccess(String patchFilePath) {Toast.makeText(getApplicationContext(), patchFilePath, Toast.LENGTH_SHORT).show();
// Beta.applyDownloadedPatch();}&#64;Overridepublic void onDownloadFailure(String msg) {Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onApplySuccess(String msg) {Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onApplyFailure(String msg) {Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();}&#64;Overridepublic void onPatchRollback() {Toast.makeText(getApplicationContext(), "onPatchRollback", Toast.LENGTH_SHORT).show();}};long start &#61; System.currentTimeMillis();// 这里实现SDK初始化&#xff0c;appId替换成你的在Bugly平台申请的appId,调试时将第三个参数设置为trueBugly.init(this, "2e5309db50", true);long end &#61; System.currentTimeMillis();}&#64;Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安装tinkerBeta.installTinker();}&#64;TargetApi(9)protected void setStrictMode() {StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());}
}

10.结束语

该组件框架是自己在暑假实习期间做的&#xff0c;由于实习公司的项目过于庞大和复杂&#xff0c;每次编译都需要花费10几分钟&#xff0c;心都碎了&#xff0c;所以才想尝试下组件化框架&#xff0c;摸索了很长时间&#xff0c;最后还是做出来了&#xff0c;大概花费2个多月的时间&#xff0c;由于最近项目上比较忙&#xff0c;所以没什么时间来完善&#xff0c;界面有点简陋&#xff0c;但逻辑基本实现了。欢迎fork and star。
有对组件化框架兴趣的同学可以加本人QQ1981367757&#xff0c;一起探讨技术。
github上地址: github.com/HelloChenJi…


作者&#xff1a;啊哈啊哈哈
链接&#xff1a;https://juejin.im/post/5a1cc83551882503eb4b0334
来源&#xff1a;掘金
著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。

推荐阅读
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了腾讯最近开源的BERT推理模型TurboTransformers,该模型在推理速度上比PyTorch快1~4倍。TurboTransformers采用了分层设计的思想,通过简化问题和加速开发,实现了快速推理能力。同时,文章还探讨了PyTorch在中间层延迟和深度神经网络中存在的问题,并提出了合并计算的解决方案。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
author-avatar
虛情徦噫d_951
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有