Android AAC架构

定义

Android Architecture Components,简称 AAC,一个处理UI的生命周期与数据的持久化的架构

AAC 的核心

Lifecycle, LiveData, ViewModel 以及 Room

  • 通过它可以非常优雅的让数据与界面交互
  • 并做一些持久化的东西
  • 高度解耦
  • 自动管理生命周期
  • 而且不用担心内存泄漏的问题.

架构图如下:

image

LifeCycle & LifecycleOwner

  • Lifecycle:它是一个持有 Activity/Fragment 生命周期状态信息的类,并且允许其他对象观察此状态。
  • LifecycleOwner:是一个具有单一方法的接口。如果一个类实现了此接口,则该类中需要持有一个 Lifecycle 对象,并通过LifecycleOwner.getLifecycle() 方法返回该对象。
    并不是只有 Activity 和 Fragment 才可以实现 LifecycleOwner 接口的,任何和 Activity/Fragment 生命周期有关系的类都可以实现此接口。通过实现此接口,该类完全是生命周期可感知的,只需要对它进行初始化,它就可以进行自己的初始化和清理操作,而不受其 Activity/Fragment 的管理

activity活动图:

image

获取当前activity的状态:

        if(getLifecycle().getCurrentState() == Lifecycle.State.RESUMED){
            //todo ...
        }

LiveData

LiveData 是一个数据持有类,它持有一个值并且该值可以被观察。不同于普通的可观察者,LiveData 遵从应用组件的生命周期,这样 Observer 便可以指定一个其应该遵循的 Lifecycle。

如果 Observer 所依附的 Lifecycle 处于 STARTED 或者 RESUMED 状态,则 LiveData 认为 Observer 处于活跃状态。

可以感知组件生命周期的 LiveData 给我们提供了一种可能:可以在多个 Activity、Fragment 之间共享它。

使用 LiveData 会有以下几个优势:

  • 避免内存泄露:因为 Observer 是绑定到 Lifecycle 对象上的,当 Lifecycle 对象被销毁的时候,LiveData 对象也会被自动清除
  • 不会因为 Activity 停止而使应用崩溃:如果 Observer 所绑定的 Lifecycle 处于闲置状态(例如:Activity 处于后台运行时),他们不会接收到改变的事件
  • 始终保持最新的数据:如果一个 Lifecycle 重新启动以后(例如:Activity 从后台重新开始运行于前台),它会接收到最新的数据(除非没有最新的数据)
  • 正确处理配置改变:如果一个 Activity 或者 Fragment 以为配置改变(例如:旋转屏幕)被重建以后,LiveData 将会接收到最新的数据
  • 资源共享:通过单例模式,可以在多个 Activity 或者 Fragment 之间共享 LiveData 数据。
  • 不再手动的处理生命周期:Fragment 只有在处于活跃的时候才会观察 LiveData 数据。由于 Fragment 提供了 Lifecycle 对象,所以 LiveData 会管理这一切。

有时候,也许想在 LiveData 被下发到 Observer 之前,改变 LiveData 的值,或者是基于当前的 LiveData 下发另一个不同的 LiveData 值。Lifecycle 包中的 Transformations 可以实现这样的功能。

Transformations.map(),使用此方法,可以将 LiveData 传递到下游

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

Transformations.switchMap(),和 map() 方法类似,使用 switchMap() 应用于 LiveData 的值并解包,然后将结果传递到下游。传递给 switchMap() 的方法必须返回一个 Lifecycle

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

使用这两个转换,允许在整个调用链中携带观察者的 Lifecycle 信息,这样的话,只有在观察者观察到 LiveData 的返回值时,才会运算这些转换。

当你需要在 ViewModel 中添加一个 Lifecycle 对象时,Transformations 或许是一个好的解决办法。

android.arch.lifecycle.Observer

用于对LiveData增加监听,使用的方法为LiveData.observe(LifecycleOwner, Observer), 对于一些不需要的observer建议及时移除掉,LiveData.removeObserver(Observer),因为如果没有移除掉,当数据刷新的时候还是会一直调用

ViewModel

ViewModel 类是用来存储和管理 UI 相关的数据,这样在配置发生变化(例如:屏幕旋转)时,数据就不会丢失。
由于应用程序组件(例如:Activity、Fragment),具有一个由 Android Framework 管理的生命周期,Activity 或 Fragment 在某些情况下(比如:内存紧张或者屏幕旋转)会发生销毁或者重新创建的情况。这样就会带来一些问题:

  • 由于 Activity 或者 Fragment 有可能会被销毁或重新创建,所以保存于其中的数据有可能会丢失
  • 在 Activity 或者 Fragment 中会经常发起一些需要一定时间才会返回结果的异步请求调用
  • 如果把处理应用数据、完成响应用户操作、处理系统通信工作的代码都写在 Activity 或者 Fragment 中,那么 Activity 或者 Fragment 将会变得非常的臃肿,给维护工作带来一定的困难

针对以上问题,Lifecycle 提供了一个叫 ViewModel 的类,一个 UI 控制器的帮助类,用来为 UI 准备数据。

在配置更改的时候,ViewModel 会被保留,以便其保存的数据可以立即传递给重新创建的 Activity 或者 Fragment 实例中。如果 Activity 被重新创建,它将会收到由之前的 Activity 或者 Fragment 创建的 ViewModel 实例。当所有者 Activity 被销毁以后,Framework 会调用 ViewModel.onCleared() 清楚系统资源

注意:由于ViewModel超出了具体的Activity和Fragment实例生命周期,所以它不应该引用View或任何可能持有对活动上下文的引用的类。 如果ViewModel需要应用程序上下文(例如,找到系统服务),则可以扩展AndroidViewModel类,并在构造函数中接收应用程序的构造函数(因为Application类扩展了Context)

viewModel生命周期图:

image

ViewModel vs SavedInstanceState
- ViewModels 提供了一种在配置更改时保存数据的简便方式,但是如果应用进程被操作系统杀死,那么数据则没有机会被恢复。
- 通过 SavedInstanceState 保存的数据,存在于操作系统进程的内存中。当用户离开应用数个小时之后,应用的进程很有可能被操作系统杀死,通过 SavedInstanceState 保存的数据,则可以在 Activity 或者 Fragment 重新创建的时候,在其中的 onCreate() 方法中通过 Bundle 恢复数据

因为ViewModel的生命周期是和Activity或Fragment分开的,所以在ViewModel中绝对不能引用任何View对象或者任何引用了Activity的Context的对象。如果ViewModel中需要Application的Context的话,可以继承AndroidViewModel。

AAC UI架构举例

image

点击①处按钮会改变Data数值,数值会在②fragment,③处的seekbar和④处的自定义TextView处及时更新,点击⑤处按钮,会显示当前activity的状态

总代码:

添加依赖

dependencies {
    ...
    ...
    compile 'android.arch.lifecycle:extensions:1.1.0'
    compile 'android.arch.lifecycle:viewmodel:1.1.0'
    ...
}
package com.example.administrator.aac;

public class Data {
    private int mValue = 0;
    private String mUnit1 = "  元";
    private String mUnit2 = " 美金";

    public Data(String unit1, String unit2) {
        mUnit1 = unit1;
        mUnit2 = unit2;
    }

    public Data(int value) {
        mValue = value;
    }

    public Data(){}

    public void setUnit1(String unit1) {
        mUnit1 = unit1;
    }

    public void setUnit2(String unit2) {
        mUnit2 = unit2;
    }

    public void setValue(int value) {
        mValue = value;
    }

    public int getNum() {
        return mValue;
    }

    public String getUnit1() {
        return mUnit1;
    }

    public String getUnit2() {
        return mUnit2;
    }
}
package com.example.administrator.aac;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Transformations;
import android.arch.lifecycle.ViewModel;

public class ShareViewModel extends ViewModel {
    private MutableLiveData<Data> mData = new MutableLiveData<>();

    //采用非Lambda表达式的定义方式
//    private LiveData<String> mMapData = Transformations.map(mData, new Function<Data, String>() {
//        @Override
//        public String apply(Data value) {
//            return value.getNum() + value.getUnit1() + "---" + value.getUnit2();
//        }
//    });

    //采用Lambda表达式做Transformations.map
    private LiveData<String> mMapData = Transformations.map(mData, value -> {
        return value.getNum() + value.getUnit1() + "---" + value.getUnit2();
    });

    //采用Lambda表达式做Transformations.switchMap
    private LiveData<String> mSitchMapData = Transformations.switchMap(mData, value -> {
        MutableLiveData<String> dataLiveData = new MutableLiveData<>();
        dataLiveData.setValue(value.getNum() + value.getUnit1() + "/" + value.getUnit2());
        return dataLiveData;
    });

    public LiveData<String> getSitchMapData() {
        return mSitchMapData;
    }

    public LiveData<String> getMapData() {
        return mMapData;
    }

    public ShareViewModel(){
        setData(new Data());
    }

    public void setData(Data data) {
        mData.setValue(data);
    }

    public void setValue(int value){
        mData.getValue().setValue(value);
    }

    public MutableLiveData<Data> getData() {
        return mData;
    }
}
package com.example.administrator.aac;

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.util.Log;
/**
 * acitivity生命周期监听,可以放一些非UI的业务逻辑
 */
public class MyLifeCycleObsever implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate(){
        Log.i("aac---", "onCreate-----");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onStart(){
        Log.i("aac---", "onStart-----");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume(){
        Log.i("aac---", "onResume-----");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause(){
        Log.i("aac---", "onPause-----");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop(){
        Log.i("aac---", "onStop-----");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy(){
        Log.i("aac---", "onDestroy-----");
    }
}

R.layout.activity_test_aac:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.administrator.aac.TestAACActivity">
    <Button
        android:id="@+id/change_value"
        android:text="增加5"
        android:textAllCaps="false"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_width="0dp"
        android:layout_height="wrap_content"/>


    <fragment
        android:id="@+id/fragment1"
        android:layout_marginTop="5dp"
        app:layout_constraintTop_toBottomOf="@id/change_value"
        android:name="com.example.administrator.aac.AACFragment1"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_width="0dp"
        android:layout_height="100dp"></fragment>

    <SeekBar
        android:layout_marginRight="5dp"
        android:layout_marginLeft="5dp"
        android:min="0"
        android:max="100"
        android:id="@+id/value_bar"
        android:layout_marginTop="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/fragment1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"/>

    <TextView
        android:layout_marginTop="20dp"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintTop_toBottomOf="@id/value_bar"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/value_tv"
        android:text="当前值是:"
        android:id="@+id/text_tip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <com.example.administrator.aac.MyTextView
        app:layout_constraintHorizontal_chainStyle="packed"
        android:id="@+id/value_tv"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@id/text_tip"
        app:layout_constraintLeft_toRightOf="@id/text_tip"
        android:text="0 元"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:background="@android:color/darker_gray"
        android:text="点击查看当前Activity状态"
        android:id="@+id/curr_status"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_width="0dp"
        android:layout_height="wrap_content"/>
</android.support.constraint.ConstraintLayout>
package com.example.administrator.aac;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;

import com.example.administrator.myapplication.R;

public class TestAACActivity extends AppCompatActivity {

    private ShareViewModel mModel;
    private SeekBar mSeekBar;
    private TextView mCurrStatusTv;

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

        //添加LifeCycleObserver,可以非UI业务逻辑放到Observer
        getLifecycle().addObserver(new MyLifeCycleObsever());
        initViews();
    }

    private void initViews() {
        mModel = ViewModelProviders.of(this).get(ShareViewModel.class);
        mSeekBar = findViewById(R.id.value_bar);
        mCurrStatusTv = findViewById(R.id.curr_status);
        mModel.getData().observe(this, new Observer<Data>() {
            @Override
            public void onChanged(@Nullable Data data) {
                mSeekBar.setProgress(data.getNum());
            }
        });

        //可以采用lambda表达式
//        mModel.getData().observe(this, data -> {
//            mSeekBar.setProgress(data.getNum());
//        });

        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                setData(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        findViewById(R.id.change_value).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int value = mModel.getData().getValue().getNum();
                if(value > 95){
                    value = 0;
                }
                setData(value + 5);
            }
        });

        mCurrStatusTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //显示当前Activity State
                mCurrStatusTv.setText("当前Activity状态是:" + getLifecycle().getCurrentState().name());
            }
        });
    }

    /**
     * 更新ViewModel的值,如果不想重新创建新的对象,可以直接取出原来的数据对象,重新set即可
     * @param value
     */
    private void setData(int value) {
        Data data = mModel.getData().getValue();
        data.setValue(value);
        mModel.setData(data);
    }
}
package com.example.administrator.aac;

import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;

public class MyTextView extends AppCompatTextView{
    private ShareViewModel mModel;
    private Observer<String> mObserver;

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

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

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

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        init(getContext());
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        //移除observer
        mModel.getMapData().removeObserver(mObserver);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
    }

    private void init(Context context) {
        mModel = ViewModelProviders.of((FragmentActivity) getContext()).get(ShareViewModel.class);
        //直接观察viewmodel的livedata,观察数据变化
//        mModel.getData().observe((FragmentActivity) getContext(), new Observer<Data>() {
//            @Override
//            public void onChanged(@Nullable Data data) {
//                String unit = data.getUnit2();
//                setText(data.getNum() + unit);
//            }
//        });
        //观察viewmodel的Transformations.map() data,可以得到转换的String
        mObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable String des) {
                setText(des);
            }
        };
        mModel.getMapData().observe((LifecycleOwner) getContext(), mObserver);
    }
}

R.layout.fragment_aacfragment1:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent"
                                             android:background="@android:color/holo_orange_light"
                                             xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toLeftOf="@id/value_tve"
        android:text="当前值是:"
        android:id="@+id/text_tipt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        app:layout_constraintHorizontal_chainStyle="packed"
        android:id="@+id/value_tve"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@id/text_tipt"
        app:layout_constraintLeft_toRightOf="@id/text_tipt"
        android:text="0 元"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</android.support.constraint.ConstraintLayout>

package com.example.administrator.aac;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.example.administrator.myapplication.R;

public class AACFragment1 extends Fragment {

    private View mRootView;
    private ShareViewModel mModel;
    private TextView mValueTv;
    private Observer<String> mObserver;

    public AACFragment1() {
        // Required empty public constructor
    }
    public static AACFragment1 newInstance(String param1, String param2) {
        AACFragment1 fragment = new AACFragment1();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        mRootView = inflater.inflate(R.layout.fragment_aacfragment1, container, false);

        init();
        return mRootView;
    }


    public void init() {
        mValueTv = mRootView.findViewById(R.id.value_tve);
        mModel = ViewModelProviders.of((FragmentActivity) getContext()).get(ShareViewModel.class);
        //直接观察viewmodel的livedata,观察数据变化
//        mModel.getData().observe((FragmentActivity) getContext(), new Observer<Data>() {
//            @Override
//            public void onChanged(@Nullable Data data) {
//                String unit = data.getUnit1();
//                mValueTv.setText(data.getNum() + unit);
//            }
//        });

        //观察viewmodel的Transformations.map() data,可以得到转换的String
        mObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable String data) {
                mValueTv.setText(data);
            }
        };
        mModel.getSitchMapData().observe(getActivity(), mObserver);
    }

    @Override
    public void onDetach() {
        super.onDetach();

        //移除observer
        mModel.getSitchMapData().removeObserver(mObserver);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值