布局加载过程中存在两个耗时点:
布局文件读取慢,涉及IO操作 
根据<tag>创建View慢(createViewFromTag()),使用反射的方式创建View。布局嵌套层数越多,控件个数越多,反射的次数就会越多 。 
 
当XML文件过大、嵌套过深时,就会导致页面发生卡顿甚至ANR。
解决方案有两种:
直接解决 :不使用IO操作以及反射 
侧面缓解 :把耗时操作放到子线程,等待加载完毕返回主线程展示即可。下面提到的AsyncLayoutInflater就是使用这个方案。 
 
AsyncLayoutInflater 采用异步加载 的方式去加载布局,可以节省主线程时间,并且在异步加载完毕后回到主线程。
使用方法 1 2 3 4 5 6 new AsyncLayoutInflater(this ).inflate(R.layout.XX, null , new AsyncLayoutInflater.OnInflateFinishedListener() {     @Override     public  void  onInflateFinished(@NonNull View view, int  i, @Nullable ViewGroup viewGroup) {         setContentView(view);     } });
 
基本原理 构造方法 1 2 3 4 5 6 7 8 9 10 11 public  final  class AsyncLayoutInflater {     LayoutInflater mInflater;//布局加载器     Handler mHandler;//处理加载完成消息     InflateThread mInflateThread;//执行加载任务      public  AsyncLayoutInflater(@NonNull Context context) {         mInflater = new BasicInflater(context);         mHandler = new Handler(mHandlerCallback);         mInflateThread = InflateThread.getInstance();     } }
 
BasicInflater 自定义加载器。实现类似PhoneLayoutInflater(默认布局加载器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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 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);     } }
 
InflateThread 创建子线程,将布局加载请求加入阻塞队列中,按照插入顺序执行LayoutInflater.inflate()加载过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 private  static  class InflateThread extends Thread {     private  static  final  InflateThread sInstance;     static  {         sInstance = new InflateThread();         sInstance.start();     }   //阻塞队列 最多支持10个加载请求     private  ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);   //对象池,缓存InflateThread对象     private  SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);       //对象池获取缓存对象     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);     }         //将inflate请求添加到 阻塞队列中     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  {           //阻塞队列获取 任务,没任务则阻塞             request = mQueue.take();         } catch  (InterruptedException ex) {             // Odd, just continue              Log.w(TAG, ex);             return;         }         try  {           //调用BasicInflater去加载布局             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);         }       //构建消息发送到Handler         Message.obtain(request.inflater.mHandler, 0, request)                 .sendToTarget();     }     @Override     public  void  run() {         while  (true ) {             runInner();         }     }       }
 
InflateThread不管最后inflate()执行成功或失败,都会把结果发回到Handler进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private  Handler.Callback mHandlerCallback = new Handler.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 ;     } };
 
Handler收到消息后,根据InflateRequest.view是否为空,判断接下执行步骤:
如果为空,回到主线程进行布局加载任务,加载完成后回调onInflateFinished()
不为空,直接回调onInflateFinished()
inflate() 发起异步加载布局请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @UiThreadpublic  void  inflate(@LayoutRes int  resid, @Nullable ViewGroup parent,         @NonNull OnInflateFinishedListener callback) {     if  (callback == null ) {         throw new NullPointerException("callback argument may not be null !");     }   //构建InflateRequest对象     InflateRequest request = mInflateThread.obtainRequest();     request.inflater = this ;     request.resid = resid;     request.parent = parent;     request.callback = callback;   //插入加载请求到阻塞队列     mInflateThread.enqueue(request); }
 
InflateRequest 主线程和子线程之间传递的数据模型,主要封装了异步加载需要的参数
1 2 3 4 5 6 7 8 9 10 private  static  class InflateRequest {     AsyncLayoutInflater inflater;//加载器     ViewGroup parent;//父布局     int  resid;//布局id     View view;//加载完成的View     OnInflateFinishedListener callback;//加载完成回调     InflateRequest() {     } }
 
OnInflateFinishedListener 布局加载完成后回调
1 2 3 4 5 public  interface OnInflateFinishedListener {     void  onInflateFinished(@NonNull View view, //加载完成的View                            @LayoutRes int  resid,             @Nullable ViewGroup parent); }
 
使用AsyncLayoutInflater加载布局后,将需要加载的layoutId以及OnInflateFinishedListener构造成InflateRequest,插入到InflateThread的阻塞队列中,等待执行。任务执行完毕后,返回执行结果(成功返回加载后的View,失败返回null)。
通过Handler发送结果回到主线程,返回结果为null,则在主线程再次执行布局加载,得到结果后直接回调onInflateFinished()。
 
局限及改进 局限 
AsyncLayoutInflater构造的View,无法直接使用handler或者调用looper.myLooper,因为没有进行初始化 
AsyncLayoutInflater构造的View,不会自动加到parent中,需要手动加入 
AsyncLayoutInflater不支持设置Factory/Factory2,未设置mPrivateFactory所以不支持包含<fragment>的布局 
最多支持10个布局加载,超出的布局需要等待。 
 
改进 
AsyncLayoutInflater是final的,无法被继承。需要copy一份代码进行修改。
 
针对4可以内部替换成线程池,将加载布局请求放入线程池管理
针对3可以修改BasicInflater实现,内部支持factory设置
1 2 3 4 5 6 7 8 9 10 11 BasicInflater(Context context) {     super (context);     if  (context instanceof  AppCompatActivity) {         // 加上这些可以保证AppCompatActivity的情况下,super .onCreate之前         // 使用AsyncLayoutInflater加载的布局也拥有默认的效果         AppCompatDelegate appCompatDelegate = ((AppCompatActivity) context).getDelegate();         if  (appCompatDelegate instanceof  LayoutInflater.Factory2) {             LayoutInflaterCompat.setFactory2(this , (LayoutInflater.Factory2) appCompatDelegate);         }     } }
 
参考链接 AsyncLayoutInfalter