最近撸了一些Jetpack的源码,发现自己阅读源码的能力越来越强了,为自己的努力点赞,相信没有谁的努力会被辜负
Databinding本来是第一个就要撸的,但是当我进入源码世界之后,真的是被它弄糊涂了。找了半天没有什么头绪,最后只能把它放在最后来看了。即便是这样,把Databinding捋顺也用了半天的时间,期间还使用了反射来验证,也是麻烦至极。因为Databind所生成的代码文件,都是使用APT编译期生成的,所以我们可以使用debug打断点来找思路,一开始我自己没有注意这点,所以浪费了很多时间。行了,废话不多说,我们开始撸源码。
一、源码探索
撸码前,我先把使用的model模型贴出来,省的之后相关的内容大家会糊涂,关于MainActivity和activity_layout的内容我就不贴了,都是基本用法。
public class UserBean extends BaseObservable {
private long id;
private String name;
private char sex;
private int age;
public UserBean(long id, String name, char sex, int age) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
@Bindable
public long getId(){
return id;
}
public void setId(long id){
this.id = id;
notifyPropertyChanged(BR.id);
}
@Bindable
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public char getSex(){
return sex;
}
public void setSex(char sex){
this.sex = sex;
notifyPropertyChanged(BR.sex);
}
@Bindable
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
notifyPropertyChanged(age);
}
Databinding的源码流程,我这里分了两步走:(1)DatabindingUtil的setContentView方法(2)ActivityMainBinding的setUser方法
1.DatabindingUtil的setContentView方法
MainActivity中是如下写:
ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
进入DataBindingUtil的setContentView方法中
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
进入bindToAddedViews方法中
private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
ViewGroup parent, int startChildren, int layoutId) {
final int endChildren = parent.getChildCount();
final int childrenAdded = endChildren - startChildren;
if (childrenAdded == 1) {
final View childView = parent.getChildAt(endChildren - 1);
return bind(component, childView, layoutId);
} else {
final View[] children = new View[childrenAdded];
for (int i = 0; i < childrenAdded; i++) {
children[i] = parent.getChildAt(i + startChildren);
}
return bind(component, children, layoutId);
}
}
childredAdded等于1,所以我们进入第一个bind方法中
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
我们进入sMapper.getDataBinder方法中查看,发现是一个抽象方法,真正的实现类应该的文件在:app->build->generated->ap_generated_sources->debug->debug->out->包路径->DataBinderMapperImpl,我们在这个类里找到了相关的内容
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYMAIN: {
if ("layout/activity_main_0".equals(tag)) {
return new ActivityMainBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
}
return null;
}
我们进入ActivityMainBindingImpl的构造方法中
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (androidx.recyclerview.widget.RecyclerView) bindings[5]
);
//此处省略部分代码
setRootTag(root);
// listeners
invalidateAll();
}
先执行了父类的构造方法,这个父类最后定位到了ViewDataBinding这个类,我们查看构造方法
(注:直接点击setRootTag方法就进入到了ViewDataBinding里了,点击继承的ActivitymainBinding类是进入不到父类的)
static {
if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
ROOT_REATTACHED_LISTENER = null;
} else {
ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
@TargetApi(VERSION_CODES.KITKAT)
@Override
public void onViewAttachedToWindow(View v) {
// execute the pending bindings.
final ViewDataBinding binding = getBinding(v);
binding.mRebindRunnable.run();
v.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
};
}
}
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
// Nested so that we don't get a lint warning in IntelliJ
if (!mRoot.isAttachedToWindow()) {
// Don't execute the pending bindings until the View
// is attached again.
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
executePendingBindings();
}
};
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
mBindingComponent = bindingComponent;
mLocalFieldObservers = new WeakListener[localFieldCount];
this.mRoot = root;
if (Looper.myLooper() == null) {
throw new IllegalStateException("DataBinding must be created in view's UI Thread");
}
if (USE_CHOREOGRAPHER) {
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}
}
这里面有三个内容:
(1)静态代码块:静态代码块中主要是一个监听,简单来说就是,当View和屏幕绑定时,这个监听就会触发,这里指的是mRootView
(2)mRebindRunnable:当静态代码块中的监听执行时,这个类的run方法就执行了
(3)构造方法:这里使用了Choreographer这个类。关于这个类,推荐大家一篇文章
我们再回到ActivityMainBindingImpl的构造方法中,之后会执行invalidateAll方法,我们进入查看
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x20L;
}
requestRebind();
}
这个requestRebind方法是父类ViewDataBinding的方法,我们进去查看
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
Lifecycle.State state = owner.getLifecycle().getCurrentState();
if (!state.isAtLeast(Lifecycle.State.STARTED)) {
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
从这里我们可以看到上面说的mChoreographer,它执行了postFrameCallback方法,当doFrame方法执行时,mRebindRunnable的run方法就执行了。mRebindRunnable中的run方法执行了两个内容:如果mRoot已经和屏幕进行了绑定,那么直接执行executePendingBindings方法;否则的话,添加监听,什么时候绑定了,什么时候执行。所以我们进入executePendingBindings方法中查看:
public void executePendingBindings() {
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
进入executeBindingsInternal方法中查看:
private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBIND, null);
// The onRebindListeners will change mPendingHalted
if (mRebindHalted) {
mRebindCallbacks.notifyCallbacks(this, HALTED, null);
}
}
if (!mRebindHalted) {
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}
这里会进入到executeBindings方法中(如果某些变量拿不准,可以debug打断点看看),这个方法是一个抽象方法,具体的实现在ActivityMainBindingImpl中,我们进入查看
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
int userAge = 0;
com.gzc.databinding.UserBean user = mUser;
java.lang.String stringValueOfUserAge = null;
java.lang.String userName = null;
java.lang.String javaLangStringStringValueOfUserAge = null;
java.lang.String javaLangStringStringValueOfUserSex = null;
java.lang.String javaLangStringIdUserId = null;
java.lang.String javaLangStringUserName = null;
char userSex = '\u0000';
java.lang.String stringValueOfUserSex = null;
long userId = 0;
if ((dirtyFlags & 0x3fL) != 0) {
if ((dirtyFlags & 0x31L) != 0) {
if (user != null) {
// read user.age
userAge = user.getAge();
}
// read String.valueOf(user.age)
stringValueOfUserAge = java.lang.String.valueOf(userAge);
// read ("年龄:") + (String.valueOf(user.age))
javaLangStringStringValueOfUserAge = ("年龄:") + (stringValueOfUserAge);
}
if ((dirtyFlags & 0x25L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
// read ("姓名:") + (user.name)
javaLangStringUserName = ("姓名:") + (userName);
}
if ((dirtyFlags & 0x29L) != 0) {
if (user != null) {
// read user.sex
userSex = user.getSex();
}
// read String.valueOf(user.sex)
stringValueOfUserSex = java.lang.String.valueOf(userSex);
// read ("性别:") + (String.valueOf(user.sex))
javaLangStringStringValueOfUserSex = ("性别:") + (stringValueOfUserSex);
}
if ((dirtyFlags & 0x23L) != 0) {
if (user != null) {
// read user.id
userId = user.getId();
}
// read ("id:") + (user.id)
javaLangStringIdUserId = ("id:") + (userId);
}
}
// batch finished
if ((dirtyFlags & 0x23L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, javaLangStringIdUserId);
}
if ((dirtyFlags & 0x25L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, javaLangStringUserName);
}
if ((dirtyFlags & 0x29L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView3, javaLangStringStringValueOfUserSex);
}
if ((dirtyFlags & 0x31L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView4, javaLangStringStringValueOfUserAge);
}
}
我们仔细查看会发现,在这里给了xml文件中和model进行绑定了的组件附了值,不过呢,此时的mUser还是空。
至此,setContentView的流程就已经结束了,我贴上一张流程图,便于理解
我们继续下一步探索
2.ActivityMainBinding的setUser方法
我们进入ActivityMainBindingImpl中的setUser中查看
public void setUser(@Nullable com.gzc.databinding.UserBean User) {
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
到了这里,估计小伙伴会像我之前一样,一个一个方法的进入查找线索,然后你打断点会发现,会快就出来了,不论是notifyPropertyChanged还是super.requestRebind方法(注:我调用setUser这个方法是在Activity的onCreate方法中)。那设置完mUser后,信息是如何在布局文件中更新的呢?小伙伴应该还记得上面有和屏幕绑定的代码,如果绑定成功之后,自然会执行mRebindRunnable的run方法,之后的话和1一样了。
有的小伙伴估计会问,如果我不是在onCreate方法调用的setUser呢,我在屏幕绑定之后调用的会怎么样?那我们继续看setUser这个方法,notifyPropertyChanged这个方法依然忽略,因为里面的mCallback依然是空,直接return了。有的同学会说:等等,别欺负我没看过源码,源码我也看了,从updateRegistration进入,一层一层的会到ViewDataBinding中的registerTo中,如下
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
listener.setTarget(observable);
}
然后到WekListener的setTarget中
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget);
}
}
mObservale的具体实现类是WeakPropertyListener
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
而addOnPropertyChanedCallback在BaseObservable中有实现
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
mCallbacks.add(callback);
}
你看看,mCallbacks明明被创建了,你还说是空?
我之前也在这里糊涂了一阵,我们要明白:上面的target是谁?是通过setUser传过来的User对象!而不是ActivityMainBinding!ActivityMainBinding所持有的mCallback还是空的呢!
我们看下super.requestRebind方法
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
Lifecycle.State state = owner.getLifecycle().getCurrentState();
if (!state.isAtLeast(Lifecycle.State.STARTED)) {
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
其实和1中的一样,最后执行到了mRebindRunnable中的run方法,最后定位到1中最后说的executeBindings方法。
流程图我就不贴了,因为基本上和1中的图相同
二、问题
1.为什么说databinding或者说MVVM比较耗内存?
我认为从上面这么绕的代码上也能知道内存的消耗肯定要比非databinding要耗内存,另外,布局文件会通过APT方式额外的生成两个副本。所以内存的消耗,可想而知。
2.我们在User的get方法中用到了@Bindable注解,有什么用?
主要是生成BR中索引