又是一個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閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,669評論 25 708
  • 個人原因,此系列更新已停止,抱歉。 上一篇《看我開發干貨集中營App(二) ~ APP初始化》中講述了此APP所依...
    hucw閱讀 3,817評論 10 45
  • 西瓜子加清水用攪拌機打碎,小火煮兩個小時,加入冰糖,融合后起鍋,過濾出汁。一次做多點放冰箱冷藏,可以保存一周,每次...
    A太陽花閱讀 403評論 0 0
  • 談到理財,很多人首先想到的不是如何合理利用自己目前所擁有的資源,而是一味的抱怨自己目前收入太低而支出太高。 其實在...
    青鳥的天空727閱讀 708評論 0 3
  • 九仙山是銅陵人熟悉的一座神靈之山。人們熟悉她的九仙石室、水操臺、恩波寺、南少林和天地會遺址,但她蘊藏著極其豐富、水...
    張哲民閱讀 306評論 4 8