基于Google-MVP-RxJava的Github客户端

  • Github
  • 项目的起因
  • 本文的目的
  • 架构和三方
    • Rx
    • 快速开发工具
    • 网络相关
    • 图片加载
    • UI
  • 功能介绍
    • Explore
    • RepositoryDetail
    • UserDetail
    • RepositoryList
    • UserList
    • Event
    • Setting
  • 架构分析
    • MVP
    • RxJava

Github

Github地址

项目的起因

Github上一个看起来很漂亮的Github客户端

成功引起了我的注意

但是,它不开源

作者在贴了一堆截图后留下了自己的商务合作邮箱

我不开心所以也想做一个

本文的目的

  1. 如果你是青铜玩家:希望本文提供的一些资料(如API,架构,三方,功能实现思路等)可以给你一些参考价值
  2. 如果你是王者玩家:欢迎指出问题和建议,尤其是优化和架构方面

架构和三方

基于 google-android-architecture-mvp-rxjava 的RxJava + Retrofit + Mvp架构,该架构会在后面详细讲解,三方库如下

Rx

  • RxJava
  • RxAndroid

快速开发工具

  • butterknife 大名鼎鼎的黄油刀,让你不再findById
  • android_dbinspector 不需要root就可以查看真机上数据库内容
  • fastjson 最快的json解析工具,阿里巴巴出品

网络相关

  • Retrofit 新一代网络请求神器
  • OkHttp logging interceptor 请求日志拦截器

图片加载

  • Glide 图片加载框架

UI

  • MaterialSearchView Material Design风格的搜索
  • CircleImageview 圆形头像
  • BaseRecyclerViewAdapterHelper 强大的RecyclerView万能适配器
  • FloatingActionButton Material Design风格的浮动按钮
  • spots-dialog 闪烁的loding进度条
  • materialish-progress 旋转的菊花圈
  • MarkdownView 将markdown格式的字符串显示成漂亮的html页面
  • WaitingDots 闪烁的loding动画
  • CodeView 将代码显示成漂亮的样式
  • material-dialogs Material风格的Dialog

功能介绍

Explore

  1. 浏览Repository和User,使用选项卡切换,并且将浏览过的数据缓存在本地(本应用所有数据都做了缓存,并且缓存时间可由用户定制)
基于Google-MVP-RxJava的Github客户端_第1张图片
基于Google-MVP-RxJava的Github客户端_第2张图片
  1. 支持关键字搜索Repository和User,可以选择排序方式(Most star,Best match,Most fork,Rencent update等),可以按标签换语言分类(排序方式和标签可用用户定制)
基于Google-MVP-RxJava的Github客户端_第3张图片
基于Google-MVP-RxJava的Github客户端_第4张图片

RepositoryDetail

  1. Repository简要信息查看,并且可以进行star,unstar,fork操作
基于Google-MVP-RxJava的Github客户端_第5张图片
基于Google-MVP-RxJava的Github客户端_第6张图片
  1. 异步加载ReadMe,按markdown格式显示ReadMe
基于Google-MVP-RxJava的Github客户端_第7张图片
  1. 显示code树,查看代码内容,因为缓存的原因,点击加载过的节点可以秒加载
基于Google-MVP-RxJava的Github客户端_第8张图片
基于Google-MVP-RxJava的Github客户端_第9张图片

UserDetail

  1. User简要信息查看,并且可以进行follow,unfollow操作
基于Google-MVP-RxJava的Github客户端_第10张图片
基于Google-MVP-RxJava的Github客户端_第11张图片

RepositoryList

  1. 查询自己的Repository
基于Google-MVP-RxJava的Github客户端_第12张图片
基于Google-MVP-RxJava的Github客户端_第13张图片
  1. 查询自己Star过的Repository
基于Google-MVP-RxJava的Github客户端_第14张图片
基于Google-MVP-RxJava的Github客户端_第15张图片
  1. 查询RepositoryDetail被fork过的Repository
基于Google-MVP-RxJava的Github客户端_第16张图片
基于Google-MVP-RxJava的Github客户端_第17张图片
  1. 查询UserDetail被拥有的Repository
基于Google-MVP-RxJava的Github客户端_第18张图片
基于Google-MVP-RxJava的Github客户端_第19张图片

UserList

  1. 查询自己,以及其他User的following和follower
基于Google-MVP-RxJava的Github客户端_第20张图片
基于Google-MVP-RxJava的Github客户端_第21张图片
  1. 查询RepositoryDetail的Contributors和Stargazers
基于Google-MVP-RxJava的Github客户端_第22张图片
基于Google-MVP-RxJava的Github客户端_第23张图片

Event

  1. 可以查询,自己,User,Repository的Event
基于Google-MVP-RxJava的Github客户端_第24张图片
基于Google-MVP-RxJava的Github客户端_第25张图片

Setting

  1. 设置Explore页面首先查询的语言,右下方应该有几个语言选项,默认的排序方式
基于Google-MVP-RxJava的Github客户端_第26张图片
基于Google-MVP-RxJava的Github客户端_第27张图片

架构分析

MVP

谷歌去年在github上发布一整套的它推荐的Android架构Demo,todo-mvp-rxjava 是之中用来示范rxjava的sample

关于它的这套架构,我画了一个栩栩如生的草图,嗯,栩栩如生

基于Google-MVP-RxJava的Github客户端_第28张图片

是不是已经被我的美术功底震惊的说不出话来,就冲这图你不给Star一个?

只看图可能容易蒙蔽,用代码来解释一下

先看接口类

public interface Contract {

    interface View {
        
        void showLoading();

        void hideLoading();

        void showError();

        void showEmpty();

        void showList(List list);

        void setPresenter(Contract.Presenter presenter);
    }

    interface Presenter {

        void loadList();

    }
}

然后是Presenter的实现类,持有了view的对象和repository的对象,分别用来加载数据和展现数据,这里偷懒了没有切换线程,后面用RxJava完成

public class PrensenterImpl implements Contract.Presenter {

    private Contract.View mView;
    private Repository mRepository;

    public PrensenterImpl(Contract.View view, Repository repository) {
        mView = view;
        mRepository = repository;
        mView.setPresenter(this);
    }

    @Override
    public void loadList() {

        //UI 线程
        mView.showLoading();

        try{
            //IO 线程
            List list = mRepository.loadList();

            //切回UI 线程
            if (list.isEmpty()){
                mView.showEmpty();
            }else {
                mView.showList(list);
            }

        }catch (Exception e){
            mView.showError();
        }finally {
            mView.hideLoading();
        }

    }

再看View的实现类,也就是Activity,持有一个Presenter的对象,并且在创建该对象的时候将自身和数据仓库类传了过去

public class MainActivity extends AppCompatActivity implements Contract.View {

    private Contract.Presenter mPresenter;

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

        Repository repository = new Repository(new RemoteDataSource(), new LocalDataSource());//得到数据仓库
        mPresenter = new PrensenterImpl(this, repository); //将自身和数据仓库类传了过去

        mPresenter.loadList();

    }

    public void setPresenter(Contract.Presenter presenter) {
        mPresenter = presenter;
    }

    @Override
    public void showLoading() {
        // 展示进度条
    }

    @Override
    public void hideLoading() {
        // 加载成功隐藏进度条
    }

    @Override
    public void showError() {
        // 加载失败
    }

    @Override
    public void showEmpty() {
        // 数据是空的
    }

    @Override
    public void showList(List list) {
        // 加载成功并且有数据耶,展示起来
    }

}

最后数据仓库先这么简单的写,后面再补充

public interface DataSource {
    List loadList();
}

public class LocalDataSource implements DataSource{
    @Override
    public List loadList() {
        return new ArrayList();//从本地读取缓存
    }
}

public class RemoteDataSource implements DataSource{
    @Override
    public List loadList() {
        return new ArrayList();//从网络加载数据
    }
}

public class Repository implements DataSource{

    private DataSource mRemoteDataSource;
    private DataSource mLocalDataSource;

    public Repository(DataSource remoteDataSource,DataSource lemoteDataSource){
        mRemoteDataSource = remoteDataSource;
        mLocalDataSource = lemoteDataSource;
    }


    @Override
    public List loadList() {
        List remote = mRemoteDataSource.loadList();
        List local = mLocalDataSource.loadList();
        // ....让两个数据源同时去加载数据,谁先加载完成就返回谁的
        return null;
    }
}

所以整个MVP的请求逻辑如下

  1. 用户到达View后开始请求数据
  2. MainActivity将请求委托给Presenter去处理
  3. Presenter通过Repository去请求数据,根据结果的不同分发回View

从来实现了数据的展示,请求,分发三层分离,时序图如下图:

基于Google-MVP-RxJava的Github客户端_第29张图片

RxJava

上一节中有两个地方是十足的伪代码,mPresenter.loadList和Repository的具体实现,使用RxJava可以很容易的完成这两个部分的实现

对RxJava还没有概念的请看 给Android 开发者的 RxJava 详解

这里直接展示用法

添加依赖

compile 'io.reactivex:rxjava:1.0.8'
compile 'io.reactivex:rxandroid:1.2.1'
  1. 使用RxJava将两个数据源的结果合并返回,返回类型改成了Observable,使用了concat操作符来合并两个数据源,使用first操作符来返回第一个结果
public interface DataSource {
    Observable loadList();
}

public class LocalDataSource implements DataSource {
    @Override
    public Observable loadList() {
        return Observable.create(new Observable.OnSubscribe() {

            @Override
            public void call(Subscriber subscriber) {
                List list = new ArrayList();//从本地读取的缓存
                subscriber.onNext(list);
            }
        });//从本地读取缓存
    }
}

public class RemoteDataSource implements DataSource{
    @Override
    public Observable loadList() {
        return null;//从网络加载数据
    }
}

public class Repository implements DataSource{

    private DataSource mRemoteDataSource;
    private DataSource mLocalDataSource;

    public Repository(DataSource remoteDataSource,DataSource lemoteDataSource){
        mRemoteDataSource = remoteDataSource;
        mLocalDataSource = lemoteDataSource;
    }


    @Override
    public Observable loadList() {

        Observable localTask = mLocalDataSource.loadList();
        Observable remoteTask = mRemoteDataSource.loadList();

        return Observable.concat(localTask, remoteTask).first();//让本地缓存先读取,网络拉去后执行,谁先拿到数据就返回谁
    }
}
  1. 使用RxJava实现mPresenter.loadList中的跳转逻辑,避免了使用八百个回调来切换线程
public class PrensenterImpl implements Contract.Presenter {

    ...

    @Override
    public void loadList() {

        //UI 线程
        mView.showLoading();

        mRepository.loadList()
                .subscribeOn(Schedulers.io()) //指定上游在IO线程执行
                .observeOn(AndroidSchedulers.mainThread()) //指定下游在UI线程
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.hideLoading();
                        mView.showError();
                    }

                    @Override
                    public void onNext(List list) {
                        mView.hideLoading();
                        if (list.isEmpty()) {
                            mView.showEmpty();
                        } else {
                            mView.showList(list);
                        }
                    }
                });


    }
}

你可能感兴趣的:(基于Google-MVP-RxJava的Github客户端)