美文网首页
Android 请求网络时实现自动 Loading 的一种方式

Android 请求网络时实现自动 Loading 的一种方式

作者: 雁过留声_泪落无痕 | 来源:发表于2022-04-02 09:16 被阅读0次

    诉求

    希望请求网络时自动弹出 Loading,网络请求结束时自动取消 Loading;如果有多个请求并行执行,希望最后一个请求结束时才取消 Loading。

    实现

    • SimpleRetrofit.kt
    object SimpleRetrofit {
    
        private val mCallMap = ConcurrentHashMap<Call, Loading>()
        private val mActivityList = CopyOnWriteArrayList<Loading>()
    
        fun init(context: Context, builder: OkHttpClient.Builder): OkHttpClient.Builder {
            (context.applicationContext as Application).registerActivityLifecycleCallbacks(
                object : EmptyActivityLifecycleCallbacks() {
                    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                        if (activity is Loading) {
                            mActivityList.add(activity)
                        }
                    }
    
                    override fun onActivityDestroyed(activity: Activity) {
                        if (activity is Loading) {
                            mActivityList.remove(activity)
                        }
                    }
                })
    
            builder.eventListener(object : EventListener() {
                override fun callStart(call: Call) {
                    super.callStart(call)
                    mActivityList.lastOrNull()?.let {
                        mCallMap[call] = it
                    }
                    handleLoading(true, call)
                }
    
                override fun callEnd(call: Call) {
                    super.callEnd(call)
                    handleLoading(false, call)
                    mCallMap.remove(call)
                }
    
                override fun callFailed(call: Call, ioe: IOException) {
                    super.callFailed(call, ioe)
                    handleLoading(false, call)
                    mCallMap.remove(call)
                }
            })
    
            return builder
        }
    
        private fun handleLoading(show: Boolean, call: Call) {
            mCallMap[call]?.handleLoading(show)
        }
    
    }
    
    • Loading.kt
    interface Loading {
    
        companion object {
            // we must use a tag to find the specified fragment.
            // you can not hold an instance of DialogFragment to call dialog.show()/dialog.hide().
            // for if you rotate the screen, then activity will be re-created, then you will
            // hold another new instance of DialogFragment, at this time you call hide() without show()
            // called before, exception will be thrown.
            // eg: Activity#onCreate -> dialog=DialogFragment() -> dialog.show() ->
            //     Activity#onCreate -> dialog=DialogFragment() -> dialog.hide() -> crash
            private const val TAG_LOADING_DIALOG_FRAGMENT = "TAG_LOADING_DIALOG_FRAGMENT"
        }
    
        var mLoading: Int
        var mHidePending: Boolean
        val mHandler: Handler
    
        fun handleLoading(show: Boolean) {
            if (show) {
                mHandler.post { showLoading() }
            } else {
                mHandler.post { hideLoading() }
            }
        }
    
        fun onDialogShown(dialog: DialogFragment) {
            if (mHidePending) {
                mHidePending = false
                dialog.dismiss()
            }
        }
    
        private fun showLoading() {
            if (mLoading++ > 0) {
                return
            }
    
            if (this is AppCompatActivity) {
                val fragment = supportFragmentManager.findFragmentByTag(TAG_LOADING_DIALOG_FRAGMENT)
                if (fragment == null) {
                    SimpleLoadingDialog.newInstance()
                        .show(supportFragmentManager, TAG_LOADING_DIALOG_FRAGMENT)
                }
            }
        }
    
        private fun hideLoading() {
            if (mLoading-- > 1) {
                return
            }
    
            if (this is AppCompatActivity) {
                val dialog = supportFragmentManager.findFragmentByTag(TAG_LOADING_DIALOG_FRAGMENT)
                if (null != dialog && dialog.isResumed) {
                    (dialog as DialogFragment).dismiss()
                } else {
                    mHidePending = true
                }
            }
        }
    
    }
    
    • LoadingActivity.kt
    abstract class LoadingActivity : AppCompatActivity(), Loading {
        override var mLoading = 0
        override val mHandler = Handler(Looper.getMainLooper())
        override var mHidePending = false
    }
    
    • SimpleLoadingDialog.kt
    class SimpleLoadingDialog : DialogFragment() {
    
        companion object {
            fun newInstance(): SimpleLoadingDialog {
                val dialog = SimpleLoadingDialog()
                dialog.arguments = Bundle().apply {
                    // add arguments
                }
                return dialog
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            isCancelable = false
        }
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            return ProgressBar(requireContext())
        }
    
        override fun onResume() {
            super.onResume()
            val activity = requireActivity()
            if (activity is Loading) {
                activity.onDialogShown(this)
            }
        }
    
    }
    

    兼容

    如果还有一部分请求是不需要弹 Loading 的怎么办?使用另一个 OkHttpClient 实例即可。

    相关文章

      网友评论

          本文标题:Android 请求网络时实现自动 Loading 的一种方式

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