一个MVP+RxJava+Retrofit的干货集中营

2,502 阅读12分钟

欢迎掘金的小伙伴们访问我的博客网站,原文链接:wensibo.top/2017/05/15/… ,未经允许不得转载!

今天想要与大家一起分享的五月份的时候用一个星期开发的一个app——干货集中营客户端,因为已经获得了掘金的专栏所以就把之前的文章发不上来与大家一起分享。
其实网上已经有许多类似的项目,代码家也在他的干货集中营中推荐了几款优秀的作品,我也借鉴了其中的一些,自己开发出一款干货集中营客户端,目前项目已经发布到github,如果你想了解整个工程的具体内容,那么你可以fork或者clone,如果你觉得我做得还可以,那么请你赐给我一个star呗!你的支持会是我努力的动力。

前言

慢慢的已经养成了每篇文章都会来一个前言,也慢慢地将写文章潜移默化的转变成写故事,所以每个故事都会有前言,也都会有结果,这次也不例外。
距离上篇引起热烈反响的文章发布已经过去一个月了,承蒙广大网友对我的支持以及建议,当然作为一个90后,我也很虚心的接受来自各方的吐槽,毕竟我并不优秀,只是一直在优秀的路上努力的奔跑着。下面我想跟大家一起分享一下这一个月里我做了些什么事,以及接下来一段时间的计划和打算。

Have Done List

  • 持续22天在github上出现,看看下面这棵贡献树
    contributions
    contributions
  • 博客浏览量突破35000
    blog
    blog
  • 看了1本好书——《网络是怎样连接的》
  • 研究Retrofit与RxJava的源码(待我葡萄成熟时再把文章放出来吧,现在太嫩了,不好意思让大家看到)
  • 2次担任演讲的主讲人(之后会写一篇文章讲述我的演讲技巧)
  • 看了锤子科技新品发布会(我是罗粉但也是加油,心疼老罗一秒)
  • 努力寻找实习(投了许多简历,但是无一幸免:sob: ,有同学可以推荐一波吗?没有的话我待会再问问)
  • 1个全新的干货集中营客户端app(也是写这篇文章的缘由)

Todo List

  • xposed插件开发(这个闪念是有一天夜里惊醒过来的想法)
  • 继续写好的文章与大家一起分享,不仅仅是技术方面的,还有许多我觉得有用的文章,都会为大家奉上,感谢老铁们的支持
  • 继续看书
  • 继续找实习(呜呜呜~~(>_<)~~,体会到工作难找了,尤其是互联网寒冬)

老铁别废话了
老铁别废话了

好啦,上面讲了一大堆废话终于可以进入正题了,我也很基动,开车咯!

项目介绍

logo
logo

GankClient(又称干货集中营客户端)是一个全新的干货集中营客户端,它获取的是来自干货集中营API的数据,利用全新的Material Design的设计语言展示数据内容。整个项目采用MVP的设计架构,并且大量使用RxJava2,网络框架使用的是Retrofit。我用下面几个装逼的句子来形容一下这个APP:

  • 更快速的加载速度
  • 更流畅的页面体验
  • 更有趣的刷新效果
  • 更精美的网页浏览
  • 更美观的视觉享受

这个只是我的个人感受,大神请轻喷:joy: ,不过还是希望大家能够喜欢这个项目,并且积极的向我pull request以及反馈bug,希望大家多多支持。如果可以的话给个star咯。

预览

谁说不给图的
谁说不给图的

全部效果图来一发

screenshot
screenshot
screenshot
screenshot
screenshot
screenshot

screenshot
screenshot
screenshot
screenshot
screenshot
screenshot

没有gif图都不好意思说话了

gif
gif

下载

酷安基佬群

如果你使用酷安的话,那就使劲点这里,或者在酷安搜索干货集中营,认准我的图标哦

干货集中营
干货集中营

fir.im

如果你没有安装酷安,那么你可以扫描下方的二维码进行下载

QRCode
QRCode

测试

如果你想测试这个项目,那么请手动clone

功能&特色

√表示已经实现的功能,X表示的的是还没实现或者需要完善的功能。(吐槽一下,掘金的MarkDown编辑器还是没有实现一些基本的语法,例如打勾勾)

√实时获取服务器的最新数据,采用CardView的布局以更好的展现数据。
√刷新效果很好玩,真的很精致,感谢Phoenix
√如果你装有Chrome浏览器,并将其设置为默认浏览器,那么你的网页浏览效果就会十分酷炫。感谢Custom-Tabs-Client ! 墙裂推荐Chrome,如果你没有安装Chrome,那也没关系,那就用传统的WebView吧!
√更加统一的过渡动画,相信你会爱上它。
√保存、分享图片与链接,也可以直接使用浏览器打开链接。
√更好看的Material Design设计风格。
√妹子很漂亮哟!


X优化webview的视频播放。
X增加使用系统浏览器的选项。
X修复在主界面中加载数据到2016/4/20左右时数据显示错乱的问题。
X增强分享功能。

分解

终于到了这个纯干货步骤了,别说话先看东西!

如何使用MVP的设计架构

我先展示以下这个项目的结构图,让大家心里有个底

  • http
    • GankRetrofit.java
    • GankRetrofitClient.java
  • mvp
    • model
      • BaseModel.java
    • presenter
      • BasePresenter.java
    • view
      • IBaseView.java
  • ui
    • activity
    • adapter
    • base
    • chromeviews
    • fragment
    • widget

可以看到,整个项目的结构还算比较清晰,整体的架构都是在mvp目录中定下来的,mvp架构分三层,model模型层,用于定义一些数据的类型;presenter呈现者层,用于处理view层的逻辑;view显示层,这里定义的都是接口,真正实现他们的是activity,这些activity只要实现相应的接口,就能够自己复写其中的方法。
当然我这么说你肯定还是一脸懵逼,而且我还必须跟你说,我这个不算最纯粹的MVP模式,最纯粹的写法应该都是面向接口编程的,就是在model,presenter,以及view下都是定义接口,而到了具体的运用场景,就定义出具体的类去实现这些接口。当然了,为了让大家能够更清楚MVP是怎么实现的,下面我会用代码的形式为大家展示,例如我要编写MainActivity的代码,首先已经知道在这个Activity中需要做如下这些事:

  • 初始化组件
  • 加载Fragment
  • FloatingActionButton的点击事件——去到DailyActivity
  • drawer menu的点击事件——去到AboutActivity

为了让Activity专注于界面的工作,而不用去考虑逻辑处理的操作,这个就是所谓的解耦,那么我们可以将逻辑的操作放到presenter中去,而与界面有关的操作就放在view下,于是我们就可以这样写(为了便于大家理解,我将项目中的代码做了适当的修改,如果你想了解更加具体的逻辑,可以仔细看看代码):

  • IBaseView
public interface IBaseView {
        //界面初始化操作
        void init();
}
  • BasePresenter&MainPresenter
/**
* 将presenter的公共操作集成为BasePresenter
*/
public abstract class BasePresenter <T extends IBaseView>{
        protected Disposable disposable;
        protected Context context;
        protected T iView;      //获取到view的对象

        public BasePresenter(Context context, T iView) {
                this.context = context;
                this.iView = iView;
        }

        public void init() {
                iView.init();
        }

        public abstract void release();
        public abstract void initPresenter();
}

/**
* 每一个界面都可以编写自己的Presenter,并指定View的泛型
*/
public class MainPresenter extends BasePresenter<IBaseView> {

        public MainPresenter(Context context, IBaseView iView) {
                super(context, iView);
        }

        @Override
        public void release() {

        }

        public void toDailyActivity() {
                Intent intent = new Intent(context, DailyActivity.class);
                context.startActivity(intent);
        }

        public void toAboutActivity() {
                Intent intent = new Intent(context, AboutActivity.class);
                context.startActivity(intent);
        }

        public void toSettingActivity() {
                Intent intent = new Intent(context, SettingActivity.class);
                context.startActivity(intent);
        }
  • MainActivity
public class MainActivity <MainPresenter> extends AppCompatActivity implements IBaseView{

        @OnClick(R.id.fab_main)
        void fabClick() {
                presenter.toDailyActivity();
        }

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
        }


        @Override
        protected void initPresenter() {
                presenter = new MainPresenter(this, this);
                presenter.init();
        }

        @Override
        public void init() {
                //所有的初始化操作
        }

        @Override
        protected void onDestroy() {
                super.onDestroy();
                presenter.release();
        }

}

大概就是这样一个套路,讲真我这样讲你肯定迷糊,我当初在学这个的时候也是十分凌乱,后来才发现只有自己动手亲自敲代码才能了解整个思路,另外大家在学习MVP的过程中应该擅于画图,不管是在纸上画还是使用思维导图都行,这样可以让你更加宏观的了解整个调用的顺序以及各个类之间的关系,这绝不是你看了一篇文章就能懂的。

你真的用好RxJava了吗

当我问这个问题的时候,其实我想说的是RxJava的用处很广,我们想当然的认为只要RxJava与Retrofit相配合就算是完成任务了,但实际上只要涉及到耗时操作、线程切换:网络请求、图片解析、数据库读取等等需要用多个线程一起完成的工作时,你都可以用到RxJava,并且如果你还一直用RxJava1的话,那么你也OUT啦,当你认识RxJava2.x的时候你会发现他更强大了。关于RxJava,我后续会写相关的文章与大家一起分享,这里我举项目中遇到的一个例子,看看RxJava的用武之地是多么的广:

/**
* 将图片保存在本地
*/
public void openWebView(final Gank gank) {
    disposable=Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(@NonNull ObservableEmitter e) throws Exception {
                    e.onNext(android.R.drawable.ic_menu_share);
            }
    }).subscribeOn(Schedulers.newThread())//开启一个新线程来进行耗时操作
            .map(new Function<Integer, Bitmap>() {
                    @Override
                    public Bitmap apply(@NonNull Integer integer) throws Exception {
                            //将资源id解析为bitmap是一个耗时操作
                            return BitmapFactory.decodeResource(activity.getResources(), integer);
                    }
            }).observeOn(AndroidSchedulers.mainThread())//回到主线程操作bitmap
            .subscribe(new Consumer<Bitmap>() {
                    @Override
                    public void accept(@NonNull Bitmap bitmap) throws Exception {
                            //拿到bitmap之后做的界面更新
                    }
            });
}

//释放资源
public void release() {
                if (disposable != null&&!disposable.isDisposed()) {
                        disposable.dispose();
                }
        }

爽快的Retrofit

还记得之前我写过Volley的一系列文章,虽然觉得这个开源框架已经不错了,但毕竟长江后浪推前浪,在Retrofit的面前,Volley简直就是小巫见大巫,看看我们应该如何在项目中使用:

  • 1、新建一个接口,用注解的形式在里面定义网络请求的方法:
public interface GankRetrofit {
        //获取MainActivity界面的数据,具体的请求路径应该详细看官方的API说明,
        // Retrofit与RxJava配合之后才能显示出更强的威力
        @GET("data/{type}/40/{page}")
        Observable<MainData> getMainData(@Path("type") String type, @Path("page") int page);
}
  • 2、获得实例:
Gson date_gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create();
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://gank.io/api/")         //指定服务器地址
        .addConverterFactory(GsonConverterFactory.create(date_gson))    //添加一个gson的解析器
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())          // 如果想使用RxJava那就需要添加这个适配器  
        .build();
GankRetrofit gankRetrofit;
gankRetrofit = retrofit.create(GankRetrofit.class);            //获取GankRetrofit的对象
  • 3、获取数据:
//返回的Observable<MainData>对象就可以使用RxJava进行解析了
gankRetrofit.getMainData("Android",40);

更好的网页浏览体验——Custom-Tabs-Client

如果你使用过Chrome浏览器,那么你应该会喜欢上它,当我们在使用webview开发的时候,会发现webview存在许多的限制,并且开发者对webview不能完全控制,于是Chrome团队为了解决开发者的这些问题,就设计出这个开源库,只要用户手机上安装Chrome并且设置为默认浏览器,那么打开网页链接的时候就会看到如下的画面:

custom-tabs-client
custom-tabs-client

是的!如此酷炫的画面是用到了Chrome浏览器的内核,并且读取Chrome的cookie,例如如果你在Chrome已经登录过github了,那么通过这个库打开的github链接同样也显示你已经登录了github。当然除了页面效果十分好之外,我们还可以自定义许多东西,例如过渡动画ToolBar上方的ActionButton等等,我的项目中也已经实现了这两个功能。接下来我带大家一起看看最简单的实例应该怎么编写:

CustomTabsIntent.Builder customTabsIntent;
 customTabsIntent = new CustomTabsIntent.Builder();     //获取到CustomTabsIntent实例
 //一系列的设置
customTabsIntent.setToolbarColor(activity.getResources().getColor(R.color.colorPrimaryDark));   //设置ToolBar的颜色
customTabsIntent.setShowTitle(true);    //设置是否显示网页的标题
customTabsIntent.setStartAnimations(activity, R.anim.slide_in_right, R.anim.slide_out_left);    //设置进入的动画
customTabsIntent.setExitAnimations(activity, R.anim.slide_in_left,R.anim.slide_out_right);      //设置退出的动画
//开启网页
CustomTabActivityHelper.openCustomTab(
    activity,       //当前的activity
    customTabsIntent.build(),
    view,
    gank,   //gank带有要打开的网页的url
    new CustomFallback() { //如果用户没有安装Chrome并将其设置为默认浏览器的话,应该做这样的处理
            @Override
            public void openUri(Activity activity, View view,Gank gank) {
                    Log.d("Gank", gank.toString());
                    super.openUri(activity, view,gank);
            }
    });

以上就是想要跟大家一起分享的关于这个项目中一些重要的点,可能有些地方讲得并不那么清楚,但我觉得只有实践才能出真知,所以老哥还是乖乖打开AS撸撸代码啦!

版本控制

目前app的版本为V1.0.0,我会用下面的时间表记录版本的迭代,如果有更新的版本,我会及时更新这个表格。

Version Date
V1.0.0 2017/5/14

致谢

作为一名Android开发者,我们都应该具有像罗老师一样感激开源世界的情怀,而我们现在能做的就是同样为开源世界做贡献,无论是写文章亦或是写程序,记得都要时刻保持一颗感恩的心。非常感谢代码家以及他的干活集中营,也非常感谢许多前辈们做过的干活集中营app。

后记

最近一个月,感觉自己身体被掏空,吐槽学校对我们专业的课程安排如此不合理,每天上那些自己不感兴趣的课程确实很无趣,但是生活再难也要感激它,只有通过自己的双手去努力,将来的你才会为现在的自己喝彩骄傲。下篇文章再见!