Android开发问题和经验总结(不断更新中)

时间:2021-7-21 作者:qvyue

安卓常见问题,Bug等以及解答,欢迎大家更新和指正!!!

Android 系统

防止OOM:

8.0之后对内存使用方式做了修改,这两个操作就可以避免全部的oom:
minSdk 26 ndk {abiFilters ‘arm64-v8a’}

Android 9 Http请求:Cleartext HTTP traffic to … not permitted

方案1:改用https

方案2:targetSdkVersion 降到27以下

方案3:更改网络安全配置(1)

1.在res文件夹下创建一个xml文件夹,然后创建一个network_security_config.xml文件,文件内容如下:

2.接着,在AndroidManifest.xml文件下的application标签增加以下属性:

方案4:更改网络安全配置(2):直接在AndroidManifest.xml配置文件的标签中直接插入android:usesCleartextTraffic=”true”

View 问题

TextView

singleLine过时了,使用lines代替。maxLines=“1”不能限制文本只显示一行

string资源字符串自定义部分文本颜色

//string.xml中配置:
%s染色]]>

//TextView设置:
TextView.setText(Html.fromHtml(getString(id, "format")))

EditText

Java代码中调用了setFilters,会覆盖布局中maxLength属性,除非同时设置了LengthFilter

CheckBox

button属性失效问题

button对应的Selector drawable,把选中和未选中状态单独放到一个drawable中,不然和selected等混在一个文件会失效

RecyclerView

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position…

RecyclerView绑定的 List 对象在更新数据之前进行了 clear,而这时紧接着迅速上滑 RecyclerView,就会造成崩溃,属于RecyclerView内部异常。

RecyclerView在多层嵌套的内容中,即使设置了wrap_content或者match_parent,但是数据项还是只显示1行,Item高度也不是wrap_content

使用可以让RecyclerView显示全的控件进行包裹,推荐使用NestedScrollView。(本人在项目中遇到这个问题,Recyclerview外层是多层LinearLayout, CardView等布局,还要在外层加上下拉刷新的功能。网上多种解决方案都试过,也没有采用inflate(layoutId, null, false)加载Item的方法,因为封装过Adapter再改代价很大。最后的解决方案是:在RecyclerView外层布局包裹NestedScrollView,禁用RecyclerView滑动,使用NestedScrollView的滑动即可[本人使用的下拉刷新是SmartRefreshLayout,内部使用NestedScrollView可以正常滑动,问题解决])

ScrollView嵌套RecyclerView的显示不全,滑动卡顿,夺取了焦点导致页面启动时RecyclerView上方布局没显示问题

有问题的代码:

滑动卡顿问题:

recyclerView.setHasFixedSize(true);
recyclerView.setNestedScrollingEnabled(false);
或者
android:nestedScrollingEnabled="false"

RecyclerView夺取焦点问题:外层布局需要增加属性(但是某些时候这个属性不起作用)

android:focusable="true"
android:focusableInTouchMode="true"

RecyclerView夺取焦点问题解决方案2:ScrollView的直接子View使用

//截获RecyclerView焦点
android:descendantFocusability="blocksDescendants" 
//android:descendantFocusability属性:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

显示不全的问题:使用NestedScrollView代替ScrollView

//没有问题的代码:

LinearLayout嵌套RecyclerView的显示不全,滑动卡顿,夺取了焦点导致页面启动时RecyclerView上方布局没显示问题(真实情况应是ScrollView嵌套LinearLayout嵌套RecyclerView会出现)

附上之前出问题的代码(问题已经改了):

解决方案是在RecyclerView外包裹RelativeLayout。原因:ScrollView嵌套RecyclerView解决以及原理详解 文中提到LinearLayout和RelativeLayout测量的不同

DiffUtil.Callback RecyclerView动态更新/增减数据需要实现的类

部分(partial)绑定vs完整(full)绑定
payloads 参数 是一个从(notifyItemChanged(int, Object)或notifyItemRangeChanged(int, int, Object))里得到的合并list。
如果payloads list 不为空,那么当前绑定了旧数据的ViewHolder 和Adapter, 可以使用 payload的数据进行一次 高效的部分更新。
如果payload 是空的,Adapter必须进行一次完整绑定(调用两参方法)。

需要实现的方法:DiffUtil.Callback:public Object getChangePayload(int oldItemPosition, int newItemPosition), RecyclerView.Adapter:public void onBindViewHolder(VH holder, int position, List payloads)

ScrollView

滚动到顶部或者底部 :

scrollView.fullScroll(ScrollView.FOCUS_DOWN);滚动到底部

scrollView.fullScroll(ScrollView.FOCUS_UP);滚动到顶部

滚动到顶部的三种方式:

ScrollView.scrollTo(``0``,``0``);``//直接置顶,瞬间回到顶部,没有滚动过程,其中Y值可以设置为大于0的值,使Scrollview停在指定位置。
ScrollView.fullScroll(View.FOCUS_UP);``//类似于手动拖回顶部,有滚动过程
ScrollView.smoothScrollTo(``0``, ``0``);``//类似于手动拖回顶部,有滚动过程,其中Y值可以设置为大于0的值,使Scrollview停在指定位置。

ViewPager

Item切换重建(主要是和Fragment搭配使用时,切换到第三个页面,第一个页面销毁)

通过setOffscreenPageLimit防止频繁销毁(setOffscreenPageLimit注释:设置当前page左右两侧应该被保持的page数量,超过这个限制,page会被销毁重建(只是销毁视图),onDestroy-onCreateView,但不会执行onDestroy。尽量维持这个值小,特别是有复杂布局的时候,因为如果这个值很大,就会占用很多内存,如果只有3-4页的话,可以全部保持active,可以保持page切换的顺滑

Tools命名空间的使用 xmlns:tools=”http://schemas.android.com/tools”

tools命名空间中的各种XML属性,这些属性支持设计时功能(例如,在片段中显示哪种布局)或编译时行为(例如应用于XML资源的缩小模式)。构建应用程序时,构建工具会删除这些属性,因此不会影响APK大小或运行时行为。

错误处理属性

tools:ignore

用于:任何元素

使用者:Lint

此属性接受以逗号分隔的lint问题ID列表,用于忽略指定的错误

例如,忽略MissingTranslation错误:

All

tools:targetApi

用于:任何元素

使用者:Lint

此属性与@TargetApiJava代码中的注释相同 :它允许指定支持此元素的API级别(作为整数或代码名称)。

表名此元素(以及任何子元素)将仅在指定的API级别或更高级别上使用。用于阻止lint警告

例如,指定 GridLayout仅适用于API级别14及更高版本:

tools:locale

用于:

使用者:Lint,Android Studio编辑器

这告诉工具给定元素中的资源的默认语言/语言环境是什么(因为工具否则假定为英语),以避免来自拼写检查器的警告。该值必须是有效的 区域设置限定符。

例如,将其添加到values/strings.xml文件(默认字符串值),以指示用于默认字符串的语言是西班牙语而不是英语:

设计时视图属性——仅在Android Studio布局预览中可见的布局特征

tools: 代替 android:

用于:

使用者:Android Studio布局编辑器

可以使用tools:前缀而不是Android框架中的android:任何属性在布局预览中插入示例数据。当在App运行之前未填充属性的值,但你希望在布局预览中预先看到效果时,这非常有用。

例如,如果android:text属性值是在运行时设置的,或者你希望看到布局的值不同于默认值,则可以添加 tools:text以仅为布局预览指定一些文本。

[图片上传失败…(image-217cda-1594718377962)]

图1.tools:text属性将“Google Voice”设置为布局预览的值

你可以添加android:命名空间属性(在运行时使用)和匹配tools:属性(仅在布局预览中覆盖运行时属性)。或者仅使用tools:属性用于布局预览。例如:




tools:context

用于:任何根

使用者:Lint,Android Studio布局编辑器

此属性声明默认情况下此布局与哪个Activity相关联。比如导入View.onClick方法到次Activity。

tools:itemCount

用于:

使用者:Android Studio布局编辑器

对于给定的RecyclerView,此属性指定布局编辑器应在“ 预览”窗口中呈现的条目数 。

例如:

tools:layout

用于:

使用者:Android Studio布局编辑器

此属性声明你希望布局预览在Fragment内绘制的布局(因为布局预览无法执行通常应用布局的活动代码)。例如:

tools:listitem / tools:listheader / tools:listfooter

用于:( 及其子类,比如)

使用者:Android Studio布局编辑器

这些属性指定在列表的条目,页眉和页脚的布局预览中显示的布局。布局中的任何数据字段都填充了数字内容,例如“项目1”,以便列表项不重复。

例如:

tools:showIn

用于:布局根需要嵌入并一同显示预览的布局

使用者:Android Studio布局编辑器

此属性将会把当前布局嵌入其引用的布局一同显示预览。例如:

资源收缩属性

shrinkResources 的使用参考

minifyEnabled 用来开启删除无用代码,比如没有引用到的代码

shrinkResources 用来开启删除无用资源,也就是没有被引用的文件(经过实测是drawable,layout,实际并不是彻底删除,而是保留文件名,但是没有内容,等等),但是因为需要知道是否被引用所以需要配合mififyEnable使用,只有当两者都为true的时候才会起到真正的删除无效代码和无引用资源的目的。例如:

android {
    ...
    buildTypes {
        release {
            //收缩资源
            shrinkResources true
            //代码混淆
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

tools:shrinkMode

用于:

使用者:资源缩减构建工具

此属性允许你指定构建工具是否应使用“安全模式”(保护安全并保留所有明确引用的资源以及可能通过调用动态 引用的资源[Resources.getIdentifier()](https://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String)))或“严格模式”(仅保留资源)在代码或其他资源中明确引用)。

默认是使用安全模式(shrinkMode="safe")。要改为使用严格模式,需更改为shrinkMode="strict"节点,如下所示:

启用严格模式时,你可能需要使用tools:keep 以保留已删除但实际需要的资源,并用于 tools:discard显式删除更多资源。

有关更多信息,请参阅 缩小资源。

tools:keep

用于:

使用者:资源缩减构建工具

此属性用于指定你需要保留的资源(通常是因为它们在运行时以间接方式引用,例如通过将动态生成的资源名称传递给 [Resources.getIdentifier()](https://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String)))。

用法:在资源目录(例如,at res/raw/keep.xml)中使用标记创建XML文件, 并将要保留在tools:keep属性中的每个资源指定为以逗号分隔的列表。可以将星号字符用作通配符。例如:

tools:discard

用于:

使用者:资源缩减构建工具

此属性用于指定你需要手动丢弃的资源(通常是因为资源引用但不会影响你的应用,或者因为Gradle插件错误地推断出资源被引用)。

用法:在资源目录(例如,at res/raw/keep.xml)中使用标记创建XML文件, 并将要保留在tools:discard属性中的每个资源指定为以逗号分隔的列表。可以将星号字符用作通配符。例如:

Style问题

declare-styleable

format可选项

"reference"//引用 
"color"//颜色 
"boolean"//布尔值 
"dimension"//尺寸值 
"float"//浮点值 
"integer"//整型值 
"string"//字符串 
"fraction"//百分数,比如200% 
"enum" 枚举值
//enum枚举例子

Activity等系统组件

Activity切换时,短暂白屏问题,主要是启动singleTask或者singleInstance标志的Activity,同时关闭当前Activity,大概率出现这个问题

Activity theme加上 **true ** 即可。

这个标志应该也可以 true ,然后onCreate应该需要去掉这个标志。

moveTaskToBack(boolean) 点返回键时,把App移到后台,而不是退出

参数为false——代表只有当前activity是task根,指应用启动的第一个activity时,才有效;

参数为true——则忽略这个限制,任何activity都可以有效。

判断Activity是否是task根,Activity本身给出了相关方法:isTaskRoot()

把App移到后台,而不是退出,最简单无脑的使用方法:MainActivity,重写onBackPressed如下,即可实现

    override fun onBackPressed() {
        moveTaskToBack(true)
    }

Activity设置为Dialog样式

方案:

  1. Activity Theme设置为:

    不使用true,而使用parent=”@style/Theme.AppCompat.DayNight.Dialog”的原因是因为在页面上输入框会被软键盘遮住,软键盘不会顶起输入法

        
            @null 
            <!--        true  <!– 浮在activity之上 –>-->
            false  
            true 
            true 
            @android:color/transparent
            @android:color/transparent 
            true  
        
  2. 布局:android:minWidth或者android:layout_width设置为很大的dp,防止宽度太小的问题

  3. 防止点击空白处页面关闭,Activity中调用:

    setFinishOnTouchOutside(false);
    

这个方案也有不完美的地方(应该算系统问题),布局较高,输入框靠下时,获取到焦点窗口上移时会被截断。但是不算大问题。

MainActivity打开新的Activity,因点击触发异常后(直接onCreate抛异常App会直接死掉),返回当前Activity时,Fragment重叠问题

新Activity强退,导致MainActivity重新走了生命周期(onCreate-onStart-onResume)Activity保存了Fragment状态,在onCreate中savedInstanceState!=null,里头保存了Fragment状态:

android:support:fragments=android.support.v4.app.FragmentManagerState@ab76c10, android:fragments=android.app.FragmentManagerState@7661309}]

方案:

//方案1:super.onCreate(null); 传null解决
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //解决Activity重建Fragment重叠问题
        super.onCreate(null);
    }
//方案2:按需清除Bundle中FragmentManagerState

输入法遮挡输入框问题

通用解决方法:AndroidManifest中Activity的windowSoftInputMode属性来调整

通用解决方法无法解决,主要是自定义软键盘的情况

ScrollView中的EditText被自定义输入法遮挡:

ScrollView子控件最后增加一个空布局,比如Space,默认隐藏;界面显示后,计算Space高度:软键盘高度-输入框底部距离当前Activity窗口底部距离,>0时,就是有遮挡问题。

监听自定义软键盘弹出/隐藏,或者输入框是否获得焦点,如果软键盘弹出/输入框获得焦点,显示Space,调用nestedScrollView.scrollTo(0, space.getHeight()),解决遮挡问题;软键盘隐藏时,隐藏Space

Kotlin 专题

Kotlin data class 和 Gson, @parcelize问题

gson 扩展方法

inline fun  Gson.fromJson(json: String) = fromJson(json, T::class.java)

@parcelize 需要在build.gradle android内设置属性

androidExtensions {
    experimental = true
}

例子:

//正确的data class写法
@Parcelize
data class Student(
    var age: Int = 0,
    var name: String? = null,
    var toy: Toy? = null
) : Parcelable {
}

@Parcelize
data class Toy(
    var price: Int = 0,
    var name: String? = null
) : Parcelable {
}

//错误的data class写法,类体内的name不会序列化,别的地方使用获取到的是null,kotlin bytecode,decompile文件可以看到
@parcelize
data class Student(
    var age: Int = 0): Parcelable {
    var name: String? = null
}


gson解析
val student2 = gson.fromJson("{n" +
                    "t"age":10,n" +
                    "t"name":"json name",n" +
                    "t"toy":{n" +
                    "tt"name":"toy",n" +
                    "tt"price":1000n" +
                    "t}n" +
                    "}", Student::class.java)
            intent.putExtra("student2", student2)

指定Java调用的类名,要用到注解 @file:JvmName(),放在package之前

也就是要写成这样,剩下的就直接改变代码里的类名就可以了。

@file:JvmName("Hello")
package kotlin

@file:JvmName(“Utils”) 和 @file:JvmMultifileClass 一起使用的场景:

//在需要合并的每个Kotlin文件加入如下属性:
@file:JvmName("Utils")
@file:JvmMultifileClass
demo:

// oldutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package demo

fun foo() {
}

//___________________________________________________________
// newutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package demo
fun bar() {
}

//__________________________________________________________
// build后,在Java文件中的调用方法:
Utils.foo();
Utils.bar();

编写和Kotlin代码文档(相当于Javadoc)

生成文档

Kotlin 的文档生成工具称为 Dokka。其使用说明请参见 Dokka README。

Dokka 有 Gradle、Maven 和 Ant 的插件,因此你可以将文档生成集成到你的构建过程中。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。