大幅提高自身技术实力最有效的途径之一就是学习世界级优秀开源项目的精髓,而本人的《带你学开源项目》系列文章将持续更新,对当前Android开发界最优秀的开源项目进行深入分析
比起阅读枯燥的技术文档,独自苦苦摸索新技术的基本用法,还有一种更好更快速也更有效的提高自身技术的方法,那就是阅读学习优质的开源项目,通过仿写、练习最终达到理解,潜移默化提升自身编程技能。
《带你学开源项目》系列将带领你深入阅读及分析当前流行的一些开源项目,并针对其中采用的新技术与精妙之处进行细致的阐述,以期让你快速掌握Android开发中的多种强大技能点。
本次的开源项目选择了Meizhi Android,本文主要介绍该项目中采用的
、
- RxJava
两种技术,这二者在Android开发者中非常流行,不仅能够
- Retrofit
,而且能
- 优美地处理异步回调
。而Meizhi Android中较好的覆盖了二者的多种应用场景,能够给多数开发者一个全面的学习。
- 提高代码的性能和稳定性
下面本人会
,同时为了读者看的清楚其中的逻辑关系,可能会做一定调整以帮助读者理解,比如把lambda表达式还原成普通java函数形式,以避免很多读者对lambda并不熟悉。
- 对原项目的代码进行详细的介绍
##二、原项目分析
###0. clone项目到本地
第一步当然是把项目clone下来,编译,运行。有兴趣的同学可以执行这一步。
###1. 添加
抓包工具首先,由于我们要分析retrofit,所以为了查看app的网络请求,有兴趣的同学可以手动在代码里添加Stetho。
- Stetho
是Facebook推出的一款黑科技,能够在chrome里轻松查看app所有的网络请求,比起iOS需要装个Charles查看http请求方便多咯。
- Stetho
###2. Retrofit结构
从下图我们可以看到,首页里有很多card,每一个card里有两个元素:
,
- 妹纸图片
,具体UI实现我们不在乎,只要明白一点,这两个元素数据是来自于两个不同的api。其中,
- 描述文字
- 妹纸图片
;
- http://gank.io/api/data/福利/10
- 描述文字
。
- http://gank.io/api/data/休息视频/10
app中为了请求网络数据,采用了Retrofit。具体关于retrofit如何配置请各位参考官网,这里只讲解如何使用
。
- Retrofit
该项目中主要创建了以下几个类来实现
结构,大家可以作为参考用于自己的项目中。
- Retrofit
#####i.
:这个类用来定义相关的
- GankApi
接口,这是符合retrofit规范的定义形式,每一个api返回的为
- http
格式结果,方便
- Observable<T>
进行进一步处理。 @GET("/data/福利/{page}") Observable getMeizhiList(@Path("page") int page); @GET("/data/休息视频/{page}") Observable getGankVideoList(@Path("page") int page);
- RxJava
#####ii.
:这个类用来对
- DrakeetRetrofit
进行相关配置并生成
- Retrofit
实例
- GankApi
OkHttpClient client = new OkHttpClient(); RestAdapter.Builder builder = new RestAdapter.Builder(); builder.setClient(new OkClient(client)) .setLogLevel(RestAdapter.LogLevel.FULL) .setEndpoint("gank.io/api")
- gankApi
- public GankApi getGankApi() {
- return gankApi;
- }
#####iii.
: 这个类用来对外生成单例
- DrakeetFactory
实例,为确保
- GankApi
实例只生成一次。 public static GankApi getGankApi() {
- GankApi
所以,在实际应用场景中,比如我们想要发起一个http请求来获取
数据,那么我们可以采用以下方式:
- 福利
- GankApi gankApi = DrakeetFactory.getGankApi();
- Observable < MeizhiList > meizhiList = gankApi.getMeizhiList(10);
###3. 首页的RxJava的实现
既然我们已经把网络框架搭建好了,那么可以开始从服务器获取数据并显示了。我们首先看首页的数据。下面,我来对首页数据进行分析,一步步推出所需要的RxJava表达式。
上面已经介绍过,每一个card里有两部分数据:
(红色方框)和
- 妹纸图片
(绿色方框)。
- 描述文本
数据来自于
- 妹纸图片
这个api,该api会返回妹纸图片的url;
- "/data/福利/{page}"
- 描述文本
- public class Video {
- public String desc;
- public Date publishDate;
- } //这是一个视频对象,存储视频描述信息和创建日期
- public class MeizhiList {
- public List<Meizhi> meizhiList;
- } //由于我们一次请求能获取到10个(根据`page`设置),所以我们用MeizhiList来存储结果
- public class VideoList {
- public List<Video> videoList;
- } //原理同上,存储多个video对象
- public class MeizhiWithVideo {
- public String url;
- public String desc;
- public Date publishDate;
- }//将video信息合并入meizhi对象中
- public class MeizhiWithVideoList {
- public List<MeizhiWithVideoList> data;
- }
- Observable<MeizhiList> meizhiListObservable = gankApi.getMeizhiList(10);
- Observable<VideoList> videoListObservable = gankApi.getVideoList(10);
- Observable<MeizhiWithVideoList> meizhiWithVideoListObservable =
- Observable.zip(meizhiListObservable, videoListObservable, this::mergeVideoWithMeizhi)
- public MeizhiWithVideoList mergeVideoWithMeizhi(MeizhiList meizhiList, VideoList videoList) { //省略...}
- meizhiWithVideoListObservable.map(new Func1<MeizhiWithVideoList, List<MeizhiWithVideo>>() {
- @Override
- public List<Meizhi> call(MeizhiList meizhiList) {
- return MeizhiWithVideoList.data;
- }
- })
- .flatMap(new Func1<List<MeizhiWithVideo>, Observable<MeizhiWithVideo>>() {
- @Override
- public Observable<MeizhiWithVideo> call(List<MeizhiWithVideo> meizhiWithVideos) {
- return Observable.from(meizhiWithVideos);
- }
- })
- .toSortedList(new Func2<MeizhiWithVideo, MeizhiWithVideo, Integer>() {
- @Override
- public Integer call(MeizhiWithVideo meizhiWithVideo1, MeizhiWithVideo meizhiWithVideo2) {
- return meizhiWithVideo2.publishedAt.compareTo(meizhiWithVideo1.publishedAt);
- }
- })
- .subscribeOn(Schedulers.computation());
- sortedMVListObservable.observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber<List<MeizhiWithVideo>>() {
- @Override
- public void onCompleted() {
- setRefresh(false); // stop refreshing data.
- }
- @Override
- public void onError(Throwable e) {
- }
- @Override
- public void onNext(List<MeizhiWithVideo> meizhiWithVideoList) {
- adapter.setData(meizhiWithVideoList);
- adapter.notifyDataSetChanged(); // update UI
- }
- })
- `BaseActivity`
- private CompositeSubscription mCompositeSubscription;
- protected void addSubscription(Subscription s) {
- if (this.mCompositeSubscription == null) {
- this.mCompositeSubscription = new CompositeSubscription();
- }
- this.mCompositeSubscription.add(s);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (this.mCompositeSubscription != null) {
- this.mCompositeSubscription.unsubscribe();
- }
- }
- public class MyActivity extends BaseActivity {
- private void loadData() {
- Subscription s = gankApi.getMeizhiList(10)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(...);
- addSubscription(s);
- }
- }
来源: https://juejin.im/post/59fafadc51882578d84ecd21