一. 什么是AsyncLayoutInflater
Helper class for inflating layouts asynchronously. To use, construct an instance of AsyncLayoutInflater on the UI thread and call inflate(int, ViewGroup, OnInflateFinishedListener).
The AsyncLayoutInflater.OnInflateFinishedListener will be invoked on the UI thread when the inflate request has completed.
This is intended for parts of the UI that are created lazily or in response to user interactions.
This allows the UI thread to continue to be responsive & animate while the relatively heavy inflate is being performed.
我们知道默认情况下 setContentView 函数是在 UI 线程执行的,这里面有很多耗时操作,
比如Xml的解析、View的反射创建等过程都是在UI线程执行的,
AsyncLayoutInflater 就是来帮我们把这些过程放在子线程执行,保持UI线程的高响应或达到懒加载的目的。
AsyncLayoutInflater的API非常简单,如下使用就可以了
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AsyncLayoutInflater(this).inflate(R.layout.activity_main, null){view, resid, parent -> setContentView(view) } } 在子线程被解析的View,将通过onInflateFinished, 也就是上面代码中的那个Lambda回调出来,这时候去调用setContentView直接传入这个View,流程中就不会再在主线程inflate了。 我们先来看一下整体类图:
二.源码分析
1. 构造方法
AsyncLayoutInflater的源码很短,比较好理解,我们先来看他的构造函数
public AsyncLayoutInflater(@NonNull Context context) { mInflater = new BasicInflater(context); mHandler = new Handler(mHandlerCallback); mInflateThread = InflateThread.getInstance(); }
主要做了三件事情:
- 创建 BasicInflater
- 创建 Handler
- 获取 InflateThread
1. BasicInflater 继承自 LayoutInflater,只重写了 onCreateView:优先加载这三个前缀的 Layout,
然后才按照默认的流程去加载,因为大多数情况下我们 Layout 中使用的View都在这三个 package 下。
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) { } } return super.onCreateView(name, attrs); } }
2.创建 Handler 很好理解,子线程创建出来的View需要回调给主线程,就是通过handler来回调的。
3. inflateThread,顾名思义,就是用来inflate布局的工作线程。
2. Inflate方法
Inflate方法也很简单,就是创建一个InflateRequest对象,并将 当前的this, resId、parent、callback等存储到这个对象中,
并调用mInflateThread的enqueue入队,之后实际的inflate则是在mInflateThread这个工作线程中的队列中完成的。
@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);
}
InflateRequest其实就是个数据实体
private static class InflateRequest { AsyncLayoutInflater inflater; ViewGroup parent; int resid; View view; OnInflateFinishedListener callback; InflateRequest() { } }
3. InflateThread
InflateThread的主要作用就是创建了一个子线程,并将inflate请求添加到了一个阻塞队列中,并按照顺序去inflate,
无论是否inflate成功,都会将request消息发送回主线程,
主线程如果发现inflate失败了,则会再次在主线程inflate。
private static class InflateThread extends Thread { private static final InflateThread sInstance; static { sInstance = new InflateThread(); sInstance.start(); } public static InflateThread getInstance() { return sInstance; }
private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);// 阻塞队列
private SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);//对象池,用来缓存InflateRequest对象,避免多次重复创建对象,导致内存抖动。
public void runInner() { InflateRequest request; try { request = mQueue.take();// 从队列中取一个request,如果没有则会阻塞在这。 } catch (InterruptedException ex) { // Odd, just continue Log.w(TAG, ex); return; } try { // Inflate layout request.view = request.inflater.mInflater.inflate( request.resid, request.parent, false); } catch (RuntimeException ex) { // 如果inflate失败了,会回到主线程inflate Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI" + " thread", ex); } Message.obtain(request.inflater.mHandler, 0, request) .sendToTarget();// 无论成功失败,都发回主线程 } @Override public void run() { while (true) { runInner();// 轮询,因为runInner中在执行阻塞队列的take,所以存在等待机制,不会快速的无限循环下去。 } } //从对象池中取出一个InflateThread对象,如果对象池中没有的话,会创建一个新的 public InflateRequest obtainRequest() { InflateRequest obj = mRequestPool.acquire(); if (obj == null) { obj = new InflateRequest();//创建新的,流程走完后会被release到对象池中 } return obj; } //将缓存的对象清空,并release回对象池,方便之后复用 public void releaseRequest(InflateRequest obj) { obj.callback = null; obj.inflater = null; obj.parent = null; obj.resid = 0; obj.view = null; mRequestPool.release(obj);
} public void enqueue(InflateRequest request) { try { mQueue.put(request);// 将请求添加到阻塞队列 } catch (InterruptedException e) { throw new RuntimeException( "Failed to enqueue async inflate request", e); } } }
4. mHandlerCallback
Inflate结束,就会把InflateRequest发回到主线程的handler callback,在这里,如果发现InflateRequest中的view是null,证明inflate失败了,那么会在主线程再次inflate。最终通过request.callback把结果回调出去。并将InflateRequest回收回对象池。
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; } };