如果我们关注Log,有时候会看到这样的warn提示:
Activity: Slow Operation: Activity ***.***.**.***Activity onStart took 1239ms
一般项目中存在一个MainActivity,属于一个App的主页面,用户在这里停留时间最长,同时也承载了最多的功能。如果MainActivity里面包含着多个Fragment,每个Fragment的布局比较复杂,就回导致在启动MainActivity时,加载Fragment的布局耗时太多,系统给出上面的提示。
Google在SDK中给出了一个解决方案:AsyncLayoutInflater。
类如其名,AsyncLayoutInflater的作用是在主线程外加载布局,然后把加载结果返回给主线程,减少主线程的卡顿。
源码
实现相对简单,仅有不到200行代码。把线程池、LayoutInflater、Handler相关知识点集中在一起,熟悉以上知识的小伙伴,可以很轻松读懂。
public final class AsyncLayoutInflater {
private static final String TAG = "AsyncLayoutInflater";
LayoutInflater mInflater;
Handler mHandler;
InflateThread mInflateThread;
public AsyncLayoutInflater(@NonNull Context context) {
mInflater = new BasicInflater(context);
mHandler = new Handler(mHandlerCallback);
mInflateThread = InflateThread.getInstance();
}
@UiThread
public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
@NonNull OnInflateFinishedListener callback) {
InflateRequest request = mInflateThread.obtainRequest();
request.inflater = this;
request.resid = resid;
request.parent = parent;
request.callback = callback;
mInflateThread.enqueue(request);
}
}
异步的实现使用了Thread和Handler。
InflateThread
继承自Thread
,负责在run方法中加载布局。
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);
public void runInner() {
InflateRequest request;
request = mQueue.take();
request.view = request.inflater.mInflater.inflate(
request.resid, request.parent, false);
Message.obtain(request.inflater.mHandler, 0, request)
.sendToTarget();
}
@Override
public void run() {
while (true) {
runInner();
}
}
public InflateRequest obtainRequest() {
InflateRequest obj = mRequestPool.acquire();
if (obj == null) {
obj = new InflateRequest();
}
return obj;
}
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) {
mQueue.put(request);、
}
}
Handler
负责把布局结果传递到主线程
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
封装了异步加载的各种属性,如:layout资源、回调接口、加载完成的view等。
private static class InflateRequest {
AsyncLayoutInflater inflater;
ViewGroup parent;
int resid;
View view;
OnInflateFinishedListener callback;
InflateRequest() {
}
}
public interface OnInflateFinishedListener {
void onInflateFinished(@NonNull View view, @LayoutRes int resid,
@Nullable ViewGroup parent);
}
BasicInflater
继承自LayoutInflater
,负责加载布局:
private static class BasicInflater extends LayoutInflater {
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
BasicInflater(Context context) {
super(context);
}
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new BasicInflater(newContext);
}
@Override
protected View onCreateView(String name, AttributeSet attrs){
for (String prefix : sClassPrefixList) {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
}
return super.onCreateView(name, attrs);
}
}
优化
AsyncLayoutInflater在性能方面做了一些优化:
对象池缓存对象:
#InflateThread
private SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);