资讯动态

Android开发问题总结

发布时间:2026/10/22 6:50:04 来源:尧图企业网站定制
1. 不同Theme主题导致Button样式偏差最开始使用Theme.MaterialComponents.DayNight.NoActionBar会导致点击Button时不同的z轴层级发生变化于是改成Theme.Material3.DayNight.NoActionBar但是发现此主题下Button的backgroud更改后不生效。对于backgroud更改后不生效有两种解决方式将xml中的Button组件修改为android.widget.button将主题修改为Theme.MaterialComponents.DayNight.NoActionBar.Bridge。鉴于第一种在button很多时过于麻烦此处采用第二种方法。但此时又回到了点击button会导致z轴层级变化的问题。此处同时发现button有点击阴影效果于是采用android:stateListAnimatornull去除点击效果。此时想到可能是点击的阴影效果导致了z轴的层级变化此时再点击测试发现z轴层级未变化。但对于项目中所有button都采用上述方式解决过于繁琐于是采用下述方式在主题下添加如下描述 item namebuttonStyle?android:attr/borderlessButtonStyle/itemstyle nameTheme.xxx parentTheme.MaterialComponents.DayNight.NoActionBar.Bridge item namebuttonStyle?android:attr/borderlessButtonStyle/item /style2. seekbar样式Android原生 seekbar实现渐变色及两端圆角SeekBar android:layout_width188dp android:layout_heightwrap_content android:backgroundnull android:focusabletrue android:maxHeight6dp android:paddingStart0dp android:paddingTop4dp android:paddingEnd0dp android:paddingBottom4dp android:progress0 android:progressDrawabledrawable/bg_ps_seek_bar android:secondaryProgress0 android:max3 android:splitTrackfalse android:thumbnull /bg_ps_seek_bar.xml ?xml version1.0 encodingutf-8? layer-list xmlns:androidhttp://schemas.android.com/apk/res/android item android:idandroid:id/background android:paddingBottom4dp android:paddingTop4dp shape corners android:radius24dp/ solid android:color#efefef/ /shape /item item android:idandroid:id/progress android:paddingBottom4dp android:paddingTop4dp scale android:scaleWidth100% android:drawabledrawable/bg_ps_progress_bar / /item /layer-listbg_ps_progress_bar.xml ?xml version1.0 encodingutf-8? shape xmlns:androidhttp://schemas.android.com/apk/res/android corners android:radius24dp/ gradient android:angle0 android:endColor#ff8a13 android:startColor#ffcd11/ /shape摘自Android SeekBar 进度条圆角_android 圆形seekbar-CSDN博客3. viewPager2的onPageSelected回调中设置某个view可见不生效背景viewpager2滑动到最后一个界面时在其同层布局中显示一个button在左右滑动时正常但在最后一个页面时如果跳转到其他界面再返回该button不会显示在viewcreated里面直接setvisible或者在回调中加post然后setvisible都能生效只有在该回调中设置时不生效。排查在该回调中设置VISIBLE不生效设置GONE生效另外通过打印也确认了setvisible确实是成功了view的可见性正确但界面依然没有显示然后用IDE的Layout Inflater观察到上述情况下该view的上下左右坐标都为0这肯定不会显示至此怀疑没有走layout添加该view的layout回调确认在上述情况下没有走onlayout回调于是在设置可见性时调用了requestLayout()但依然还是没有走onlayout回调至此查询资料了解到layout是一层一层往父view传递可能是中间某层把flag设置为PFLAG_FORCE_LAYOUT导致该view没有执行到layout所以不会显示。此处逻辑未理顺后续补充安卓绘制原理之 那些年遇到的requestLayout埋下的坑_android requestlayout-CSDN博客https://zhuanlan.zhihu.com/p/634363134. LiveData的postvalue和setvalue快速多次调用只有最后一次生效问题根据源码postvalue()函数会先加锁并设置标志位然后新建runable扔到主线程运行在此期间如果再次post由于上一次设置了标志位此时会直接退出不会新建runable运行所以只有最后一次的值生效setvalue()函数最终会调用到considerNotify()函数此时observer.mActive值为false所以直接退出观察observer.mActive()的值随当前宿主的生命周期变化而变化只有大于等于 onStart返回true所以要想快速多次更改LiveData值都生效不能用postvalue用setvalue并且在当前界面的onStart生命周期之后。LiveData 机制详解_livedata setvalue 和 postvalue是不是都会进行生命周期监听-CSDN博客5. service导致的内存泄漏背景app应用是一款只有一个activity多个fragment的结构在MainActivity创建时使用bindService绑定了某个服务当时考虑到主activity销毁时进程都销毁了且bing启动的service生命周期跟随其activity所以没有手动解绑服务当切换到系统设置界面修改系统导航方式后返回app界面发现activity重走了销毁到创建的完整生命周期并且有内存泄漏排查通过AS的profiler工具发现了MainActivity如下的一条泄漏链判断是因为service导致的内存泄漏。但在详细查看bindservice传递的参数时发现其没有持有入参context且该context可以为appContext所以不会是该参数导致的内存泄漏后面联想到bindservice启动的服务正常情况下生命周期与activity一致肯定是哪里持有了该activity对象但查看入参没有显示传入activity对象网上查阅文章跟踪源码发现可能是在启动时通过getOuterContext()获取到了activity对象并保存到services的map中以监听在activity销毁时解绑释放service。但按此说法在activity走destroy时该service应该释放了啊为什么还会内存泄漏原因该情况在网上案例较少最初未找到相关博文参考类似博文在旋转屏幕时viewmodel未销毁考虑本文的bug原因应与其一致。在旋转屏幕或者修改系统导航栏时activity重走生长周期但其与正常的重走不太一直该现象时系统对该acticity执行的是reLaunch平常是执行的launch。activity在创建时会保存一条ActivityClientRecord记录在reLaunch的情况下该条记录不会被删除只是新建了activity对象理解此处的生命周期应该是以ActivityClientRecord为准所以在此种情况下未解绑释放服务解决1. 在activity的onDestroy时手动解绑service2. 修改configChange使得在旋转屏幕或者修改系统导航时activity不会重建。详细原因可参考Activity屏幕旋转重建ViewModel不销毁问题分析_android viewmodel 不跟着activity销毁-CSDN博客6. 自定义dialog在屏幕旋转后重建异常dialog继承自DialogFragment且采用建造者模式new XxxDialog.Builder(Type type) .setxxx() ... .build()当前页面存在dialog时进行屏幕旋转会发生崩溃因为建造者模式将XxxDialogd构造器私有化dialog被系统重建时因为其继承自Fragment没找到无参且可访问构造器所以报错解决1. 在dialog走destroy时通过isRemoving()判断是否dismiss但此种方式有两个问题一是此时调用dismiss()会报错Can not perform this action after onSaveInstanceState查看源码dismiss内部会判断在savestate后进行dismiss会抛异常可通过调用dismissAllowingStateLoss()解决但会有问题二即使dismissdialog依然会被重建。2. 将相关信息通过onSaveInstanceState保存并在重建时在onRestoreInstanceState中获取随后在onStart中构建view。简单操作也在onCreateView中根据type参数判断如果为空即不重建销毁后不显示此问题告诫在继承android四大组件时不要随意重载其构造函数此过程中进一步学习了onSaveInstanceState和onRestoreInstanceState参考Android onSaveInstanceState/onRestoreInstanceState 原来要这么理解-CSDN博客7. 关闭访问所有文件权限后的异常背景应用打开访问所有文件权限后从activityA进入到activityB将应用退到后台并在设置中关闭应用访问所有文件的权限再次打开应用应用出现crash。排查看日志是访问一个类的静态变量的静态方法报了空指针即该静态变量为空但该静态变量在activityA中明确赋值了后续也没有置空的操作联想到前面改变configchange导致的activity重建此处会不会是类似的改变权限后导致activity重建看日志activityB确实重建了但即使重建也不应该影响该静态变量的值毕竟是静态变量首先不受activity生命周期影响再者即使内存回收也不应该影响到静态变量。继续排查发现关闭权限后再进应用应用的进程号变了也就是之前的进程被杀了起了新进程。起了新进程后创建的第一个activity为activityB没有启动activityA而变量的初始化是在activityA中所以在新进程中静态变量的值为空导致空指针异常。解决进程重建时没有重启activityA未进行其中的一系列初始化操作此处做法是将空指针处的变量初始化放在application中但此时仍有问题activityA中还包含了其他的初始化逻辑其他逻辑可能会有问题所以此处考虑在当前情况下销毁activityB进入activityA走初始化流程具体怎么判断时在application中监听activity的生命周期通过启动的第一个activity是否是主activity来判断。8. RadioGroup异常Android日常踩坑_androidstudio动态给radiogroup的checkedid重复打开也增加-CSDN博客9. android中activity/service异常被杀场景// 情景1 修改系统导航方式只有activity重启relaunchservice不会重启在新activity中bindservice会获取到新的service但在新activity中获取viewmodel与旧activity的viewmodel是同一个对象 // 情况2 activity被系统异常销毁并重新启动 activity重新启动service不会重启在新activity中获取viewmodel与旧activity的viewmodel不一致 // 情景3 vivo iqoo最近列表杀应用进程未销毁但activity、service都重启10. 多次快速调用startactivity导致singletop启动模式不生效场景在后台收到联网请求用户信息返回401时跳转登录界面在较短时间内收到多次401导致startactivity调用了多次原因在极短时间内多次调用startactivity会导致singletop启动模式失效目前判断原因是应用启动有一定前置时间若两次启动间隔太短导致某个状态量还未更新所以启动多次解决使用其他状态量进行辅助避免多次调用startActivity或者使用SingleInstance模式singleTop启动模式真的可以防止多次打开栈顶的Activity么_启动页 使用singletop 问题-CSDN博客在使用startActivityForResult时设置的SingleTop不起作用_singletop 无效-CSDN博客11. fragment B返回fragment A时报错fragment is unknown to the FragmentNavigator详细报错java.lang.IllegalArgumentException: The fragment xxx{32ffc66} is unknown to the FragmentNavigator. Please use the navigate() function to add fragments to the FragmentNavigator managed FragmentManager. at androidx.navigation.fragment.FragmentNavigator$onAttach$2.onBackStackChangeCommitted(FragmentNavigator.kt:208) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2188) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2115) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2052) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3327) at androidx.fragment.app.FragmentManager.dispatchPause(FragmentManager.java:3255) at androidx.fragment.app.Fragment.performPause(Fragment.java:3323) at androidx.fragment.app.FragmentStateManager.pause(FragmentStateManager.java:692) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:318) at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:114) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1685) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3319) at androidx.fragment.app.FragmentManager.dispatchPause(FragmentManager.java:3255) at androidx.fragment.app.FragmentController.dispatchPause(FragmentController.java:296) at androidx.fragment.app.FragmentActivity.onPause(FragmentActivity.java:284) at android.app.Activity.performPause(Activity.java:9724) at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1798) at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:6709) at android.app.ActivityThread.performPauseActivity(ActivityThread.java:6669) at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:6609) at android.app.servertransaction.PauseActivityItem.execute(PauseActivityItem.java:55) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:60) at android.app.servertransaction.TransactionExecutor.executeLifecycleItem(TransactionExecutor.java:240) at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:122) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:90) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:3342) at android.os.Handler.dispatchMessage(Handler.java:118) at android.os.Looper.loopOnce(Looper.java:237) at android.os.Looper.loop(Looper.java:325) at android.app.ActivityThread.main(ActivityThread.java:10404) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:635) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:970)两个fragment都是使用navigator进行的导航最开始怀疑是navigation导航fragment时哪里状态没对排查后发现没有问题继续分析日志发现返回上一界面后有申请权限操作进一步查阅发现问题所在fragment B返回fragment A时fragment A在onviewcreated中进行了权限申请系统触发activity onPause由于权限申请对话框--FragmentManager 尝试暂停 Fragment B-- 但 Fragment B 已经被 Navigation 标记为移除中状态-- 状态不一致导致 unknown Fragment 异常。12. 使用DialogFragment调用show()方法时报错Can not perform this action after onSaveInstanceState。这是因为在onSaveInstanceState执行后即 Activity 准备被销毁或进入后台时系统不允许再执行commit提交事务因为此时状态已经固化强行提交会导致界面状态丢失或崩溃。方案一最严谨的通用校验推荐在调用dialog.show()之前同时检查Activity 是否存活和是否处于可提交状态方案二使用commitAllowingStateLoss灵活但风险自担如果你能接受“状态丢失”的风险比如弹窗内容不涉及用户输入的重要数据可以使用showNow()或手动commitAllowingStateLoss()13. kotlin中定义了一个接口接口中定义了几个方法均有函数体但在java中实现时仍然需要实现所有接口否则报错kotlin中有一个类如下interface XXXListener { fun onXXXChanged(data: String) {} fun onXXChanged(data: String) {} }在java中实现该类时必须实现其两个方法才能编译通过。同时新建了kotlin类去实现该接口在kotlin实现类中不用实现所有的方法。这是因为Kotlin 源码中提供默认函数体{}默认实现放在 Kotlin 生成的DefaultImpls辅助类中Kotlin 实现类可以直接继承这个默认实现所以只实现接口中的部分方法也能通过编译。Java 实现类则仍需要实现所有接口这说明该接口当前的 JVM 编译形式没有让 Java 直接继承默认的方法。常见原因是 Kotlin 使用了旧式默认实现兼容模式将默认实现放在生成的DefaultImpls辅助类中而 Java 编译器不会自动调用它。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑