Android开发问题总结

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:stateListAnimator="@null"去除点击效果。此时,想到可能是点击的阴影效果,导致了z轴的层级变化,此时再点击测试,发现z轴层级未变化。

        但对于项目中所有button都采用上述方式解决,过于繁琐,于是采用下述方式,在主题下添加如下描述        <item name="buttonStyle">?android:attr/borderlessButtonStyle</item>

    <style name="Theme.xxx" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
        <item name="buttonStyle">?android:attr/borderlessButtonStyle</item>
    </style>

2. seekbar样式

Android原生 seekbar实现渐变色及两端圆角

<SeekBar
                        android:layout_width="188dp"
                        android:layout_height="wrap_content"
                        android:background="@null"
                        android:focusable="true"
                        android:maxHeight="6dp"
                        android:paddingStart="0dp"
                        android:paddingTop="4dp"
                        android:paddingEnd="0dp"
                        android:paddingBottom="4dp"
                        android:progress="0"
                        android:progressDrawable="@drawable/bg_ps_seek_bar"
                        android:secondaryProgress="0"
                        android:max="3"
                        android:splitTrack="false"
                        android:thumb="@null" />
bg_ps_seek_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@android:id/background"
        android:paddingBottom="4dp"
        android:paddingTop="4dp">
        <shape>
            <corners android:radius="24dp"/>
            <solid android:color="#efefef"/>
        </shape>
    </item>
 
    <item
        android:id="@android:id/progress"
        android:paddingBottom="4dp"
        android:paddingTop="4dp">
        <scale android:scaleWidth="100%"
            android:drawable="@drawable/bg_ps_progress_bar"   />
    </item>
</layer-list>
bg_ps_progress_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<shape  xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="24dp"/>
    <gradient
        android:angle="0"
        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/63436313

4. 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时,手动解绑service
           2. 修改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()解决,但会有问题二,即使dismiss,dialog依然会被重建。
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重启(relaunch),service不会重启,在新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博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值