1、简介(此间代码基于Android API 28)
官方解释
Helper class for inflating layouts asynchronously. To use, construct
an instance of {@link AsyncLayoutInflater} on the UI thread and call
{@link #inflate(int, ViewGroup, OnInflateFinishedListener)}. The
{@link OnInflateFinishedListener} will be invoked on the UI thread
when the inflate request has completed.
翻译一下
一个异步加载布局的帮助类,使用的话在UI线程中构造(ps:构造的时候直接new的handler,要使用这个handler切换到主线程,所以需要在UI线程中new),调用inflate方法。当加载完成后会回调OnInflateFinishedListener中的方法(纯手工翻译,英语好的别喷)
2、类的结构&源码详解
2.1内部类
2.1.1 InflateRequest
private static class InflateRequest {
AsyncLayoutInflater inflater;
ViewGroup parent;
int resid;
View view;
OnInflateFinishedListener callback;
}
- OO思想 封装了一个request对象;
- 将 inflate(int resid, ViewGroup parent,OnInflateFinishedListener callback)中参数 封装
- 然后 enqueue【入队列[阻塞队列——具体看下面inflate方法分析]】
2.1.2 InflateThread
private static class InflateThread extends Thread {
private static final InflateThread sInstance;
// 直接构造,直接start 这个thread
static {
sInstance = new InflateThread();
// 类加载的时候就直接跑起来
sInstance.start();
}
public static InflateThread getInstance() {
return sInstance;
}
// 阻塞队列(保存封装过得request)
private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);
private SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);
public void runInner() {
InflateRequest request;
try {
request = mQueue.take();
} catch (InterruptedException ex) {
return;
}
request.view = request.inflater.mInflater.inflate(
request.resid, request.parent, false);
}
Message.obtain(request.inflater.mHandler, 0, request)
.sendToTarget();
}
@Override
public void run() {
// 异步加载布局并使用handler进行,具体原因 看下文方法分析
while (true) {
runInner();
}
}
public void enqueue(InflateRequest request) {
try {
mQueue.put(request);
} catch (InterruptedException e) {
throw new RuntimeException(
"Failed to enqueue async inflate request", e);
}
}
public void runInner() {
InflateRequest request;
try {
// take()如果队列为空 会阻塞
request = mQueue.take();
} catch (InterruptedException 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);
}
Message.obtain(request.inflater.mHandler, 0, request)
.sendToTarget();
}
}
- 就是一个Thread,并且在类加载的时候就调用了start方法。
- 阻塞队列(保存封装过得request),调用AsyncLayoutInflate.inflate()方法时候入队列(InflateThread #enqueue方法)
runInner方法详解
//异步加载
request.view = request.inflater.mInflater.inflate(
request.resid, request.parent, false);
//这个handler是AsyncLayoutInflate构造时候new出来的,注释中要求在UI(主线程)中构造,所以这个handler为主线程handler,实现线程切换
Message.obtain(request.inflater.mHandler, 0, request)
.sendToTarget();
// 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;
}
};
2.1.3 BasicInflater
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) 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中包名下的UI
3、源码解析
3.1重要方法
上面都分析过了
3.2设计思路(本人拙见)
看的比较简单就先不写了
4.总结
除了这个方法优化布局加载,其实Google也推出了类似SwiftUI的一种声明式UI-Compose、还有Facebook的 Litho,另外还可以利用APT的方式将xml在编译的时候直接转换为java代码(X2C框架),省去运行时候xml的读取和反射构造VIew,后续也会有关于Compose和其他布局加载优化的文章
网友评论