每天学习一个Android中的常用框架——7.IcePick

文章目录

  • 1.简介
  • 2.特性
  • 3.演示
    • 3.1 集成
    • 3.2 布局文件
    • 3.3 基本功能
    • 3.4 新功能——构建定制的Bundle
  • 4.源码地址

1.简介

IcePick——这是我在网上搜集资料的时候偶然发现的一个框架。作为一个与ButterKnife类似的简化开发框架,可能是由于其功能的局限性,使用百度搜索这个框架时,并没有过多的介绍资料。出于对使用冷门框架的好奇性,于是在本篇博客中简单介绍一下IcePick这个框架,以及其最新的版本3.2所提供的新功能——构建定制的Bundle。

IcePick已在3年前就停止了更新,也算是趋于比较稳定的版本了,官网上导入依赖的语句还是已经过时的compileprovided。使用这个框架之前,应该先去查看一下它的GitHub官网介绍:IcePick。对于它的功能介绍,我们放在下一节中。

2.特性

在一个Android应用中,遇到某些场景,我们往往需要手动去保存Activity或者Fragment的状态,例如我们在玩王者荣耀的时候,突然来了一个电话,接听完电话之后我们返回到游戏中,这个时候我们希望游戏还是之前那个进度(谁都不想一返回游戏就看到一个大大的Defeat吧),那么我们就需要将其状态保存起来,这样在其被摧毁时,我们还能够根据保存的状态回到之前的进度。

一般来说,保存信息使用的是Bundle类型的对象,而与保存状态有关的方法是:

  • onSaveInstanceState():如果调用, 则会发生在onPause()onStop()方法之前。
  • onRestoreInstanceState():如果调用,则会发生在onStartonResume方法之间。只有在Activity被系统回收,重新创建Activity的情况下才会被调用。注意,若onRestoreInstanceState()被调用,onSaveInstanceState()必也被调用,但反之不亦。若想深入研究的读者可以参考此篇博客:浅析onRestoreInstanceState调用时机

这里我们稍微复习一下Activity(这里主要分析Activity)保存数据的业务逻辑:

  • 正常状态:当用户想要正常退出这个Activity,例如按下了手机上的Back键时,用户显然想要关闭这个Activity, 此时是没有必要保存数据以供下次恢复的activity的,这时就不会调用onSaveInstanceState()或者onRestoreInstanceStat()
  • 异常状态:如果系统由于系统约束(而不是正常的应用程序行为)而破坏了Activity,那么尽管实际 Activity实例已经消失,但是系统还是会记住它已经存在,这样如果用户导航回到它,系统会创建一个新的实例的Activity使用一组保存的数据来描述Activity在被销毁时的状态。系统用于恢复以前状态的已保存数据称为“实例状态”,是存储在Bundle对象中的键值对的集合。该情况一般有以下情景:
    • 按下Home键
    • 按下电源键(关闭屏幕显示/熄屏)
    • 从当前的Activity中启动另一个Activity
    • 屏幕方向切换

也就是说,我们这里讨论的状态保存,更多是出于异常状态下的。

为了保存状态,传统的做法是,通过重写onSaveInstanceState()方法里的实现,将某些数据添加到Bundle对象中,然后再在onCreate()中传入的Bundle对象中取出数据即可。这种做法适用于需要存储数据量较少的情况,若数据量过多,则需要写很多的重复代码去保存和回复数据,很耗费时间。

正因为产生了这种效率问题,IcePick便应运而生。跟ButterKnife类似,该框架同样适用了注解的形式去解决保存状态的问题,让我们正式开始使用它吧!

3.演示

3.1 集成

鉴于IcePick在GitHub上的依赖导入存在一点问题(下面会贴出问题的所在),这里给出作者本人导入的相关依赖,修改moudle下的build.gradle,代码如下:

	implementation 'frankiesardo:icepick:3.2.0'
    compileOnly 'frankiesardo:icepick-processor:3.2.0'
    annotationProcessor 'frankiesardo:icepick-processor:3.2.0'

另外:原本官网上是没有第三行依赖,即annotationProcessor 'frankiesardo:icepick-processor:3.2.0',但是若不添加这行,就会报出如图所示的错误:
每天学习一个Android中的常用框架——7.IcePick_第1张图片
原因:根据Google提供的官方文档:如果添加了注释编译器(即compileOnly ),就需要在dependencies中增加annotationProcessor 依赖内容,Google官网原文如下:
每天学习一个Android中的常用框架——7.IcePick_第2张图片
解决方法:添加上文中所提及的全部依赖即可。

3.2 布局文件

布局文件比较简单,只需要提供一个按钮和一个文本控件即可,代码如下:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_add_number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="按一次加一"/>

    <TextView
        android:id="@+id/tv_number"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="100sp"
        android:gravity="center_horizontal"/>


LinearLayout>

3.3 基本功能

现在想要实现这样一个功能:每次点击一下按钮,文本就充当计数器的角色,显示 + 1,假设现在文本上已经显示到了10。当我们让Actvitiy处于异常状况时(这里就使用最简单的翻转让Activity销毁然后又创建),希望Activity上的文本控件依旧显示10,就需要调用onSaveInstanceState()了,而不使用onRestoreInstanceState()的原因是因为在Activity的创建方法onCreate()中不会调用它,所以只能调用onSaveInstanceState()了,MainActivity代码如下:

public class MainActivity extends AppCompatActivity {

    private Button btn_add_number;

    private TextView tv_number;

    @State
    int mNumber = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 使用IcePick读取数据
        Icepick.restoreInstanceState(this, savedInstanceState);
        // 获取控件实例
        btn_add_number = findViewById(R.id.btn_add_number);
        tv_number = findViewById(R.id.tv_number);
        // 初始化文本控件
        tv_number.setText(mNumber + "");
        // 为按钮注册点击事件
        btn_add_number.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tv_number.setText(++mNumber + "");
            }
        });
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        // 使用IcePick存入数据
        Icepick.saveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }
}

写到这里,你可以开启模拟器去测试一下,无论你以怎样的异常情况结束了当前Activity,当再创建的时候,mNumber总是会等于原来的那个值。

简单总结一下,IcePick的使用步骤:

  1. 在想要保存的字段上加上@State注解,表示被IcePick进行管理
  2. onSaveInstanceState()中调用Icepick.saveInstanceState(this, outState);来存储状态
  3. onCreate()中调用Icepick.restoreInstanceState(this, savedInstanceState);来恢复状态

怎么样,是不是很简单?相比于传统的get/put调用,这三个步骤明显要简化了许多,从此不再需要写繁琐的重复代码,节省了大量的时间。

注意:加上了@State的字段,不能使用privatestaticfinal等修饰符,否则会报出如图所示的错误:
在这里插入图片描述

3.4 新功能——构建定制的Bundle

这个新功能是我在查看IcePick的官方文档中发现的,原文如下:

From version 3.2.0 you can supply a class parameter to the State annotation. This class should implement the Bundler interface and you can use it to provide custom serialisation and deserialisation for your types.

class MyFragment {
  @State(MyCustomBundler.class) MyCustomType myCustomType;
}

class MyCustomBundler implements Bundler<MyCustomType> {
  void put(String key, MyCustomType value, Bundle bundle) {
    ...
  }

  MyCustomType get(String key, Bundle bundle) {
    ...
  }
}

This is useful if you want to use icepick in conjunction with Parceler.

大致意思是,通过IcePick,可以构造一个自定义的Bundle,从而实现某种定制的存储结构。由于并非主要功能,有兴趣的读者可以研究一下这个demo,作者这里就不再演示。

当然,IcePick的功能还不止这些,想要深入研究的还是那句老话:查看官方文档,没有任何教程要比官方文档详细了。

4.源码地址

AFL——Android框架学习

你可能感兴趣的:(Android)