- Android Jetpack开发:原理解析与应用实战
- 黄林晴
- 872字
- 2022-07-05 09:43:57
2.3 使用Lifecycle解决实际项目中常见的问题
了解Lifecycle的基本使用之后,接着来看如何使用Lifecycle解决实际项目中常见的问题。
2.3.1 Dialog内存泄漏问题分析
Dialog是Android开发中最常用的组件之一,相信每位读者都使用过。现在一起实现一个Dialog,用于网络请求的简单提示。Dialog的代码很简单,直接设置一个布局即可,具体如下:
class TipDialog(context: Context) : Dialog(context) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.item_tip_dialog) } }
接下来模拟如下场景:
- 进入页面时开始网络请求,显示Dialog。
- 请求结束时(两秒后),退出当前页面。
下面实现上述需求,Activity的代码如下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) TipDialog(this).show() Handler().postDelayed({ finish() }, 2000) } }
运行程序,两秒后,软件出现异常崩溃,错误日志如下:
00:58:23.618 28162-28162/com.example.jetpackdemo E/WindowManager: android.view.WindowLeaked: Activity com.example.jetpackdemo.ui.activity.MainActivity has leaked window DecorView@df7a4a4[MainActivity] that was originally added here at android.view.ViewRootImpl.<init>(ViewRootImpl.java:818) at android.view.ViewRootImpl.<init>(ViewRootImpl.java:798) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:399) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:110) at android.app.Dialog.show(Dialog.java:353) at com.example.jetpackdemo.ui.activity.MainActivity.onCreate(MainActivity.kt:23) at android.app.Activity.performCreate(Activity.java:8146) at android.app.Activity.performCreate(Activity.java:8130) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1310) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3668) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3866) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:140) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:100) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2296) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:254) at android.app.ActivityThread.main(ActivityThread.java:8234) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1006)
相信很多开发者都遇到过这个错误,这是因为在Activity关闭的时候,Dialog没有关闭,进而导致内存泄漏。了解了出现异常的原因,解决起来就很容易了,在销毁Activity的时候,关闭Dialog即可,示例代码如下:
class MainActivity : AppCompatActivity() { var dialog: TipDialog? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) dialog = TipDialog(this) dialog?.show() Handler().postDelayed({ finish() }, 2000) } override fun onDestroy() { super.onDestroy() dialog?.dismiss() } }
上面的方法虽然可以解决内存泄漏问题,但若弹窗类型很多,则需要在onDestroy中编写许多额外的处理逻辑,且容易忘记,不过,如果使用Lifecycle组件,就可以完美地解决这个问题。
2.3.2 使用Lifecycle打造一个完美的Dialog
由于Dialog中的参数Context必须是Activity的上下文,因此开发者完全可以在Dialog中使用Lifecycle组件来感知生命周期,在2.2.1节中已经介绍,只要是在androidx.fragment.app.Fragment、ComponentActivity及其子类Activity中,就可以直接使用Lifecycle组件。所以这成了一个取舍问题,如果xxxActivity继承的是Activity,将无法直接使用Lifecycle,那就只能自定义LifecycleOwner或者在Activity中注册了,但是这样做完全没有必要,这里默认xxxActivity继承的是ComponentActivity。
修改TipDialog的代码,使得TipDialog可以感应生命周期变化,示例如下:
class TipDialog(context: Context) : Dialog(context), LifecycleObserver { init { if (context is ComponentActivity) { (context as ComponentActivity).lifecycle.addObserver(this) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.item_tip_dialog) } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) private fun onDestroy() { if (isShowing) { dismiss() } } }
上面的代码在2.2.1节中已经详细介绍了,此处就不再赘述。当Dialog所依附的Activity被销毁时,Dialog也可以自动关闭,再也不用担心Dialog的内存泄漏问题了。如此一来,就使用Lifecycle实现了一个完美的Dialog。
注意
在实际项目开发中,Dialog使用不当会出现除了内存泄漏之外的其他问题,这需要开发者自行处理,这里解决的只是此种情境下产生的内存泄漏问题。