jetpack-livedata

379 阅读5分钟

livedata是jetpack中的框架,可用于activity等监听数据的变化。前题是他们要实现LifecycleOwner。

是用来观察带有生命周期对象的利器,如:继承于SupportActivity的activity。。

用法案例(伪代码)

两步:

1.创建Observer对象,用于刷新ui。

2.调用livedata的observe方法。

1) // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };

2)//这里的this,一般是SupportActivity的子类。
 livedata.observe(this, nameObserver);

 
   @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        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);
    }

observe方法流程:

1.LifecycleOwner的getLifecycle()的当前状态,即不能是DESTROYED。否则立即返回,什么都不执行。这里的getLifecycle(),在SupportActivity中是LifecycleRegistry。后面分析。

2.将observer和LifecycleOwner一起封装到LifecycleBoundObserver

3.将observer本身和封装好的LifecycleBoundObserver放入livedata的成员变量mObservers中(因为livedata被观察的数据可以有多个

4.判断如果是相同的observer被添加到不同的lifecycles,则抛IllegalArgumentException

5.如果mObservers中已经存在相同observer,则返回不重复添加。 后面分析ObserverWrapper的数据结构,以及putIfAbsent方法。

6.lifecycle开始观察LifecycleBoundObserver。

========================== 注册好观察者对象之后,接着看被观察者livedata数据更新的方法

更新数据的方法:

1.setvalue

2.postvalue://通过主线程的handler将setvalue任务发送到messagequeue。

猜测下面代码的执行顺序

   * liveData.postValue("a");
   * liveData.setValue("b");

结果:先执行b,后执行a

原理:同handler发送消息机制

postvalue执行流程:

1.判断等待执行的数据是否已设置。

a)若等待执行的数据为(NOT_SET)状态,则添加任务到主线程messagequeue。

b)若已有设置数据等待执行,则返回

 protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);//就是用主线程的handler发送任务到messagequeue
    }
    
   // postvalue的runnable任务

 private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };

2.分析代码:

设计思想: 线程安全

思考:为什么postValue和mPostValueRunnable都会需要synchronized代码块?要知道postValue支持异步,但mPostValueRunnable永远是在主线程中执行。为什么还要给他加锁

private final Object mDataLock = new Object();
 synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }

答案: mPostValueRunnable只会在主线程中执行。因为他的handler是发送到主线程的messagequeue中。假如这时子线程发送更新数据的任务,如果不等待主线程的mPendingData传值给newValue,并且将自己设置为(NOT_SET)这两步都执行完成,postValue中的mPendingData将可能不是NOT_SET,导致异步数据添加失败。具体答案,请看后面的内容:查缺补漏。

衍生出的新问题: 主线程在执行任务,或者异步添加任务会导致相互等待锁,导致线程阻塞,不过由于涉及mPendingData相关的代码量少(都两行),执行速度快。所以性能方面也不会太受影响,但是还是存在数据更新延迟的问题。

需要注意的点:observe,removeObserver,setValue方法,都必须是在主线程。如下源码:

    @MainThread
    protected void setValue(T value) {}
     @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {}
    @MainThread
    removeObserver

查漏补缺:

问题:假设我一个Test类有两个方法,set,get,如果里面都用了synchronized代码块,且锁对象是同一个mDataLock.

当一个线程执行set中的synchronized代码块,如果代码块未执行完,其他持有相同锁的get方法,get中synchronized代码块里的代码能够执行吗?

答案:get不能执行。

测试代码:

package com.example.resthal;

public class Test {
	private static final Object mDataLock=new Object();
	public static void main(String[] args) {
		Test test=new Test();
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {

				@Override
				public void run() {
							test.set();
				}
			}).start();
			
			new Thread(new Runnable() {
				
				@Override
				public void run() {
						test.get();
				}
			}).start();
			
		}
	}
	
	
	void set(){
		System.out.println("start set"+Thread.currentThread().toString());
		synchronized (mDataLock) {
			try {
				System.out.println("####set mDataLock start####"+Thread.currentThread().toString());
				Thread.sleep(2000);
				System.out.println("####set mDataLock end####"+Thread.currentThread().toString());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("end set"+Thread.currentThread().toString());
	}
	
	void get(){
		System.out.println("start get:"+Thread.currentThread().toString());
		synchronized (mDataLock) {
			System.out.println("***get mDataLock start***"+Thread.currentThread().toString());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("***get mDataLock end***"+Thread.currentThread().toString());
		}
		System.out.println("end get:"+Thread.currentThread().toString());
		
	}
}

控制台结果:

start setThread[Thread-0,5,main]
####set mDataLock start####Thread[Thread-0,5,main]
start get:Thread[Thread-1,5,main]
start setThread[Thread-2,5,main]
start get:Thread[Thread-3,5,main]
start setThread[Thread-4,5,main]
start get:Thread[Thread-5,5,main]
start setThread[Thread-6,5,main]
start get:Thread[Thread-7,5,main]
start setThread[Thread-8,5,main]
start get:Thread[Thread-9,5,main]
####set mDataLock end####Thread[Thread-0,5,main]
end setThread[Thread-0,5,main]
***get mDataLock start***Thread[Thread-9,5,main]
***get mDataLock end***Thread[Thread-9,5,main]
end get:Thread[Thread-9,5,main]
####set mDataLock start####Thread[Thread-8,5,main]
####set mDataLock end####Thread[Thread-8,5,main]
end setThread[Thread-8,5,main]
***get mDataLock start***Thread[Thread-7,5,main]
***get mDataLock end***Thread[Thread-7,5,main]
end get:Thread[Thread-7,5,main]
####set mDataLock start####Thread[Thread-6,5,main]
####set mDataLock end####Thread[Thread-6,5,main]
end setThread[Thread-6,5,main]
***get mDataLock start***Thread[Thread-5,5,main]
***get mDataLock end***Thread[Thread-5,5,main]
end get:Thread[Thread-5,5,main]
####set mDataLock start####Thread[Thread-4,5,main]
####set mDataLock end####Thread[Thread-4,5,main]
end setThread[Thread-4,5,main]
***get mDataLock start***Thread[Thread-3,5,main]
***get mDataLock end***Thread[Thread-3,5,main]
end get:Thread[Thread-3,5,main]
####set mDataLock start####Thread[Thread-2,5,main]
####set mDataLock end####Thread[Thread-2,5,main]
end setThread[Thread-2,5,main]
***get mDataLock start***Thread[Thread-1,5,main]
***get mDataLock end***Thread[Thread-1,5,main]
end get:Thread[Thread-1,5,main]

总结

可以看到:set mDataLock start和set mDataLock end, get mDataLock start和get mDataLock end,中间不会有其他线程的 同步代码块代码内容 执行。