diff --git a/CONTRIBUTING_APP.md b/CONTRIBUTING_APP.md new file mode 100644 index 000000000..6f7f0e1d6 --- /dev/null +++ b/CONTRIBUTING_APP.md @@ -0,0 +1,6 @@ +**如果您使用 **MVPArms** 开发过开源的 **APP** 或者 **Demo** ,恳请您将项目地址 **Pull Request** 到这个页面,方便更多的人学习,希望大家在学习 **MVPArms** 获得提升的同时,也能帮助其他兄弟更快的成长,将开源精神传递下去!** + + +* [**DiyCode** : 这是一个基于 MVPArms 搭建的 DiyCode 社区客户端](https://github.com/linsneider/DiyCodeAndroid) +* [**MVPFrames** : 这个项目是将MVPArms和MVPArt框架合并成一个通用型项目,并增加了数据库](https://github.com/DesignQu/MVPFrames) +* [**Ganamrs** : 这是一个干果开源客户端,使用 MVPArms 框架做的一个简单 demo ](https://github.com/lianhuo/Ganamrs) diff --git a/MVPArms.md b/MVPArms.md index 2ecec6111..00d83ea3b 100644 --- a/MVPArms.md +++ b/MVPArms.md @@ -1,5 +1,5 @@ # MVPArms -[ ![Bintray](https://img.shields.io/badge/bintray-v2.1.0-brightgreen.svg) ](https://bintray.com/jessyancoding/maven/MVPArms/2.1.0/link) +[ ![Bintray](https://img.shields.io/badge/bintray-v2.1.5-brightgreen.svg) ](https://bintray.com/jessyancoding/maven/MVPArms/2.1.5/link) [ ![Build Status](https://travis-ci.org/JessYanCoding/MVPArms.svg?branch=master) ](https://travis-ci.org/JessYanCoding/MVPArms) [ ![API](https://img.shields.io/badge/API-15%2B-blue.svg?style=flat-square) ](https://developer.android.com/about/versions/android-4.0.3.html) [ ![License](http://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square) ](http://www.apache.org/licenses/LICENSE-2.0) @@ -23,21 +23,19 @@ ## Notice +* [MVPArms 学习项目](https://github.com/JessYanCoding/MVPArms/blob/master/CONTRIBUTING_APP.md) + * [意见收集](https://github.com/JessYanCoding/MVPArms/issues/40) * [更新日志](https://github.com/JessYanCoding/MVPArms/wiki/UpdateLog) * [常见 issues](https://github.com/JessYanCoding/MVPArms/wiki/Issues) -* [版本更新](https://github.com/JessYanCoding/MVPArms/wiki#1.6) - * [我们为什么要把 Dagger2 ,MVP 以及 Rxjava 引入项目中?](http://www.jianshu.com/p/91c2bb8e6369) * 看了上面的文章,对为什么使用这些技术应该比较了解了,使用这些技术对项目后期的维护和迭代特别是大型项目非常有帮助,但是在开发前期每写一个页面要多写很多 `MVP`,`Dagger2` 的类和接口,这对于开发前期确实比较头疼,现在本框架已经可以通过 [Template](https://github.com/JessYanCoding/MVPArmsTemplate) 自动生成一些 `MVP`,`Dagger2` 的模版代码,现在大家可以非常轻松的使用本框架. -* 使用此框架自带自动适配功能,请参考 [AutoLayout 使用方法](https://github.com/hongyangAndroid/AndroidAutoLayout). - -* 此框架使用 `RxPermissions` 用于权限管理(适配 Android 6.0 ),并提供 PermissionUtil 工具类一行代码实现权限请求. +* 使用此框架自带自动适配功能(可不使用),请参考 [AutoLayout 使用方法](https://github.com/hongyangAndroid/AndroidAutoLayout). * 作为通用框架,本框架不提供与 **UI** 有关的任何第三方库(除了 `AutoLayout` 屏幕适配方案). @@ -69,7 +67,21 @@ 16. [`Glide`此库为本框架默认封装图片加载库,可参照着例子更改为其他的库,Api和`Picasso`差不多,缓存机制比`Picasso`复杂,速度快,适合处理大型图片流,支持gfit,`Fresco`太大了!,在5.0以下优势很大,5.0以上系统默认使用的内存管理和`Fresco`类似.](https://github.com/bumptech/glide) 17. [`LeakCanary`Square出品的专门用来检测`Android`和`Java`的内存泄漏,通过通知栏提示内存泄漏信息.](https://github.com/square/leakcanary) - +## Who is using MVPArms? + +* **MVPArms** 从诞生之初,一直真诚的为开发者做着力所能及的事,从详细的 [**Wiki**文档](https://github.com/JessYanCoding/MVPArmsTemplate) 到高效的[代码生成器](https://github.com/JessYanCoding/MVPArms/wiki),无一不透露着 **MVPArms** 对开发者诚挚的付出和关怀 + +* **MVPArms** 经过长达一年半时间殷勤的耕耘,逐渐的变得成熟且稳定,这不得不归功于 **MVPArms** 大家庭中的每一位成员,一直以来真诚的反馈和建议,在此衷心感谢他们为 **MVPArms** 做出的不可磨灭的贡献 + +* 但是 **MVPArms** 远不止于此,还有更多的路要走,还会继续成长,变得更加强大,现在我们诚挚的邀请您也成为咱们 **MVPArms** 大家庭中的一员 + +**小顶家装 工长端** | **小顶家装 工人端** | **小顶家装 材料端** | **小顶网** | **智播** | +:-------------------------------------------------------------------:|:----------:|:---------------:|:--------:|:--------------:| +[](http://www.dggxdjz.com) | [](http://www.dggxdjz.com) | [](http://www.dggxdjz.com) | [](http://www.dgg.net/appload.htm) | [](http://www.zhibocloud.cn/)| +**天天视频** | **天天直播** | **中斗通航** | **中斗祥云** | **麋鹿旅行** | +[](http://sj.qq.com/myapp/detail.htm?apkName=com.dzwh.ttys) | [](http://www.25pp.com/android/detail_7611392/) | [](https://fir.im/3176) | | [](http://android.myapp.com/myapp/detail.htm?apkName=com.elk.tourist) | +**汇财富** | **觅窝** | **(Internal App ...)** | **(In Progress App ...)** | **(Your App ...)** | +[](http://android.myapp.com/myapp/detail.htm?apkName=com.tahone.client) | [](http://miwo.ai/) | | | | ## Update * Tuesday, 13 June 2017: [**ProgressManager**](https://github.com/JessYanCoding/ProgressManager) diff --git a/MVP_generator_solution b/MVP_generator_solution index 658231867..0b29059d0 100644 --- a/MVP_generator_solution +++ b/MVP_generator_solution @@ -77,7 +77,7 @@ public class ${NAME}Activity extends BaseActivity<${NAME}Presenter> implements $ @Override public void showMessage(@NonNull String message) { checkNotNull(message); - UiUtils.SnackbarText(message); + UiUtils.snackbarText(message); } @Override @@ -149,11 +149,11 @@ import com.jess.arms.mvp.IModel; #parse("File Header.java") public interface ${NAME}Contract { - //对于经常使用的关于UI的方法可以定义到BaseView中,如显示隐藏进度条,和显示文字消息 + //对于经常使用的关于UI的方法可以定义到IView中,如显示隐藏进度条,和显示文字消息 interface View extends IView { } - //Model层定义接口,外部只需关心model返回的数据,无需关心内部细节,及是否使用缓存 + //Model层定义接口,外部只需关心Model返回的数据,无需关心内部细节,即是否使用缓存 interface Model extends IModel{ } @@ -250,7 +250,7 @@ public class ${NAME}Fragment extends BaseFragment<${NAME}Presenter> implements $ @Override public void showMessage(@NonNull String message) { checkNotNull(message); - UiUtils.SnackbarText(message); + UiUtils.snackbarText(message); } @Override diff --git a/README.md b/README.md index c9582227a..66746b43b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # MVPArms -[ ![Bintray](https://img.shields.io/badge/bintray-v2.1.0-brightgreen.svg) ](https://bintray.com/jessyancoding/maven/MVPArms/2.1.0/link) +[ ![Bintray](https://img.shields.io/badge/bintray-v2.1.5-brightgreen.svg) ](https://bintray.com/jessyancoding/maven/MVPArms/2.1.5/link) [ ![Build Status](https://travis-ci.org/JessYanCoding/MVPArms.svg?branch=master) ](https://travis-ci.org/JessYanCoding/MVPArms) [ ![API](https://img.shields.io/badge/API-15%2B-blue.svg?style=flat-square) ](https://developer.android.com/about/versions/android-4.0.3.html) [ ![License](http://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square) ](http://www.apache.org/licenses/LICENSE-2.0) @@ -25,20 +25,18 @@ ## Notice +* [MVPArms Learning Project](https://github.com/JessYanCoding/MVPArms/blob/master/CONTRIBUTING_APP.md) + * [Collection Box](https://github.com/JessYanCoding/MVPArms/issues/40) * [Update Log](https://github.com/JessYanCoding/MVPArms/wiki/UpdateLog) * [Common Issues](https://github.com/JessYanCoding/MVPArms/wiki/Issues) -* [Version Updata](https://github.com/JessYanCoding/MVPArms/wiki#1.6) - * The use of these technologies for the latter part of the project maintenance and iterative, especially large projects is very helpful, but is to develop a pre-write a page to write a lot of `MVP`,` Dagger2` class and interface, which is indeed a headache for the development of pre- Now the framework has been able to [Template](https://github.com/JessYanCoding/MVPArmsTemplate) automatically generate some `MVP`,` Dagger2` template code, and now we can very easily use the framework. * Use this frame comes with automatic adaptation function, please refer to [AndroidAutoLayout](https://github.com/hongyangAndroid/AndroidAutoLayout). -* This framework uses `RxPermissions` for rights management (adaptation Android 6.0), and provides a PermissionUtil tool class line of code to implement the permission request. - * This framework does not provide any third-party libraries associated with the **UI**(except for the [`AndroidAutoLayout`](https://github.com/hongyangAndroid/AndroidAutoLayout) screen adaptation scheme). @@ -61,7 +59,15 @@ 16. [`Glide`](https://github.com/bumptech/glide) 17. [`LeakCanary`](https://github.com/square/leakcanary) +## Who is using MVPArms? +**小顶家装 工长端** | **小顶家装 工人端** | **小顶家装 材料端** | **小顶网** | **智播** | +:-------------------------------------------------------------------:|:----------:|:---------------:|:--------:|:--------------:| +[](http://www.dggxdjz.com) | [](http://www.dggxdjz.com) | [](http://www.dggxdjz.com) | [](http://www.dgg.net/appload.htm) | [](http://www.zhibocloud.cn/)| +**天天视频** | **天天直播** | **中斗通航** | **中斗祥云** | **麋鹿旅行** | +[](http://sj.qq.com/myapp/detail.htm?apkName=com.dzwh.ttys) | [](http://www.25pp.com/android/detail_7611392/) | [](https://fir.im/3176) | | [](http://android.myapp.com/myapp/detail.htm?apkName=com.elk.tourist) | +**汇财富** | **觅窝** | **(Internal App ...)** | **(In Progress App ...)** | **(Your App ...)** | +[](http://android.myapp.com/myapp/detail.htm?apkName=com.tahone.client) | [](http://miwo.ai/) | | | | ## Update * Tuesday, 13 June 2017: [**ProgressManager**](https://github.com/JessYanCoding/ProgressManager) diff --git a/app/build.gradle b/app/build.gradle index 712667d50..db514238d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -70,7 +70,7 @@ dependencies { compile rootProject.ext.dependencies["paginate"] //arms compile project(':arms') -// compile 'me.jessyan:arms:2.1.0' +// compile 'me.jessyan:arms:2.1.5' //test testCompile rootProject.ext.dependencies["junit"] debugCompile rootProject.ext.dependencies["canary-debug"] diff --git a/app/src/main/java/me/jessyan/mvparms/demo/app/GlobalConfiguration.java b/app/src/main/java/me/jessyan/mvparms/demo/app/GlobalConfiguration.java index 6109b98ed..dd6432769 100644 --- a/app/src/main/java/me/jessyan/mvparms/demo/app/GlobalConfiguration.java +++ b/app/src/main/java/me/jessyan/mvparms/demo/app/GlobalConfiguration.java @@ -16,12 +16,11 @@ import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.jess.arms.base.App; -import com.jess.arms.base.delegate.AppDelegate; +import com.jess.arms.base.delegate.AppLifecycles; import com.jess.arms.di.module.GlobalConfigModule; import com.jess.arms.http.GlobalHttpHandler; import com.jess.arms.http.RequestInterceptor; import com.jess.arms.integration.ConfigModule; -import com.jess.arms.integration.IRepositoryManager; import com.jess.arms.utils.UiUtils; import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.RefWatcher; @@ -38,9 +37,6 @@ import me.jessyan.mvparms.demo.BuildConfig; import me.jessyan.mvparms.demo.R; import me.jessyan.mvparms.demo.mvp.model.api.Api; -import me.jessyan.mvparms.demo.mvp.model.api.cache.CommonCache; -import me.jessyan.mvparms.demo.mvp.model.api.service.CommonService; -import me.jessyan.mvparms.demo.mvp.model.api.service.UserService; import me.jessyan.progressmanager.ProgressManager; import okhttp3.Interceptor; import okhttp3.Request; @@ -49,16 +45,28 @@ import timber.log.Timber; /** - * app的全局配置信息在此配置,需要将此实现类声明到AndroidManifest中 + * app 的全局配置信息在此配置,需要将此实现类声明到 AndroidManifest 中 * Created by jess on 12/04/2017 17:25 * Contact with jess.yan.effort@gmail.com */ +public final class GlobalConfiguration implements ConfigModule { +// public static String sDomain = Api.APP_DOMAIN; - -public class GlobalConfiguration implements ConfigModule { @Override public void applyOptions(Context context, GlobalConfigModule.Builder builder) { + if (!BuildConfig.LOG_DEBUG) //Release 时,让框架不再打印 Http 请求和响应的信息 + builder.printHttpLogLevel(RequestInterceptor.Level.NONE); + builder.baseurl(Api.APP_DOMAIN) + //如果 BaseUrl 在 App 启动时不能确定,需要请求服务器接口动态获取,请使用以下代码 + //并且使用 Okhttp (AppComponent中提供) 请求服务器获取到正确的 BaseUrl 后赋值给 GlobalConfiguration.sDomain + //切记整个过程必须在第一次调用 Retrofit 接口之前完成,如果已经调用过 Retrofit 接口,将不能动态切换 BaseUrl +// .baseurl(new BaseUrl() { +// @Override +// public HttpUrl url() { +// return HttpUrl.parse(sDomain); +// } +// }) .globalHttpHandler(new GlobalHttpHandler() {// 这里可以提供一个全局处理Http请求和响应结果的处理类, // 这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取 @Override @@ -79,7 +87,6 @@ public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, return response; } - /* 这里如果发现token过期,可以先请求最新的token,然后在拿新的token放入request里去重新请求 注意在这个回调之前已经调用过proceed,所以这里必须自己去建立网络请求,如使用okhttp使用新的request去请求 create a new request and modify it accordingly using the new token @@ -128,7 +135,7 @@ public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) { .enableComplexMapKeySerialization();//支持将序列化key为object的map,默认只能序列化key为string的map }) .retrofitConfiguration((context1, retrofitBuilder) -> {//这里可以自己自定义配置Retrofit的参数,甚至你可以替换系统配置好的okhttp对象 -// retrofitBuilder.addConverterFactory(FastJsonConverterFactory.create());//比如使用fastjson替代gson + // retrofitBuilder.addConverterFactory(FastJsonConverterFactory.create());//比如使用fastjson替代gson }) .okhttpConfiguration((context1, okhttpBuilder) -> {//这里可以自己自定义配置Okhttp的参数 okhttpBuilder.writeTimeout(10, TimeUnit.SECONDS); @@ -141,20 +148,30 @@ public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) { } @Override - public void registerComponents(Context context, IRepositoryManager repositoryManager) { - repositoryManager.injectRetrofitService(CommonService.class, UserService.class); - repositoryManager.injectCacheService(CommonCache.class); - } + public void injectAppLifecycle(Context context, List lifecycles) { + // AppLifecycles 的所有方法都会在基类Application对应的生命周期中被调用,所以在对应的方法中可以扩展一些自己需要的逻辑 + lifecycles.add(new AppLifecycles() { - @Override - public void injectAppLifecycle(Context context, List lifecycles) { - // AppDelegate.Lifecycle 的所有方法都会在基类Application对应的生命周期中被调用,所以在对应的方法中可以扩展一些自己需要的逻辑 - lifecycles.add(new AppDelegate.Lifecycle() { + @Override + public void attachBaseContext(Context base) { +// MultiDex.install(base); //这里比 onCreate 先执行,常用于 MultiDex 初始化,插件化框架的初始化 + } @Override public void onCreate(Application application) { - if (BuildConfig.LOG_DEBUG) {//Timber日志打印 + if (BuildConfig.LOG_DEBUG) {//Timber初始化 + //Timber 是一个日志框架容器,外部使用统一的Api,内部可以动态的切换成任何日志框架(打印策略)进行日志打印 + //并且支持添加多个日志框架(打印策略),做到外部调用一次 Api,内部却可以做到同时使用多个策略 + //比如添加三个策略,一个打印日志,一个将日志保存本地,一个将日志上传服务器 Timber.plant(new Timber.DebugTree()); + // 如果你想将框架切换为 Logger 来打印日志,请使用下面的代码,如想切换为其他日志框架请根据下面的方式扩展 +// Logger.addLogAdapter(new AndroidLogAdapter()); +// Timber.plant(new Timber.DebugTree() { +// @Override +// protected void log(int priority, String tag, String message, Throwable t) { +// Logger.log(priority, tag, message, t); +// } +// }); } //leakCanary内存泄露检查 ((App) application).getAppComponent().extras().put(RefWatcher.class.getName(), BuildConfig.USE_CANARY ? LeakCanary.install(application) : RefWatcher.DISABLED); @@ -173,32 +190,36 @@ public void injectActivityLifecycle(Context context, List= Build.VERSION_CODES.LOLLIPOP) { - activity.setActionBar((android.widget.Toolbar) activity.findViewById(R.id.toolbar)); - activity.getActionBar().setDisplayShowTitleEnabled(false); - } - } - } - if (activity.findViewById(R.id.toolbar_title) != null) { - ((TextView) activity.findViewById(R.id.toolbar_title)).setText(activity.getTitle()); - } - if (activity.findViewById(R.id.toolbar_back) != null) { - activity.findViewById(R.id.toolbar_back).setOnClickListener(v -> { - activity.onBackPressed(); - }); - } } - @Override public void onActivityStarted(Activity activity) { Timber.w(activity + " - onActivityStarted"); + if (!activity.getIntent().getBooleanExtra("isInitToolbar", false)) { + //由于加强框架的兼容性,故将 setContentView 放到 onActivityCreated 之后,onActivityStarted 之前执行 + //而 findViewById 必须在 Activity setContentView() 后才有效,所以将以下代码从之前的 onActivityCreated 中移动到 onActivityStarted 中执行 + activity.getIntent().putExtra("isInitToolbar", true); + //这里全局给Activity设置toolbar和title,你想象力有多丰富,这里就有多强大,以前放到BaseActivity的操作都可以放到这里 + if (activity.findViewById(R.id.toolbar) != null) { + if (activity instanceof AppCompatActivity) { + ((AppCompatActivity) activity).setSupportActionBar((Toolbar) activity.findViewById(R.id.toolbar)); + ((AppCompatActivity) activity).getSupportActionBar().setDisplayShowTitleEnabled(false); + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.setActionBar((android.widget.Toolbar) activity.findViewById(R.id.toolbar)); + activity.getActionBar().setDisplayShowTitleEnabled(false); + } + } + } + if (activity.findViewById(R.id.toolbar_title) != null) { + ((TextView) activity.findViewById(R.id.toolbar_title)).setText(activity.getTitle()); + } + if (activity.findViewById(R.id.toolbar_back) != null) { + activity.findViewById(R.id.toolbar_back).setOnClickListener(v -> { + activity.onBackPressed(); + }); + } + } } @Override @@ -236,6 +257,7 @@ public void injectFragmentLifecycle(Context context, List 标签,的方式创建 Fragment 请务必在标签中加上 android:id 或者 android:tag 属性,否则 setRetainInstance(true) 无效 // 在 Activity 中绑定少量的 Fragment 建议这样做,如果需要绑定较多的 Fragment 不建议设置此参数,如 ViewPager 需要展示较多 Fragment f.setRetainInstance(true); } @@ -243,7 +265,12 @@ public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstan @Override public void onFragmentDestroyed(FragmentManager fm, Fragment f) { //这里应该是检测 Fragment 而不是 FragmentLifecycleCallbacks 的泄露。 - ((RefWatcher) ((App) f.getActivity().getApplication()).getAppComponent().extras().get(RefWatcher.class.getName())).watch(f); + ((RefWatcher) ((App) f.getActivity() + .getApplication()) + .getAppComponent() + .extras() + .get(RefWatcher.class.getName())) + .watch(f); } }); } diff --git a/app/src/main/java/me/jessyan/mvparms/demo/mvp/contract/UserContract.java b/app/src/main/java/me/jessyan/mvparms/demo/mvp/contract/UserContract.java index daf3c27e8..9117dfc93 100644 --- a/app/src/main/java/me/jessyan/mvparms/demo/mvp/contract/UserContract.java +++ b/app/src/main/java/me/jessyan/mvparms/demo/mvp/contract/UserContract.java @@ -15,7 +15,7 @@ * Contact with jess.yan.effort@gmail.com */ public interface UserContract { - //对于经常使用的关于UI的方法可以定义到BaseView中,如显示隐藏进度条,和显示文字消息 + //对于经常使用的关于UI的方法可以定义到IView中,如显示隐藏进度条,和显示文字消息 interface View extends IView { void setAdapter(DefaultAdapter adapter); void startLoadMore(); @@ -23,7 +23,7 @@ interface View extends IView { //申请权限 RxPermissions getRxPermissions(); } - //Model层定义接口,外部只需关心model返回的数据,无需关心内部细节,及是否使用缓存 + //Model层定义接口,外部只需关心Model返回的数据,无需关心内部细节,即是否使用缓存 interface Model extends IModel{ Observable> getUsers(int lastIdQueried, boolean update); } diff --git a/app/src/main/java/me/jessyan/mvparms/demo/mvp/presenter/UserPresenter.java b/app/src/main/java/me/jessyan/mvparms/demo/mvp/presenter/UserPresenter.java index 2ff8c4e0f..f2f21b0ad 100644 --- a/app/src/main/java/me/jessyan/mvparms/demo/mvp/presenter/UserPresenter.java +++ b/app/src/main/java/me/jessyan/mvparms/demo/mvp/presenter/UserPresenter.java @@ -68,13 +68,13 @@ public void onRequestPermissionFailure() { }, mRootView.getRxPermissions(), mErrorHandler); - if (pullToRefresh) lastUserId = 1;//上拉刷新默认只请求第一页 + if (pullToRefresh) lastUserId = 1;//下拉刷新默认只请求第一页 //关于RxCache缓存库的使用请参考 http://www.jianshu.com/p/b58ef6b0624b - boolean isEvictCache = pullToRefresh;//是否驱逐缓存,为ture即不使用缓存,每次上拉刷新即需要最新数据,则不使用缓存 + boolean isEvictCache = pullToRefresh;//是否驱逐缓存,为ture即不使用缓存,每次下拉刷新即需要最新数据,则不使用缓存 - if (pullToRefresh && isFirst) {//默认在第一次上拉刷新时使用缓存 + if (pullToRefresh && isFirst) {//默认在第一次下拉刷新时使用缓存 isFirst = false; isEvictCache = false; } @@ -84,23 +84,23 @@ public void onRequestPermissionFailure() { .retryWhen(new RetryWithDelay(3, 2))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔 .doOnSubscribe(disposable -> { if (pullToRefresh) - mRootView.showLoading();//显示上拉刷新的进度条 + mRootView.showLoading();//显示下拉刷新的进度条 else - mRootView.startLoadMore();//显示下拉加载更多的进度条 + mRootView.startLoadMore();//显示上拉加载更多的进度条 }).subscribeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread()) .doAfterTerminate(() -> { if (pullToRefresh) - mRootView.hideLoading();//隐藏上拉刷新的进度条 + mRootView.hideLoading();//隐藏下拉刷新的进度条 else - mRootView.endLoadMore();//隐藏下拉加载更多的进度条 + mRootView.endLoadMore();//隐藏上拉加载更多的进度条 }) .compose(RxUtils.bindToLifecycle(mRootView))//使用Rxlifecycle,使Disposable和Activity一起销毁 .subscribe(new ErrorHandleSubscriber>(mErrorHandler) { @Override public void onNext(List users) { lastUserId = users.get(users.size() - 1).getId();//记录最后一个id,用于下一次请求 - if (pullToRefresh) mUsers.clear();//如果是上拉刷新则清空列表 + if (pullToRefresh) mUsers.clear();//如果是下拉刷新则清空列表 preEndIndex = mUsers.size();//更新之前列表总长度,用于确定加载更多的起始位置 mUsers.addAll(users); if (pullToRefresh) diff --git a/arms/build.gradle b/arms/build.gradle index b5745e2a3..d0c1a59fc 100644 --- a/arms/build.gradle +++ b/arms/build.gradle @@ -73,6 +73,7 @@ dependencies { } compile(rootProject.ext.dependencies["rxlifecycle2"]) { exclude module: 'rxjava' + exclude module: 'jsr305' } compile(rootProject.ext.dependencies["rxlifecycle2-components"]) { exclude module: 'support-v4' diff --git a/arms/proguard-rules.pro b/arms/proguard-rules.pro index 667b2ad89..cd7e450de 100644 --- a/arms/proguard-rules.pro +++ b/arms/proguard-rules.pro @@ -102,9 +102,6 @@ # Application classes that will be serialized/deserialized over Gson -keep class com.sunloto.shandong.bean.** { *; } -################umeng############### --keep class com.umeng.** { *; } --keep public class * extends com.umeng.** ################glide############### -keep public class * implements com.bumptech.glide.module.GlideModule @@ -137,15 +134,13 @@ -keep interface com.zhy.autolayout.** { *; } - - ################Rxjava and RxAndroid############### -dontwarn org.mockito.** -dontwarn org.junit.** -dontwarn org.robolectric.** --keep class rx.** { *; } --keep interface rx.** { *; } +-keep class io.reactivex.** { *; } +-keep interface io.reactivex.** { *; } -keepattributes Signature -keepattributes *Annotation* @@ -154,7 +149,7 @@ -keep interface com.squareup.okhttp.** { *; } -dontwarn com.squareup.okhttp.** --dontwarn rx.** +-dontwarn io.reactivex.** -dontwarn retrofit.** -keep class retrofit.** { *; } -keepclasseswithmembers class * { @@ -165,94 +160,62 @@ -dontwarn java.lang.invoke.* --keep class rx.schedulers.Schedulers { +-keep class io.reactivex.schedulers.Schedulers { public static ; } --keep class rx.schedulers.ImmediateScheduler { +-keep class io.reactivex.schedulers.ImmediateScheduler { public ; } --keep class rx.schedulers.TestScheduler { +-keep class io.reactivex.schedulers.TestScheduler { public ; } --keep class rx.schedulers.Schedulers { +-keep class io.reactivex.schedulers.Schedulers { public static ** test(); } --keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { +-keepclassmembers class io.reactivex.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } --keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { +-keepclassmembers class io.reactivex.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { long producerNode; long consumerNode; } --keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { - rx.internal.util.atomic.LinkedQueueNode producerNode; +-keepclassmembers class io.reactivex.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { + io.reactivex.internal.util.atomic.LinkedQueueNode producerNode; } --keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { - rx.internal.util.atomic.LinkedQueueNode consumerNode; +-keepclassmembers class io.reactivex.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { + io.reactivex.internal.util.atomic.LinkedQueueNode consumerNode; } --dontwarn rx.internal.util.unsafe.** - - +-dontwarn io.reactivex.internal.util.unsafe.** -################nineoldandroids############### --keep class com.nineoldandroids.animation.** { *; } --keep interface com.nineoldandroids.animation.** { *; } --keep class com.nineoldandroids.view.** { *; } --keep interface com.nineoldandroids.view.** { *; } - -################epresso############### +################espresso############### -keep class android.support.test.espresso.** { *; } -keep interface android.support.test.espresso.** { *; } -################autobahn############### --keep class de.tavendo.autobahn.** { *; } --keep interface de.tavendo.autobahn.** { *; } - - -################shareSdk############### --keep class cn.sharesdk.** { *; } --keep interface cn.sharesdk.** { *; } - -################crop############### --keep class com.soundcloud.android.** { *; } --keep interface com.soundcloud.android.** { *; } - ################annotation############### -keep class android.support.annotation.** { *; } -keep interface android.support.annotation.** { *; } -################pickerview############### --keep class com.bigkoo.pickerview.** { *; } --keep interface com.bigkoo.pickerview.** { *; } - -################carousellayoutmanager############### --keep class com.azoft.carousellayoutmanager.** { *; } --keep interface com.azoft.carousellayoutmanager.** { *; } - -################messagepack############### --keep class org.** { *; } --keep interface org.** { *; } +################RxLifeCycle################# +-keep class com.trello.rxlifecycle2.** { *; } +-keep interface com.trello.rxlifecycle2.** { *; } -################javassist############### --keep class javassist.** { *; } --keep interface javassist.** { *; } -################RxLifeCycle################# --keep class com.trello.rxlifecycle.** { *; } --keep interface com.trello.rxlifecycle.** { *; } +################RxPermissions################# +-keep class com.tbruyelle.rxpermissions2.** { *; } +-keep interface com.tbruyelle.rxpermissions2.** { *; } ################RxCache################# --dontwarn io.rx_cache.internal.** --keep class io.rx_cache.internal.Record { *; } --keep class io.rx_cache.Source { *; } +-dontwarn io.rx_cache2.internal.** +-keep class io.rx_cache2.internal.Record { *; } +-keep class io.rx_cache2.Source { *; } -keep class io.victoralbertos.jolyglot.** { *; } -keep interface io.victoralbertos.jolyglot.** { *; } diff --git a/arms/src/main/java/com/jess/arms/base/BaseActivity.java b/arms/src/main/java/com/jess/arms/base/BaseActivity.java index c8ceb6203..22a3e7377 100644 --- a/arms/src/main/java/com/jess/arms/base/BaseActivity.java +++ b/arms/src/main/java/com/jess/arms/base/BaseActivity.java @@ -12,6 +12,9 @@ import javax.inject.Inject; +import butterknife.ButterKnife; +import butterknife.Unbinder; + import static com.jess.arms.utils.ThirdViewUtil.convertAutoView; /** @@ -20,6 +23,7 @@ */ public abstract class BaseActivity

extends RxAppCompatActivity implements IActivity { protected final String TAG = this.getClass().getSimpleName(); + private Unbinder mUnbinder; @Inject protected P mPresenter; @@ -32,13 +36,27 @@ public View onCreateView(String name, Context context, AttributeSet attrs) { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + try { + int layoutResID = initView(savedInstanceState); + if (layoutResID != 0) {//如果initView返回0,框架则不会调用setContentView(),当然也不会 Bind ButterKnife + setContentView(layoutResID); + //绑定到butterknife + mUnbinder = ButterKnife.bind(this); + } + } catch (Exception e) { + e.printStackTrace(); + } initData(savedInstanceState); } @Override protected void onDestroy() { super.onDestroy(); - if (mPresenter != null) mPresenter.onDestroy();//释放资源 + if (mUnbinder != null && mUnbinder != Unbinder.EMPTY) + mUnbinder.unbind(); + this.mUnbinder = null; + if (mPresenter != null) + mPresenter.onDestroy();//释放资源 this.mPresenter = null; } diff --git a/arms/src/main/java/com/jess/arms/base/BaseApplication.java b/arms/src/main/java/com/jess/arms/base/BaseApplication.java index 7c7f20c71..7243513c5 100644 --- a/arms/src/main/java/com/jess/arms/base/BaseApplication.java +++ b/arms/src/main/java/com/jess/arms/base/BaseApplication.java @@ -1,8 +1,10 @@ package com.jess.arms.base; import android.app.Application; +import android.content.Context; import com.jess.arms.base.delegate.AppDelegate; +import com.jess.arms.base.delegate.AppLifecycles; import com.jess.arms.di.component.AppComponent; /** @@ -16,14 +18,25 @@ * 请配合官方wiki文档https://github.com/JessYanCoding/MVPArms/wiki,学习本框架 */ public class BaseApplication extends Application implements App { - private AppDelegate mAppDelegate; + private AppLifecycles mAppDelegate; + /** + * 这里会在 {@link BaseApplication#onCreate} 之前被调用,可以做一些较早的初始化 + * 常用于 MultiDex 以及插件化框架的初始化 + * + * @param base + */ + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + this.mAppDelegate = new AppDelegate(base); + this.mAppDelegate.attachBaseContext(base); + } @Override public void onCreate() { super.onCreate(); - this.mAppDelegate = new AppDelegate(this); - this.mAppDelegate.onCreate(); + this.mAppDelegate.onCreate(this); } /** @@ -33,10 +46,9 @@ public void onCreate() { public void onTerminate() { super.onTerminate(); if (mAppDelegate != null) - this.mAppDelegate.onTerminate(); + this.mAppDelegate.onTerminate(this); } - /** * 将AppComponent返回出去,供其它地方使用, AppComponent接口中声明的方法返回的实例,在getAppComponent()拿到对象后都可以直接使用 * @@ -44,7 +56,7 @@ public void onTerminate() { */ @Override public AppComponent getAppComponent() { - return mAppDelegate.getAppComponent(); + return ((App) mAppDelegate).getAppComponent(); } } diff --git a/arms/src/main/java/com/jess/arms/base/delegate/ActivityDelegateImpl.java b/arms/src/main/java/com/jess/arms/base/delegate/ActivityDelegateImpl.java index fd8264635..366a10222 100644 --- a/arms/src/main/java/com/jess/arms/base/delegate/ActivityDelegateImpl.java +++ b/arms/src/main/java/com/jess/arms/base/delegate/ActivityDelegateImpl.java @@ -8,9 +8,6 @@ import org.simple.eventbus.EventBus; -import butterknife.ButterKnife; -import butterknife.Unbinder; - /** * Created by jess on 26/04/2017 20:23 * Contact with jess.yan.effort@gmail.com @@ -19,7 +16,6 @@ public class ActivityDelegateImpl implements ActivityDelegate { private Activity mActivity; private IActivity iActivity; - private Unbinder mUnbinder; public ActivityDelegateImpl(Activity activity) { this.mActivity = activity; @@ -31,15 +27,6 @@ public void onCreate(Bundle savedInstanceState) { if (iActivity.useEventBus())//如果要使用eventbus请将此方法返回true EventBus.getDefault().register(mActivity);//注册到事件主线 iActivity.setupActivityComponent(((App) mActivity.getApplication()).getAppComponent());//依赖注入 - try { - int layoutResID = iActivity.initView(savedInstanceState); - if (layoutResID != 0)//如果initView返回0,框架则不会调用setContentView() - mActivity.setContentView(layoutResID); - } catch (Exception e) { - e.printStackTrace(); - } - //绑定到butterknife - mUnbinder = ButterKnife.bind(mActivity); } @Override @@ -69,10 +56,8 @@ public void onSaveInstanceState(Bundle outState) { @Override public void onDestroy() { - if (mUnbinder != Unbinder.EMPTY) mUnbinder.unbind(); - if (iActivity.useEventBus())//如果要使用eventbus请将此方法返回true + if (iActivity != null && iActivity.useEventBus())//如果要使用eventbus请将此方法返回true EventBus.getDefault().unregister(mActivity); - this.mUnbinder = null; this.iActivity = null; this.mActivity = null; } @@ -90,7 +75,6 @@ public void writeToParcel(Parcel dest, int flags) { protected ActivityDelegateImpl(Parcel in) { this.mActivity = in.readParcelable(Activity.class.getClassLoader()); this.iActivity = in.readParcelable(IActivity.class.getClassLoader()); - this.mUnbinder = in.readParcelable(Unbinder.class.getClassLoader()); } public static final Creator CREATOR = new Creator() { diff --git a/arms/src/main/java/com/jess/arms/base/delegate/AppDelegate.java b/arms/src/main/java/com/jess/arms/base/delegate/AppDelegate.java index 22ee7c37f..e8d9ef1be 100644 --- a/arms/src/main/java/com/jess/arms/base/delegate/AppDelegate.java +++ b/arms/src/main/java/com/jess/arms/base/delegate/AppDelegate.java @@ -2,6 +2,7 @@ import android.app.Application; import android.content.ComponentCallbacks2; +import android.content.Context; import android.content.res.Configuration; import com.jess.arms.base.App; @@ -22,35 +23,41 @@ /** * AppDelegate可以代理Application的生命周期,在对应的生命周期,执行对应的逻辑,因为Java只能单继承 - * 而我的框架要求Application要继承于BaseApplication - * 所以当遇到某些三方库需要继承于它的Application的时候,就只有自定义Application继承于三方库的Application - * 再将BaseApplication的代码复制进去,而现在就不用再复制代码,只用在对应的生命周期调用AppDelegate对应的方法(Application一定要实现APP接口) + * 所以当遇到某些三方库需要继承于它的Application的时候,就只有自定义Application并继承于三方库的Application,这时就不用再继承BaseApplication + * 只用在自定义Application中对应的生命周期调用AppDelegate对应的方法(Application一定要实现APP接口),框架就能照常运行 *

* Created by jess on 24/04/2017 09:44 * Contact with jess.yan.effort@gmail.com */ -public class AppDelegate implements App { +public class AppDelegate implements App, AppLifecycles { private Application mApplication; private AppComponent mAppComponent; @Inject protected ActivityLifecycle mActivityLifecycle; private final List mModules; - private List mAppLifecycles = new ArrayList<>(); + private List mAppLifecycles = new ArrayList<>(); private List mActivityLifecycles = new ArrayList<>(); private ComponentCallbacks2 mComponentCallback; - public AppDelegate(Application application) { - this.mApplication = application; - this.mModules = new ManifestParser(mApplication).parse(); + public AppDelegate(Context context) { + this.mModules = new ManifestParser(context).parse(); for (ConfigModule module : mModules) { - module.injectAppLifecycle(mApplication, mAppLifecycles); - module.injectActivityLifecycle(mApplication, mActivityLifecycles); + module.injectAppLifecycle(context, mAppLifecycles); + module.injectActivityLifecycle(context, mActivityLifecycles); } } + @Override + public void attachBaseContext(Context base) { + for (AppLifecycles lifecycle : mAppLifecycles) { + lifecycle.attachBaseContext(base); + } + } - public void onCreate() { + @Override + public void onCreate(Application application) { + this.mApplication = application; mAppComponent = DaggerAppComponent .builder() .appModule(new AppModule(mApplication))//提供application @@ -67,11 +74,7 @@ public void onCreate() { mApplication.registerActivityLifecycleCallbacks(lifecycle); } - for (ConfigModule module : mModules) { - module.registerComponents(mApplication, mAppComponent.repositoryManager()); - } - - for (Lifecycle lifecycle : mAppLifecycles) { + for (AppLifecycles lifecycle : mAppLifecycles) { lifecycle.onCreate(mApplication); } @@ -81,8 +84,8 @@ public void onCreate() { } - - public void onTerminate() { + @Override + public void onTerminate(Application application) { if (mActivityLifecycle != null) { mApplication.unregisterActivityLifecycleCallbacks(mActivityLifecycle); } @@ -95,7 +98,7 @@ public void onTerminate() { } } if (mAppLifecycles != null && mAppLifecycles.size() > 0) { - for (Lifecycle lifecycle : mAppLifecycles) { + for (AppLifecycles lifecycle : mAppLifecycles) { lifecycle.onTerminate(mApplication); } } @@ -114,7 +117,7 @@ public void onTerminate() { * * @return */ - private GlobalConfigModule getGlobalConfigModule(Application context, List modules) { + private GlobalConfigModule getGlobalConfigModule(Context context, List modules) { GlobalConfigModule.Builder builder = GlobalConfigModule .builder(); @@ -138,12 +141,6 @@ public AppComponent getAppComponent() { } - public interface Lifecycle { - void onCreate(Application application); - - void onTerminate(Application application); - } - private static class AppComponentCallbacks implements ComponentCallbacks2 { private Application mApplication; private AppComponent mAppComponent; diff --git a/arms/src/main/java/com/jess/arms/base/delegate/AppLifecycles.java b/arms/src/main/java/com/jess/arms/base/delegate/AppLifecycles.java new file mode 100644 index 000000000..45d39aa20 --- /dev/null +++ b/arms/src/main/java/com/jess/arms/base/delegate/AppLifecycles.java @@ -0,0 +1,17 @@ +package com.jess.arms.base.delegate; + +import android.app.Application; +import android.content.Context; + +/** + * Created by jess on 18/07/2017 17:43 + * Contact with jess.yan.effort@gmail.com + */ + +public interface AppLifecycles { + void attachBaseContext(Context base); + + void onCreate(Application application); + + void onTerminate(Application application); +} diff --git a/arms/src/main/java/com/jess/arms/base/delegate/FragmentDelegateImpl.java b/arms/src/main/java/com/jess/arms/base/delegate/FragmentDelegateImpl.java index ce1359cab..24d43db53 100644 --- a/arms/src/main/java/com/jess/arms/base/delegate/FragmentDelegateImpl.java +++ b/arms/src/main/java/com/jess/arms/base/delegate/FragmentDelegateImpl.java @@ -97,7 +97,7 @@ public void onDestroyView() { @Override public void onDestroy() { - if (iFragment.useEventBus())//如果要使用eventbus请将此方法返回true + if (iFragment != null && iFragment.useEventBus())//如果要使用eventbus请将此方法返回true EventBus.getDefault().unregister(mFragment);//注册到事件主线 this.mUnbinder = null; this.mFragmentManager = null; diff --git a/arms/src/main/java/com/jess/arms/di/module/ClientModule.java b/arms/src/main/java/com/jess/arms/di/module/ClientModule.java index 1b05a2956..6c95b0b34 100644 --- a/arms/src/main/java/com/jess/arms/di/module/ClientModule.java +++ b/arms/src/main/java/com/jess/arms/di/module/ClientModule.java @@ -4,6 +4,7 @@ import android.content.Context; import android.support.annotation.Nullable; +import com.google.gson.Gson; import com.jess.arms.http.GlobalHttpHandler; import com.jess.arms.http.RequestInterceptor; import com.jess.arms.utils.DataHelper; @@ -49,12 +50,13 @@ public class ClientModule { */ @Singleton @Provides - Retrofit provideRetrofit(Application application, @Nullable RetrofitConfiguration configuration, Retrofit.Builder builder, OkHttpClient client, HttpUrl httpUrl) { + Retrofit provideRetrofit(Application application, @Nullable RetrofitConfiguration configuration, Retrofit.Builder builder, OkHttpClient client + , HttpUrl httpUrl, Gson gson) { builder .baseUrl(httpUrl)//域名 .client(client)//设置okhttp .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//使用rxjava - .addConverterFactory(GsonConverterFactory.create());//使用Gson + .addConverterFactory(GsonConverterFactory.create(gson));//使用Gson if (configuration != null) configuration.configRetrofit(application, builder); return builder.build(); diff --git a/arms/src/main/java/com/jess/arms/di/module/GlobalConfigModule.java b/arms/src/main/java/com/jess/arms/di/module/GlobalConfigModule.java index 42ff00cdc..7b2e766b5 100644 --- a/arms/src/main/java/com/jess/arms/di/module/GlobalConfigModule.java +++ b/arms/src/main/java/com/jess/arms/di/module/GlobalConfigModule.java @@ -4,7 +4,9 @@ import android.support.annotation.Nullable; import android.text.TextUtils; +import com.jess.arms.http.BaseUrl; import com.jess.arms.http.GlobalHttpHandler; +import com.jess.arms.http.RequestInterceptor; import com.jess.arms.utils.DataHelper; import com.jess.arms.widget.imageloader.BaseImageLoaderStrategy; import com.jess.arms.widget.imageloader.glide.GlideImageLoaderStrategy; @@ -27,6 +29,7 @@ @Module public class GlobalConfigModule { private HttpUrl mApiUrl; + private BaseUrl mBaseUrl; private BaseImageLoaderStrategy mLoaderStrategy; private GlobalHttpHandler mHandler; private List mInterceptors; @@ -36,6 +39,7 @@ public class GlobalConfigModule { private ClientModule.OkhttpConfiguration mOkhttpConfiguration; private ClientModule.RxCacheConfiguration mRxCacheConfiguration; private AppModule.GsonConfiguration mGsonConfiguration; + private RequestInterceptor.Level mPrintHttpLogLevel; /** * @author: jess @@ -44,6 +48,7 @@ public class GlobalConfigModule { */ private GlobalConfigModule(Builder builder) { this.mApiUrl = builder.apiUrl; + this.mBaseUrl = builder.baseUrl; this.mLoaderStrategy = builder.loaderStrategy; this.mHandler = builder.handler; this.mInterceptors = builder.interceptors; @@ -53,6 +58,7 @@ private GlobalConfigModule(Builder builder) { this.mOkhttpConfiguration = builder.okhttpConfiguration; this.mRxCacheConfiguration = builder.rxCacheConfiguration; this.mGsonConfiguration = builder.gsonConfiguration; + this.mPrintHttpLogLevel = builder.printHttpLogLevel; } public static Builder builder() { @@ -71,6 +77,12 @@ List provideInterceptors() { @Singleton @Provides HttpUrl provideBaseUrl() { + if (mBaseUrl != null) { + HttpUrl httpUrl = mBaseUrl.url(); + if (httpUrl != null) { + return httpUrl; + } + } return mApiUrl == null ? HttpUrl.parse("https://api.github.com/") : mApiUrl; } @@ -140,9 +152,17 @@ AppModule.GsonConfiguration provideGsonConfiguration() { return mGsonConfiguration; } + @Singleton + @Provides + @Nullable + RequestInterceptor.Level providePrintHttpLogLevel() { + return mPrintHttpLogLevel; + } + public static final class Builder { private HttpUrl apiUrl; + private BaseUrl baseUrl; private BaseImageLoaderStrategy loaderStrategy; private GlobalHttpHandler handler; private List interceptors; @@ -152,15 +172,24 @@ public static final class Builder { private ClientModule.OkhttpConfiguration okhttpConfiguration; private ClientModule.RxCacheConfiguration rxCacheConfiguration; private AppModule.GsonConfiguration gsonConfiguration; + private RequestInterceptor.Level printHttpLogLevel; private Builder() { } - public Builder baseurl(String baseurl) {//基础url - if (TextUtils.isEmpty(baseurl)) { - throw new IllegalArgumentException("baseurl can not be empty"); + public Builder baseurl(String baseUrl) {//基础url + if (TextUtils.isEmpty(baseUrl)) { + throw new IllegalArgumentException("BaseUrl can not be empty"); } - this.apiUrl = HttpUrl.parse(baseurl); + this.apiUrl = HttpUrl.parse(baseUrl); + return this; + } + + public Builder baseurl(BaseUrl baseUrl) { + if (baseUrl == null) { + throw new IllegalArgumentException("BaseUrl can not be null"); + } + this.baseUrl = baseUrl; return this; } @@ -213,6 +242,11 @@ public Builder gsonConfiguration(AppModule.GsonConfiguration gsonConfiguration) return this; } + public Builder printHttpLogLevel(RequestInterceptor.Level printHttpLogLevel) { //是否让框架打印 Http 的请求和响应信息 + if (printHttpLogLevel == null) throw new IllegalArgumentException("printHttpLogLevel == null. Use RequestInterceptor.Level.NONE instead."); + this.printHttpLogLevel = printHttpLogLevel; + return this; + } public GlobalConfigModule build() { return new GlobalConfigModule(this); diff --git a/arms/src/main/java/com/jess/arms/http/BaseUrl.java b/arms/src/main/java/com/jess/arms/http/BaseUrl.java new file mode 100644 index 000000000..d78fea2d6 --- /dev/null +++ b/arms/src/main/java/com/jess/arms/http/BaseUrl.java @@ -0,0 +1,17 @@ +package com.jess.arms.http; + +import okhttp3.HttpUrl; + +/** + * Created by jess on 11/07/2017 14:58 + * Contact with jess.yan.effort@gmail.com + */ + +public interface BaseUrl { + /** + * 针对于 BaseUrl 在 App 启动时不能确定,需要请求服务器接口动态获取的应用场景 + * 在调用 Retrofit 接口之前,使用 Okhttp 或其他方式,请求到正确的 BaseUrl 并通过此方法返回 + * @return + */ + HttpUrl url(); +} diff --git a/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java b/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java index 85f423493..4dec7ad33 100644 --- a/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java +++ b/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java @@ -32,26 +32,42 @@ @Singleton public class RequestInterceptor implements Interceptor { private GlobalHttpHandler mHandler; + private final Level printLevel; + + public enum Level { + NONE, //不打印log + REQUEST, //只打印请求信息 + RESPONSE, //只打印响应信息 + ALL //所有数据全部打印 + } @Inject - public RequestInterceptor(@Nullable GlobalHttpHandler handler) { + public RequestInterceptor(@Nullable GlobalHttpHandler handler, @Nullable Level level) { this.mHandler = handler; + if (level == null) + printLevel = Level.ALL; + else + printLevel = level; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); - boolean hasRequestBody = request.body() != null; + boolean logRequest = printLevel == Level.ALL || (printLevel != Level.NONE && printLevel == Level.REQUEST); - //打印请求信息 - Timber.tag(getTag(request, "Request_Info")).w("Params : 「 %s 」%nConnection : 「 %s 」%nHeaders : %n「 %s 」" - , hasRequestBody ? parseParams(request.newBuilder().build().body()) : "Null" - , chain.connection() - , request.headers()); + if (logRequest) { + boolean hasRequestBody = request.body() != null; + //打印请求信息 + Timber.tag(getTag(request, "Request_Info")).w("Params : 「 %s 」%nConnection : 「 %s 」%nHeaders : %n「 %s 」" + , hasRequestBody ? parseParams(request.newBuilder().build().body()) : "Null" + , chain.connection() + , request.headers()); + } - long t1 = System.nanoTime(); + boolean logResponse = printLevel == Level.ALL || (printLevel != Level.NONE && printLevel == Level.RESPONSE); + long t1 = logResponse ? System.nanoTime() : 0; Response originalResponse; try { originalResponse = chain.proceed(request); @@ -59,16 +75,17 @@ public Response intercept(Chain chain) throws IOException { Timber.w("Http Error: " + e); throw e; } - long t2 = System.nanoTime(); + long t2 = logResponse ? System.nanoTime() : 0; - String bodySize = originalResponse.body().contentLength() != -1 ? originalResponse.body().contentLength() + "-byte" : "unknown-length"; - - //打印响应时间以及响应头 - Timber.tag(getTag(request, "Response_Info")).w("Received response in [ %d-ms ] , [ %s ]%n%s" - , TimeUnit.NANOSECONDS.toMillis(t2 - t1), bodySize, originalResponse.headers()); + if (logResponse) { + String bodySize = originalResponse.body().contentLength() != -1 ? originalResponse.body().contentLength() + "-byte" : "unknown-length"; + //打印响应时间以及响应头 + Timber.tag(getTag(request, "Response_Info")).w("Received response in [ %d-ms ] , [ %s ]%n%s" + , TimeUnit.NANOSECONDS.toMillis(t2 - t1), bodySize, originalResponse.headers()); + } //打印响应结果 - String bodyString = printResult(request, originalResponse); + String bodyString = printResult(request, originalResponse.newBuilder().build(), logResponse); if (mHandler != null)//这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取 return mHandler.onHttpResultResponse(bodyString, chain, originalResponse); @@ -80,35 +97,45 @@ public Response intercept(Chain chain) throws IOException { * 打印响应结果 * * @param request - * @param originalResponse + * @param response + * @param logResponse * @return * @throws IOException */ @Nullable - private String printResult(Request request, Response originalResponse) throws IOException { + private String printResult(Request request, Response response, boolean logResponse) throws IOException { //读取服务器返回的结果 - ResponseBody responseBody = originalResponse.body(); + ResponseBody responseBody = response.body(); String bodyString = null; if (isParseable(responseBody.contentType())) { - BufferedSource source = responseBody.source(); - source.request(Long.MAX_VALUE); // Buffer the entire body. - Buffer buffer = source.buffer(); - - //获取content的压缩类型 - String encoding = originalResponse - .headers() - .get("Content-Encoding"); + try { + BufferedSource source = responseBody.source(); + source.request(Long.MAX_VALUE); // Buffer the entire body. + Buffer buffer = source.buffer(); - Buffer clone = buffer.clone(); + //获取content的压缩类型 + String encoding = response + .headers() + .get("Content-Encoding"); + Buffer clone = buffer.clone(); - //解析response content - bodyString = parseContent(responseBody, encoding, clone); - Timber.tag(getTag(request, "Response_Result")).w(isJson(responseBody.contentType()) ? CharactorHandler.jsonFormat(bodyString) : bodyString); + //解析response content + bodyString = parseContent(responseBody, encoding, clone); + } catch (IOException e) { + e.printStackTrace(); + } + if (logResponse) { + Timber.tag(getTag(request, "Response_Result")).w(isJson(responseBody.contentType()) ? + CharactorHandler.jsonFormat(bodyString) : isXml(responseBody.contentType()) ? + CharactorHandler.xmlFormat(bodyString) : bodyString); + } } else { - Timber.tag(getTag(request, "Response_Result")).w("This result isn't parsed"); + if (logResponse) { + Timber.tag(getTag(request, "Response_Result")).w("This result isn't parsed"); + } } return bodyString; } diff --git a/arms/src/main/java/com/jess/arms/integration/ActivityLifecycle.java b/arms/src/main/java/com/jess/arms/integration/ActivityLifecycle.java index 27aae6209..c719751e9 100644 --- a/arms/src/main/java/com/jess/arms/integration/ActivityLifecycle.java +++ b/arms/src/main/java/com/jess/arms/integration/ActivityLifecycle.java @@ -36,7 +36,7 @@ public class ActivityLifecycle implements Application.ActivityLifecycleCallbacks private AppManager mAppManager; private Application mApplication; private Map mExtras; - private FragmentLifecycle mFragmentLifecycle; + private FragmentManager.FragmentLifecycleCallbacks mFragmentLifecycle; private List mFragmentLifecycles; @Inject diff --git a/arms/src/main/java/com/jess/arms/integration/AppManager.java b/arms/src/main/java/com/jess/arms/integration/AppManager.java index 4b709b682..54f42b712 100644 --- a/arms/src/main/java/com/jess/arms/integration/AppManager.java +++ b/arms/src/main/java/com/jess/arms/integration/AppManager.java @@ -33,7 +33,7 @@ public final class AppManager { protected final String TAG = this.getClass().getSimpleName(); public static final String APPMANAGER_MESSAGE = "appmanager_message"; - public static final String IS_NOT_ADD_ACTIVITY_LIST = "is_add_activity_list";//是否加入到activity的list,管理 + public static final String IS_NOT_ADD_ACTIVITY_LIST = "is_not_add_activity_list";//true 为不需要加入到 Activity 容器进行统一管理,反之亦然 public static final int START_ACTIVITY = 0; public static final int SHOW_SNACKBAR = 1; public static final int KILL_ALL = 2; @@ -155,7 +155,9 @@ public void setCurrentActivity(Activity currentActivity) { * @return */ public Activity getCurrentActivity() { - return mCurrentActivity; + return mCurrentActivity != null ? + mCurrentActivity : mActivityList != null && mActivityList.size() > 0 ? + mActivityList.get(mActivityList.size() - 1) : null; } /** diff --git a/arms/src/main/java/com/jess/arms/integration/ConfigModule.java b/arms/src/main/java/com/jess/arms/integration/ConfigModule.java index b51e49269..ef1fdc153 100644 --- a/arms/src/main/java/com/jess/arms/integration/ConfigModule.java +++ b/arms/src/main/java/com/jess/arms/integration/ConfigModule.java @@ -4,7 +4,7 @@ import android.content.Context; import android.support.v4.app.FragmentManager; -import com.jess.arms.base.delegate.AppDelegate; +import com.jess.arms.base.delegate.AppLifecycles; import com.jess.arms.di.module.GlobalConfigModule; import java.util.List; @@ -24,18 +24,10 @@ public interface ConfigModule { void applyOptions(Context context, GlobalConfigModule.Builder builder); /** - * 使用{@link IRepositoryManager}给框架注入一些网络请求和数据缓存等服务 - * @param context - * @param repositoryManager - */ - void registerComponents(Context context,IRepositoryManager repositoryManager); - - - /** - * 使用{@link AppDelegate.Lifecycle}在Application的生命周期中注入一些操作 + * 使用{@link AppLifecycles}在Application的生命周期中注入一些操作 * @return */ - void injectAppLifecycle(Context context, List lifecycles); + void injectAppLifecycle(Context context, List lifecycles); /** * 使用{@link Application.ActivityLifecycleCallbacks}在Activity的生命周期中注入一些操作 diff --git a/arms/src/main/java/com/jess/arms/integration/IRepositoryManager.java b/arms/src/main/java/com/jess/arms/integration/IRepositoryManager.java index 5b91de70b..5fc8fdb93 100644 --- a/arms/src/main/java/com/jess/arms/integration/IRepositoryManager.java +++ b/arms/src/main/java/com/jess/arms/integration/IRepositoryManager.java @@ -1,7 +1,5 @@ package com.jess.arms.integration; -import android.content.Context; - /** * Created by jess on 17/03/2017 11:15 * Contact with jess.yan.effort@gmail.com @@ -9,20 +7,6 @@ public interface IRepositoryManager { - /** - * 注入RetrofitService,在{@link ConfigModule#registerComponents(Context, IRepositoryManager)}中进行注入 - * @param services - */ - void injectRetrofitService(Class... services); - - - /** - * 注入CacheService,在{@link ConfigModule#registerComponents(Context, IRepositoryManager)}中进行注入 - * @param services - */ - void injectCacheService(Class... services); - - /** * 根据传入的Class获取对应的Retrift service * @@ -41,4 +25,9 @@ public interface IRepositoryManager { */ T obtainCacheService(Class cache); + /** + * 清理所有缓存 + */ + void clearAllCache(); + } diff --git a/arms/src/main/java/com/jess/arms/integration/ManifestParser.java b/arms/src/main/java/com/jess/arms/integration/ManifestParser.java index 7c9589eb5..379dc894d 100644 --- a/arms/src/main/java/com/jess/arms/integration/ManifestParser.java +++ b/arms/src/main/java/com/jess/arms/integration/ManifestParser.java @@ -1,6 +1,5 @@ package com.jess.arms.integration; -import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -17,7 +16,7 @@ public final class ManifestParser { private final Context context; - public ManifestParser(Application context) { + public ManifestParser(Context context) { this.context = context; } diff --git a/arms/src/main/java/com/jess/arms/integration/RepositoryManager.java b/arms/src/main/java/com/jess/arms/integration/RepositoryManager.java index 48ceabb23..918fe7ed0 100644 --- a/arms/src/main/java/com/jess/arms/integration/RepositoryManager.java +++ b/arms/src/main/java/com/jess/arms/integration/RepositoryManager.java @@ -1,62 +1,34 @@ package com.jess.arms.integration; -import android.content.Context; - -import com.jess.arms.utils.Preconditions; - -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; +import dagger.Lazy; import io.rx_cache2.internal.RxCache; import retrofit2.Retrofit; /** * 用来管理网络请求层,以及数据缓存层,以后可能添加数据库请求层 - * 需要在{@link ConfigModule}的实现类中先inject需要的服务 + *

* Created by jess on 13/04/2017 09:52 * Contact with jess.yan.effort@gmail.com */ @Singleton public class RepositoryManager implements IRepositoryManager { - private Retrofit mRetrofit; - private RxCache mRxCache; - private final Map mRetrofitServiceCache = new LinkedHashMap<>(); - private final Map mCacheServiceCache = new LinkedHashMap<>(); + private Lazy mRetrofit; + private Lazy mRxCache; + private final Map mRetrofitServiceCache = new HashMap<>(); + private final Map mCacheServiceCache = new HashMap<>(); @Inject - public RepositoryManager(Retrofit retrofit, RxCache rxCache) { + public RepositoryManager(Lazy retrofit, Lazy rxCache) { this.mRetrofit = retrofit; this.mRxCache = rxCache; } - /** - * 注入RetrofitService,在{@link ConfigModule#registerComponents(Context, IRepositoryManager)}中进行注入 - * @param services - */ - @Override - public void injectRetrofitService(Class... services) { - for (Class service : services) { - if (mRetrofitServiceCache.containsKey(service.getName())) continue; - mRetrofitServiceCache.put(service.getName(), mRetrofit.create(service)); - } - - } - - /** - * 注入CacheService,在{@link ConfigModule#registerComponents(Context, IRepositoryManager)}中进行注入 - * @param services - */ - @Override - public void injectCacheService(Class... services) { - for (Class service : services) { - if (mCacheServiceCache.containsKey(service.getName())) continue; - mCacheServiceCache.put(service.getName(), mRxCache.using(service)); - } - } - /** * 根据传入的Class获取对应的Retrift service * @@ -66,9 +38,15 @@ public void injectCacheService(Class... services) { */ @Override public T obtainRetrofitService(Class service) { - Preconditions.checkState(mRetrofitServiceCache.containsKey(service.getName()) - ,"Unable to find %s,first call injectRetrofitService(%s) in ConfigModule",service.getName(),service.getSimpleName()); - return (T) mRetrofitServiceCache.get(service.getName()); + T retrofitService; + synchronized (mRetrofitServiceCache) { + retrofitService = (T) mRetrofitServiceCache.get(service.getName()); + if (retrofitService == null) { + retrofitService = mRetrofit.get().create(service); + mRetrofitServiceCache.put(service.getName(), retrofitService); + } + } + return retrofitService; } /** @@ -80,8 +58,22 @@ public T obtainRetrofitService(Class service) { */ @Override public T obtainCacheService(Class cache) { - Preconditions.checkState(mCacheServiceCache.containsKey(cache.getName()) - ,"Unable to find %s,first call injectCacheService(%s) in ConfigModule",cache.getName(),cache.getSimpleName()); - return (T) mCacheServiceCache.get(cache.getName()); + T cacheService; + synchronized (mCacheServiceCache) { + cacheService = (T) mCacheServiceCache.get(cache.getName()); + if (cacheService == null) { + cacheService = mRxCache.get().using(cache); + mCacheServiceCache.put(cache.getName(), cacheService); + } + } + return cacheService; + } + + /** + * 清理所有缓存 + */ + @Override + public void clearAllCache() { + mRxCache.get().evictAll(); } } diff --git a/arms/src/main/java/com/jess/arms/utils/CharactorHandler.java b/arms/src/main/java/com/jess/arms/utils/CharactorHandler.java index 2878aad2e..18d464613 100644 --- a/arms/src/main/java/com/jess/arms/utils/CharactorHandler.java +++ b/arms/src/main/java/com/jess/arms/utils/CharactorHandler.java @@ -2,14 +2,25 @@ import android.text.InputFilter; import android.text.Spanned; +import android.text.TextUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.io.StringReader; +import java.io.StringWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + /** * Created by zhiyicx on 2016/3/16. */ @@ -62,23 +73,54 @@ public static String str2HexStr(String str) { /** * json 格式化 - * @param bodyString + * + * @param json * @return */ - public static String jsonFormat(String bodyString) { + public static String jsonFormat(String json) { + if (TextUtils.isEmpty(json)) { + return "Empty/Null json content"; + } String message; try { - if (bodyString.startsWith("{")) { - JSONObject jsonObject = new JSONObject(bodyString); + json = json.trim(); + if (json.startsWith("{")) { + JSONObject jsonObject = new JSONObject(json); message = jsonObject.toString(4); - } else if (bodyString.startsWith("[")) { - JSONArray jsonArray = new JSONArray(bodyString); + } else if (json.startsWith("[")) { + JSONArray jsonArray = new JSONArray(json); message = jsonArray.toString(4); } else { - message = bodyString; + message = json; } } catch (JSONException e) { - message = bodyString; + message = json; + } + return message; + } + + + /** + * xml 格式化 + * + * @param xml + * @return + */ + public static String xmlFormat(String xml) { + if (TextUtils.isEmpty(xml)) { + return "Empty/Null xml content"; + } + String message; + try { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); + } catch (TransformerException e) { + message = xml; } return message; } diff --git a/arms/src/main/java/com/jess/arms/utils/DataHelper.java b/arms/src/main/java/com/jess/arms/utils/DataHelper.java index b0a74ed5b..39629b8d9 100644 --- a/arms/src/main/java/com/jess/arms/utils/DataHelper.java +++ b/arms/src/main/java/com/jess/arms/utils/DataHelper.java @@ -167,7 +167,7 @@ public static File getCacheFile(Context context) { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File file = null; file = context.getExternalCacheDir();//获取系统管理的sd卡缓存文件 - if (file == null) {//如果获取的为空,就是用自己定义的缓存文件夹做缓存路径 + if (file == null) {//如果获取的文件为空,就使用自己定义的缓存文件夹做缓存路径 file = new File(getCacheFilePath(context)); makeDirs(file); } diff --git a/build.gradle b/build.gradle index 90392c3b9..0233dd9ba 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.3.3' //Gradle Android Maven plugin classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' //Gradle Bintray Plugin @@ -19,6 +19,7 @@ allprojects { repositories { jcenter() maven { url "https://jitpack.io" } + maven { url "https://maven.google.com" } } } diff --git a/config.gradle b/config.gradle index 593b92f2a..c31212b59 100644 --- a/config.gradle +++ b/config.gradle @@ -5,17 +5,17 @@ ext { buildToolsVersion : "25.0.3", minSdkVersion : 15, targetSdkVersion : 25, - versionCode : 125, - versionName : "2.1.0" + versionCode : 130, + versionName : "2.1.5" ] version = [ - androidSupportSdkVersion: "25.3.1", + androidSupportSdkVersion: "25.4.0", retrofitSdkVersion : "2.3.0", - dagger2SdkVersion : "2.10", - butterknifeSdkVersion : "8.6.0", + dagger2SdkVersion : "2.11", + butterknifeSdkVersion : "8.7.0", rxlifecycleSdkVersion : "1.0", - rxlifecycle2SdkVersion : "2.0.1", + rxlifecycle2SdkVersion : "2.1.0", espressoSdkVersion : "2.2.2", canarySdkVersion : "1.5.1" ] @@ -34,7 +34,7 @@ ext { "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${version["retrofitSdkVersion"]}", "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava:${version["retrofitSdkVersion"]}", "retrofit-adapter-rxjava2" : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitSdkVersion"]}", - "okhttp3" : "com.squareup.okhttp3:okhttp:3.8.0", + "okhttp3" : "com.squareup.okhttp3:okhttp:3.8.1", "okhttp-urlconnection" : "com.squareup.okhttp:okhttp-urlconnection:2.0.0", "glide" : "com.github.bumptech.glide:glide:3.8.0", "picasso" : "com.squareup.picasso:picasso:2.5.2", @@ -48,7 +48,7 @@ ext { "numberprogressbar" : "com.daimajia.numberprogressbar:library:1.2@aar", "nineoldandroids" : "com.nineoldandroids:library:2.4.0", "paginate" : "com.github.markomilos:paginate:0.5.1", - "vlayout" : "com.alibaba.android:vlayout:1.0.6@aar", + "vlayout" : "com.alibaba.android:vlayout:1.0.9@aar", //rx1 "rxandroid" : "io.reactivex:rxandroid:1.2.1", @@ -63,10 +63,10 @@ ext { //rx2 "rxandroid2" : "io.reactivex.rxjava2:rxandroid:2.0.1", - "rxjava2" : "io.reactivex.rxjava2:rxjava:2.1.0", + "rxjava2" : "io.reactivex.rxjava2:rxjava:2.1.1", "rxlifecycle2" : "com.trello.rxlifecycle2:rxlifecycle:${version["rxlifecycle2SdkVersion"]}", "rxlifecycle2-components" : "com.trello.rxlifecycle2:rxlifecycle-components:${version["rxlifecycle2SdkVersion"]}", - "rxcache2" : "com.github.VictorAlbertos.RxCache:runtime:1.8.0-2.x", + "rxcache2" : "com.github.VictorAlbertos.RxCache:runtime:1.8.1-2.x", "rxpermissions2" : "com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar", "rxerrorhandler2" : "me.jessyan:rxerrorhandler:2.0.2", @@ -75,12 +75,12 @@ ext { "dagger2-compiler" : "com.google.dagger:dagger-compiler:${version["dagger2SdkVersion"]}", "androideventbus" : "org.simple:androideventbus:1.0.5.1", "otto" : "com.squareup:otto:1.3.8", - "gson" : "com.google.code.gson:gson:2.8.0", + "gson" : "com.google.code.gson:gson:2.8.1", "multidex" : "com.android.support:multidex:1.0.1", "javax.annotation" : "javax.annotation:jsr250-api:1.0", "arouter" : "com.alibaba:arouter-api:1.2.1.1", "arouter-compiler" : "com.alibaba:arouter-compiler:1.1.2.1", - "progressmanager" : "me.jessyan:progressmanager:1.2.5", + "progressmanager" : "me.jessyan:progressmanager:+", //test "junit" : "junit:junit:4.12", @@ -91,6 +91,7 @@ ext { "espresso-intents" : "com.android.support.test.espresso:espresso-intents:${version["espressoSdkVersion"]}", "mockito-core" : "org.mockito:mockito-core:1.+", "timber" : "com.jakewharton.timber:timber:4.5.1", + "logger" : "com.orhanobut:logger:2.1.1", "canary-debug" : "com.squareup.leakcanary:leakcanary-android:${version["canarySdkVersion"]}", "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:${version["canarySdkVersion"]}", "umeng-analytics" : "com.umeng.analytics:analytics:6.0.1" diff --git a/image/android_logo.png b/image/android_logo.png new file mode 100644 index 000000000..7cf776bba Binary files /dev/null and b/image/android_logo.png differ diff --git a/image/hui_cai_fu_logo.png b/image/hui_cai_fu_logo.png new file mode 100644 index 000000000..af830c460 Binary files /dev/null and b/image/hui_cai_fu_logo.png differ diff --git a/image/mi_lu_logo.png b/image/mi_lu_logo.png new file mode 100644 index 000000000..55e192e5e Binary files /dev/null and b/image/mi_lu_logo.png differ diff --git a/image/mi_wo_logo.png b/image/mi_wo_logo.png new file mode 100644 index 000000000..2d0ba9a26 Binary files /dev/null and b/image/mi_wo_logo.png differ diff --git a/image/tiantian_live_logo.png b/image/tiantian_live_logo.png new file mode 100644 index 000000000..1e68881c9 Binary files /dev/null and b/image/tiantian_live_logo.png differ diff --git a/image/tiantian_video_logo.png b/image/tiantian_video_logo.png new file mode 100644 index 000000000..6cd986822 Binary files /dev/null and b/image/tiantian_video_logo.png differ diff --git a/image/tong_hang_logo.png b/image/tong_hang_logo.png new file mode 100644 index 000000000..4d012bd68 Binary files /dev/null and b/image/tong_hang_logo.png differ diff --git a/image/top_net_work_logo.png b/image/top_net_work_logo.png new file mode 100644 index 000000000..2deb0a6ab Binary files /dev/null and b/image/top_net_work_logo.png differ diff --git a/image/xiang_yun_logo.png b/image/xiang_yun_logo.png new file mode 100644 index 000000000..0ed3f25bc Binary files /dev/null and b/image/xiang_yun_logo.png differ diff --git a/image/xiaoding_foreman_logo.png b/image/xiaoding_foreman_logo.png new file mode 100644 index 000000000..dfb4c74dc Binary files /dev/null and b/image/xiaoding_foreman_logo.png differ diff --git a/image/xiaoding_material_logo.png b/image/xiaoding_material_logo.png new file mode 100644 index 000000000..437157b39 Binary files /dev/null and b/image/xiaoding_material_logo.png differ diff --git a/image/xiaoding_worker_logo.png b/image/xiaoding_worker_logo.png new file mode 100644 index 000000000..db74b0675 Binary files /dev/null and b/image/xiaoding_worker_logo.png differ diff --git a/image/zhibo_logo.png b/image/zhibo_logo.png new file mode 100644 index 000000000..61cd1ba6d Binary files /dev/null and b/image/zhibo_logo.png differ