Android bug最近遇到的幾個(gè)坑解決分享
前言
6-7月的事情著實(shí)有點(diǎn)多。沒有清風(fēng)亦沒有鮮花,連蟬都熱到燥不動(dòng)了,人自然也變得慵懶了許多。
月頭計(jì)劃和諸君探討下Compose(Compose-Desktop)和MVI實(shí)際投產(chǎn)后的感受,眼看即將到月底,低頭看著才寫一半的草稿,沉默良久,大抵這個(gè)月是沒有希望成稿了。
終究,只能挑一些最近遇到的問題寫一寫,欺騙自己還未曾擺爛。
Android R、S中的外部文件管理權(quán)限
想來是舒適太久了,目前負(fù)責(zé)的核心項(xiàng)目均是面向特定Android-Pad開發(fā)的,在 Android 9
已經(jīng)躺了一年,已經(jīng)快忘記了 適配 這一祖?zhèn)靼ぁ?/p>
但還有部分即將推出的項(xiàng)目是面向普通用戶的,順便談一談這個(gè) 老問題
在 Android 10
中提出了分區(qū)存儲(chǔ),對(duì)于外部存儲(chǔ)空間的讀寫,除了需要處理FileProvider外,還需要配置 requestLegacyExternalStorage
。
在 Android R、S
中,進(jìn)行了更嚴(yán)格的限制,需要獲取完整的外部存儲(chǔ)控制權(quán)限。
作者按:筆者負(fù)責(zé)的項(xiàng)目受技術(shù)之外因素的影響,改變現(xiàn)有的文件存儲(chǔ)路徑的阻力非常大
判斷與申請(qǐng)完全的控制權(quán)限
補(bǔ)充聲明權(quán)限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
那么,相關(guān)權(quán)限如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
并且需要運(yùn)行時(shí)動(dòng)態(tài)獲取權(quán)限,關(guān)鍵代碼如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { //略去引導(dǎo)彈窗等相關(guān)交互邏輯 startActivityForResult(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION), REQ_CODE_FILE_MANAGE) return } } // do something
可以結(jié)合 ActivityResultContracts
改造 onActivityResult,不再贅述。
用戶對(duì)軟件授予權(quán)限后可讀寫文件。
如何快速改造
隨著Compose跨平臺(tái)技術(shù)越來越熱,將應(yīng)用的業(yè)務(wù)在多個(gè)平臺(tái)上復(fù)用也越發(fā)有價(jià)值,而平臺(tái)的差異性內(nèi)容應(yīng)當(dāng)在平臺(tái)內(nèi)部解決。
那么 如何在不修改Presenter/ViewModel 和 Model層的前提下,便捷的解決此類適配問題 越發(fā)具有價(jià)值。
當(dāng)然,此類問題的解決不能脫離實(shí)際空談,便不做具體展開。目前在項(xiàng)目中先運(yùn)用了 Proxy
方式,對(duì)業(yè)務(wù)層追加了前置邏輯,進(jìn)行了簡單處理。
我已經(jīng)設(shè)想了一個(gè)框架,并非PermissionX之類處理Android動(dòng)態(tài)權(quán)限的框架,有后續(xù)實(shí)質(zhì)進(jìn)展后再與諸君分享。
藍(lán)牙居然搜索不到設(shè)備
如果直接適配Android 12,大概率不會(huì)出現(xiàn)該問題(未經(jīng)過大量rom驗(yàn)證),target是低版本,以往的業(yè)務(wù)代碼不到位就有可能受到影響
未適配Android 12 藍(lán)牙權(quán)限的應(yīng)用,在部分手機(jī)上發(fā)現(xiàn)未打開 "訪問我的位置信息" 時(shí)(不是定位權(quán)限!?。?,會(huì)導(dǎo)致搜索不到藍(lán)牙設(shè)備。大抵是ROM廠商的杰作。
參考地圖類SDK的操作,增加以下檢測(cè)代碼:
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { //引導(dǎo)用戶開啟定位:Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) }
在 target Android12 中:
- 請(qǐng)求精確位置,需同時(shí)申請(qǐng) ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 權(quán)限
- (動(dòng)態(tài))申請(qǐng)藍(lán)牙相關(guān)權(quán)限時(shí),不再需要申請(qǐng)?jiān)O(shè)備位置信息相關(guān)權(quán)限
理論上不需要再加這一檢測(cè),但仍需經(jīng)過大量ROM測(cè)試。建議保留該段檢測(cè)邏輯。
一個(gè)高star庫中的I/O和多線程的綜合性問題
前幾日和好友閑聊時(shí),好友提到他們的項(xiàng)目中使用了一個(gè)開源的下載庫 Aria
,但時(shí)不時(shí)會(huì)出現(xiàn)下載圖片失敗的問題,實(shí)質(zhì)是庫的bug。
閱讀源碼后感到問題出在細(xì)節(jié)知識(shí)上,本篇也需要水一下字?jǐn)?shù),索性拿來討論一二。
庫中運(yùn)用到的技術(shù):
- 多線程&鎖
- IO
- Android的Handler
其中,多線程和鎖的知識(shí)本篇不談,我亦寫有相關(guān)系列;庫作者使用Android中的Handler簡化多線程間的通信,也不再展開;IO部分系統(tǒng)展開也很長,我已有系列文章計(jì)劃,本篇結(jié)合問題簡單談一談。
和文件服務(wù)器對(duì)比,移動(dòng)端的文件上傳下載并發(fā)量很小,其速度制約一般在于:服務(wù)器單連接流量限制,網(wǎng)絡(luò)條件。
對(duì)于單個(gè)文件采用多線程方式上傳、下載,只能突破 單連接流量限制,以充分使用網(wǎng)絡(luò)資源。多個(gè)文件并行上下載的產(chǎn)品意義遠(yuǎn)大于充分吃掉網(wǎng)絡(luò)帶寬資源。
注意:上下載的高帶寬占用一定程度上會(huì)影響其他網(wǎng)絡(luò)層業(yè)務(wù)
一般對(duì)于下載而言,使用多線程時(shí)用常規(guī)的 "碎文件合并" 的思路即可。用 RandomAccessFile
并不是一個(gè)好主意。
多線程寫RandomAccessFile并非一件很美好的事情,以后的文章中細(xì)聊
在使用Java經(jīng)典IO時(shí),使用Buffer減少IO次數(shù)可以獲得很好的性能提升,需要注意及時(shí) flush
,該庫中用Buffer設(shè)計(jì)對(duì) RandomAccessFile
進(jìn)行了一層封裝。想來也是源自三方庫.
將部分代碼簡化后類似如下代碼:
class Demo { @Test fun mockDownload() { Looper.prepare() val appContext = InstrumentationRegistry.getInstrumentation().targetContext val file = File(appContext.cacheDir, "testtmp") if (file.exists()) { file.delete() } file.createNewFile() assertEquals(0, file.length()) val data = "hello".encodeToByteArray() val bFile = BufferedRandomAccessFile(file, "rwd", 1024) val handler = object : Handler(Looper.myLooper()) { override fun handleMessage(msg: Message?) { super.handleMessage(msg) //模擬文件合并線程,僅斷言文件大小 if (msg?.what == 1) { assertEquals(data.size.toLong(), file.length()) Looper.myLooper()?.quit() } } } //模擬下載線程 thread(priority = 1) { val ins = ByteArrayInputStream(data) try { val buffer = ByteArray(1024) var len: Int while (ins.read(buffer).also { len = it } != -1) { bFile.write(buffer, 0, len) } //模擬下載完成消息 handler.sendEmptyMessage(1) // Thread.yield() -- 模擬一下線程被切換或者因?yàn)殒i導(dǎo)致的同步 } finally { ins.close() // bFile.flush() will be invoked in close bFile.close() } } Looper.loop() } }
觀察這段代碼可以發(fā)現(xiàn),存在不安全因素:最后一個(gè)buffer塊的內(nèi)容大小未必是1024,需等到 bFile.close()
時(shí)方可寫入文件。
雖然 handler.sendEmptyMessage(1)
發(fā)送的消息會(huì)被異步執(zhí)行,但并不意味著 bFile.close()
一定會(huì)先執(zhí)行。鎖、AQS、系統(tǒng)線程調(diào)度等均可能會(huì)導(dǎo)致該問題。
以上就是Android bug最近遇到的幾個(gè)坑解決分享的詳細(xì)內(nèi)容,更多關(guān)于Android bug解決的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
listview 選中高亮顯示實(shí)現(xiàn)方法
當(dāng)點(diǎn)擊左側(cè)ListView后,選中的一行就會(huì)一直呈高亮狀態(tài)顯示,圖中選中行字的顏色顯示為藍(lán)色(注意:是選中行后一直高亮,而不是只是點(diǎn)擊時(shí)高亮),如果再次點(diǎn)擊另外的一行, 則新的那一行就高亮,下面就來實(shí)現(xiàn)這個(gè)高亮效果的顯示2012-11-11Android 動(dòng)畫之TranslateAnimation應(yīng)用詳解
本節(jié)講解TranslateAnimation動(dòng)畫,TranslateAnimation比較常用,比如QQ,網(wǎng)易新聞菜單條的動(dòng)畫,就可以用TranslateAnimation實(shí)現(xiàn),本文將詳細(xì)介紹通過TranslateAnimation 來定義動(dòng)畫,需要的朋友可以參考下2012-12-12Android ListView與ScrollView沖突的解決方法總結(jié)
這篇文章主要介紹了Android ListView與ScrollView沖突的解決方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-04-04Android-ViewModel和LiveData使用詳解
這篇文章主要介紹了Android-ViewModel和LiveData使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android關(guān)鍵字persistent詳細(xì)分析
這篇文章主要介紹了Android關(guān)鍵字persistent的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-04-04Android使用開源組件PagerBottomTabStrip實(shí)現(xiàn)底部菜單和頂部導(dǎo)航功能
這篇文章主要介紹了Android使用PagerBottomTabStrip實(shí)現(xiàn)底部菜單和頂部導(dǎo)航功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08Android編程實(shí)現(xiàn)Home鍵的屏蔽,捕獲與修改方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)Home鍵的屏蔽,捕獲與修改方法,實(shí)例分析了使用onAttachedToWindow捕獲Home鍵的相關(guān)技巧,需要的朋友可以參考下2016-06-06