玩转Android Jetpack系列之ViewMode

栏目: IOS · Android · 发布时间: 6年前

内容简介:Android Jetpack是谷歌在2018年I/O开发者大会上推出的新一代组件、工具和架构指导,旨在加快开发者的 Android 应用开发速度。 ——[官方介绍网站]Google爸爸老司机在2018年的I/O大会上发车了,推出了新一代的开发组件、工具和架构指导,并打包在一起,取名为“Jetpack”,顾名思义,Jetpack直译过来的意思就是喷气背包,Google也使用了一个很形象的穿戴喷气背包的android机器人来做形象代言,如下图。通过图片可以很明确的感受到谷歌爸爸的意图:带你飞!。简单的概况,J

1. 什么是Android Jetpack?

Android Jetpack是谷歌在2018年I/O开发者大会上推出的新一代组件、 工具 和架构指导,旨在加快开发者的 Android 应用开发速度。 ——[官方介绍网站] 1

Google爸爸老司机在2018年的I/O大会上发车了,推出了新一代的开发组件、工具和架构指导,并打包在一起,取名为“Jetpack”,顾名思义,Jetpack直译过来的意思就是喷气背包,Google也使用了一个很形象的穿戴喷气背包的android机器人来做形象代言,如下图。

玩转Android Jetpack系列之ViewMode

通过图片可以很明确的感受到谷歌爸爸的意图:带你飞!。简单的概况,Jetpack推出的主要目的是为了能够让开发者更加快速、方便以及高质量的完成产品开发,Jetpack的主要作用可以概况为以下几点:

  • 加速开发速度,Jetpack包含的一系列组件可以分开使用,也可以相互组合使用,同时还完美支持Kotlin语言的功能特点进一步提升开发效率
  • 聚焦开发的核心,Jetpack提供的组件可以协助管理日常繁琐且容易出错的地方,比如生命周期的管理,后台任务的管理,导航的处理等等,这样开发可以将开发的注意力放到更加核心的地方上。
  • 提升应用开发的质量,利用Jetpack组件进行开发可以有效减少内存溢出、崩溃的概率,提升应用开发的质量,并提供向后的兼容性

Jetpack组件主要分为四个方向:基础,架构,行为和UI。详情见下表:

基础 架构 行为 UI
AppCompat Data Binding Download Manager Animation & transitions
Android KTX Lifecycles Media & playback Auto
Multidex LiveData Notifications Emoji
Test Navigation Permissions Fragment
- Paging Sharing Layout
- Room Slices Palette
- ViewMode - TV
- WorkManager - Wear OS by Google

如上表格,有些内容是很早就有的,有些是最近推出的,Jetpack将这些内容打包在一起,共同组成Jetpack,本系列主要对Jetpack新推出的架构这一块内容进行叙述。

2. ViewMode概述

ViewMode主要用来管理和存储与UI绑定的数据,同时ViewMode还与UI的生命周期相关联。例如ViewMode的一大特色在于:与ViewMode相关联的UI界面如果因为某些原因需要重新绘制创建时,例如横竖屏切换,导致Activity销毁并重新创建时,ViewMode仍然可以保留之前读取到的数据不会因为Activity的销毁而丢失,这样我们无需额外再浪费资源去再次请求数据。

有时候Activity或者Fragment的生命周期的变动是不受控制的,经常会因为各种事件的调度导致界面需要重新创建。当Activity需要重新创建的时候,之前与之绑定的数据也会丢失。比如你的应用界面通过list来展示用户名单,如果界面重新创建时,之前获取的用户名单数据需要再次重新获取,但是用户名单数据相对来说是一个比较稳定的静态数据,再次获取一次数据显然浪费了系统资源。

有的同学看到这里后可能会有疑问:不对呀,Android中不是提供了onSaveInstanceState()方法来保存数据吗,然后重新执行OnCreate的时候,通过Bundle参数来再次获取保存的数据?这种方式也是可行的,但是有个限制,即这种方式只能保存数据量较小的情况,并且数据被序列化才行。如果遇到数据量较大的时候,比如图片数据,这种方案显示力不从心了。

我们在日常请求数据后对UI进行绑定还有一个常见的问题,有时候我们会把数据的的请求放到异步去操作,这样不会因为长时间获取数据导致UI进程的堵塞。但是随之带来的问题也挺多,例如我们需要管理和维护好获取到数据后的回调,另外在销毁当前UI的时候,我们需要确保异步任务中的资源有效的得到了清理,防止出现内存溢出。一旦我们的界面需要重新绘制的时候,我们上述所有的异步操作需要重新创建和执行,这样显然浪费了系统的开销。

我们日常使用的Activity或者Fragment,他们的主要职责就是展示UI,以及与用户的操作行为进行交互,或者与系统的一些事件进行通信,例如权限管理对话框。如果还要求Activity对数据请求的事件进行管理和维护,这个已经超出了Activity本来的意图,并且随着事务的增多,Activity会越来越臃肿,一旦出了问题,需要花费大量精力去维护。

ViewMode的推出正是基于上述问题给出的解决方案,完美高效的将UI控制器和数据业务进行分离,UI控制器只负责UI展示相关的工作,数据业务只负责获取数据的相关工作。

3. ViewMode的使用方法

通过上文的铺垫,那么如何使用ViewMode呢,为了方便说明,本文采用一个简单的例子,通过Activity中的list展示一组用户的姓名,下面详细进行说明。ViewMode是一个抽象类,所以我们需要通过extends集成它才能够使用,代码如下所示:

public class MyViewMode extends ViewModel {

    private MutableLiveData<List<User>> users;

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

    private void loadUsers(){
        String[] names = {"张三","李四","王五","John","小明","Leo","Wang","Li","Ha","Yun"};
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < names.length; i++){
            User user = new User();
            user.setName(names[i]);
            userList.add(user);
        }
        users.setValue(userList);
    }
}
复制代码

上述例子较为简单,代码行数没有多少,主要定义了一个类型为MutableLiveData变量:users与两个方法。这里的users就是我们用来存放数据的变量容器,可能有的同学注意到了,它的类型是MutableLiveData,这个类型是什么呢?看起来很眼熟。如果各位有印象的话,我们在上文中介绍Jetpack的时候,在介绍Jetpack构成的时候,架构中有一个LiveData。恩对,这个变量类型也是Jetpack的一员。可不要小看它,它的本领很多,我们会在下一文中单独对它进行讨论。大家只要这里记住,他是用来存储数据的容器即可,而MutableLiveData是对LiveData的扩展,主要实现了set和post方法来方便更改LiveData的值。

loadUser的方法很容易理解,为了方便测试,我们这里定义了一个字符串数组,然后通过for循环进行遍历存储到list中,最后通过LiveData提供的setValue方法将数据存放到users中。getUser是针对数据users的get方法,为了防止每次读取user的时候都要创建一次数据,对数据进行判空处理,只有为空的情况下才调用loadUser去加载数据。

ViewMode的实现方式就是这么简单,它不需要关心UI是如何呈现的,它只关心数据如何获取。完全与UI无关。
下面看下Activity的实现方式,主要代码如下:

public class MainActivity extends AppCompatActivity {

    private ListView mListView;

    private MyAdapter adapater;

    private List<User> mDatas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        MyViewMode model = ViewModelProviders.of(this).get(MyViewMode.class);
        model.getUsers().observe(this, new Observer<List<User>>() {
            @Override
            public void onChanged(@Nullable List<User> users) {
                mDatas = users;
                adapater.notifyDataSetChanged();
            }
        });
    }
    .
    .
    .
复制代码

看完代码是不是发现太简单了,没有什么多余需要说明的,主要是在OnCreate方法中,首先获取我们定义的MyViewMode,获取的方式是通过ViewModelProviders的of方法绑定activity的实例进行初始化,然后通过get方法来获取到MyViewMode的实例。

接着第二句通过调用我们在MyViewMode已经定义过的getUser方法来获取LiveData数据,LiveData数据可以再通过observe方法进行数据回调的返回,如上代码中的onChanged回调。所以我们只要在onChange方法中做好数据刷新UI的操作即可。

注意:ViewMode中不能引用任何View的实例,也不能引用任何持有Activity或者Context的实例。如果有些请求数据的情况必须用到Context,在继承ViewMode的时候,可以改为继承AndroidViewMode,这个类会返回一个带有Context的构造函数。

4. ViewMode的生命周期

ViewMode在其生命周期的范围内会一直保存在内存中,当依附的Activity被finish后才会销毁,或者当依附的Fragment detached后进行销毁。为了验证,我们在上述的例子中,每个生命周期中打出相应的log,然后在MyViewMode的loadUser和getUser也打出相应的log。然后程序运行后执行横竖屏切换来让生命周期重新Oncreate,然后查看得到的log如下:

2018-07-31 10:34:48.573 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onCreate
2018-07-31 10:34:48.607 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel getUsers
2018-07-31 10:34:48.607 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel loadUsers
2018-07-31 10:34:48.612 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onResume
2018-07-31 10:34:51.044 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onStop
2018-07-31 10:34:51.045 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onDestroy
2018-07-31 10:34:51.102 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onCreate
2018-07-31 10:34:51.142 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel getUsers
2018-07-31 10:34:51.148 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onResume
2018-07-31 10:35:01.437 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onStop
2018-07-31 10:35:01.438 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onDestroy
2018-07-31 10:35:01.485 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onCreate
2018-07-31 10:35:01.509 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel getUsers
2018-07-31 10:35:01.519 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onResume
2018-07-31 10:35:04.550 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onStop
2018-07-31 10:35:04.551 30315-30315/jinfeng.myapplication E/wangjinfeng: ViewModel onCleared
2018-07-31 10:35:04.551 30315-30315/jinfeng.myapplication E/wangjinfeng: MainActivity onDestroy
复制代码

当打开应用后,执行onCreate的时候,ViewMode会通过调用getUsers和loadUsers来获取数据,当切换手机横竖屏后,MainActivity会destroy并重新onCreate来重构当前界面,所以我们在log中会看到生命周期再次重新触发onCreate,但是ViewMode仅仅调用了getUsers来返回数据,并没有调用loadUsers,我们再回头看上面getUsers的逻辑,当判断users数据为空的情况下,才会去执行loadUsers,这样也就意味着,我们切换横竖屏后,activity被销毁并重建后,user的数据并没有丢失,所以并没有重新执行获取数据的操作。

细心的读者可能发现了,在日志的最后,ViewMode会执行onCleared操作,这个是ViewMode的一个回调,表明当前Activity要彻底关闭,ViewMode需要做一些回收清理的操作,如下代码:

@Override
    protected void onCleared() {
        super.onCleared();
        /**
         * 这里可以执行一些资源释放、数据清理的操作
         */
    }
复制代码

下面用一张图来标注ViewMode的生命周期与Activity的生命周期的关联,如下图所示:

玩转Android Jetpack系列之ViewMode

5. 应用举例:利用ViewMode进行Fragment之间的数据交互

我们在平日里会遇到这样的场景,比如文件管理器这个应用,文件管理器的主界面通过一个MainFragment进行封装实现,主要呈现各个文件类别的入口,比如有音乐、视频、图片、文档等的入口,点击对应的入口项,会进入对应的详情页,而详情页是由另外一个DetailFragment实现。我们传统的实现方案是在DetailFragment中抽象暴露出一些回调接口,然后当在MainFragment进行点击不同的入口时,执行DetailFragment中的回调接口来展示不同的Detail详情内容。

同样,我们也可以利用ViewMode来实现上述场景,关键代码如下所示:

public class SharedViewModel extends ViewModel {
    //selected保存的是被选中的item的状态或者数据
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    //主要通过masterFragment进行调用交互,用来更新selected中的值
    public void select(Item item) {
        selected.setValue(item);
    }

    //主要给detailFragment进行回调,用来通知selected的值的更新情况
    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 -> {
            //当点击某一个item的时候,更新viewmode中的selected的值
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //在onCreate中绑定ViewMode的selected的值,当有更新时通知DetailFragment
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, item -> {
           // Update the UI.
        });
    }
}
复制代码

上述代码的逻辑很简单,MasterFragment与DetailFragment并不直接进行交互,而是各自与ViewMode进行交互,MasterFragment用来更新维护ViewMode中的数据,DetailFragment可以收到来自ViewMode中数据更新的通知。这样便达到了两个frangment之间的数据通信。

6. 后记

ViewMode其实还有很多应用场景,但是需要和LiveData、Room等其他的JetPack构件一起使用效果更佳,所以这里先卖个关子,等后续内容将LiveData、Room等内容都涉及到后,我们统一利用这些构件来尝试做一些更牛叉的事情。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Lean Startup

The Lean Startup

Eric Ries / Crown Business / 2011-9-13 / USD 26.00

更多中文介绍:http://huing.com Most startups fail. But many of those failures are preventable. The Lean Startup is a new approach being adopted across the globe, chan ging the way companies are built and ......一起来看看 《The Lean Startup》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具