AsyncLayoutInflater特点
- layout的parent ViewGroup的generateLayoutParams是线程安全的;
- inflater中的所有view内部不能创建Handler或者调用Looper.myLooper;
- 异步inflate失败的layout最终都会在UI线程进行inflate;
- inflate出来的view不会被添加到parent;
- 不支持设置LayoutInflater.Factory和LayoutInflater.Factory2;
- 不能inflate包含fragments的layout;
用法
//初始化
AsyncLayoutInflater mAsyncLayoutInflater = new AsyncLayoutInflater(context);
......
//调用
mAsyncLayoutInflater.inflate(layoutId, parent, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {
......
}
});
业务方通过设置AsyncLayoutInflater.OnInflateFinishedListener监听,获取异步inflate的结果。
源码分析
关键属性和方法
LayoutInflater mInflater;
Handler mHandler;
InflateThread mInflateThread;
-
LayoutInflater mInflater
- AsyncLayoutInflater中封装的LayoutInlater对象,用于在异步线程中进行inflate;
- mInflate实际指向的是内部继承于LayoutInflater定制的BasicInflater,具体功能见后文;
-
Handler mHandler
- 用于异步线程和UI线程通信
-
InflateThread mInflateThread
- 内部异步线程,在该线程中执行layout inflate
-
public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
@NonNull OnInflateFinishedListener callback)@UiThread public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent, @NonNull OnInflateFinishedListener callback) { if (callback == null) { throw new NullPointerException("callback argument may not be null!"); } InflateRequest request = mInflateThread.obtainRequest(); request.inflater = this; request.resid = resid; request.parent = parent; request.callback = callback; mInflateThread.enqueue(request); }
将新的resId的异步inflate操作封装到InflateRequest对象中,并通过enqueue@InflateThread方法加入到异步线程的任务队列中,
public void enqueue(InflateRequest request) { try { mQueue.put(request); } catch (InterruptedException e) { throw new RuntimeException( "Failed to enqueue async inflate request", e); } }
关键类—InflateThread
InflateThread 继承于Thread,是个单实例线程类,其关键成员fields如下
private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);
private SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);
- InflateRequest是个简单的数据类,每次调用
public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent, @NonNull OnInflateFinishedListener callback)
方法都会将inflate需要的信息封装到一个InflateRequest对象,并加入到mQueue中。 - mQueue是存储了待inflate的InflateRequest的阻塞队列,
- mRequestPool中保存的是已经inflate的InflateRequest缓存池
InflateThread在其static代码块中完成初始化和启动
static {
sInstance = new InflateThread();
sInstance.start();
}
-
InflateThread线程run方法是个无线循环,借助ArrayBlockingQueue,当mQueue队列中有任务时执行任务;没有任务时,线程阻塞,当新任务过来时,线程唤醒再执行任务。
public void run() { while (true) { runInner(); } }
public void runInner() { //Step1 Begin InflateRequest request; try { request = mQueue.take(); } catch (InterruptedException ex) { // Odd, just continue Log.w(TAG, ex); return; } try { request.view = request.inflater.mInflater.inflate( request.resid, request.parent, false); } catch (RuntimeException ex) { // Probably a Looper failure, retry on the UI thread Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI" + " thread", ex); } //Step1 End //Step2 Begin Message.obtain(request.inflater.mHandler, 0, request) .sendToTarget(); //Step2 End }
异步线程的run方法体每次循环,执行如下操作
-
Step1: 从mQueue阻塞队列取出头部InflateRequest,然后执行inflate@LayoutInflate,
-
Step2: 借助mHandler回到UI线程,执行如下操作,
- 如果Step1中异步inflate成功,则相应resId对应的view会成功创建;如果Step1中异步inflate失败,则在UI线程再次inflate@LayoutInflate。
- 通过onInflateFinished回调将异步inflate结果通知给调用方;
- 将当前已经inflate过且各个fields重设后的InflateRequest对象加入到mRequestPool对象池,供下一次复用
private Callback mHandlerCallback = new Callback() { @Override public boolean handleMessage(Message msg) { InflateRequest request = (InflateRequest) msg.obj; if (request.view == null) { request.view = mInflater.inflate( request.resid, request.parent, false); } request.callback.onInflateFinished( request.view, request.resid, request.parent); mInflateThread.releaseRequest(request); return true; } };
相关方法
//复用InflateRequest public InflateRequest obtainRequest() { InflateRequest obj = mRequestPool.acquire(); if (obj == null) { obj = new InflateRequest(); } return obj; } //回收InflateRequest public void releaseRequest(InflateRequest obj) { obj.callback = null; obj.inflater = null; obj.parent = null; obj.resid = 0; obj.view = null; mRequestPool.release(obj); }
obtainRequest从mRequestPool中获取InflateRequest对象,不成功就新建;
releaseRequest将已经inflate过的InflateRequest加入复用对象池。
-
关键类—BasicInflater
private static class BasicInflater extends LayoutInflater {
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
......
@Override
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack
// at it.
}
}
return super.onCreateView(name, attrs);
}
}
sClassPrefixList
数组包含3个android包路径,android.widget.,android.webkit.,android.app
,onCreateView中会有限加载这3个路径下的类,因为绝大多数UI文件都包含在这三个类中,在一定程度上节省了时间。
扩展知识点
- ArrayBlockingQueue(java.util.concurrent.ArrayBlockingQueue)
- 有界阻塞队列,FIFO,固定大小的数组,在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的队列,就不能再增加其容量。试图向已满队列中放入元素会导致放入操作受阻塞;试图从空队列中检索元素将导致类似阻塞。
- SynchronizedPool(androidx.core.util.Pools.SynchronizedPool)
- 线程安全的对象缓冲池,acquire方法取,release方法存