Android 5竖屏应用进入最近任务并旋转时崩溃求助
这个问题我之前也遇到过,是Android 5.0(API 21)系统的一个小bug导致的!让我给你拆解一下原因和解决办法:
问题根源
虽然你已经把所有Activity设置为竖屏(android:screenOrientation="portrait"),但当设备开启自动旋转且应用进入后台后,系统依然会向Activity发送配置变更事件。而在Android 5.0中,ActionMenuView在后台状态下,它内部的ActionMenuPresenter可能已经被销毁置为null,此时系统调用onConfigurationChanged时就会触发空指针异常,也就是你看到的NullPointerException在ActionMenuPresenter.updateMenuView方法上。
解决方案
这里有几个可靠的解决办法,你可以根据自己的项目情况选择:
方法一:自定义安全版的ActionMenuView
通过继承ActionMenuView,重写onConfigurationChanged方法,先检查内部的Presenter是否为空,再执行父类逻辑,避免NPE:
class SafeActionMenuView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ActionMenuView(context, attrs, defStyleAttr) { override fun onConfigurationChanged(newConfig: Configuration?) { try { // 通过反射获取内部的mPresenter字段,判断是否为空 val presenterField = ActionMenuView::class.java.getDeclaredField("mPresenter") presenterField.isAccessible = true val presenter = presenterField.get(this) if (presenter != null) { super.onConfigurationChanged(newConfig) } } catch (e: Exception) { // 反射失败时降级执行父类方法 super.onConfigurationChanged(newConfig) } } }
之后,你需要把布局中使用的ActionMenuView替换成这个自定义类。如果是使用Toolbar,可以在自定义Toolbar中引用这个SafeActionMenuView,或者直接替换系统默认的ActionMenuView。
方法二:在销毁OptionsMenu时清空ActionView
针对API 21单独处理,在onDestroyOptionsMenu中把自定义的ActionView置为null,避免后台时出现无效引用:
private var mainMenu: Menu? = null override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.menu_mainpages, menu) mainMenu = menu // 保存Menu引用 val menuItem = menu.getItem(0) val profilePictureView = LayoutInflater.from(this) .inflate(R.layout.profile_image_layout, coordinator_main, false) menuItem?.actionView = profilePictureView return true } override fun onDestroyOptionsMenu() { super.onDestroyOptionsMenu() // 仅在Android 5.0上处理 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { mainMenu?.getItem(0)?.actionView = null } }
方法三:阻止系统发送不必要的配置变更
在AndroidManifest.xml中给Activity添加configChanges属性,让系统不重建Activity,而是直接调用onConfigurationChanged,同时强制保持竖屏:
<activity android:name=".MainActivity" android:screenOrientation="portrait" android:configChanges="orientation|screenSize">
然后在Activity中重写onConfigurationChanged:
override fun onConfigurationChanged(newConfig: Configuration) { // 强制锁定为竖屏,忽略系统的旋转事件 requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT super.onConfigurationChanged(newConfig) }
推荐选择
我个人更推荐方法一或者方法二,因为方法三虽然简单,但如果你的应用后续需要处理其他配置变更(比如语言切换),可能会有冲突。方法一从View层面解决问题,一劳永逸;方法二则是针对特定场景的快速修复。
内容的提问来源于stack exchange,提问作者user11857736

