AsynclayoutInflater源码分析

AsyncLayoutInflater特点
  • layout的parent ViewGroup的generateLayoutParams是线程安全的;
  • inflater中的所有view内部不能创建Handler或者调用Looper.myLooper;
  • 异步inflate失败的layout最终都会在UI线程进行inflate;
  • inflate出来的view不会被添加到parent;
  • 不支持设置LayoutInflater.Factory和LayoutInflater.Factory2;
  • 不能inflate包含fragments的layout;
用法
        
//初始化        
AsyncLayoutInflater mAsyncLayoutInflater = new AsyncLayoutInflater(context);
    ......

//调用
mAsyncLayoutInflater.inflate(layoutId, parent, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {
    ......
}
});

业务方通过设置AsyncLayoutInflater.OnInflateFinishedListener监听,获取异步inflate的结果。

源码分析

关键属性和方法
LayoutInflater mInflater;
Handler mHandler;
InflateThread mInflateThread;
  • LayoutInflater mInflater

    • AsyncLayoutInflater中封装的LayoutInlater对象,用于在异步线程中进行inflate;
    • mInflate实际指向的是内部继承于LayoutInflater定制的BasicInflater,具体功能见后文;
  • Handler mHandler

    • 用于异步线程和UI线程通信
  • InflateThread mInflateThread

    • 内部异步线程,在该线程中执行layout inflate
  • public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
    @NonNull OnInflateFinishedListener callback)

    @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);
    }
    

    将新的resId的异步inflate操作封装到InflateRequest对象中,并通过enqueue@InflateThread方法加入到异步线程的任务队列中,

    public void enqueue(InflateRequest request) {
        try {
            mQueue.put(request);
        } catch (InterruptedException e) {
            throw new RuntimeException(
                    "Failed to enqueue async inflate request", e);
        }
    }
    
关键类—InflateThread

InflateThread 继承于Thread,是个单实例线程类,其关键成员fields如下

private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);
private SynchronizedPool<InflateRequest> mRequestPool = new SynchronizedPool<>(10);
  • InflateRequest是个简单的数据类,每次调用public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent, @NonNull OnInflateFinishedListener callback)方法都会将inflate需要的信息封装到一个InflateRequest对象,并加入到mQueue中。
  • mQueue是存储了待inflate的InflateRequest的阻塞队列,
  • mRequestPool中保存的是已经inflate的InflateRequest缓存池

InflateThread在其static代码块中完成初始化和启动

static {
    sInstance = new InflateThread();
    sInstance.start();
}
  • InflateThread线程run方法是个无线循环,借助ArrayBlockingQueue,当mQueue队列中有任务时执行任务;没有任务时,线程阻塞,当新任务过来时,线程唤醒再执行任务。

    public void run() {
        while (true) {
        		runInner();
        }
    }
    
    public void runInner() {
        //Step1 Begin
        InflateRequest request;
        try {
            request = mQueue.take();
        } catch (InterruptedException ex) {
            // Odd, just continue
            Log.w(TAG, 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);
        }
        //Step1 End
        
        //Step2 Begin
        Message.obtain(request.inflater.mHandler, 0, request)
                .sendToTarget();
        //Step2 End            
    }
    

    异步线程的run方法体每次循环,执行如下操作

    • Step1: 从mQueue阻塞队列取出头部InflateRequest,然后执行inflate@LayoutInflate,

    • Step2: 借助mHandler回到UI线程,执行如下操作,

      • 如果Step1中异步inflate成功,则相应resId对应的view会成功创建;如果Step1中异步inflate失败,则在UI线程再次inflate@LayoutInflate。
      • 通过onInflateFinished回调将异步inflate结果通知给调用方;
      • 将当前已经inflate过且各个fields重设后的InflateRequest对象加入到mRequestPool对象池,供下一次复用
      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
      public InflateRequest obtainRequest() {
          InflateRequest obj = mRequestPool.acquire();
          if (obj == null) {
              obj = new InflateRequest();
          }
          return obj;
      }
      
      //回收InflateRequest
      public void releaseRequest(InflateRequest obj) {
          obj.callback = null;
          obj.inflater = null;
          obj.parent = null;
          obj.resid = 0;
          obj.view = null;
          mRequestPool.release(obj);
      }
      

      obtainRequest从mRequestPool中获取InflateRequest对象,不成功就新建;

      releaseRequest将已经inflate过的InflateRequest加入复用对象池。

关键类—BasicInflater
  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) {
                  // In this case we want to let the base class take a crack
                  // at it.
              }
          }

          return super.onCreateView(name, attrs);
      }
  }

sClassPrefixList数组包含3个android包路径,android.widget.,android.webkit.,android.app,onCreateView中会有限加载这3个路径下的类,因为绝大多数UI文件都包含在这三个类中,在一定程度上节省了时间。

扩展知识点

  • ArrayBlockingQueue(java.util.concurrent.ArrayBlockingQueue)
    • 有界阻塞队列,FIFO,固定大小的数组,在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的队列,就不能再增加其容量。试图向已满队列中放入元素会导致放入操作受阻塞;试图从空队列中检索元素将导致类似阻塞
  • SynchronizedPool(androidx.core.util.Pools.SynchronizedPool)
    • 线程安全的对象缓冲池,acquire方法取,release方法存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值