從零開始搭建一個項目(rxJava+Retrofit+Dagger2) --第1章

雞湯:積跬步撇叁,至千里

本篇文章是從零開始搭建框架的第二章壳贪,將要講述的是flux架構氢妈。

架構簡述

關于架構眼坏,比較傳統(tǒng)的該屬MVC模式(Model-View-Controllor),這個模式設計之初的有個作用就是解耦,將代碼的各個模塊分離出來地粪。

Android一開始的架構模式也是MVC取募,

View:對應布局文件

Model:業(yè)務邏輯和實體模型

Controller:對應Activity

看起來好像沒什么問題,實際上Activity作為一個Controller做的事情太多了蟆技,數(shù)據(jù)綁定玩敏,事件處理等代碼都是寫在Activity中的。等到項目一大质礼,代碼一多旺聚,程序就會看起來很臃腫。

因為這些原因眶蕉,才會有后來的MVP架構(Model-View-Presenter)砰粹,以及MVVM等架構。

MVP:

View:對應Activity造挽,負責View的繪制和事件處理

Model:業(yè)務邏輯和實體模型

Presenter:負責view和model間的交互碱璃。

MVP的設計理念MVC是一樣的,解耦饭入,應該說所有的架構的設計理念都是解耦嵌器。

MVC的數(shù)據(jù)流向


MVP的數(shù)據(jù)流向


從上述兩張圖可以看出MVC的data和view是打通的,而且是沒有制約的谐丢,很容易引起數(shù)據(jù)的混亂爽航,反觀MVP在data和view中間多了一層Presenter,Presenter負責解析數(shù)據(jù)乾忱,并通知view讥珍,而view需要什么數(shù)據(jù),也是向Presenter請求的饭耳。這樣就不容易引起數(shù)據(jù)混亂串述。

好执解!說了這么多寞肖,接下來我們來了解下flux架構吧纲酗!
什么,前面講了這么多MVC和MVP的東西新蟆,你說接下來要講flux了觅赊,你是再搞我們嗎?

我的心情這樣的:

Flux架構

Flux是由facebook推出的架構理念,相比起MVC琼稻,更加簡潔和清晰吮螺。

Flux將應用分成4部分

View:視圖層
Action:view發(fā)出的事件,比如點擊事件等
Dispatcher:用來接收Actions帕翻,執(zhí)行對應的回調(diào)函數(shù)
Store:用來存放應用的狀態(tài)鸠补,一旦應用發(fā)生變動(數(shù)據(jù)變化),就提醒View更新

Flux最大的特點就是數(shù)據(jù)的單向流動,這樣就不會出現(xiàn)數(shù)據(jù)混亂的問題
如圖所示

整體流程
1.用戶訪問View
2.view發(fā)出用戶指定的Action嘀掸,如點擊事件
3.Dispatcher收到Action紫岩,要求store進行相應的更新
4.store更新完之后,發(fā)出"change"事件
5.View收到"change"事件后睬塌,更新頁面泉蝌。

不過上圖中缺少了一部分,數(shù)據(jù)的來源揩晴,再補上一張圖


流程變?yōu)?br> 1.用戶訪問View
2.view從ActionCreator中請求數(shù)據(jù)
3.ActionCreator加載網(wǎng)絡數(shù)據(jù)勋陪,并發(fā)出Action
4.Dispatcher收到Action,要求store進行相應的更新
5.store更新完之后硫兰,發(fā)出"change"事件
6.View收到"change"事件后诅愚,更新頁面。

根據(jù)上述所講的Flux架構劫映。事情的起點用戶訪問view呻粹,
對應的ActionCreator請求數(shù)據(jù)網(wǎng)絡數(shù)據(jù),并發(fā)送對應的Action

編寫ActionCreator和對應的Action

tip:接下來的內(nèi)容涉及RxJava,Retrofit2苏研,請還不知道這些是什么的同學等浊,先去入個門

新建網(wǎng)絡請求類HttpService 和數(shù)據(jù)反序列化類 DateDeserializer,注釋寫的比較詳細摹蘑,所以不多加解釋了筹燕!

public interface HttpService {

    String BASE_URL = "http://gank.io/api/";

    String DATE_HISTORY = "day/history";

    //獲取gank的歷史數(shù)據(jù)
    @GET(DATE_HISTORY)
    Observable<DateData> getDateHistory();

    //獲取某一天的數(shù)據(jù)
    @GET("day/{year}/{month}/{day}")
    Observable<DayData> getDayGank(@Path("year") int year, @Path("month") int month, @Path("day") int day);

    
    class Factory {
        private static OkHttpClient mOkHttpClient;

        private static final int CACHE_MAX_TIME = 12 * 60 * 60;
        private static final String DATE_PATTERN1 = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS";
        private static final String DATE_PATTERN2 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

        static {
            //設置緩存策略
            Interceptor interceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Response response = chain.proceed(request);
                    if (request.url().toString().startsWith(BASE_URL)) {
                        int maxTime = CACHE_MAX_TIME;
                        Date receiveDate = response.headers().getDate("Date");
                        if (null != receiveDate) {
                            //設置緩存的到期時候
                            Calendar calendar = Calendar.getInstance();
                            calendar.setTime(receiveDate);
                            int hour = calendar.get(Calendar.HOUR_OF_DAY);
                            int min = calendar.get(Calendar.MINUTE);

                            maxTime = 24 * 3600 - hour * 3600 - min * 60;
                        }
                        return response.newBuilder()
                                .header("Cache-Control", "max-age=" + maxTime)
                                .build();
                    }
                    return response;
                }
            };

            File cacheDir = new File(AppUtil.getCacheDir(), "http_reponse");
            mOkHttpClient = new OkHttpClient.Builder()
                    .retryOnConnectionFailure(true) //連接失敗是否重連
                    .connectTimeout(15, TimeUnit.SECONDS) //超時時間15s
                    .addNetworkInterceptor(interceptor) //把定義好的攔截器加入到okhttp
                    .cache(new Cache(cacheDir, 10 * 1024 * 1024))
                    .build();
        }

        private static final Gson dateGson = new GsonBuilder()
                .registerTypeAdapter(Date.class, new DateDeserializer(DATE_PATTERN1, DATE_PATTERN2))  //定義json的解析樣本
                .serializeNulls()
                .create();

        private static final HttpService mGankService = new Retrofit.Builder()
                .client(mOkHttpClient)
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(dateGson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())    //允許以 Observable 形式返回
                .build()
                .create(HttpService.class);

        public static HttpService getGankService() {
            return Factory.mGankService;
        }

    }
}

數(shù)據(jù)反序列化類 DateDeserializer

public class DateDeserializer implements JsonDeserializer {

    private List<SimpleDateFormat> mDateFormatList;

    public DateDeserializer(String... patterns) {
        mDateFormatList = new ArrayList<>(patterns.length);
        for(String pattern : patterns) {
            mDateFormatList.add(new SimpleDateFormat(pattern, Locale.US));
        }
    }

    @Override
    public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        for (SimpleDateFormat dateFormat : mDateFormatList) {
            try {
                return dateFormat.parse(json.getAsString());
            }catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

新建一個TodayGankFragment類,用來和用戶交互衅鹿。
public class TodayGankFragment extends Fragment {

    public static final String TAG = TodayGankFragment.class.getSimpleName();

    public static TodayGankFragment newInstance() {
        return new TodayGankFragment();
    }


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.frag_today, container, false);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        TodayGankActionCreator creator = new TodayGankActionCreator();
        //view從對應的Creator請求數(shù)據(jù)
        creator.getTodayGank();
    }
}

TodayGankFragment類很簡單撒踪,加載一個簡單的布局,然后從對應的Creator請求數(shù)據(jù)大渤。

那么這個TodayGankActionCreator類是怎么樣的呢制妄!

新建TodayGankActionCreator類,過濾和初步解析http請求的數(shù)據(jù)。

public class TodayGankActionCreator {

    //定義數(shù)據(jù)轉(zhuǎn)化模板
    private static SimpleDateFormat sDataFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);

    public void getTodayGank() {

        //RxJava處理數(shù)據(jù)
        HttpService.Factory.getGankService()
                .getDateHistory()
                .subscribeOn(Schedulers.io())
                .filter(new Func1<DateData, Boolean>() {
                    @Override
                    public Boolean call(DateData dateData) {
                        return (null != dateData && null != dateData.results && dateData.results.size() > 0);//接口請求成功泵三,這邊返回true
                    }
                })
                .map(new Func1<DateData, Calendar>() {
                    @Override
                    public Calendar call(DateData dateData) {
                        Calendar calendar = Calendar.getInstance(Locale.CHINA);
                        try {
                            calendar.setTime(sDataFormat.parse(dateData.results.get(0)));  //設置時間為最新一天耕捞,一般是今天
                        } catch (ParseException e) {
                            e.printStackTrace();
                            calendar = null;
                        }
                        return calendar;
                    }
                })
                .flatMap(new Func1<Calendar, Observable<DayData>>() {
                    @Override
                    public Observable<DayData> call(Calendar calendar) {
                        return HttpService.Factory.getGankService()        //再次請求數(shù)據(jù)衔掸,獲取當天的數(shù)據(jù)
                                .getDayGank(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH));
                    }
                })
                .map(new Func1<DayData, List<GankItem>>() {
                    @Override
                    public List<GankItem> call(DayData dayData) {
                        //獲取當天的數(shù)據(jù),然后在getGankList方法中執(zhí)行具體的解析工作
                        return getGankList(dayData);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<GankItem>>() {
                    @Override
                    public void call(List<GankItem> gankItems) {
                        //數(shù)據(jù)正常處理之后調(diào)用此方法
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        //數(shù)據(jù)處理過程中報錯時調(diào)用
                    }
                });

    }
    
    
    private List<GankItem> getGankList(DayData dayData) {
        if (dayData == null || dayData.results == null) {
            return null;
        }
        //對數(shù)據(jù)進行處理俺抽,解析成你所需要的model

        return null;
    }

}

附上兩張圖


api的回調(diào).png
程序返回的數(shù)據(jù).png

從兩張圖可以看出敞映,程序已經(jīng)可以正常返回數(shù)據(jù),并以對象的形式磷斧,接下來看具體需要如何形式的model振愿,進一步解析即可。

本章就先到這里弛饭。

從零開始系列第0章
從零開始系列第1章
從零開始系列第2章
從零開始系列完結(jié)章

本人也只是Android開發(fā)路上一只稍大一點的菜鳥冕末,如果各位讀者中發(fā)現(xiàn)文章中有誤之處,請幫忙指出侣颂,你的批評和鼓勵都是我前進的動力栓霜。

寫在文末:如果讀者朋友有什么問題或者意見可以在評論里指出.
代碼地址為https://github.com/niknowzcd/FluxDemo1

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市横蜒,隨后出現(xiàn)的幾起案子胳蛮,更是在濱河造成了極大的恐慌,老刑警劉巖丛晌,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仅炊,死亡現(xiàn)場離奇詭異,居然都是意外死亡澎蛛,警方通過查閱死者的電腦和手機抚垄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谋逻,“玉大人呆馁,你說我怎么就攤上這事』僬祝” “怎么了浙滤?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長气堕。 經(jīng)常有香客問我纺腊,道長,這世上最難降的妖魔是什么茎芭? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任揖膜,我火速辦了婚禮,結(jié)果婚禮上梅桩,老公的妹妹穿的比我還像新娘壹粟。我一直安慰自己,他們只是感情好宿百,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布趁仙。 她就那樣靜靜地躺著洪添,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幸撕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天外臂,我揣著相機與錄音坐儿,去河邊找鬼。 笑死宋光,一個胖子當著我的面吹牛貌矿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罪佳,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼逛漫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赘艳?” 一聲冷哼從身側(cè)響起酌毡,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蕾管,沒想到半個月后枷踏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡掰曾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年旭蠕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旷坦。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡掏熬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秒梅,到底是詐尸還是另有隱情旗芬,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布捆蜀,位于F島的核電站岗屏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏漱办。R本人自食惡果不足惜这刷,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娩井。 院中可真熱鬧暇屋,春花似錦、人聲如沸洞辣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至定鸟,卻和暖如春而涉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背联予。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工啼县, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沸久。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓季眷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卷胯。 傳聞我的和親對象是個殘疾皇子子刮,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,303評論 25 707
  • 前言 看了下上篇博客的發(fā)表時間到這篇博客,竟然過了11個月裳朋,罪過病线,罪過。這一年時間也是夠折騰的鲤嫡,年初離職跳槽到鵝廠...
    西木柚子閱讀 21,248評論 12 184
  • 雞湯:養(yǎng)成一個好習慣需要很久送挑,打破這個習慣卻只需要一瞬間的念頭 接上一章的內(nèi)容,如果還沒看過的朋友暖眼,請到文章底部查...
    niknowzcd閱讀 1,971評論 7 23
  • 似夏非夏的天惕耕,躁動的音樂,心跳不自覺得隨節(jié)奏而動卻一直跟不上诫肠。生了一場病司澎,貌似沉淪了很久,渾渾噩噩栋豫,周圍一切像流水...
    深夜清吧閱讀 176評論 0 0
  • 初秋挤安,在山道上,邂逅一簇不知名的野花丧鸯,黃而淡蛤铜,有幽幽的馨香。風吹過,花朵頷首低眉围肥,仿佛和我說著深深淺淺的心事剿干,聊著...
    惠風和暢806閱讀 300評論 2 2