诉求
希望请求网络时自动弹出 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 实例即可。
网友评论