美文网首页
AsyncLayoutInflater

AsyncLayoutInflater

作者: 程序员要多喝水 | 来源:发表于2020-01-02 19:57 被阅读0次

AsyncLayoutInflater顾名思义异步加载xml,如果布局比较复杂,且耗时较多情况下,可以使用此方式异步加载布局,使用方法很简单;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "asyncLayoutInflater";

    private LinearLayout content;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        content = findViewById(R.id.content);

        new AsyncLayoutInflater(this).inflate(R.layout.activity_include, content,
                new AsyncLayoutInflater.OnInflateFinishedListener() {
            @Override
            public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
                viewGroup.addView(view);
            }
        });

    }
}

这样activity_include布局就加载到id为content的View中了;
来分析其源码:

    LayoutInflater mInflater;
    Handler mHandler;
    InflateThread mInflateThread;

    public AsyncLayoutInflater(@NonNull Context context) {
        mInflater = new BasicInflater(context);
        mHandler = new Handler(mHandlerCallback);
        mInflateThread = InflateThread.getInstance();
    }

看到这,初步猜测就是LayoutInflater+Handler+Thread实现了;
BasicInflater是继承LayoutInflater类,重写了onCreateView方法,无特别之处;

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

InflateThread,一个线程池,单例模式,类加载后就会初始化并且运行线程,并且线程是不会退出的,一直while(true)在执行;

private static class InflateThread extends Thread {
        private static final InflateThread sInstance;
        static {
            sInstance = new InflateThread();
            sInstance.start();
        }

        public static InflateThread getInstance() {
            return sInstance;
        }
        //阻塞队列,10个最多;
        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) {
                // 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);
            }
            //加载完毕后发送Message消息回调Handler的callBack
            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) {
            try {
                mQueue.put(request);
            } catch (InterruptedException e) {
                throw new RuntimeException(
                        "Failed to enqueue async inflate request", e);
            }
        }
    }
}

布局加载,往生产者消费者模型中添加加载布局任务;

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

因为new Handler(mHandlerCallback),Handler创建方式是有CallBack的,因此会执行回调过程代码:

  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);
            }
            //回调onInflateFinished
            request.callback.onInflateFinished(
                    request.view, request.resid, request.parent);
            mInflateThread.releaseRequest(request);
            return true;
        }
    };

整个过程比较清晰,流程简单,涉及单例模式,生产者-消费者模型,对象池缓存,Handler的CallBack回调,Handler有3种回调方式,依次顺序为Message回调,Handler构造方法CallBakc回调,最后是重写HandlerMessage方法回调;

缺点:
1.生产者-消费者模型最大10个,超过线程需等待;
2.单个线程,执行效率较低,不退出执行,虽然有生产者-消费者阻塞,但是线程一直开着,未退出;
3.不支持LayoutInflater.Factory 或者 LayoutInflater.Factory2;
4.在加载完后需要手动addView到parent中,即调用viewGroup.addView(view);
5.添加inflater任务到生产者消费者模型后,等待时机触发未有cancel取消方式;

改造下使用线程池方式:

public class MyAsyncLayoutInflater {

    private static final String TAG = "MyAsyncLayoutInflater";

    LayoutInflater mInflater;
    Handler mHandler;
    InflateThread mInflateThread;

    public MyAsyncLayoutInflater(@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) {
        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);
    }

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

    public interface OnInflateFinishedListener {
        void onInflateFinished(View view, int resid, ViewGroup parent);
    }

    private static class InflateRequest{
        MyAsyncLayoutInflater inflater;
        ViewGroup parent;
        int resid;
        View view;
        OnInflateFinishedListener callback;

        InflateRequest() {
        }
    }

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

    private static class InflateRequestRunnable implements Runnable{
        private InflateRequest request;

        public InflateRequestRunnable(InflateRequest request) {
            this.request = request;
        }

        @Override
        public void run() {
            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();
        }
    }


    private static class InflateThread{
        private static final InflateThread sInstance;
        //这里线程池写法都是参考AsyncTask里面的
        public static final Executor THREAD_POOL_EXECUTOR;
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        private static final long KEEP_ALIVE_SECONDS = 30;
        //无限制队列
        private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>();

        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);

            public Thread newThread(Runnable r) {
                return new Thread(r, "MyAsyncLayoutInflater #" + mCount.getAndIncrement());
            }
        };

        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
            sInstance = new InflateThread();
        }

        public static InflateThread getInstance() {
            return sInstance;
        }

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

        public void enqueue(InflateRequest request) {
            THREAD_POOL_EXECUTOR.execute(new InflateRequestRunnable(request));
        }
    }
    
    //取消inflater过程
    public void cancelInflater(){
         mHandler.removeCallbacksAndMessages(null);
    }
}

使用方式:

        AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(this);
        for(int i =0;i<100000;i++){
            asyncLayoutInflater.inflate(R.layout.activity_include, content,
                    new AsyncLayoutInflater.OnInflateFinishedListener() {
                        @Override
                        public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
                            viewGroup.addView(view);
                        }
                    });
        }


//        MyAsyncLayoutInflater asyncLayoutInflater = new MyAsyncLayoutInflater(this);
//        for(int i =0;i<100000;i++){
//            asyncLayoutInflater.inflate(R.layout.activity_include, content,
//                    new MyAsyncLayoutInflater.OnInflateFinishedListener() {
//                        @Override
//                        public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
//                            viewGroup.addView(view);
//                        }
//                    });
//        }

最后对比两种方式加载时间,线程池优势还是很大的,当然一切还是根据实际需求为主,此处主要学习Android源码以及其设计模式;

相关文章

网友评论

      本文标题:AsyncLayoutInflater

      本文链接:https://www.haomeiwen.com/subject/thaooctx.html