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
表达式中,比如TextView
中android:text
之类的属性我们都是直接赋值一个String
过去的,这就是正向绑定,我们给View
的值能够直接反应到View
上,而反向绑定就是View
值的变化和也能反应给我们.
2.5.1 使用双向绑定
所有使用之前所有使用@{}
包裹的都是正向绑定,而双向绑定是@={}
,并且只支持变量,字段,Setter
(比如User#setName
,就写@={user.name}
)的直接编写并且不支持复杂表达式
2.5.2 兼容LiveData与ObservableField
实际上,android:text
不只能接受String
,当使用双向绑定时,它也能接受MutableLiveData<String>
和ObservableField<String>
作为赋值对象,这种赋值会将TextView
的android:text
的变化绑定到LiveData(实际上是MutableLiveData)
或者是ObservableField
上,以便我们在View
的控制层(Activity
/Fragment
)更好地观察他们的变化.
当然除了ObservableField
在androidx.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(){
//…
@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<X, GenericQuickAdapter.GenericViewHolder> {
//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
导入时即可将其依赖的包全部导入该模块,包括common
,livedata
,process
,runtime
,viewmodel
,service
等。
如果要使用Lifecycle
中的注解,你还需要添加如下注解处理器,以便在编译时,完成对相应注解的处理。
annotationProcessor ‘androidx.lifecycle:lifecycle-compiler:2.0.0’
对于一个App
来说,使用Lifecycle
组件是没有任何侵入性的,因为他已经天然的融合到Google
的appcompat
库中了,而如今无论是什么应用程序都几乎离不开appcompat
,可以说集成Lifecycle
只是启用了之前没用过的功能罢了。
3.2 LifecycleOwner
LifecycleOwner
是Lifecycle
组件包中的一个接口,所有需要管理生命周期的类型都必须实现这个接口。
public interface LifecycleOwner
{
/**
- Returns the Lifecycle of the provider.
- @return The lifecycle of the provider.
*/
@NonNull
Lifecycle getLifecycle();
}
但其实很多时候我们根本无需关心LifecycleOwner
的存在。在Android
中, Fragment
、Activity
、Service
都是具有生命周期的组件,但是Google
已经让他们都实现了LifecycleOwner
这个接口,分别是androdx.fragment.app.Fragment
、AppCompatActivity
、androidx.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
一般是它的两个子类MutableLiveData
和MediatorLiveData
.
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);
}
}
分别是postValue
和setValue
,其中setValue
内部检查线程是否为主线程,不允许在子线程中使用,用了就报错.postValue
会将值通过主线程的Handler
转发到主线程上.
LiveData
可以有初始值,也可以没有,如果在没有初始值得情况下被订阅,则订阅者不会收到任何的值.
4.3 MediatorLiveData
MediatorLiveData
继承自MutableLiveData
,它主要用来实现多个LiveData
数据源的合并.
public class MediatorLiveData extends MutableLiveData {
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
@MainThread
public void addSource(@NonNull LiveData source, @NonNull Observer<? super S> 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<LiveData<?>, Source<?>> source : mSources) {
source.getValue().plug();
}
}
@CallSuper
@Override
protected void onInactive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().unplug();
}
}
private static class Source implements Observer {
final LiveData mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData liveData, final Observer<? super V> 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
多了两个方法addSource
和removeSource
,通过这两个方法我们可以将其他LiveData
合并到此LiveData
上,当其他LiveData
发生改变时,此LiveData
就能收到通知.
@MainThread
public void addSource(@NonNull LiveData source, @NonNull Observer<? super S> 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);
在Android
的Lifecycle
实现中框架向Activity
中添加了一个继承了系统Fragment
的ReportFragment
来汇报组件的生命周期,如果你使用的是appcompat
的Fragment
,那么它对你就是不可见的,所以一定要避免使用系统的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
的有关知识.
RxJava
在MVVM
中主要用于发布事件,下面是需要注意的一些点.
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
接口的防止多重点击机制.
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
最后
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
5c.png)
[外链图片转存中…(img-fTiH6M21-1711552187832)]
[外链图片转存中…(img-k9RbIvTV-1711552187832)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
[外链图片转存中…(img-dboo3uzw-1711552187832)]
[外链图片转存中…(img-Katzwfty-1711552187833)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-17ESvzu7-1711552187833)]
最后
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
[外链图片转存中…(img-S8AFkJYZ-1711552187834)]
最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!