Architecture Components
DataBinding
DataBinding 是一个数据绑定库
在app模块的build.gradle配置
dataBinding{
enabled = true
}
将xml改成databinding的格式
在layout文件的最外层group中,按下alt+enter,弹出convert to data binding layout
<layout 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">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
</LinearLayout>
</layout>
Activity,Fragment绑定视图
Activity 替换原来的setContentView。
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
Fragment
DataBindingUtil.inflate(inflater,R.layout.activity_main,container,false);
return binding.getRoot();
返回类名:layout的文件名(首字母大写)+Binding ActivityMainBinding的具体位置:
app/build/generated/source/apt/debug/包名/databinding/
响应点击事件
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!"
android:onClick="@{myobject::invoke}"/>
其他还有
android:onClick="@{presenter.onClick()}" //1.方法引用
android:onClick="@{()->presenter.onClick()}" //2.lamda表达式
android:onClick="@{(view)->presenter.onClick(view)}" //3.lamda表达式
android:onClick="@{()->presenter.onClick(item)}"//4.带参数lamda表达式
android:onClick="@{(view)->presenter.onClick(view, item)}"//5.带参数lamda表达式
建议写法
使用封装过的View.OnClickListener来统一处理点击事件,包裹一层的目的是为了不依赖于具体实现
public interface Present extends View.OnClickListener {
@Override
void onClick(View v);;
}
然后Activity或者Fragment实现Present,则必须改写onClick方法
public class MainActivity extends AppCompatActivity implements Present{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.setPresent(this);
}
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "方法引用", Toast.LENGTH_SHORT).show();
}
}
BindingAdapter
//必须是public static的
@BindingAdapter("app:goneUnless")
public static void goneUnless(View view, Boolean visible) {
view.visibility = visible ? View.VISIBLE : View.GONE;
}
databinding框架也为我们提供了很多adapter
2018-09-04_102909.pngBindingConversion
实现类型的转换
/**
* 将日期date转化为字符串
* @param
* @return
*/
@BindingConversion
public static String convertDate(Date date){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
/**
* 将字符串颜色值转化为ColorDrawable
* @param colorString 如:#ff0000
* @return
*/
@BindingConversion
public static ColorDrawable convertColorToDrawable(String colorString) {
int color = Color.parseColor(colorString);
return new ColorDrawable(color);
}
InverseBindingAdapter
- 作用于方法,方法须为公共静态方法。
- 方法的第一个参数必须为View类型,如TextView等
- 用于双向绑定
- 需要与@BindingAdapter配合使用
- attribute:String类型,必填,表示当值发生变化时,要从哪个属性中检索这个变化的值,示例:"android:text"
- event: String类型,非必填;如果填写,则使用填写的内容作为event的值;如果不填,在编译时会根据attribute的属性名再加上后缀“AttrChanged”生成一个新的属性作为event的值,举个例子:attribute属性的值为”android:text”,那么默认会在”android:text”后面追加”AttrChanged”字符串,生成”android:textAttrChanged”字符串作为event的值.
event属性的作用: 当View的值发生改变时用来通知dataBinding值已经发生改变了。开发者一般需要使用@BindingAdapter创建对应属性来响应这种改变。
RecyclerView与DataBinding
可以直接在xml中设置layoutManager,同时也可以指定adapter,它会自己去寻找setAdapter方法
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.GridLayoutManager"
app:spanCount="2"
android:adapter="@{adapter}"></android.support.v7.widget.RecyclerView>
简化viewholder
public class BaseBindingViewHolder extends RecyclerView.ViewHolder {
public BaseBindingViewHolder(View itemView) {
super(itemView);
}
}
adapter的基本写法
public class UserAdapter extends RecyclerView.Adapter {
private Context mContext;
private List<User> items;
public UserAdapter(Context context, List<User> items) {
mContext = context;
this.items = items;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemMainBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext),R.layout.item_main,parent,false);
return new BaseBindingViewHolder(binding.getRoot());
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ItemMainBinding binding = DataBindingUtil.getBinding(holder.itemView);//获取binding
binding.setVariable(BR.user,items.get(position));//设置item_main里面的数据对象
binding.executePendingBindings();
}
@Override
public int getItemCount() {
return items.size();
}
}
Lifecycles
官网上的解释:The android.arch.lifecycle package provides classes and interfaces that let you build lifecycle-aware components—which are components that can automatically adjust their behavior based on the current lifecycle state of an activity or fragment.
添加依赖
implementation "android.arch.lifecycle:extensions:1.1.1"
官网上的意思是,很多时候我们需要在onstart中进行耗时的很耗时的操作时,这个时候可能activity已经停止了,这会出现状态的竞争
@Override
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start();
}
});
}
Lifecycles来解决这个问题,用两个主要的枚举来追踪关联的生命周期State和Event
2018-09-04_102923.png实现LifecycleObserver
public class BoundLocationListener implements LifecycleObserver {
private final Context mContext;
private LocationManager mLocationManager;
private final LocationListener mListener;
public BoundLocationListener(LifecycleOwner lifecycleOwner,
LocationListener listener, Context context) {
mContext = context;
mListener = listener;
//想要观察 Activity 的声明周期,必须将其添加到观察者中。添加下面的代码
//才能是 BoundLocationListener 实例监听到生命周期
lifecycleOwner.getLifecycle().addObserver(this);
}
//可以使用 @OnLifecycleEvent 注解来监听 Activity 生命周期的变化
// 可以使用下面的注解来添加 addLocationListener() 方法
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void addLocationListener() {
mLocationManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mListener);
Log.d("BoundLocationMgr", "Listener added");
// Force an update with the last location, if available.
Location lastLocation = mLocationManager.getLastKnownLocation(
LocationManager.GPS_PROVIDER);
if (lastLocation != null) {
mListener.onLocationChanged(lastLocation);
}
}
// 可以使用下面的注解来移除 removeLocationListener() 方法
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void removeLocationListener() {
if (mLocationManager == null) {
return;
}
mLocationManager.removeUpdates(mListener);
mLocationManager = null;
Log.d("BoundLocationMgr", "Listener removed");
}
}
LifecycleOwner 是一个单一方法的接口,它表示实现类具有一个 Lifecycle。它有一个 getLifecycle()) 方法,该方法必须由实现类实现。activity和fragment已经实现了LifecyclerOwner方法
ViewModel
ViewModel用于为UI组件提供数据,并且能够在旋转屏幕等Configuration Change发生时,仍能保持里面的数据。当UI组件恢复时,可以立刻向UI提供数据
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
ViewModel的生命周期
2018-09-04_102934.png直到Activity被销毁时,调用onCleared(),其他无论调用几次onCreated,ViewModel都是最开始创建的
public class TestViewModel extends ViewModel{
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
LiveData
使用LiveData的优点
- UI和实时数据保持一致
因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。 - 避免内存泄漏
观察者被绑定到组件的生命周期上,当被绑定的组件销毁(destory)时,观察者会立刻自动清理自身的数据。 - 不会再产生由于Activity处于stop状态而引起的崩溃
例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。 - 不需要再解决生命周期带来的问题
LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。 - 实时数据刷新
当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据 - 解决Configuration Change问题
在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。 - 数据共享
如果对应的LiveData是单例的话,就能在app的组件间分享数据。这部分详细的信息可以参考继承LiveData
MutableLiveData
LiveData并没有提供可用的方法来更改储存的数据。MutableLiveData可以通过postValue()或者setValue()来编辑数值。区别是
setValue是在主线程调用,postValue在非主线程调用。
Extend LiveData
LiveData的活跃状态包括:STARTED或者RESUMED两种状态
public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager mStockManager;
private SimplePriceListener mListener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price); //updates the value of the LiveData instance and notifies any active observers about the change.
}
};
//把StockLiveData写成单例模式,那么还可以在不同的组件间共享数据
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
private StockLiveData(String symbol) {
mStockManager = new StockManager(symbol);
}
@Override
protected void onActive() { //called when the LiveData object has an active observer
mStockManager.requestPriceUpdates(mListener);
}
@Override
protected void onInactive() { //called when the LiveData object doesn't have any active observers
mStockManager.removeUpdates(mListener);
}
}
Transform LiveData
官方api
通过Transformations实现转换的功能
-
map LiveData<Y> map (LiveData<X> source, Function<X, Y> func)
applies the given function on the main thread to each value emitted by source LiveData and returns LiveData, which emits resulting values.
The given function func will be executed on the main thread.
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
- switchMap LiveData<Y> switchMap (LiveData<X> trigger, Function<X, LiveData<Y>> func)
private final MutableLiveData<String> addressInput = new MutableLiveData();
public final LiveData<String> postalCode =
Transformations.switchMap(addressInput, (address) -> {
return repository.getPostCode(address);
});
switchMap的fun带有返回值LiveData
MediatorLiveData
合并LiveData用
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
移除方法:removeSource()
补充
美团 的 用LiveDataBus替代RxBus、EventBus——Android消息总线的演进之路
public final class LiveDataBus {
private final Map<String, BusMutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DEFAULT_BUS;
}
public <T> MutableLiveData<T> with(String key, Class<T> type) {
if (!bus.containsKey(key)) {
bus.put(key, new BusMutableLiveData<>());
}
return (MutableLiveData<T>) bus.get(key);
}
public MutableLiveData<Object> with(String key) {
return with(key, Object.class);
}
private static class ObserverWrapper<T> implements Observer<T> {
private Observer<T> observer;
public ObserverWrapper(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onChanged(@Nullable T t) {
if (observer != null) {
if (isCallOnObserve()) {
return;
}
observer.onChanged(t);
}
}
private boolean isCallOnObserve() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (stackTrace != null && stackTrace.length > 0) {
for (StackTraceElement element : stackTrace) {
if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&
"observeForever".equals(element.getMethodName())) {
return true;
}
}
}
return false;
}
}
private static class BusMutableLiveData<T> extends MutableLiveData<T> {
private Map<Observer, Observer> observerMap = new HashMap<>();
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void observeForever(@NonNull Observer<T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverWrapper(observer));
}
super.observeForever(observerMap.get(observer));
}
@Override
public void removeObserver(@NonNull Observer<T> observer) {
Observer realObserver = null;
if (observerMap.containsKey(observer)) {
realObserver = observerMap.remove(observer);
} else {
realObserver = observer;
}
super.removeObserver(realObserver);
}
private void hook(@NonNull Observer<T> observer) throws Exception {
//get wrapper's version
Class<LiveData> classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class<?> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
}
作者:美团技术团队
链接:https://juejin.im/post/5b5ac0825188251acd0f3777
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。