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

欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/05/15/GankClient/ ,未经允许不得转发!

Google_I/O_2017

前言

今天想要与大家一起分享的是近一个星期内开发的一个新app——干货集中营客户端,其实网上已经有许多类似的项目,代码家也在他的干货集中营中推荐了几款优秀的作品,我也借鉴了其中的一些,自己开发出一款干货集中营客户端,目前项目已经发布到github,如果你想了解整个工程的具体内容,那么你可以fork或者clone,如果你觉得我做得还可以,那么请你赐给我一个star呗!你的支持会是我努力的动力。
慢慢的已经养成了每篇文章都会来一个前言,也慢慢地将写文章潜移默化的转变成写故事,所以每个故事都会有前言,也都会有结果,这次也不例外。
距离上篇引起热烈反响的文章发布已经过去一个月了,承蒙广大网友对我的支持以及建议,当然作为一个90后,我也很虚心的接受来自各方的吐槽,毕竟我并不优秀,只是一直在优秀的路上努力的奔跑着。下面我想跟大家一起分享一下这一个月里我做了些什么事,以及接下来一段时间的计划和打算。

Have Done List

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

Todo List

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

老铁别废话了

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

项目介绍

logo

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

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

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

预览

谁说不给图的

全部效果图来一发

screenshot
screenshot
screenshot

screenshot
screenshot
screenshot

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

gif
gif

下载

如果你想测试这个项目,那么请手动clone,如果你想尝试一下APP,那么你可以使劲戳这里 ,或者扫下面的二维码。后期我会发布到应用市场,希望大家可以多多支持!

QRCode

功能&特色

加醋的表示已经实现,没有加粗的表示尚未实现或者仍需改善的功能

  • 实时获取服务器的最新数据,采用CardView的布局以更好的展现数据。
  • 刷新效果很好玩,真的很精致,感谢Phoenix
  • 如果你装有Chrome浏览器,并将其设置为默认浏览器,那么你的网页浏览效果就会十分酷炫。感谢Custom-Tabs-Client ! 墙裂推荐Chrome,如果你没有安装Chrome,那也没关系,那就用传统的WebView吧!
  • 更加统一的过渡动画,相信你会爱上它。
  • 保存、分享图片与链接,也可以直接使用浏览器打开链接。
  • **更好看的Material Design设计风格。 **
  • **妹子很漂亮哟! **
  • 优化webview的视频播放。
  • 增加使用系统浏览器的选项。
  • 修复在主界面中加载数据到2016/4/20左右时数据显示错乱的问题。
  • 增强分享功能。

分解

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

如何使用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

是的!如此酷炫的画面是用到了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。

后记

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

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • 个人原因,此系列更新已停止,抱歉。 上一篇《看我开发干货集中营App(二) ~ APP初始化》中讲述了此APP所依...
    hucw阅读 3,793评论 10 45
  • 西瓜子加清水用搅拌机打碎,小火煮两个小时,加入冰糖,融合后起锅,过滤出汁。一次做多点放冰箱冷藏,可以保存一周,每次...
    A太阳花阅读 382评论 0 0
  • 谈到理财,很多人首先想到的不是如何合理利用自己目前所拥有的资源,而是一味的抱怨自己目前收入太低而支出太高。 其实在...
    青鸟的天空727阅读 693评论 0 3
  • 九仙山是铜陵人熟悉的一座神灵之山。人们熟悉她的九仙石室、水操台、恩波寺、南少林和天地会遗址,但她蕴藏着极其丰富、水...
    张哲民阅读 286评论 4 8