一個MVP+RxJava+Retrofit的乾貨集中營

溫斯渤發表於2017-07-09

歡迎掘金的小夥伴們訪問我的部落格網站,原文連結: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。

後記

最近一個月,感覺自己身體被掏空,吐槽學校對我們專業的課程安排如此不合理,每天上那些自己不感興趣的課程確實很無趣,但是生活再難也要感激它,只有通過自己的雙手去努力,將來的你才會為現在的自己喝彩驕傲。下篇文章再見!

相關文章