Android开发框架【库】

Android Architecture Components

2018-03-12  本文已影响267人  前行的乌龟

前言


Android Architecture Components,简称 AAC ,是 Google IO 2017 大会新推出的 app 架构,从使用感受上说是对 MVVM 的官方加强,AAC 可以解决我们常见的一些内存泄露的场景使用,添加了更易操作的 view 生命周期管理。

拓展一下,MVC / MVP / MVVM 这种代码架构组织方式都是基于:UI 驱动数据 ,我们常用的 EventBus 框架则是:数据驱动 UI。数据和 UI 谁驱动谁,这其实就是我们架构的核心,可以决定我们如何组织代码机构,如何具体的书写代码。

仔细想了2天,我说下自己的体会:

之前我页尝试过使用 EventBus 来搭建我的事件驱动型 app 架构,尝试过会发现问题很多,info 类爆炸,内存泄露等问题很难解决,不是我一个人恩那个解决的。但是今天我们带来的这个官方的 AAC 架构 ,就是基于事件驱动型的新的架构体系,大大解决了我们在组件化过程中的痛点,可以实现高度解耦。

大家可以试想,EventBus 是一个中间件,一个可以在所有 module 中通用的,EventBus 内部维护了所有的消费 info 数据的消费对象,EventBus.getInstance() 我们可以注册消费对象,这就简单的实现了 module 之间的解耦,module A 提供数据,不论游多少 module 去消费这个 info 数据,module A 都可以不关心,页不用持有这些消费 module 的引用。这就是基于事件的 app 架构的基础,之后不论游多复杂的架构,都是基于这个思想的,基于事件的架构核心痛点就是:高度解耦。具体应用应用就是为组件化,插件化扫平障碍

大家也可以看看我摘录别人博客的一些思路:


Snip20180313_1.png

我说的也是我自己个人的认识,有异议欢迎大家在下面喷我啊,一定要喷啊......

学习资料


AAC 的是去年出的,第一时间获得了高关注,奈何本人小白一个,现在才刚刚看到这个 AAC 架构,网上的学习资料很多,基础的部分大家还是详细去看看我贴出来的资料,我就简单的总结一下知识点,然后会重点说一下我的认识。

基础学习资料:

官方demo:

官方视频学习资源:

开源学习 demo:

本文 demo:

补充资料:

AAC 主要内容


上面4个就是这次 AAC 架构的核心 API 了,通过这几个 API 我们可以搭建一套基于事件的 app 架构出来,LiveData 和其他一些 API 可以简单的看做是 Google 版的 RXJAVA ,只不过是针对 android 系统的,功能上也是很简单,没有 RXJAVA 那么强大的变换和线程控制。但是这正是我们所需要的,因为有了 RXJAVA ,我们在复杂的业务场景中用 RXJAVA 就好了,LiveData 使用简单,学习成本低,我们用来搭建基于数据流的响应式架构体系的 app 是最合适的。

AAC 架构图如下:


AAC 架构图

添加依赖


 allprojects {
        repositories {
            jcenter()
            maven { url 'https://maven.google.com' }  //添加此行
        }
    }
//For Lifecycles, LiveData, and ViewModel
compile "android.arch.lifecycle:runtime:1.1.0"
compile "android.arch.lifecycle:extensions:1.1.0"
annotationProcessor "android.arch.lifecycle:compiler:1.1.0"

//For Room
compile "android.arch.persistence.room:runtime:1.1.0-alpha1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.0-alpha1"

// For Room RxJava support, add:
compile "android.arch.persistence.room:rxjava2:1.1.0-alpha1"

Lifecycle


很多文章说的废话多,不直接, Android 页面生命周期加强,把页面的生命周期变成一个 Observable ,这个 Observable 我们无法直接使用,而是配合注解写在需要 hook 生命周期的位置,最大的好处就是脱离了 callback 或是 Proxy 代理类,在代码上灵活太多了,Lifecycle 会扫描所有代码中有标记的方法,然后注册给生命周期的 Observable 对象里面去,这种写法是不是和 EventBus 一样啊。最直接的在写代码时这就是 基于事件的魅力,我们不用再耗费心神设计代码结构,写那些 callback / Proxy 代理类了。

所以 AAC 这个基于事件的架构是学起来最通透的,一切都是 Observable / Observer,都是观察者和被观察者的关系,一切都是基于注册的方式运行。

既然一切都是 Observable / Observer 的,那么管理页面生命周期的 Observable / Observer 哪里来呢,这就要说到 LifecycleActivity / LifecycleFragment 了,我是用 API 26做基准编译代码的,AppCompatActivity / V4 包下的 Fragment 都已经兼容了 AAC 架构了,所以 LifecycleActivity / LifecycleFragment 过时了,大家注意一下:


LifecycleActivity 过时了
LifecycleFragment 过时了

然后我们通过这个 API 可以获取这个 Observable Lifecycle

Lifecycle lifecycle = getLifecycle();

那么这个 Observer 呢,我们直接 new 一个 LifecycleObserver 对象出来也行,或是某个类实现这个接口也行,最后注册到 Observable 就行,下面看代码:

MyLifecyleTextView 实现 LifecycleObserver 接口

@SuppressLint("AppCompatCustomView")
public class MyLifecyleTextView extends TextView implements LifecycleObserver {

    public MyLifecyleTextView(Context context) {
        super(context);
    }

    public MyLifecyleTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyLifecyleTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public MyLifecyleTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void creat() {
        Log.d("AAA", "oncreat...");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void start() {
        Log.d("AAA", "onstart...");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void resume() {
        Log.d("AAA", "onresume...");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void pasue() {
        Log.d("AAA", "onpause...");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void stop() {
        Log.d("AAA", "onstop...");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void destroy() {
        Log.d("AAA", "ondestroy...");
    }

}

综合使用

public class MainActivity extends AppCompatActivity {

    private MyLifecyleTextView tx_lifectle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tx_lifectle = findViewById(R.id.tx_lifecley);

        getLifecycle().addObserver(tx_lifectle);

        getLifecycle().addObserver(new LifecycleObserver() {

            @OnLifecycleEvent(Lifecycle.Event.ON_START)
            public void start() {
                Log.d("AAA", "new_onstart...");
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
            public void resume() {
                Log.d("AAA", "new_onresume...");
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
            public void pasue() {
                Log.d("AAA", "new_onpause...");
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            public void stop() {
                Log.d("AAA", "new_onstop...");
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
            public void destroy() {
                Log.d("AAA", "new_ondestroy...");
            }

        });

    }
}

上面的代码基本包含了 Lifecycle 的常用应用场景了,不管你事让某个自定义 view hook 某个生命周期也行,还是直接注册一个观察者对象也行,在代码上我们完全脱离了页面生命周期函数里,页面或是 penserent ,viewmodule 都不用再保存相关的 callback ,proxy,listener 在页面的生命周期函数里执行了,在 app 代码层面实现了基于观察者模式的代码思路。减少强制持有属性,功能,代理对象,这也是一种解耦啊,和以前对象.方法比起来,代码是越写越灵活啊,这就是趋势啊。

好了,上面 XBB 了一下,Lifecycle 还没说完呢,我们了解了 Lifecycle 如何使用,代码里我们可以看到有很多带注解的函数

 @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            public void stop() {
                Log.d("AAA", "new_onstop...");
            }

既然说了 Lifecycle 像 EventBus ,那么大家就不会对这注解函数陌生了吧,我来说说这个 Lifecycle.Event.ON_STOP 。API 的名字起来很规矩,大家一看应该就知道了,ON_STOP 就是代表了 onStop() 这个生命周期函数了,以此类推就不用多重复了

另外通过 Lifecycle 我们还可以获取当前 view 的生命周期状态,这在之前是需要自己去维护的,有了这个 API 我们在很多时候会方便很多啊。

Lifecycle.State currentState = getLifecycle().getCurrentState();
        if( currentState == Lifecycle.State.STARTED ){
              xxxxxxxxx
        }

另外官方有 Lifecycle 对应的生命周期图,大家看看


lifecycle-states.png

Lifecycle 注册的生命周期回调方法需要说一下是即使生效的,这点必须要说清楚,不说有的童鞋可能会以为第一次无效,其实我们想想,对于声明周期来说,第一次无效是不符合设计思路和场景需求的。

Lifecycle 的最后我得数据说一下这个接口 LifecycleRegistryOwner ,AppCompatActivity / V4 包下的 Fragment 都是通过实现这个接口来兼容 AAC 架构的,其实这个接口很简单,里买就一个方法,需要我们返回 Lifecycle 的核心功能类 LifecycleRegistry 即可。看代码,自己实现 Lifecycle :

import android.arch.lifecycle.LifecycleRegistry;
import android.arch.lifecycle.LifecycleRegistryOwner;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity implements LifecycleRegistryOwner {

    private LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        proxy.zj.com.lifecycledemo.MyTv mTv = findViewById(R.id.dfttv);
        mTv.setLifecycle(getLifecycle());
        getLifecycle().addObserver(mTv);
        mTv.setLifeCycleEnable(true);

    }

    @Override
    public LifecycleRegistry getLifecycle() {
        return lifecycleRegistry;
    }
}

其实我们用不到 LifecycleRegistryOwner 这个借口,但是为啥要说呢,系统只是在 Activity 和 Fragment 层面兼容了 AAC ,我们要是需要自己的自定义 view 对外提供声明周期管理的话记得自己实现了。按着上面的代码来就行,很简单,一看就会,不会来找我。

摘一段官方文档的翻,出自:[译] Architecture Components 之 Handling Lifecycles

Lifecycles 的最佳实践


liveData


liveData 简单来说就是一个可以根据观察者自身生命周期,在观察者需要结束时自动解绑的 Observable,并且结合了 DataBingding 的特点,liveData 自身数据改变时可以通知所有的观察者对象。哈哈,所以说完上面 Lifecycle 才能来看 liveData ,这个生命周期管理自然是依托 Lifecycle 了,他俩本身就是一个体系下的东东啊。

liveData 是 abstract 的一个类,有3个关键方法:

其他不多说,先来看看一个最简单的 liveData 例子:

MyApplication

public class MyApplication extends Application {

    private static MyApplication INSTANCE;
    public MyLiveData liveData;

    public static MyApplication getInstance() {
        return INSTANCE;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        INSTANCE = this;
        liveData = new MyLiveData();
    }

LiveData

public class MyLiveData extends LiveData<String> {

    @Override
    protected void setValue(String value) {
        super.setValue(value);
        Log.d("BBB", "setValue..." + value);
    }

    @Override
    protected void onActive() {
        super.onActive();
        Log.d("BBB", "onActive...");
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.d("BBB", "onInactive...");
    }
}

MainActivity

public class MainActivity extends AppCompatActivity {

    private MyLifecyleTextView tx_lifectle;
    private Button btn_01;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        tx_lifectle = findViewById(R.id.tx_lifecley);
        getLifecycle().addObserver(tx_lifectle);

        getLifecycle().addObserver(new LifecycleObserver() {

            @OnLifecycleEvent(Lifecycle.Event.ON_START)
            public void start() {
                Log.d("AAA", "new_onstart...");
            }
        });

        btn_01 = findViewById(R.id.btn_one);
        btn_01.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                data.setValue("AAAAAAAAA");
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });

        MyApplication.getInstance().liveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                Log.d("BBB", "数据改变..." + s);
            }
        });
}

liveData 是可以使用泛型的,Google 推出 liveData 的初衷,就是用来包裹数据,包装成一个 Observable 的,所以一定要支持泛型才能使用的方便啊。

这里提醒一句,有人推荐使用 MutableLiveData ,我的确是看到游 demo 使用 MutableLiveData 了。

这个例子,我们重点来看一下 onActive / onInactive 这个2个方法和观察者生命周期的互动,这点很重要,弄懂这点,这2个方法我们才能得心应手,要不会出问题的。 我会打印 mainactivity 的生命周期函数 和 livadata 的相关方法

mainactivity 启动 启动一个新的页面 返回 mainactivity 页面

根据上文所说, liveData 注册的观察者数量从 0 到 1时会执行 onActive , liveData 注册的观察者数量回到 0 时会执行 onInactive ,我们看看这个经典的例子,一个页面启动另一个页面再回来,这包含一个完整的生命周期了。

那我们要是再第二个页面 oncreat 函数里面也注册一个观察者呢,大家猜猜啊,挺有意思的。

启动一个新的页面 返回 mainactivity 页面

观察以上,可以得出一下结论:

另外我们可以获取 liveData 里面的数据

liveData.getValue();

观察者在注册到 liveData 后,不会触发执行一次 setvalue 方法,这点搞清楚基本就 OK 了

Lifecycle 和 liveData 综合使用的例子


看这个 UserData 他可以在 view 生命周期变动时调 setvalue 方法,通知所有观察者数据有变动

public class UserData extends LiveData implements LifecycleObserver {

    private static final String TAG = "UserData";

    public UserData() {
    }

    @Override
    protected void onActive() {
        super.onActive();
        Log.e(TAG, "onActive");
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.e(TAG, "onInactive");
    }

    @Override
    protected void setValue(Object value) {
        super.setValue(value);
        Log.e(TAG, "setValue");
    }
}

这种情况比较少使用,你想这个 liveData 要和具体一个 view 的生命周期绑定,根据生命周期变动处理数据,发送新数据给所有注册者。在组件化的思路里,比较适合一个模块对外提供公共数据,然后这个模块有变动或是注销,再通知其他有数据关联的模块数据变动。

LiveData 有以下优点:


看一个经典的 LiveData 应用例子


一个定位的例子,这里写的简单,manage ,factroy ,都没写,但是这个例子展示了:如何用 LiveData 包装数据,结合单例作为 app 的全局数据使用,这个单例可以写再这个自定义的 LiveData 里,也可以写在 manage 里有管理类来管理要更好一点

public class LocationLiveData extends LiveData<Location> {

    private static LocationLiveData sInstance;

    private LocationManager locationManager;

    @MainThread
    public static LocationLiveData get(Context context) {
        if (sInstance == null) {
            sInstance = new LocationLiveData(context.getApplicationContext());
        }
        return sInstance;
    }

    private SimpleLocationListener listener = new SimpleLocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            setValue(location);
        }
    };

    private LocationLiveData(Context context) {
        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    }

    @Override
    protected void onActive() {
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
    }

    @Override
    protected void onInactive() {
        locationManager.removeUpdates(listener);
    }
}

Transformations


LiveData 就是一个 Observable ,那么官方提供了一个 Transformations 类,包含 map 和 switchMap 转换操作,和 Rxjava 的 map 、flatMap 一样,

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
private LiveData<User> getUser(String id) {
  // ... 这里可以通过远程获取数据
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

map() 方法接受一个数据,返回一个新的数据,这个看着不难,switchMap() 方法接受一个数据,然后依赖另一个 LiveData 加工,然后返回数据。要是看着不太懂的话,看这里:[译] Architecture Components 之 LiveData

ViewModel


这个最好理解,ViewModel 就是 MVP 中,P 的角色,当然 ViewModel 还有他的独特之处。ViewModel 的生命周期虽然总体上还是跟着 view 的,但是 ViewModel 存在的时间比 view 要长一些。我们看看这几个场景:

ViewModel 生命周期图

ViewModel 生命周期图表明了 ViewModel 的生命周期是有些区别,单总体趋同与 view 的生命周期的,提供了一种新的 view 数据保存模式,比如屏幕旋转时 activity 重新创建 ViewModel 还是原来那个对象。

ViewModel 是个基类,需要我们继承他,没有特殊方法需要去处理,集成完后,直接创建对象就可以使用了。

  MyViewModule myViewModule2 = ViewModelProviders.of(this).get(MyViewModule.class);

还有一个 AndroidViewModel ,期中可以获取 application ,只不过这个 application 需要我们自己传入,另外 ViewModel 的 of 方法还可以接受一个 ViewModelProvider.NewInstanceFactory 的参数,可以支持自定义构造方法,想传几个参数都没问题

public class MyViewModule extends AndroidViewModel {

    public String name;

    public MyViewModule(@NonNull Application application, String name) {
        super(application);
        this.name = name;
    }

    public void show() {
        Toast.makeText(getApplication(), "测试...", Toast.LENGTH_SHORT).show();
    }

    public static class Factroy extends ViewModelProvider.NewInstanceFactory {

        public Application application;
        public String name;

        public Factroy(Application application, String name) {
            this.application = application;
            this.name = name;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            return (T) new MyViewModule(application, name);
        }
    }
}

创建 MyViewModule 对象

MyViewModule.Factroy factroy = new MyViewModule.Factroy(getApplication(), "AAA");
MyViewModule myViewModule = ViewModelProviders.of(this, factroy).get(MyViewModule.class);

使用 ViewModule 可以在同一个 actitivity 的多个 Fragment 之间共享数据

public class SharedViewModel extends ViewModel {  
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();  
    public void select(Item item) {  
        selected.setValue(item);  
    }  
    public LiveData<Item> getSelected() {  
        return selected;  
    }  
}  
public class MasterFragment extends Fragment {  
    private SharedViewModel model;  
    public void onActivityCreated() {  
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);  
        itemSelector.setOnClickListener(item -> {  
            model.select(item);  
        });  
    }  
}  
public class DetailFragment extends LifecycleFragment {  
    public void onActivityCreated() {  
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);  
        model.getSelected().observe(this, { item ->  
           // 更新 UI  
        });  
    }  
}  

写到这里其实我们可以看到一个问题,ViewModel 内部不应该持有外部 view 的引用,ViewModel 的声明周期比 Fragment / Activity 都长,ViewModel 要是持有了外部 view 的引用就会造成内存泄露,需要注意!

ViewModel 和外部 view 的交互看过上面的大家应该都明悟了,就是 LiveData 了,用这个 LiveData 代替 view 的交互接口同 ViewModel 通信,ViewModel 返回 LiveData ,然后 view 获取这个 LiveData 再去注册 updata 方法,这样避免 ViewModel 持有外部 view 造成的内存泄露

ViewModel vs SavedInstanceState

ViewModels 提供了一种在配置更改时保存数据的简便方式,但是如果应用进程被操作系统杀死,那么数据则没有机会被恢复。

通过 SavedInstanceState 保存的数据,存在于操作系统进程的内存中。当用户离开应用数个小时之后,应用的进程很有可能被操作系统杀死,通过 SavedInstanceState 保存的数据,则可以在 Activity 或者 Fragment 重新创建的时候,在其中的 onCreate() 方法中通过 Bundle 恢复数据。

Room


谷歌推出的一个Sqlite ORM库,不过使用起来还不错,使用注解,极大简化数据库的操作,有点类似Retrofit的风格

不过 Room 还是没有脱离 SQL 语言,在注解中还是要写 SQL 语句的,一般移动端的 数据库没有太高的要求,这样我们其实可以使用 Room 这个数据库,Room 存的是对象,取的也是对象,是对象类型数据库了。要是 app 项目的数据库要求高的话,在来使用 Room 也是个不错的选择

Room 的详细学习看这个:浅谈Android Architecture Components | Android 架构组件 Room 介绍及使用

AAC 框架带给我们的改变


上面我们已经基本看过了 AAC 框架新的 API ,经过 AAC 改造,我们不用担心内存泄露的场景了,页面的生命周期函数处理可以写在页面之外,更灵活了,整个 app 的架构可以像 Rxjava 一样,改成基于数据和事件的流式架构了,可以让我们更简单容易分离,组合代码结构。

说带具体的还是 LiveData 对数据层的改造对我们影响最大,我们再来看看上面官方给出的 AAC 架构图


AAC 架构图

Google 官方的页面,数据流程如下:

很明显,这是一个最简单的数据,显示例子,展示的一个页面拥有一个 LiveData 对象来更新数据,这是传统的逻辑思路。但是大家想想,LiveData 是数据流式的,我们完全可以让 Respositroy 返回一个静态的全局的 LiveData ,需要的页面去注册 updata 方法,页面关闭的时候可以自动接触绑定,这才是 LiveData 的初衷啊,当然使用嘛,不要死板,怎么灵活,怎么简单,便利,怎么来。

上面涉及到我们对数据的应用范围了和类型了,LiveData 的出现让我们对数据的包装使用产生了一些思考

数据的作用范围我分为3种:

数据的类型我分为2种:

结合不同的数据的应用范围,对数据的处理:

数据的经典应用场景举例:

总结一下就是用 LiveData 改造了一下 repositroy 数据层,UI 层通过 ViewModule 获取这个 LiveData,建立通道联系刷新数据,这样把整个 app 基于数据流改造成响应式架构,适应组件化,平台化高度封装,业务模块分离的需求。

本文 demo 思路


首先本文 demo 如下:

思路是用 LiveData 改造了一下 repositroy 数据层,UI 层通过 ViewModule 获取这个 LiveData,建立通道联系刷新数据。简单用 RXJava 延迟 2 秒发送一条数据,模拟一下网络请求。

比较好的代码研究资料:

我的 demo 思路如下:


device-2018-03-30-205908.png

先来看看我的页面,主界面中游2个红色背景的 textview 分别用来显示2个级别的数据:单次请求的网络数据和全局数据,全局数据在另外一个页面中加载,然后再返回看看 LiveData 的同步效果是否好用。

数据层都封装再 Respositroy 中,Respositroy 持有一个 netDataSource 用于获取网络数据,然后 Respositroy 处理数据然后返回。其中数据都是用 response 来包裹,response 中有 code 用来发送数据加载中的各种状态

Snip20180330_5.png

画了一个简单的数据流动图,可以看到我这里的 Respositroy 数据层封装的还是比较简单的,Respositroy 内部只维护了一个 BookNetDataSource 远程数据源。

来简单看下代码:

BookNetDataSource -> 远程数据源

public class BookNetDataSource {

    private Book mPrivateBook = new Book("私有数据在此");
    private Book mPublicBook = new Book("共有数据在此");

    public Observable<Book> getPrivateBook() {
        return getData(new Book("私有数据如如下:private"));
    }

    public Observable<Book> getPublicBook() {
        return getData(new Book("全局数据如如下: public"));
    }

    private Observable<Book> getData(final Book book) {
        return Observable.timer(2, TimeUnit.SECONDS)
                .map(new Function<Long, Book>() {
                    @Override
                    public Book apply(Long aLong) throws Exception {
                        return book;
                    }
                }).subscribeOn(Schedulers.io());
    }
}

BookNetDataSource 返回了2个数据,private 数据表示一次性数据,public 表示全局缓存数据,用 rxjava 延迟2秒模拟一下网络状态

BookRespositroy 数据层

public class BookRespositroy {

    public static MutableLiveData<Response<Book>> PUBLIC_LIVEDATA;

    private MutableLiveData<Response<Book>> mBookLiveData;
    private BookNetDataSource mBookNetDataSource;

    static {
        PUBLIC_LIVEDATA = new MutableLiveData<>();
    }

    public static MutableLiveData<Response<Book>> getPublicLiveData() {
        return PUBLIC_LIVEDATA;
    }

    public static void refreshPublicData() {
        PUBLIC_LIVEDATA.setValue(new Response<Book>(Response.CODE_LOADING, null));
        new BookNetDataSource().getPublicBook()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Book>() {
                    @Override
                    public void accept(Book book) throws Exception {
                        PUBLIC_LIVEDATA.setValue(new Response<Book>(Response.CODE_SUCCESS, book));
                    }
                });
    }

    public BookRespositroy() {
        mBookNetDataSource = new BookNetDataSource();
        mBookLiveData = new MutableLiveData<>();
    }

    public MutableLiveData<Response<Book>> getPrivateLiveData() {
        return mBookLiveData;
    }

    public void refreshPrivateData() {
        mBookLiveData.setValue(new Response<Book>(Response.CODE_LOADING, null));
        mBookNetDataSource.getPrivateBook()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Book>() {
                    @Override
                    public void accept(Book book) throws Exception {
                        mBookLiveData.setValue(new Response<Book>(Response.CODE_SUCCESS, book));
                    }
                });
    }
}

PUBLIC_LIVEDATA 这个是全局缓存数据,这个由 Respositroy 数据层维护是比较恰当的,mBookLiveData 这个是一次性数据,这个一次性对应的是 BookRespositroy 这个对象的生命周期,MainActivity 通过 viewModule 获取到这2个 LiveData 管道然后注册自己的 UI 刷新方法

省略无关代码

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private MyViewModule myViewModule;
    private ProgressDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 初始化 DataBinding
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        // 使用自定义构造器方式创建 ViewModule 对象
        MyViewModule.Factroy factroy = new MyViewModule.Factroy(getApplication(), "AAA");
        myViewModule = ViewModelProviders.of(this, factroy).get(MyViewModule.class);

        // 从 ViewModule 中获取页面私有数据源的管道,建立联系,Response 中包含请求响应状态码
        myViewModule.getPrivateBookLiveData().observe(this, new Observer<Response<Book>>() {
            @Override
            public void onChanged(@Nullable Response<Book> response) {
                if (response == null) {
                    return;
                }
                int code = response.code;
                if (code == Response.CODE_LOADING) {
                    dialog.show();
                    return;
                }
                if (code == Response.CODE_SUCCESS) {
                    dialog.dismiss();
                    binding.setViewData(response.data.getName());
                    return;
                }
            }
        });

        // 从 ViewModule 中获取全局公共数据源的管道,建立联系
        BookRespositroy.getPublicLiveData().observe(this, new Observer<Response<Book>>() {
            @Override
            public void onChanged(@Nullable Response<Book> response) {
                if (response == null) {
                    return;
                }
                int code = response.code;
                if (code == Response.CODE_LOADING) {
                    dialog.show();
                    return;
                }
                if (code == Response.CODE_SUCCESS) {
                    dialog.dismiss();
                    binding.setAppData(response.data.getName());
                    return;
                }
            }
        });
    }

最后 MainActivity 通过 viewModule 通知 respositroy 获取数据,然后刷新 livedata ,livedata 调用 setVale 方法就能通知所有注册其上的对象更新数据了。

 public void refreshPrivateData() {
        mBookLiveData.setValue(new Response<Book>(Response.CODE_LOADING, null));
        mBookNetDataSource.getPrivateBook()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Book>() {
                    @Override
                    public void accept(Book book) throws Exception {
                        mBookLiveData.setValue(new Response<Book>(Response.CODE_SUCCESS, book));
                    }
                });
    }

注意这里面,loading 的状态是由 respositroy 发送的而不是 viewmodule ,加载状态通过 response 中的 code 码来承载。

核心点:

最后


最后想说一下 respositroy 数据层的封装一定要量力而行,我这里 respositroy 层支持持有 net 远程数据源了,这个一般即使我们的需求了,没必要过度去封装,反而不美。但是要是对数据游比较多的缓存需求,那么就需要再 respositroy 中封装一个 DataSource 来管理数据源了,比如图片加载库,glide 、fresco ,他们都是对数据缓存有要读要起的,你看代码的代码思路专门有一个数据源管理类对外提供数据。

另外若是 respositroy 数据层中业务处理很复杂的话,我们可以把具体的业务逻辑处理分离成u 一个个的 helper 类,google 源码中就有很多的 helper 类来辅助管理具体的功能

若是 app 设计有自己的特殊业务 code 的话,我们可以把处理放在 respositroy 数据层中,在 app 初始化时配置一个静态的处理类出来,然后我们在 respositroy 数据层中获取这个处理类来优先处理特务业务 code ,为啥不放在 respositroy 的基类中,是因为很多 api 我们不需要处理特殊 code ,为了代码灵活一下牺牲下代码封装性。

ps:最后的最后,说一下写的不好,我的认识也浅显,欢迎大家踊跃喷我,希望通过大家的评论提高进步。

上一篇下一篇

猜你喜欢

热点阅读