Android技术栈(四)Android-Jetpack-MVVM-完全实践,Android组件化开发教程

你在xml设置的变量他会在这个类中为你生成对应的GetterSetter.你可以调用它们给界面赋值,比如之前的我们定义的action.

//这里的代码是Java8的lambda
mBinding.setAction(v->{
//TODO
})

2.3.5 使用BR文件

它还会为你生成一个类似RBR文件,里面包含了你在DataBinding风格xml中定义的所有变量名的引用(由于使用的是APT生成,有时候需要Rebuild Project才能刷新),比如我们之前的action,它会为我们生成BR.action,我们可以这么使用它

mBinding.setVariable(BR.action,new View.OnClickListener(){
@Override
void onClick(View v){
//TODO
}
})

2.3.6 传递复杂对象

在之前给xml中的变量中赋值时,我们用的都是一些类似String的简单对象,其实我们也可以定义一些复杂的对象,一次性传递到xml布局中

//java
public class File
{
public File(String name,
String size,
String path)
{
this.name = name;
this.size = size;
this.path = path;
}
public final String name;
public final String size;
public final String path;
}
//xml







个人认为绑定到xml中的数据最好是不可变的,所以上面的字段中我使用了final,但这不是必须的,根据你自己的需求来进行定制

2.3.7 绑定并非立即发生

这里有一点值得注意的是,你给ViewDataBinding的赋值并不是马上生效的,而是在当前方法执行完毕回到事件循环后,并保证在下一帧渲染之前得到执行,如果需要立即执行,请调用ViewDataBinding#executePendingBindings

2.3.8 使用android:id

如果你使用了android:id,那么这个View就也可以当成一个变量在下文的DataBinding表达式中使用,就像写Java.它还会帮你View绑定到ViewDataBinding中,你可以这么使用它们

//xml


//在java中my_text被去掉下划线,更符合java的命名习惯
mBinding.myText.setText(“This is a new text”);

用过ButterKnife的同学可能都知道,ButterKnife出过一次与gradle版本不兼容的事故,但是DataBinding是与gradle打包在一起发布的,一般不会出现这种问题,如果你不想用ButterKnife但有不想让DataBinding的风格的写法入侵你的xml太狠的话,只使用android:id将会是一个不错的选择.

2.4 正向绑定

某些第三方View是肯定没有适配DataBinding的,业界虽然一直说MVVM好,但现在MVP的开发方式毕竟还是主流,虽然这种情况我们可以用android:id,然后在Activity/Fragment中解决,但有时候我们想直接在xml中配置,以消除一些样板代码,这时候就需要自定义正向绑定.

2.4.1 自定义正向绑定适配器

我们可以使用@BindingAdapter自定义在xml中可使用的View属性,名字空间是不需要的,加了反而还会给你警告.

@Target(ElementType.METHOD)
public @interface BindingAdapter {

/**

  • 与此绑定适配器关联的属性。
    */
    String[] value();

/**

  • 是否必须为每个属性分配绑定表达式,或者是否可以不分配某些属性。
  • 如果为false,则当至少一个关联属性具有绑定表达式时,将调用BindingaAapter。
    */
    boolean requireAll() default true;
    }

//@BindingAdapter需要一个静态方法,该方法的第一个参数是与该适配器兼容的View类型
//从第二个参数开始,依次是你自定义的属性传进来的值.
//使用requireAll来指定这些属性是全部需要,还是只要一个就可以
//如果requireAll = false,触发适配器绑定时,没有被设置的属性将获得该类型的默认值
//框架优先使用自定义的适配器处理绑定

@BindingAdapter(value = {“load_async”, “error_handler”},requireAll = true)
public static void loadImage(ImageView view, String url, String error) {
Glide.with(view)
.load(url)
.error(Glide.with(view).load(error))
.into(view);
}
//在xml中使用它(下面那两个网址都不是实际存在的)

2.4.2 第三方View适配

DataBinding风格的xml还能在一定程度上适配第三方View

//如果你的自定义View中有这么一个Setter↓
public class RoundCornerImageView extends AppCompatImageView{
//…
public void setRadiusDp(float dp){
//TODO
}
}
//那么你可以在xml中使用radiusDp来使用它
radiusDp="@{100}"
android:id="@+id/progress"
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:layout_gravity=“center”
android:scaleType=“centerCrop”
android:src="@drawable/progress"/>
//它会自己为你去找名称为setRadiusDp并且能接受100为参数的方法.

2.4.3 xml中的属性重定向

使用@BindingMethod来将xml属性重定向:

@Target(ElementType.ANNOTATION_TYPE)
public @interface BindingMethod {
//需要重定向的View类型
Class type();
//需要重定向的属性名
String attribute();
//需要重定向到的方法名
String method();
}
//这是DataBinding源码中,DataBinding对于系统自带的TextView编写的适配器
//这是androidx.databinding.adapters.TextViewBindingAdapter的源码
@BindingMethods({
@BindingMethod(type = TextView.class, attribute = “android:autoLink”, method = “setAutoLinkMask”),
@BindingMethod(type = TextView.class, attribute = “android:drawablePadding”, method = “setCompoundDrawablePadding”),
@BindingMethod(type = TextView.class, attribute = “android:editorExtras”, method = “setInputExtras”),
//…
})
public class TextViewBindingAdapter {
//…
}
//这样就可以建立起xml中属性与View中Setter的联系

2.4.4 添加转换层

使用@BindingConversion为添加转换层

@BindingConversion
public static ColorDrawable toDrawable(int color) {
return new ColorDrawable(color);
}
//可以把color整形转换为android:src可接受的ColorDrawable类型
//但是转换只适用于直接的赋值
//如果你写了复杂的表达式,比如使用了?:这种三元运算符
//那就照顾不到你了

2.5 反向绑定

有正向绑定就一定有反向绑定,正向绑定和反向绑定一起构成了双向绑定.

在我们之前编写的DataBinding表达式中,比如TextViewandroid:text之类的属性我们都是直接赋值一个String过去的,这就是正向绑定,我们给View的值能够直接反应到View上,而反向绑定就是View值的变化和也能反应给我们.

2.5.1 使用双向绑定

所有使用之前所有使用@{}包裹的都是正向绑定,而双向绑定是@={},并且只支持变量,字段,Setter(比如User#setName,就写@={user.name})的直接编写并且不支持复杂表达式

2.5.2 兼容LiveData与ObservableField

实际上,android:text不只能接受String,当使用双向绑定时,它也能接受MutableLiveDataObservableField作为赋值对象,这种赋值会将TextViewandroid:text的变化绑定到LiveData(实际上是MutableLiveData)或者是ObservableField上,以便我们在View的控制层(Activity/Fragment)更好地观察他们的变化.

当然除了ObservableFieldandroidx.databinding包下还有不装箱的ObservableInt,ObservableFloat等等.

但是为了支持LiveData我们必须开启第二版的DataBinding APT.

在你的gradle.properties添加

android.databinding.enableV2=true

现在我们可以通过LiveData(实际上是MutableLiveData)android:text的变化绑定到Activity/Fragment

//xml




//然后在Activity/Fragment中
MutableLiveData liveText = new MutableLiveData();
mBinding.setLiveText(liveText);
liveText.observe(this,text->{
//TODO 观察View层变化
});

2.5.3 自定义反向绑定适配器

下面我们回到androidx.databinding.adapters.TextViewBindingAdapter的源码,继续对自定义反向绑定适配器进行分析.

//我们可以看到源码中使用了@InverseBindingAdapter自定义了一个反向绑定器
//指定了其属性以及相关联的事件
@InverseBindingAdapter(attribute = “android:text”, event = “android:textAttrChanged”)
public static String getTextString(TextView view) {
return view.getText().toString();
}
//并为这个事件添加了一个可接受InverseBindingListener的属性
//为了说明方便,下面的代码已简化,源码并非如此,但主要逻辑相同
@BindingAdapter(value = {“android:textAttrChanged”})
public static void setTextWatcher(TextView view , InverseBindingListener textAttrChanged){
view.addTextChangedListener(new TextWatcher

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

(){
//…
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
textAttrChanged.onChange();
}
});
}
//至此android:text的反向绑定完成
//当你使用@={}时实际上是用android:textAttrChanged属性向TextView设置了TextWatcher
//传入的InverseBindingListener是反向绑定监听器
//当调用InverseBindingListener的onChange时
//会调用@BindingAdapter所注解的方法将获得数据并写回到变量中.

2.6 配合DataBinding打造通用RecyclerView.Adapter

下面进行一个小小的实战吧,我们可以站在巨人的肩膀上造轮子.

//导入万能适配器作为基类,可以大大丰富我们通用适配器的功能
implementation ‘com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.46’

由于基类很强大所以代码不多:

//X是泛型,可以是你在item中所使用的java bean
public class GenericQuickAdapter
extends BaseQuickAdapter {
//BR中的变量名
protected final int mName;
//layoutResId是DataBinding风格的xml
public GenericQuickAdapter(int layoutResId, int name) {
super(layoutResId);
mName = name;
openLoadAnimation();
}

@Override
protected void convert(GenericViewHolder helper, X item) {
//触发DataBinding
helper.getBinding().setVariable(mName, item);
}

public static class GenericViewHolder extends BaseViewHolder {
private ViewDataBinding mBinding;
public GenericViewHolder(View view) {
super(view);
//绑定View获得ViewDataBinding
mBinding = DataBindingUtil.bind(view);
}

@SuppressWarnings(“unchecked”)
public T getBinding() {
return (T) mBinding;
}
}
}
//实例化
GenericQuickAdapter adapter = new GenericQuickAdapter<>(R.layout.item_file,BR.file);
//在xml中使用起来就像这样









3 Lifecycle

Android中,组件的管理组件的生命周期一直是一个比较麻烦的东西,而自Google推出Android Jetpack组件包以来,这个问题得到的比较妥善的解决,Lifecycle组件后来也成为Android Jetpack的核心。

3.1 导入

AndroidX为例,要使用Lifecycle组件,先在模块的build.gradle文件中添加依赖:

api ‘androidx.lifecycle:lifecycle-extensions:2.1.0-alpha02’

由于Lifecycle组件由多个包构成,使用api导入时即可将其依赖的包全部导入该模块,包括commonlivedataprocessruntimeviewmodelservice等。

如果要使用Lifecycle中的注解,你还需要添加如下注解处理器,以便在编译时,完成对相应注解的处理。

annotationProcessor ‘androidx.lifecycle:lifecycle-compiler:2.0.0’

对于一个App来说,使用Lifecycle组件是没有任何侵入性的,因为他已经天然的融合到Googleappcompat库中了,而如今无论是什么应用程序都几乎离不开appcompat,可以说集成Lifecycle只是启用了之前没用过的功能罢了。

3.2 LifecycleOwner

LifecycleOwnerLifecycle组件包中的一个接口,所有需要管理生命周期的类型都必须实现这个接口。

public interface LifecycleOwner
{
/**

  • Returns the Lifecycle of the provider.
  • @return The lifecycle of the provider.
    */
    @NonNull
    Lifecycle getLifecycle();
    }

但其实很多时候我们根本无需关心LifecycleOwner的存在。在Android中, FragmentActivityService都是具有生命周期的组件,但是Google已经让他们都实现了LifecycleOwner这个接口,分别是androdx.fragment.app.FragmentAppCompatActivityandroidx.lifecycle.LifecycleService.

在项目中,只要继承这些类型,可以轻松的通过LifecycleOwner#getLifecycle()获取到Lifecycle实例.这是一种解耦实现,LifecycleOwner不包含任何有关生命周期管理的逻辑,实际的逻辑都在Lifecycle实例中,我们可以通过传递Lifecycle实例而非LifecycleOwner来防止内存泄漏.

Lifecycle这个类的只有这三个方法:

@MainThread
public abstract void removeObserver(@NonNull LifecycleObserver observer);
@MainThread
@NonNull
public abstract State getCurrentState();
@MainThread
public abstract void addObserver(@NonNull LifecycleObserver observer);

getCurrentState()可以返回当前该LifecycleOwner的生命周期状态,该状态与LifecycleOwner上的某些回调事件相关,只会出现以下几种状态,在Java中以一个枚举类抽象出来定义在Lifecycle类中。

public enum State
{
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
}

  • DESTROYED,在组件的onDestroy调用前,会变成该状态,变成此状态后将不会再出现任何状态改变,也不会发送任何生命周期事件

  • INITIALIZED,构造函数执行完成后但onCreate未执行时为此状态,是最开始时的状态

  • CREATED,在onCreate调用之后,以及onStop调用前会变成此状态

  • STARTED,在onStart调用之后,以及onPause调用前会变成此状态

  • RESUMED,再onResume调用之后会变成此状态

addObserver,此方法可以给LifecycleOwner添加一个观察者,来接收LifecycleOwner上的回调事件。回调事件也是一个枚举,定义在Lifecycle类中:

public enum Event
{
/**

  • Constant for onCreate event of the {@link LifecycleOwner}.
    /
    ON_CREATE,
    /
    *
  • Constant for onStart event of the {@link LifecycleOwner}.
    /
    ON_START,
    /
    *
  • Constant for onResume event of the {@link LifecycleOwner}.
    /
    ON_RESUME,
    /
    *
  • Constant for onPause event of the {@link LifecycleOwner}.
    /
    ON_PAUSE,
    /
    *
  • Constant for onStop event of the {@link LifecycleOwner}.
    /
    ON_STOP,
    /
    *
  • Constant for onDestroy event of the {@link LifecycleOwner}.
    /
    ON_DESTROY,
    /
    *
  • An {@link Event Event} constant that can be used to match all events.
    */
    ON_ANY
    }

每种事件都对应着Fragment/Activity中的事件。

3.3 LifecycleObserver

LifecycleObserver是生命周期的观察者,可能是这个包中我们最常用的接口了.

查看源码得知,他就是一个空接口,不包含任何实现,但是若我们想使用,还是得继承此接口。

public interface LifecycleObserver { }

继承LifecycleObserver后使用@OnLifecycleEvent注解(这时之前申明得注解处理器派上了用场),并设置需要监听的生命周期回调事件。

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void test()
{
///TODO…
}

然后在Activity/Fragment中:

getLifecycle().addObserver(yourLifecycleObserver);

即可在运行时收到相应的的回调事件,但是注意添加@OnLifecycleEvent注解的方法应该是包内访问权限或是public的,否则可能在编译时会报错,或者收不到回调。

若想在运行时移除LifecycleObserver,同样也还有Lifecycle#removeObserver方法。

4 LiveData

LiveData是对Android组件生命周期感知的粘性事件,也就是说,在LiveData持有数据时,你去订阅它就能收到他最后一次接收到的数据.在实战中,我们能用到的LiveData一般是它的两个子类MutableLiveDataMediatorLiveData.

4.1 LiveData基本使用

我们可以通过LiveData#observe来观察它所持有的值的变化,还可以通过LiveData#getValue来直接获取内部保存的值(非线程安全)

//LiveData 一般是用来给ViewModel保存数据的
public class MyViewModel extends ViewModel{
private MutableLiveData mIsLoading = new MutableLiveData<>();
LiveData isLoading(){
return mIsLoading;
}
}
//Activity/Fragment观察ViewModel
mViewModel.isLoading().observe(this, isLoading -> {
//TODO 发生在主线程,触发相关处理逻辑
});
//LiveData是依赖Lifecycle实现的
//传入的this是LifecycleOwner
//LiveData只会通知激活态的(STARTED和RESUMED)的LifecycleOwner
//并且在Activity/Fragment被重建也能重新接收到LiveData保存的数据
//在组件DESTROYED时,LiveData会把它移出观察者列表

//当然你也可以不关联LifecycleOwner,让订阅一直保持.
//需要这样时需要使用observeForever
mViewModel.isLoading().observeFo(isLoading -> {
//TODO
});
//这个订阅永远不会被取消
//除非你显示调用LiveData#removeObserver

4.2 MutableLiveData

顾名思义就是可变的LiveData,基类LiveData默认是不可变的,MutableLiveData开放了能够改变其内部所持有数据的接口.

public class MutableLiveData extends LiveData {
/**

  • Creates a MutableLiveData initialized with the given {@code value}.
  • @param value initial value
    /
    public MutableLiveData(T value) {
    super(value);
    }
    /
    *
  • Creates a MutableLiveData with no value assigned to it.
    */
    public MutableLiveData() {
    super();
    }
    @Override
    public void postValue(T value) {
    super.postValue(value);
    }
    @Override
    public void setValue(T value) {
    super.setValue(value);
    }
    }

分别是postValuesetValue,其中setValue内部检查线程是否为主线程,不允许在子线程中使用,用了就报错.postValue会将值通过主线程的Handler转发到主线程上.

LiveData可以有初始值,也可以没有,如果在没有初始值得情况下被订阅,则订阅者不会收到任何的值.

4.3 MediatorLiveData

MediatorLiveData继承自MutableLiveData,它主要用来实现多个LiveData数据源的合并.

public class MediatorLiveData extends MutableLiveData {
private SafeIterableMap, Source> mSources = new SafeIterableMap<>();
@MainThread
public void addSource(@NonNull LiveData source, @NonNull Observer onChanged) {
Source e = new Source<>(source, onChanged);
Source existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
“This source was already added with the different observer”);
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}
@MainThread
public void removeSource(@NonNull LiveData toRemote) {
Source source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
}
}

@CallSuper
@Override
protected void onActive() {
for (Map.Entry, Source> source : mSources) {
source.getValue().plug();
}
}

@CallSuper
@Override
protected void onInactive() {
for (Map.Entry, Source> source : mSources) {
source.getValue().unplug();
}
}

private static class Source implements Observer {
final LiveData mLiveData;
final Observer mObserver;
int mVersion = START_VERSION;

Source(LiveData liveData, final Observer observer) {
mLiveData = liveData;
mObserver = observer;
}

void plug() {
mLiveData.observeForever(this);
}

void unplug() {
mLiveData.removeObserver(this);
}

@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
}

它比MutableLiveData多了两个方法addSourceremoveSource,通过这两个方法我们可以将其他LiveData合并到此LiveData上,当其他LiveData发生改变时,此LiveData就能收到通知.

@MainThread
public void addSource(@NonNull LiveData source, @NonNull Observer onChanged)
@MainThread
public void removeSource(@NonNull LiveData toRemote)

通过查看源码,我们可以知道在有观察者时LiveData#onActive会被回调,MediatorLiveData会在内部迭代,用observeForever订阅所有被合并进来的LiveData,这样就能接收所有LiveData的变化,在没有观察者时LiveData#onInactive会被回调,此时执行反操作removeObserver.

4.4 变换

使用androidx.lifecycle.Transformations这个工具类可以将持有一种类型的LiveData转换为另一种LiveData.他有类似于RxJava的使用方式.

LiveData boolLiveData = getBoolLiveData();
LiveData stringLiveData = Transformations.map(boolLiveData,bool->Boolean.toString(bool));

上面只是一个演示,实际上可以执行更为复杂的逻辑,并且这种转换是惰性的,在没有激活态观察者时,这种转换不会发生.

5 ViewModel

5.1 自定义ViewModel

ViewModel其实没什么可说的,其源码主要的部分其实就只有这些

public abstract class ViewModel {
protected void onCleared() {
}
}

简直一目了然,我们可以在ViewModel上使用LiveData作为字段保存数据,并编写业务逻辑(数据处理逻辑).就像这样

public class MyViewModel extends ViewModel
{
public MutableLiveData username = new MutableLiveData<>();
public MutableLiveData password = new MutableLiveData<>();
public MutableLiveData text = new MutableLiveData<>();
public void action1(){
//TODO
}
public void initName(){
username.setValue(“Luke Luo”);
}
//…
@Override
protected void onCleared() {
//TODO 清理资源
}
}

onCleared会在组件销毁的时候回调,我们可以重写这个方法在ViewModel销毁时添加一些自定义清理逻辑.

ViewModel还有一个子类AndroidViewModel也是一目了然,只是保存了Application实例而已.

public class AndroidViewModel extends ViewModel {
@SuppressLint(“StaticFieldLeak”)
private Application mApplication;

public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}

/**

  • Return the application.
    */
    @SuppressWarnings(“TypeParameterUnusedInFormals”)
    @NonNull
    public T getApplication() {
    //noinspection unchecked
    return (T) mApplication;
    }
    }

5.2 自定义ViewModel构造方式

我们可以通过ViewModelProviders来获取ViewModel,这样获取的ViewModel会绑定组件的生命周期(即在销毁时自动调用onCleared)

mViewModel = ViewModelProviders.of(this).get(CustomViewModel.class);

AndroidLifecycle实现中框架向Activity中添加了一个继承了系统FragmentReportFragment来汇报组件的生命周期,如果你使用的是appcompatFragment,那么它对你就是不可见的,所以一定要避免使用系统的Fragment(在API28中已被标记为弃用).

ViewModel通过Lifecycle来管理自身释放,在组件的ON_DESTROY事件来到时,它的onCleared()也会被调用.

如果你想有自定义构造函数参数的ViewModel那你就得继承ViewModelProvider.AndroidViewModelFactory

//自定义构造函数的ViewModel
public class NaviViewModel extends AndroidViewModel
{
private AMapNavi mNavi;
public NaviViewModel(AMapNavi navi,Application application)
{
super(application);
mNavi = navi;
}
//…
}

//继承并重写create
public final class NaviViewModelFactory
extends ViewModelProvider.AndroidViewModelFactory
{
private final AMapNavi navi;
private final Application application;

public NaviViewModelFactory(@NonNull Context context, AMapNavi navi)
{
super((Application) context.getApplicationContext());
this.application = (Application) context.getApplicationContext();
this.navi = navi;
}

@NonNull
@Override
public T create(@NonNull Class modelClass)
{
try
{
Constructor constructor = modelClass
.getConstructor(Application.class, AMapNavi.class);
return constructor.newInstance(application, navi);
} catch (Exception e)
{
return super.create(modelClass);
}
}
}
//使用
NaviViewModelFactory factory = new NaviViewModelFactory(context, navi);
mViewModel = ViewModelProviders.of(this, factory).get(NaviViewModel.class);

说白了就是反射调用构造函数创建,也是一目了然.

6 RxJava

本篇文章只是针对响应式编程在MVVM体系下的应用,不对RxJava展开深度讨论,但是后面还会专门出一篇文章讨论RxJava的有关知识.

RxJavaMVVM中主要用于发布事件,下面是需要注意的一些点.

6.1 使用AutoDispose

RxJava是响应式编程这种思想在JVM这个平台上的实现,所以它一开始并没有为Android平台的特点而做出优化.

就像上面所介绍过的一样,Android的组件是有明确的生命周期的,如果在组件销毁后,RxJava仍有后台线程在运行且你的Observer引用了你的Activity,就会造成内存泄漏.

但其实RxJava是提供了释放机制的,那就是Disposeable,只不过这个实现这个机制的逻辑需要我们手动在Activity#onDestroy中进行硬编码,这会带来大量的样板代码.

为了解决这一局面,在Android Jetpack还没有诞生的时候,有大神开发了RxLifecycle,但是这个框架需要强制继承基类,对于一些现有项目的改造来说,其实是不太友好的,个人感觉并没有从根本上解决问题.

Android Jetpack诞生后AutoDispose给了我们另外一条出路.它使用RxJava2中的as运算符,将订阅者转换成能够自动释放订阅者对象.

在你的build.gradle中添加依赖:

implementation ‘io.reactivex.rxjava2:rxjava:2.2.6’
implementation ‘io.reactivex.rxjava2:rxandroid:2.1.0’
implementation ‘com.uber.autodispose:autodispose:1.1.0’
implementation ‘com.uber.autodispose:autodispose-android-archcomponents:1.1.0’

一个简单的示例:

Observable.just(new Object())
//使用AutoDispose#autoDisposable
//并使用AndroidLifecycleScopeProvider#form
//指定LifecycleOwner和需要在哪一个事件进行销毁
//关键↓是这行
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(activity, Lifecycle.Event.ON_DESTROY)))
.subscribe();

上面代码的时间订阅将会在组件的Lifecycle.Event.ON_DESTROY事件来到时被释放,当然你也可以指定其他事件时释放.

6.2 防止多重点击

首先你可以使用JW大神的RxBinding来实现这一需求,但是今天我们不讨论RxBinding,因为网上的讨论RxBinding的文章已经太多了,随便抓一篇出来都已经非常优秀.

今天我们模仿RxBinding实现一个简单的,轻量化的,基于Java动态代理的,并且兼容所有第三方View所自定义Listener接口的防止多重点击机制.

二话不说先上代码:

import androidx.collection.ArrayMap;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import com.uber.autodispose.AutoDispose;
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider;
import io.reactivex.subjects.PublishSubject;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static io.reactivex.android.schedulers.AndroidSchedulers.mainThread;

public final class RxOnClick
{
//默认最低的可取的时间
private static final int MINI_TIME = 200;

private final Class mInterface;

private X mInner;

private LifecycleOwner mOwner;

private int mTime;

private Lifecycle.Event mEvent;

private RxOnClick(Class type)
{
mInterface = type;
}
//从一个创建接口类型创建
public static RxOnClick create(Class type)
{
return new RxOnClick<>(type);
}
//实际处理事件的Listener
public RxOnClick inner(X inner)
{
mInner = inner;
return this;
}
//依附于的组件也就是LifecycleOwner
public RxOnClick owner(LifecycleOwner owner)
{
mOwner = owner;
return this;
}
//只去time毫秒内的第一个结果作为有效结果
public RxOnClick throttleFirst(int time)
{
mTime = time;
return this;
}
//在哪一个事件进行释放
public RxOnClick releaseOn(Lifecycle.Event event)
{
mEvent = event;
return this;
}
//创建代理类实例
@SuppressWarnings(“unchecked”)
public X build()
{
//检查参数
if (mInterface == null || !mInterface.isInterface())
{
throw new IllegalArgumentException();
}
if (mTime < MINI_TIME)
{
mTime = MINI_TIME;
}
if (mEvent == null)
{
mEvent = Lifecycle.Event.ON_DESTROY;
}
if (mOwner == null || mInner == null)
{
throw new IllegalStateException();
}
//用反射遍历获取所有方法
Map> subjectMap = new ArrayMap<>();
for (Method method : mInterface.getDeclaredMethods())
{
PublishSubject subject = PublishSubject.create();
subject.throttleFirst(mTime, TimeUnit.MILLISECONDS)
.observeOn(mainThread())
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(mOwner, mEvent)))
.subscribe(args -> method.invoke(mInner, args));
subjectMap.put(method, subject);
}
//使用动态代理代理代理该接口并使用PublishSubject进行转发
return (X) Proxy.newProxyInstance(mInterface.getClassLoader(),
new Class[]{mInterface},
(proxy, method, args) -> {
//Object类的方法直接调用
if (Object.class.equals(method.getDeclaringClass()))
{
return method.invoke(proxy, args);
}
//否则转换为Rx事件流
PublishSubject subject = subjectMap.get(method);
if (subject != null)
{
subject.onNext(args);
}
return null;
});
}
}

你可能感兴趣的:(程序员,面试,android,移动开发)