从零开始搭建一个项目(rxJava+Retrofit+Dagger2) --第1章

鸡汤:积跬步,至千里

本篇文章是从零开始搭建框架的第二章,将要讲述的是flux架构。

架构简述

关于架构,比较传统的该属MVC模式(Model-View-Controllor),这个模式设计之初的有个作用就是解耦,将代码的各个模块分离出来。

Android一开始的架构模式也是MVC,

View:对应布局文件

Model:业务逻辑和实体模型

Controller:对应Activity

看起来好像没什么问题,实际上Activity作为一个Controller做的事情太多了,数据绑定,事件处理等代码都是写在Activity中的。等到项目一大,代码一多,程序就会看起来很臃肿。

因为这些原因,才会有后来的MVP架构(Model-View-Presenter),以及MVVM等架构。

MVP:

View:对应Activity,负责View的绘制和事件处理

Model:业务逻辑和实体模型

Presenter:负责view和model间的交互。

MVP的设计理念MVC是一样的,解耦,应该说所有的架构的设计理念都是解耦。

MVC的数据流向


MVP的数据流向


从上述两张图可以看出MVC的data和view是打通的,而且是没有制约的,很容易引起数据的混乱,反观MVP在data和view中间多了一层Presenter,Presenter负责解析数据,并通知view,而view需要什么数据,也是向Presenter请求的。这样就不容易引起数据混乱。

好!说了这么多,接下来我们来了解下flux架构吧!
什么,前面讲了这么多MVC和MVP的东西,你说接下来要讲flux了,你是再搞我们吗?

我的心情这样的:

Flux架构

Flux是由facebook推出的架构理念,相比起MVC,更加简洁和清晰。

Flux将应用分成4部分

View:视图层
Action:view发出的事件,比如点击事件等
Dispatcher:用来接收Actions,执行对应的回调函数
Store:用来存放应用的状态,一旦应用发生变动(数据变化),就提醒View更新

Flux最大的特点就是数据的单向流动,这样就不会出现数据混乱的问题
如图所示

整体流程
1.用户访问View
2.view发出用户指定的Action,如点击事件
3.Dispatcher收到Action,要求store进行相应的更新
4.store更新完之后,发出"change"事件
5.View收到"change"事件后,更新页面。

不过上图中缺少了一部分,数据的来源,再补上一张图


流程变为
1.用户访问View
2.view从ActionCreator中请求数据
3.ActionCreator加载网络数据,并发出Action
4.Dispatcher收到Action,要求store进行相应的更新
5.store更新完之后,发出"change"事件
6.View收到"change"事件后,更新页面。

根据上述所讲的Flux架构。事情的起点用户访问view,
对应的ActionCreator请求数据网络数据,并发送对应的Action

编写ActionCreator和对应的Action

tip:接下来的内容涉及RxJava,Retrofit2,请还不知道这些是什么的同学,先去入个门

新建网络请求类HttpService 和数据反序列化类 DateDeserializer,注释写的比较详细,所以不多加解释了!

public interface HttpService {

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

    String DATE_HISTORY = "day/history";

    //获取gank的历史数据
    @GET(DATE_HISTORY)
    Observable<DateData> getDateHistory();

    //获取某一天的数据
    @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;
        }

    }
}

数据反序列化类 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请求数据
        creator.getTodayGank();
    }
}

TodayGankFragment类很简单,加载一个简单的布局,然后从对应的Creator请求数据。

那么这个TodayGankActionCreator类是怎么样的呢!

新建TodayGankActionCreator类,过滤和初步解析http请求的数据。

public class TodayGankActionCreator {

    //定义数据转化模板
    private static SimpleDateFormat sDataFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);

    public void getTodayGank() {

        //RxJava处理数据
        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()        //再次请求数据,获取当天的数据
                                .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) {
                        //获取当天的数据,然后在getGankList方法中执行具体的解析工作
                        return getGankList(dayData);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<GankItem>>() {
                    @Override
                    public void call(List<GankItem> gankItems) {
                        //数据正常处理之后调用此方法
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        //数据处理过程中报错时调用
                    }
                });

    }
    
    
    private List<GankItem> getGankList(DayData dayData) {
        if (dayData == null || dayData.results == null) {
            return null;
        }
        //对数据进行处理,解析成你所需要的model

        return null;
    }

}

附上两张图


api的回调.png
程序返回的数据.png

从两张图可以看出,程序已经可以正常返回数据,并以对象的形式,接下来看具体需要如何形式的model,进一步解析即可。

本章就先到这里。

从零开始系列第0章
从零开始系列第1章
从零开始系列第2章
从零开始系列完结章

本人也只是Android开发路上一只稍大一点的菜鸟,如果各位读者中发现文章中有误之处,请帮忙指出,你的批评和鼓励都是我前进的动力。

写在文末:如果读者朋友有什么问题或者意见可以在评论里指出.
代码地址为https://github.com/niknowzcd/FluxDemo1

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,474评论 25 707
  • 前言 看了下上篇博客的发表时间到这篇博客,竟然过了11个月,罪过,罪过。这一年时间也是够折腾的,年初离职跳槽到鹅厂...
    西木柚子阅读 21,207评论 12 185
  • 鸡汤:养成一个好习惯需要很久,打破这个习惯却只需要一瞬间的念头 接上一章的内容,如果还没看过的朋友,请到文章底部查...
    niknowzcd阅读 1,957评论 7 23
  • 似夏非夏的天,躁动的音乐,心跳不自觉得随节奏而动却一直跟不上。生了一场病,貌似沉沦了很久,浑浑噩噩,周围一切像流水...
    深夜清吧阅读 169评论 0 0
  • 初秋,在山道上,邂逅一簇不知名的野花,黄而淡,有幽幽的馨香。风吹过,花朵颔首低眉,仿佛和我说着深深浅浅的心事,聊着...
    惠风和畅806阅读 290评论 2 2