AsyncLayoutInflater 源码详解

2020-01-06  本文已影响0人  房房1524

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和其他布局加载优化的文章

上一篇下一篇

猜你喜欢

热点阅读