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

Github

Github地址

项目的起因

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

成功引起了我的注意

但是,它不开源

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

我不开心所以也想做一个

本文的目的

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

架构和三方

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

Rx

快速开发工具

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

网络相关

图片加载

  • Glide 图片加载框架

UI

功能介绍

Explore

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

RepositoryDetail

  1. Repository简要信息查看,并且可以进行star,unstar,fork操作
  1. 异步加载ReadMe,按markdown格式显示ReadMe
  1. 显示code树,查看代码内容,因为缓存的原因,点击加载过的节点可以秒加载

UserDetail

  1. User简要信息查看,并且可以进行follow,unfollow操作

RepositoryList

  1. 查询自己的Repository
  1. 查询自己Star过的Repository
  1. 查询RepositoryDetail被fork过的Repository
  1. 查询UserDetail被拥有的Repository

UserList

  1. 查询自己,以及其他User的following和follower
  1. 查询RepositoryDetail的Contributors和Stargazers

Event

  1. 可以查询,自己,User,Repository的Event

Setting

  1. 设置Explore页面首先查询的语言,右下方应该有几个语言选项,默认的排序方式

架构分析

MVP

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

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

是不是已经被我的美术功底震惊的说不出话来,就冲这图你不给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

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

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<List>() {

            @Override
            public void call(Subscriber<? super List> 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<List>() {
                    @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);
                        }
                    }
                });


    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容