網(wǎng)站首頁 編程語言 正文
Crash
Crash是指程序閃退,導(dǎo)致APP不能正常使用。Crash產(chǎn)生的原因有很多,下面只是列舉了一些常見原因。
空指針
空指針應(yīng)該是項(xiàng)目中最容易產(chǎn)生crash的情況了,舉個(gè)例子,我們獲取某個(gè)對(duì)象的屬性或方法時(shí),這個(gè)對(duì)象為Null時(shí),如何沒有判空,則會(huì)出現(xiàn)空指針異常NullPointException,所以這就要求使用對(duì)象的時(shí)候進(jìn)行非空判斷,在這點(diǎn),我覺得kotlin就做得很好,利用空安全可以很好地避免NullPointException。
角標(biāo)越界
在使用數(shù)組或者集合的時(shí)候會(huì)出現(xiàn)IndexOutOfBoundsException,在根據(jù)index進(jìn)行取值時(shí),最好先判斷該索引值是否存在或者使用try-catch捕捉異常。
集合元素刪除操作
比如我們需要將集合中滿足條件的元素刪除掉
list.forEach {
if (it == 3) {
list.removeAt(it)
}
}
這樣做會(huì)引起Crash,會(huì)報(bào)ConcurrentModificationException,針對(duì)這個(gè)問題,我們可以從后面開始遍歷
for (index in list.size - 1 downTo 0) {
if (list[index] == 3) {
list.removeAt(index)
}
}
也可以使用迭代器進(jìn)行遍歷刪除元素
val iterator = list.iterator()
while (iterator.hasNext()) {
val a = iterator.next()
if (a == 3) {
iterator.remove()
}
}
當(dāng)多個(gè)線程同時(shí)操作某個(gè)數(shù)組時(shí),不要進(jìn)行數(shù)組的增刪改查等操作,這樣同樣也會(huì)引起相關(guān)的Crash或數(shù)據(jù)查詢不準(zhǔn)確等問題。
異步操作后對(duì)界面元素的處理
在fragment中使用Context前最好先加上判斷isAdded判斷,特別是異步操作后使用Context,很有可能出現(xiàn)報(bào)錯(cuò)(Fragment not attached to a context)而閃退,所有的異步回調(diào)后若要操作View,都要判斷view是否為空,否則會(huì)出現(xiàn)界面銷毀后View為空,空指針閃退問題。
Intent傳遞數(shù)據(jù)過大
Intent傳512K以下的數(shù)據(jù)可以正常傳遞,高于512K則會(huì)出錯(cuò),因?yàn)榭紤]到Intent還要包括要啟動(dòng)的Activity等信息,所以實(shí)際可以傳的數(shù)據(jù)應(yīng)該略小于512K。
val data = ByteArray(1024 * 1024)
val intent = Intent(this, ExpActivity::class.java)
intent.putExtra("test", data)
startActivity(intent)
這段代碼會(huì)導(dǎo)致Crash
?Caused by: android.os.TransactionTooLargeException: data parcel size 1049012 bytes
因?yàn)槲覀冊贗ntent中攜帶的數(shù)據(jù)要從APP進(jìn)程傳輸?shù)紸MS進(jìn)程,再由AMS進(jìn)程傳輸?shù)侥繕?biāo)Activity所在進(jìn)程,普通的由 Zygote 孵化而來的用戶進(jìn)程,所映射的Binder內(nèi)存大小是不到1M,但是,在使用Intent傳遞數(shù)據(jù)時(shí),1M并不是安全上限,因?yàn)锽inder可能正在處理其它的傳輸工作。總而言之,startActivity攜帶的數(shù)據(jù)會(huì)經(jīng)過Binder內(nèi)核再傳遞到目標(biāo)Activity中去,因?yàn)閎inder映射內(nèi)存的限制,所以startActivity也會(huì)這個(gè)限制。
在子線程中操作UI
子線程中是不能操作UI的,如果在子線程中某個(gè)時(shí)機(jī)想要改變UI,可以使用Handler或者kotlin協(xié)程切換,需要注意的是,在子線程中也不可以操作Dialog和Toast。但是,這有個(gè)很有意思的點(diǎn),舉個(gè)例子,如果你在onCreate中開啟一個(gè)子線程改變UI,會(huì)發(fā)現(xiàn)程序運(yùn)行正常,沒報(bào)錯(cuò),像這樣
class ExpActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_exp)
val name = findViewById<TextView>(R.id.name)
Thread { name.text = "name" }.start()
}
}
但是,你延遲一秒后再操作UI,又會(huì)閃退報(bào)錯(cuò)
class ExpActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_exp)
val name = findViewById<TextView>(R.id.name)
Thread {
Thread.sleep(1000)
name.text = "name"
}.start()
}
}
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
這到底是為什么呢?這個(gè)的關(guān)鍵是ViewRootImpl類,它會(huì)去檢查當(dāng)前線程是不是主線程,如果不是就會(huì)拋出異常。像上面的情況,在onCreate中未延時(shí)直接操作UI不閃退,是因?yàn)榇藭r(shí)ViewRootImpl還沒有被初始化,這個(gè)時(shí)候程序沒有去檢測當(dāng)前線程是不是主線程,所以沒有拋異常。嚴(yán)格地講,在ViewRootImpl構(gòu)造的時(shí)候賦值的,賦值的就是當(dāng)前的Thread對(duì)象,也就是說,你ViewRootImpl在哪個(gè)線程創(chuàng)建的,你后續(xù)的UI更新就需要在哪個(gè)線程執(zhí)行,跟是不是UI線程毫無關(guān)系。
ANR
ANR是指程序未響應(yīng),在Android系統(tǒng)中,AMS和WMS會(huì)檢測App的響應(yīng)時(shí)間,如果App在特定時(shí)間無法響應(yīng)屏幕觸摸或鍵盤輸入事件,或者特定事件沒有處理完畢,就會(huì)出現(xiàn)ANR。
不同Context規(guī)定的上限時(shí)間不同:
- 主線程對(duì)輸入事件5秒內(nèi)沒有處理完畢。
- 主線程在執(zhí)行BroadcastReceiver的onReceive()函數(shù)時(shí)10秒內(nèi)沒有處理完畢。
- 主線程在Service的各個(gè)生命周期函數(shù)時(shí)20秒內(nèi)沒有處理完畢。
避免ANR就要盡量避免在主線程中做耗時(shí)操作,耗時(shí)操作盡量放在子線程中。
我們可以通過/data/anr/traces.txt文件來分析ANR的產(chǎn)生,通過adb命令可以導(dǎo)出該文件,不過traces文件記錄的東西可能比較多,分析的時(shí)候需要針對(duì)性地搜索出相關(guān)記錄,該文件會(huì)記錄進(jìn)程ID,包名,造成ANR的原因和產(chǎn)生ANR的具體行數(shù)。
原文鏈接:https://blog.csdn.net/qq_45485851/article/details/122347379
相關(guān)推薦
- 2022-12-24 Kubernetes?ApiServer三大server權(quán)限與數(shù)據(jù)存儲(chǔ)解析_云和虛擬化
- 2022-12-11 教你使用MongoDB導(dǎo)入導(dǎo)出備份數(shù)據(jù)_MongoDB
- 2022-06-19 C++詳細(xì)分析講解函數(shù)參數(shù)的擴(kuò)展_C 語言
- 2022-08-06 .Net?Core中使用EFCore生成反向工程_實(shí)用技巧
- 2022-09-22 提高接口并發(fā)量,防止崩潰
- 2022-05-26 解決redis批量刪除key值的問題_Redis
- 2022-08-15 創(chuàng)建型設(shè)計(jì)模式之建造者模式
- 2022-11-23 Pandas?DataFrame操作數(shù)據(jù)增刪查改_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支