architecture-components

版权声明:本文为博主原创文章,转载请注明出处(http://blog.csdn.net/jaden_hool) https://blog.csdn.net/Jaden_hool/article/details/78974672

开发者经常面临的问题

Android应用由四大组件构成,各组件可以被独立且无序的调起,用户会在各个App之间来回切换。组件启动后,生命周期会受用户的操作和系统影响,不完全受开发者控制。而由于设备内存问题,进程随时可能被系统强杀,所以不要将数据和状态直接存储在组件中,也不要让组件互相依赖。

问题实例

  • 内存泄漏:在Activity中发起网络请求,在网络请求返回之前退出Activity, 那么Activity就被泄漏了。

  • 崩溃:Activity destroy后,还被其他类操作,从而引发崩溃,例如Glide图片库;

  • Activity类臃肿:数据,逻辑,控件代码都堆积在Activity中,导致Activity臃肿,不易维护和测试。

  • Fragment通信困难:开发中经常会遇到Activity中含有多个Fragment的情况, 并且Fragment之间需要通信。通常会利用Activity转发Fragment之间的通信,而且Fragment之间还需要依赖对方的生命周期和通信细节。

  • 数据易销毁:如果将内存中的数据保存在Activity中,由于Activity很容易被销毁重建,那么数据也就很容易被销毁。

通用的框架原则

  • 关注点分离:不要在Activity/Fragment中添加非UI控制、非系统接口调用的代码。尽量让他们保持精炼,以免引起生命周期相关的问题。这些类是系统创建和管理的,并不完全受开发者控制。

  • 模型驱动UI:应该用数据模型驱动UI展示,最好是持久模型,因为当系统强杀进程或者网络不稳定时,持久模型能让程序继续工作。模型独立于组件之外,不会受到生命周期的影响。

应用框架组件

Android官方在17年IO大会上发布了一套框架组件,帮助开发者开发优质的App.

LifeCycle

将Activity/Fragment的生命周期剥离到其他类中,减少组件类中的代码。

LifeCycle用两个枚举类追踪组件的状态。
- Event:从Framework层分发的,匹配Activity和Fragment中的生命周期方法回调。
- State:当前组件的状态。

使用方式
public class LearnLifeCycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop() {

    }
}

public class LearnLifeCycleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLifecycle().addObserver(new LearnLifeCycleObserver());
    }
}
实践
  • 将UI和数据分离,UI层不需要持有数据。

  • 构建数据驱动UI的应用,UI控制器负责根据数据更新UI以及将用户的操作反馈给数据层。

  • 将数据逻辑放在ViewModel中,将数据拉取逻辑放在其他合适的组件中,例如repository.

  • 使用MVP模式,引入Presenter, 让程序更加容易被测试。

LiveData

一个自带观察者模式的数据封装类,不同的是LiveData有生命周期,会利用LifeCycle自动监听与它绑定的组件的生命周期。当数据发生改变时,Activity或者Fragment要处于活动状态,观察者才会接收到数据改变的回调,此时界面就可以安全的进行渲染,而不会出现Activity销毁后还更改界面的情况。

优点
  • 不会造成泄漏内存。
  • 不会由于操作销毁的Activity而发生崩溃。
  • 不需要手动处理生命周期。
  • 数据总能实时更新。
  • 数据不会受configuration更改的影响。
  • 易于共享享数据。
使用

final MutableLiveData<String> userNameLiveData = new MutableLiveData<>();
userNameLiveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        //Activity的lifecycle state处于STARTED或RESUMED时才会回调
        mUserNameTV.setText(s);
    }
});
mUserNameSetBtn.setOnClickListener(new View.OnClickListener(){

    @Override
    public void onClick(View v) {
        //更新LiveData的数据,以尝试触发观察者的通知
        userNameLiveData.setValue("jayden");
    }
});
两个扩展类
MutableLiveData

开放了setValue和postValue方法,用于主动改变LiveData的值,并通知观察者。

MediatorLiveData

可以同时观察多个LiveData, 当被观察的LiveData发生改变时,可以对LiveData的数据进行加工后再通知MediatorLiveData.

转换LiveData
Transformations#map

利用MediatorLiveData将LiveData的数据加工后再通知给观察者。

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
Transformations#switchMap

和map方法类似,而且也是用MediatorLiveData实现。但是switchMap方法的第二个形参接口的返回值是LiveData, 当userId发生改变后,getUser(id)返回另一个LiveData: liveData1, user的观察者开始观察liveData1的变化。

举个例子:id为1的用户切换成id为2的用户后,getUser(id)方法返回的LiveData就是id为2的用户的用户信息,以后如果id为2的用户信息发生改变,user的观察者就会接收到通知。

private LiveData<User> getUser(String id) {
  ...;
}

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

ViewModel

用来存储和管理和UI相关的有生命周期的数据。当configuration改变,例如屏幕旋转,语言切换时,ViewModel中的数据不会被销毁。

创建ViewModel

利用LiveData持有数据

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;

    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // 异步拉取数据
    }
}

在Activity中监听数据改变

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        //监听LiveData
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果由于屏幕旋转或语言切换导致Activity重建,ViewModelProviders.of(this).get(MyViewModel.class);获取的ViewModel还是Activity首次创建时所构建的,只有当Activity销毁后,ViewModel才会被清除。

ViewModel的生命周期

在多个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 onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

两个Fragment拿到的ViewModel对象是同一个。

这种方式的优点:
- Activity不需要了解Fragment之间的通信,完全松耦合。
- 两个Fragment都不需要依赖对方的生命周期和通信细节,即使一个Fragment被销毁,也不会影响另一个Fragment.

最终的框架

推荐的框架原则

  • 在manifest文件中定义的程序入口:activities, services, broadcast receivers等,都不能作为数据的来源。

  • 明确定义各模块的职责。

  • 尽可能少地暴露每个模块的信息。

  • 定义模块间的交互时,考虑如何让他们易于测试。

  • 将数据持久化,让程序在离线时更加可用。

  • 数据存储库应该指定一个数据源作为单一的数据来源。

推荐阅读

猜你喜欢

转载自blog.csdn.net/Jaden_hool/article/details/78974672