Android Other

JetPack架构组件介绍

2019-11-17  本文已影响0人  平凡小天地

MVP & MVVM

MVP结构

image.png

MVP存在的问题:

MVVM & LiveData

MVVM中的View和Model之间通过ViewModel通信,AndroidX中的LiveData采用观察者模式,Model的更新,可直接被View监听到,不需要用户手动定义接口更新数据,而是自动同步给View实现UI更新

gN16bqjm98KHgahmF5UWDFvhZDmjDoZPrHPl7L9Ni9w1q7Slvi.png

JetpPack总体介绍

2017年的Google I/O大会上,Google推出了一系列譬如 Lifecycle、ViewModel、LiveData等一系列 更适合用于MVVM模式开发 的架构组件。

cV2Pi6nGTwnctvC2lyhC0n84Ae8zuHjmP5cBhRh970hrtmC3sX.png

ViewModel

https://developer.android.com/reference/android/arch/lifecycle/ViewModel.html

如何使用:

public class UserActivity extends Activity {

     @Override

     protected void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);

         setContentView(R.layout.user_activity_layout);

         final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);

         viewModel.userLiveData.observer(this, new Observer() {

            @Override

             public void onChanged(@Nullable User data) {

                 // update ui.

             }

         });

         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

             @Override

             public void onClick(View v) {

                  viewModel.doAction();

             }

         });

     }

 }
public class UserModel extends ViewModel {

     public final LiveData<User> userLiveData = new LiveData<>();

     public UserModel() {

         // trigger user load.

     }

     void doAction() {

         // depending on the action, do necessary business logic calls and update the

         // userLiveData.

     }

 }

创建逻辑:

通过Activity中的fieldViewModelStore 去管理,以ViewModel的类名为key存储在map中,如果之前map中已经有这个key的数据,那么直接返回,如果没有创建过,则重新创建

/**

 * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or

 * an activity), associated with this {@code ViewModelProvider}.

 * <p>

 * The created ViewModel is associated with the given scope and will be retained

 * as long as the scope is alive (e.g. if it is an activity, until it is

 * finished or process is killed).

 *

 * @param key        The key to use to identify the ViewModel.

 * @param modelClass The class of the ViewModel to create an instance of it if it is not

 *                   present.

 * @param <T>        The type parameter for the ViewModel.

 * @return A ViewModel that is an instance of the given type {@code T}.

 */

@NonNull

@MainThread

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {

    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {

        //noinspection unchecked

        return (T) viewModel;

    } else {

        //noinspection StatementWithEmptyBody

        if (viewModel != null) {

            // TODO: log a warning.

        }

    }

    if (mFactory instanceof KeyedFactory) {

        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);

    } else {

        viewModel = (mFactory).create(modelClass);

    }

    mViewModelStore.put(key, viewModel);

    //noinspection unchecked

    return (T) viewModel;

}

销毁逻辑

/**

 * Destroy all fragments.

 */

@Override

protected void onDestroy() {

    super.onDestroy();

    if (mViewModelStore != null && !isChangingConfigurations()) {

        mViewModelStore.clear();

    }

    mFragments.dispatchDestroy();

}

如果ViewModel中有使用到一些生命周期较长的数据,应在onClear回调中手动销毁

nVatL2mqR0YlqGBH85gjukPle4omO8zcHto44Tc6lRz3YQe6Ly.png

生命周期:

用户在使用产品的过程中,可能随时会被中断或界面发生重建,如果数据的丢失会造成很差的用户体验,当然现在都知道在onSaveInstanceState()中保存数据,在界面重建时恢复数据,这种虽然可以解决问题,但会造成每个界面都要编写板寸和恢复的代码,而且数据量过大时会影响执行性能,VIewModel的出现就是解决这个问题,它会在活动重建时仍然保存数据,在活动创建完成后从中获取数据。

androidx中Activity的ondestroy方法:

/**

 * Destroy all fragments.

 */

@Override

protected void onDestroy() {

    super.onDestroy();

    if (mViewModelStore != null && !isChangingConfigurations()) {

        mViewModelStore.clear();

    }

    mFragments.dispatchDestroy();

}
FJDJejVwTr1WbwEt7R2sTjZqUqdID4V6LndyG5KTtb4ECO7k4F.png

总结:

数据和界面的分离,使数据驱动界面

解决了运行中断和界面重建时的数据保存问题

实现Activity中Fragment之间的数据交互

配合LiveData实时获取最新数据

LifeCycle

https://developer.android.com/reference/android/arch/lifecycle/Lifecycle

如何使用:

java8:

If you use Java 8 Language, then observe events with DefaultLifecycleObserver. To include it you should add "android.arch.lifecycle:common-java8:<version>" to your build.gradle file.

 class TestObserver implements DefaultLifecycleObserver {

     @Override

     public void onCreate(LifecycleOwner owner) {

         // your code

     }

 }

** java7:**

 class TestObserver implements LifecycleObserver {

   @OnLifecycleEvent(ON_CREATE)

   void onCreated(LifecycleOwner source) {}

   @OnLifecycleEvent(ON_ANY)

   void onAny(LifecycleOwner source, Event event) {}

 }

State & Event:

public abstract class Lifecycle {

        //注册LifecycleObserver (比如Presenter)

        public abstract void addObserver(@NonNull LifecycleObserver observer);

        //移除LifecycleObserver 

        public abstract void removeObserver(@NonNull LifecycleObserver observer);

        //获取当前状态

        public abstract State getCurrentState();

        public enum Event {

            ON_CREATE,

            ON_START,

            ON_RESUME,

            ON_PAUSE,

            ON_STOP,

            ON_DESTROY,

            ON_ANY

        }
       public enum State {

            DESTROYED,

            INITIALIZED,

            CREATED,

            STARTED,

            RESUMED;

            public boolean isAtLeast(@NonNull State state) {

                return compareTo(state) >= 0;

            }

       }

}

Event发送时序:

4CnsQLLTHmmpaO4arXyVUnz52dtdwLIrO0kOqYig1UqBiko55D.png

总结:

LiveData

https://developer.android.com/reference/androidx/lifecycle/LiveData.html

如何使用:

(1)定义LiveData 变量,对外开放LiveData和修改value的方法:主线程调用setValue,后台线程调用postValue

class ProfileLiveDataViewModel : ViewModel() {

    private val _xytestData = MutableLiveData(XyTestData())

    val xyTestData: LiveData<XyTestData> = _xytestData

    fun changeHomeTown() {

        _xytestData.value?.apply {

            innerData.homeTown = innerData.homeTown + "——湖南"

        }

        _xytestData.value = _xytestData.value

    }

}

(2)触发修改

binding.likeButton.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

        viewModel.onLike();

        viewModel.changeHomeTown();

    }

});

(3)observer监听LiveData 的回调,onChanged一定 发生在主线程

viewModel.getXyTestData().observe(activity, new Observer<XyTestData>() {

    @Override

    public void onChanged(XyTestData xyTestData) {

        Log.d("xiaoyu_activity home : ",xyTestData.innerData.homeTown);

        binding.lastname.setText(xyTestData.innerData.homeTown);

    }
});

具有生命周期的观察者模式

(1)添加监听:

@MainThread

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {

    assertMainThread("observe");

    if (owner.getLifecycle().getCurrentState() == DESTROYED) {

        // ignore

        return;

    }

    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

    if (existing != null && !existing.isAttachedTo(owner)) {

        throw new IllegalArgumentException("Cannot add the same observer"

                + " with different lifecycles");

    }

    if (existing != null) {

        return;

    }

    owner.getLifecycle().addObserver(wrapper);

}

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {

    @NonNull

    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {

        super(observer);

        mOwner = owner;

    }

    @Override

    boolean shouldBeActive() {

        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);

    }

    @Override

    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {

        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {

            removeObserver(mObserver);

            return;

        }

        activeStateChanged(shouldBeActive());

    }

    @Override

    boolean isAttachedTo(LifecycleOwner owner) {

        return mOwner == owner;

    }

    @Override

    void detachObserver() {

        mOwner.getLifecycle().removeObserver(this);

    }

}

(2)setValue时去通知observers:

private void considerNotify(ObserverWrapper observer) {

    if (!observer.mActive) {

        return;

    }

    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.

    //

    // we still first check observer.active to keep it as the entrance for events. So even if

    // the observer moved to an active state, if we've not received that event, we better not

    // notify for a more predictable notification order.

    if (!observer.shouldBeActive()) {

        observer.activeStateChanged(false);

        return;

    }

    if (observer.mLastVersion >= mVersion) {

        return;

    }

    observer.mLastVersion = mVersion;

    //noinspection unchecked

    observer.mObserver.onChanged((T) mData);

}

永久生效的观察者模式:

@MainThread

public void observeForever(@NonNull Observer<? super T> observer) {

    assertMainThread("observeForever");

    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);

    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

    if (existing instanceof LiveData.LifecycleBoundObserver) {

        throw new IllegalArgumentException("Cannot add the same observer"

                + " with different lifecycles");

    }

    if (existing != null) {

        return;

    }

    wrapper.activeStateChanged(true);

}

这种情况下LiveData不会自动释放observer,需要我们手动调用removeObserver释放,避免内存泄漏。

Transformation:

map

 val popularity: LiveData<Popularity> = Transformations.map(_likes) {

    when {

        it > 9 -> Popularity.STAR

        it > 4 -> Popularity.POPULAR

        else -> Popularity.NORMAL

    }

}

switchmap

 MutableLiveData userIdLiveData = ...;

 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->

     repository.getUserById(id));

 void setUserId(String userId) {

      this.userIdLiveData.setValue(userId);

 }

类似于rxjava中的map和flatMap

LiveData/ MutableLiveData /MediatorLiveData

LiveData只可读,不可修改

MutableLiveData,开放了setValue和PostValue

MediatorLiveData,支持LiveData组合

* MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();

* liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));

* liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

* </pre>

* <p>

* Let's consider that we only want 10 values emitted by {@code liveData1}, to be

* merged in the {@code liveDataMerger}. Then, after 10 values, we can stop listening to {@code

* liveData1} and remove it as a source.

* <pre>

* liveDataMerger.addSource(liveData1, new Observer<Integer>() {

*      private int count = 1;

*

*      {@literal @}Override public void onChanged(@Nullable Integer s) {

*          count++;

*          liveDataMerger.setValue(s);

*          if (count > 10) {

*              liveDataMerger.removeSource(liveData1);

*          }

*      }

* });

总结:

​LiveData​遵循观察者模式,当生命周期状态发送改变时,​LiveData​会通知观察者对象,您可以合并代码以更新这些观察者对象中的UI。您的观察者可以在任意变化的情况下更新UI,而不是每次数据发生变更时更新UI。

观察者被绑定到生命周期对象上,并在其关联的生命周期被销毁后会进行清理。所以不会有内存泄露。

不会因为Activity停止而崩溃

如果观察者的生命周期处于不活动状态下,例如Activity在后台栈中,那不会接收到任意的​LiveData​事件。

UI组件只需要观察相关的数据,不需要停止和恢复观察。​LiveData​自动管理这些,因为他知道相关的生命周期的变化。

如果一个生命周期变得不活动,它将在再次激活时接收最新的数据。例如,后台的某个Activity在返回到前台后会接收最新的数据。

如果一个Activity或者Fragment由于配置改变而重新创建,例如设备旋转,它会立即接收到最新的可用数据。

您可以使用单例模式扩展LiveData对象,以包装系统服务,以便它们可以在您的应用程序中共享。LiveData对象一旦连接到系统服务,然后任何需要资源的观察者都可以看到LiveData对象。

提供了一个数据更新UI,实现通信的非常轻量的方式

DataBinding

如何使用:

(1)gradle配置:

android {...

    dataBinding {

       enabled true

    }}

(2)在xml中直接将UI与ViewModel绑定,监听ViewModel中数据的变化,将变化直接体现在view上

<layout xmlns:android="http://schemas.android.com/apk/res/android"

            xmlns:app="http://schemas.android.com/apk/res-auto">

        <data>

            <variable

                name="viewmodel"

                type="com.myapp.data.ViewModel" />

        </data>

        <ConstraintLayout... /> <!-- UI layout's root element -->

    </layout>

(3)编译后直接根据layout文件生成Binding类

(4)通过binding对象直接获取到各个id的view:

val binding: ObservableFieldProfileBinding =

        DataBindingUtil.setContentView(this, R.layout.observable_field_profile)

binding.user = observableFieldProfile

好处:

存在的问题:

Android 业务层架构思考

Snip20191117_9.png

说明:

好处:

参考:

https://developer.android.com/jetpack
https://blog.csdn.net/Alexwll/article/details/83302173
https://medium.com/@MinaSamy/android-architecture-components-lifecycle-433ace1ec05d
https://medium.com/exploring-android/exploring-the-new-android-architecture-components-c33b15d89c23
http://timmy6.github.io/2018/08/13/android-lifecycle/
https://proandroiddev.com/5-common-mistakes-when-using-architecture-components-403e9899f4cb
https://codelabs.developers.google.com/codelabs/android-databinding/#0
https://developer.android.com/reference/android/arch/lifecycle/Transformations

上一篇 下一篇

猜你喜欢

热点阅读