美文网首页
Android 异步UI之二

Android 异步UI之二

作者: 折剑游侠 | 来源:发表于2021-10-19 18:43 被阅读0次

    前面写了下Android异步UI,简单来说就是在子线程中通过WindowManager.addView()的方式创建UI,那么UI的绘制流程就执行在这个子线程中。又众所周知ViewRootImpl.checkThread()检查更新UI的线程是否是创建UI的线程,那么在创建UI的这个子线程中我们可以任意操作UI。

    要维护一个子线程,当然是选择HandlerThread啊,但是像前面一样写样板代码也太难受了。封装一下这套逻辑,调用时最好能一行代码搞定,下面开撸。

    AsyncHandlerThread

    abstract class AsyncHandlerThread @JvmOverloads constructor(
        private val asyncActivity: WeakReference<FragmentActivity>,
        name: String = "AsyncHandlerThread"
    ) :
        HandlerThread(name), LifecycleEventObserver {
        private var handler: H? = null
    
        fun inflate() {
            start()
            handler = H(looper, asyncActivity)
            handler?.sendEmptyMessage(INFLATE_UI)
            asyncActivity.get()?.lifecycle?.addObserver(this)
        }
    
        open fun getLayoutId() = R.layout.common_view_empty
    
        abstract fun onInflated(root: View?)
    
        abstract fun onFinish()
    
        private inner class H(
            looper: Looper,
            private val asyncActivity: WeakReference<FragmentActivity>
        ) :
            Handler(looper) {
            var root: View? = null
            var wm: WindowManager? = null
    
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    INFLATE_UI -> {
                        root = LayoutInflater.from(asyncActivity.get()).inflate(getLayoutId(), null)
                        wm = asyncActivity.get()
                            ?.getSystemService(Context.WINDOW_SERVICE) as WindowManager?
                        val param = WindowManager.LayoutParams()
                        param.width = WindowManager.LayoutParams.MATCH_PARENT
                        param.height = WindowManager.LayoutParams.MATCH_PARENT
                        wm?.addView(root, param)
    
                        //处理返回键
                        root?.run {
                            isFocusable = true
                            isFocusableInTouchMode = true
                            requestFocus()
    
                            setOnKeyListener { _, keyCode, event ->
                                if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
                                    onStop()
                                    true
                                } else {
                                    false
                                }
                            }
                        }
    
                        onInflated(root)
                    }
                }
            }
        }
    
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                quitSafely()
                onFinish()
            }
        }
    
        open fun onStop() {
            handler?.run {
                removeCallbacksAndMessages(null)
                wm?.removeViewImmediate(root)
                root = null
                wm = null
            }
            handler = null
            asyncActivity.get()?.finish()
        }
    
        companion object {
            private const val INFLATE_UI = 10001
        }
    }
    

    对外提供inflate()方法,启动HandlerThread并创建UI。实现LifecycleEventObserver接口,在onDestroy()时释放资源。

    更新UI需要在这个子线程中进行,封装下线程切换方法。

        fun post(block: () -> Unit) {
            handler?.post {
                block()
            }
        }
    
        fun postDelay(mills: Long, block: () -> Unit) {
            handler?.postDelayed({
                block()
            }, mills)
        }
    

    基类写好了,但是每次都去写实现类也很麻烦啊,没关系,kotlin invoke约定。

    AsyncView

    object AsyncView {
        operator fun invoke(
            activity: FragmentActivity,
            layout: Int? = null,
            finish: (() -> Unit)? = null,
            block: AsyncHandlerThread.() -> Unit
        ) = object : AsyncHandlerThread(WeakReference(activity)) {
            override fun getLayoutId(): Int {
                return layout ?: super.getLayoutId()
            }
    
            override fun onInflated(root: View?) {
                block()
            }
    
            override fun onFinish() {
                finish?.invoke()
            }
        }.apply { inflate() }
    }
    

    如此一来调用处就很简洁了

    class AsyncActivity : BaseActivity() {
        private var async: AsyncHandlerThread? = null
        var tv: TextView? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            async = AsyncView(this@AsyncActivity, R.layout.business_activity_async) {
    
            }
        }
    }
    

    但是这个lambda内部this指向AsyncHandlerThread,通过这个AsyncHandlerThread点handler点root去findViewById也太麻烦了,给AsyncHandlerThread加个bindView()方法:

        fun <T : View> bindView(id: Int): T? {
            return handler?.root?.findViewById(id) as T?
        }
    

    那么完整版的AsyncHandlerThread

    abstract class AsyncHandlerThread @JvmOverloads constructor(
        private val asyncActivity: WeakReference<FragmentActivity>,
        name: String = "AsyncHandlerThread"
    ) :
        HandlerThread(name), LifecycleEventObserver {
        private var handler: H? = null
    
        fun inflate() {
            start()
            handler = H(looper, asyncActivity)
            handler?.sendEmptyMessage(INFLATE_UI)
            asyncActivity.get()?.lifecycle?.addObserver(this)
        }
    
        fun <T : View> bindView(id: Int): T? {
            return handler?.root?.findViewById(id) as T?
        }
    
        fun post(block: () -> Unit) {
            handler?.post {
                block()
            }
        }
    
        fun postDelay(mills: Long, block: () -> Unit) {
            handler?.postDelayed({
                block()
            }, mills)
        }
    
        open fun getLayoutId() = R.layout.common_view_empty
    
        abstract fun onInflated(root: View?)
    
        abstract fun onFinish()
    
        private inner class H(
            looper: Looper,
            private val asyncActivity: WeakReference<FragmentActivity>
        ) :
            Handler(looper) {
            var root: View? = null
            var wm: WindowManager? = null
    
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    INFLATE_UI -> {
                        root = LayoutInflater.from(asyncActivity.get()).inflate(getLayoutId(), null)
                        wm = asyncActivity.get()
                            ?.getSystemService(Context.WINDOW_SERVICE) as WindowManager?
                        val param = WindowManager.LayoutParams()
                        param.width = WindowManager.LayoutParams.MATCH_PARENT
                        param.height = WindowManager.LayoutParams.MATCH_PARENT
                        wm?.addView(root, param)
    
                        root?.run {
                            isFocusable = true
                            isFocusableInTouchMode = true
                            requestFocus()
    
                            setOnKeyListener { _, keyCode, event ->
                                if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
                                    onStop()
                                    true
                                } else {
                                    false
                                }
                            }
                        }
    
                        onInflated(root)
                    }
                }
            }
        }
    
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                quitSafely()
                onFinish()
            }
        }
    
        open fun onStop() {
            handler?.run {
                removeCallbacksAndMessages(null)
                wm?.removeViewImmediate(root)
                root = null
                wm = null
            }
            handler = null
            asyncActivity.get()?.finish()
        }
    
        companion object {
            private const val INFLATE_UI = 10001
        }
    }
    

    再糖一下,去掉this,给FragmentActivity加个扩展函数

    fun FragmentActivity.async(
        layout: Int? = null,
        finish: (() -> Unit)? = null,
        block: AsyncHandlerThread.() -> Unit
    ) = AsyncView(this, layout, finish, block)
    

    调用处

    class AsyncActivity : BaseActivity() {
        private var async: AsyncHandlerThread? = null
        var tv: TextView? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            async = async(R.layout.business_activity_async) {
                tv = bindView(R.id.tvAsync)
                tv?.text = "chenxuan"
                tv?.setSingleClick {
                    tv?.text = "chenxuan"
                }
            }
    
            async?.postDelay(3000) {
                tv?.text = "async"
            }
        }
    }
    

    最终做到了一行代码调用AsyncView(){}。但是在主线程中操作UI还是得通过AsyncHandlerThread的handler.post()切换到子线程中执行,理由嘛就等同于在子线程中操作UI需要post()到主线程中,只不过异步UI中主线程和子线程反过来了。

    相关文章

      网友评论

          本文标题:Android 异步UI之二

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