Android機(jī)型適配之痛

CSDN移動(dòng)將持續(xù)為您優(yōu)選移動(dòng)開發(fā)的精華內(nèi)容,共同探討移動(dòng)開發(fā)的技術(shù)熱點(diǎn)話題,涵蓋移動(dòng)應(yīng)用、開發(fā)工具、移動(dòng)游戲及引擎、智能硬件、物聯(lián)網(wǎng)等方方面面。如果您想投稿、參與內(nèi)容翻譯工作,或?qū)で蠼硤?bào)道,請(qǐng)發(fā)送郵件至tangxy#csdn.net(請(qǐng)把#改成@)。

Android平臺(tái)的誕生為手機(jī)智能化的普及立下汗馬功勞,但其最大的缺點(diǎn)也越來越凸顯,那就是碎片化嚴(yán)重:設(shè)備繁多、品牌眾多、版本各異,芯片、攝像頭、分辨率不統(tǒng)一等等,這些都逐漸成為Android系統(tǒng)發(fā)展的障礙,碎片化嚴(yán)重不僅造成Android系統(tǒng)混亂,也導(dǎo)致Android應(yīng)用隱形開發(fā)成本的增多。本文中詳細(xì)介紹了Android琳瑯滿目的適配問題。

一、個(gè)性化十足的Launcher

快捷方式雖然看起來只是一個(gè)很小的功能點(diǎn),但是它涉及到的機(jī)型適配問題很多。

快捷方式創(chuàng)建代碼:

[java]view plaincopy

ntent?addShortCut?=newIntent("com.android.launcher.action.INSTALL_SHORTCUT");

addShortCut.putExtra(Intent.EXTRA_SHORTCUT_NAME,?title);

//?不允許重復(fù)創(chuàng)建

addShortCut.putExtra("duplicate",false);

addShortCut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,?icon);

addShortCut.putExtra(Intent.EXTRA_SHORTCUT_INTENT,?intent);

sendBroadcast(addShortCut);

1. 無法創(chuàng)建快捷方式

越來越多的手機(jī)廠商取消了快捷方式的概念,導(dǎo)致我們無法通過代碼創(chuàng)建一個(gè)自己真實(shí)需要的快捷方式,數(shù)據(jù)顯示,這樣的手機(jī)約占13%。

2. 重復(fù)創(chuàng)建快捷方式

通常情況下,我們是不希望自己的快捷方式被重復(fù)創(chuàng)建。使用addShortCut.putExtra("duplicate", false);方法就能達(dá)到目的,但是市面上至少有8%的手機(jī),即使設(shè)置了duplicate為false,還是可以重復(fù)創(chuàng)建快捷方式。

代表手機(jī)品牌為:華為、中興、HTC。

Android Launcher源碼:

2.1 重復(fù)創(chuàng)建快捷方式的解決方案V1.X

我們最早使用的解決快捷方式重復(fù)創(chuàng)建的方法是:在創(chuàng)建快捷方式前先執(zhí)行刪除操作。這種方式其實(shí)很聰明,因?yàn)榧词故窃诳旖莘绞讲淮嬖诘那闆r下執(zhí)行刪除操作也不會(huì)有任何異常。這樣看來問題解決得太輕松了,但是遺憾的是刪除快捷方式同樣存在適配問題,數(shù)據(jù)顯示大約21%的手機(jī)無法正常刪除快捷方式。

另外一種方法是:自行保存快捷方式的創(chuàng)建記錄,通過一個(gè)字段來記錄快捷方式是否已經(jīng)創(chuàng)建過了,以此來決定是否創(chuàng)建新的快捷方式。這種做法也是因?yàn)槌霈F(xiàn)快捷方式無法刪除情況后對(duì)解決方案進(jìn)行了一個(gè)小的升級(jí),雖然可以解決問題,但是如果程序被清除了數(shù)據(jù),那么一切都亂了,還是無法徹底的規(guī)避重復(fù)的問題。

2.2 重復(fù)創(chuàng)建快捷方式的解決方案V2.X

遇到難解的問題還是看看源碼吧,Android的Launcher源碼在創(chuàng)建快捷方式的時(shí)候不僅會(huì)判斷duplicate的值,還會(huì)在數(shù)據(jù)庫中查詢一下將要被創(chuàng)建的快捷方式是否已經(jīng)存在,我們也照做就OK了。

此外,我們也注意到,查詢數(shù)據(jù)庫的時(shí)候訪問地址URI是一個(gè)很重要的因素,問題是數(shù)據(jù)庫的URI比較多,Android標(biāo)準(zhǔn)的URI就有3個(gè):

2.2版本以前的URI是:content://com.android.launcher.settings/favorites?notify=true

2.2~4.3版本的URI是:content://com.android.launcher2.settings/favorites?notify=true

4.4版本以上的目前都是:content://com.android.launcher3.settings/favorites?notify=true

不僅僅Android自己的Launcher數(shù)據(jù)庫地址眾多,廠商自己定義的地址就更加豐富多彩,如OPPO R827T的訪問URI為:content://com.oppo.launcher.settings /favorites?notify=true;HTC Z715e的訪問地址為:content://com.htc.launcher.settings/favorites?notify=true。事實(shí)上遠(yuǎn)遠(yuǎn)不止這些,還有不計(jì)其數(shù)的第三方Launcher應(yīng)用,很多開發(fā)者也會(huì)修改數(shù)據(jù)庫訪問地址,目前僅我們掌握的不同訪問地址就有多達(dá)40種左右。

通過權(quán)限查詢URI:

通過數(shù)據(jù)庫的讀寫權(quán)限來查詢對(duì)應(yīng)的URI相信大家也不陌生,感覺上像是找到了終極的解決方案,且看下去...

問題一:如果使用完整的權(quán)限進(jìn)行查詢--權(quán)限眾多,我們目前掌握的超過50種。

問題二:如果使用不完整的權(quán)限進(jìn)行查詢(READ_SETTINGS)對(duì)應(yīng)關(guān)系復(fù)雜,大約有 32% 的手機(jī)會(huì)對(duì)應(yīng)兩個(gè)以上的URI。

例如:

GT-I8262D:

authority:com.sec.android.app.launcher.settings ReadPermission:com.android.launcher.permission.READ_SETTINGS

authority:com.sec.android.app.launcher.settings.id ReadPermission:com.android.launcher.permission.READ_SETTINGS

Lenovo A278t:

authority:com.aspire.mm.Settings ReadPermission:com.aspire.mm.permission.READ_SETTINGS

authority:com.huaqin.launcherEx.settings ReadPermission:com.huaqin.launcherEx.permission.READ_SETTINGS

authority:com.huaqin.thememgr.Settings ReadPermission:com.huaqin.thememgr.permission.READ_SETTINGS

二、多姿多彩的Camera

1. Intent調(diào)用手機(jī)內(nèi)相機(jī)程序

如果我們?cè)O(shè)置了照片的存儲(chǔ)路徑,那么很可能會(huì)遇到一下三種問題:

問題一:onActivityResult方法中的data返回為空(數(shù)據(jù)表明,93%的機(jī)型的data將會(huì)是Null,所以如果我們指定了路徑,就不要使用data來獲取照片,起碼在使用前要做空判斷)。

問題二:照片無法存儲(chǔ)。

如果自定義存儲(chǔ)路徑是/mnt/sdcard/lowry/,而手機(jī)SD卡下在拍照前沒有名為lowry的文件夾,那么部分手機(jī)拍照后圖片不會(huì)保存,導(dǎo)致我們無法獲得照片,大多數(shù)手機(jī)的相機(jī)遇到文件夾不存在的情況都會(huì)自己創(chuàng)建出不存在的文件夾,而個(gè)別手機(jī)卻不會(huì)創(chuàng)建,其代表機(jī)型為:三星I8258、華為H30-T00、紅米等。

解決的方法就是在指定存儲(chǔ)路徑前先判斷路徑中的文件夾是否都存在,不存在先創(chuàng)建再調(diào)用相機(jī)。

問題三:照片可以存儲(chǔ),但是名字不對(duì)。

file:///mnt/sdcard/123 1.jpg,由于URI的fromFile方法會(huì)將路徑中的空格用“%20”取代。

其實(shí)對(duì)于大多數(shù)的手機(jī)這都不算事,手機(jī)在解析存儲(chǔ)路徑的時(shí)候都會(huì)將“%20”替換為空格,這樣實(shí)際上最終的照片名字還是我們當(dāng)初指定的名字:123 1.jpg,遺憾的是個(gè)別手機(jī)(如酷派7260)系統(tǒng)自帶的相機(jī)沒有將“%20”讀成空格,拍照后的照片的名字是123%201.jpg,我們用路徑“file:///mnt/sdcard/123 1.jpg”能找到照片才怪!

總結(jié):

(1)使用onActivityResult中的intent(data)前要做空判斷。

(2)指定拍照路徑時(shí),先檢查路徑中的文件夾是否都存在,不存在時(shí)先創(chuàng)建文件夾再調(diào)用 ? 相機(jī)拍照。

(3)指定拍照存儲(chǔ)路徑時(shí),照片的命名中不要包含空格等特殊符號(hào)。

2. 通過Camera的open方法調(diào)用手機(jī)攝像頭

2.1 連續(xù)自動(dòng)對(duì)焦crash

原因:第一次對(duì)焦未結(jié)束,應(yīng)用層又發(fā)起的第二次對(duì)焦,引起對(duì)焦失敗。

解決方案一:傳入AutoFocusCallback;

解決方案二:延時(shí)操作;

解決方案三:異常捕獲。

2.2 攝像頭個(gè)數(shù)判斷錯(cuò)誤

現(xiàn)象:當(dāng)我們使用Camera.getNumberOfCameras()方法檢測(cè)攝像頭數(shù)量時(shí)返回的結(jié)果不準(zhǔn)確,如果我們嘗試打開一個(gè)不存在的攝像頭肯定會(huì)拋出異常,這也提醒我們?cè)陂_啟Camera攝像頭時(shí)需要加異常保護(hù)。

代表機(jī)型:聯(lián)想278T、酷派8022

2.3 閃光燈的判斷

我們常用的判斷手機(jī)是否有閃光燈的方法應(yīng)該有以下兩種:

判斷是否支持閃光燈方法一:使用getSupportedFlashModes方法;

判斷是否支持閃光燈方法二:通過PackageManager判斷。

方法一有3.7%的機(jī)器結(jié)果錯(cuò)誤,無法準(zhǔn)確地判斷出手機(jī)是否有閃光燈,主要的品牌包含:酷派、天語、聯(lián)想、三星等。方法二有9.7%的機(jī)器結(jié)果錯(cuò)誤,主要品牌包含:VIVO、金立、酷派、天語、朵唯、三星等。

我們建議在判斷手機(jī)是否有閃光燈的時(shí)候?qū)⑦@兩種方法聯(lián)合使用,出現(xiàn)錯(cuò)誤的概率將大大降低。

2.4 常亮狀態(tài)與其他狀態(tài)間的切換

前提條件是我們?cè)O(shè)置閃光燈為常亮(Parameters.FLASH_MODE_TORCH),并且閃光燈成功常亮。此時(shí)我們?cè)谠O(shè)置閃光燈模式為Parameters.FLASH_MODE_AUTO后閃光燈依然常亮,這樣的機(jī)型約占熱門機(jī)型的12%。遇到這種情況我們需要先設(shè)置閃光燈模式為Parameters.FLASH_MODE_OFF關(guān)閉閃光燈后再設(shè)置其他模式。

2.5 釋放Camera后閃光燈依舊閃亮

既然開了,我們就要負(fù)責(zé)關(guān),說實(shí)話,以前這個(gè)問題根本不在我的考慮范內(nèi),因?yàn)槲覀冊(cè)谑褂肅amera的時(shí)候都會(huì)在Activity被銷毀或者暫停時(shí)釋放Camera。這個(gè)時(shí)候無論閃光燈是什么狀態(tài),都會(huì)隨著Camera的釋放而關(guān)閉。直到我遇見了OPPO R815T,我的世界觀發(fā)生了變化,這貨如果設(shè)置了閃光燈常亮,即使釋放了Camera閃光燈依舊穩(wěn)穩(wěn)地亮著。

而且由于Camera被釋放掉了,你再也沒辦法關(guān)閉閃光燈了,關(guān)閉App、卸載App,你還是扣電池關(guān)機(jī)吧.....所以,如果你的程序中有設(shè)置閃光燈為常亮狀態(tài)的操作,建議在釋放Camera前先將閃光燈設(shè)置為關(guān)閉(Parameters.FLASH_MODE_OFF)狀態(tài)。

2.6 CameraInfo的另類情況

官方文檔中有關(guān)于調(diào)整相機(jī)預(yù)覽角度的例子:

在這個(gè)例子中CameraInfo非常重要,最終的角度計(jì)算就是根據(jù)CameraInfo中orientation值得到的,所以如果這個(gè)值不準(zhǔn)確的話,那么我們的角度就有可能出現(xiàn)錯(cuò)誤。

VIVO V1手機(jī)第一次獲取CameraInfo的orientation值是90,而當(dāng)執(zhí)行了mCamera = Camera.open();之后再獲取CameraInfo的orientation值就是0,而且以后獲取的都是 0 ,除非重啟手機(jī)。

無論是這款手機(jī)上的哪個(gè)應(yīng)用,只要執(zhí)行了一次Camera.open()之后,其他所有程序中獲取CameraInfo的orientation都是是0。

手機(jī)自帶的相機(jī)卻能很好的使用反編譯系統(tǒng)相機(jī)后果然發(fā)現(xiàn)系統(tǒng)相機(jī)并沒有像官方給出的例子來進(jìn)行角度的矯正。

解決方案:

按照此手機(jī)系統(tǒng)相機(jī)的做;

對(duì)該手機(jī)CameraInfo的orientation值寫死為90。

三、不止是2的雙卡

雙卡的問題解決的基本思路:

推斷:手機(jī)內(nèi)置的系統(tǒng)APP都可以正常使用這些功能,因此肯定存在廠商自定義API來實(shí)現(xiàn)這些功能;

反編譯:Framework、系統(tǒng)App、系統(tǒng)數(shù)據(jù)庫;

定位:TelephoneManager擴(kuò)展、SMSManager擴(kuò)展、電話服務(wù)擴(kuò)展、短信服務(wù)擴(kuò)展、數(shù)據(jù)庫字段擴(kuò)展。

四、UI適配

說到UI適配其實(shí)很是讓人頭疼,下面的圖片是某個(gè)產(chǎn)品為了進(jìn)行UI適配所做的工作,可以看出相當(dāng)繁瑣。

除了分辨率的適配,有時(shí)候布局文件中的某個(gè)標(biāo)簽還會(huì)引起一些問題,我們先看下面一段布局代碼:

正確結(jié)果:

錯(cuò)誤結(jié)果:

這就是因?yàn)锳ndroid 3.0以下版本在FrameLayout中使用layout_marginTo標(biāo)簽,必須要設(shè)置gravity才能生效。

那么如何解決這個(gè)問題呢?在設(shè)置android:layout_marginTop的組件中再設(shè)置一下 android:layout_gravity="top"即可。

五、還有更奇葩的

1. 廠商的抽象方法

如果你需要實(shí)現(xiàn)InputConnection接口,那么你一定要注意下面這個(gè)很奇葩的異常:

反編譯了下此款手機(jī)的Framework,發(fā)現(xiàn)廠商在InputConnection接口中增加了一個(gè)抽象方法performYLPrivateCommand。

2. 距離傳感器

2.1 不同手機(jī)event.values[0]值簡直是千變?nèi)f化

簡單說幾個(gè)有代表性的:

一部分手機(jī)比較正常,靠近時(shí)為0遠(yuǎn)離時(shí)為1(0,1);

有點(diǎn)小個(gè)性的手機(jī)數(shù)值將變大,比如(0,100),(3,5),(3,100)等等;

213手機(jī)的數(shù)值就比較莫名其妙,(1.001,5.003),你是表明精確度高?

2.2 數(shù)值與遠(yuǎn)近關(guān)系不統(tǒng)一

既然我們是通過數(shù)值來判斷當(dāng)前是否出于近耳狀態(tài),那么是不是應(yīng)該這個(gè)數(shù)值的大小是有說道的?靠近時(shí)的數(shù)值小一點(diǎn),遠(yuǎn)離時(shí)的數(shù)值大一些,起碼我見過的99%的手機(jī)是這樣子的。但是就有幾款神經(jīng)病手機(jī)(100W)偏偏是靠近時(shí)的數(shù)值比遠(yuǎn)離時(shí)的數(shù)值大,這是個(gè)坑,開發(fā)者要注意~~??!

2.3 getMaximumRange方法返回值不對(duì)

有一句API:SensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY).getMaximumRange(),文檔解釋這個(gè)應(yīng)該獲取的是傳感器數(shù)值變化的最大范圍,比如如果靠近時(shí)的值是0,遠(yuǎn)離時(shí)的值是1。那么getMaximumRange()的值應(yīng)該是1才不會(huì)影響我們的判斷,我這里僅僅是從API角度和我們?nèi)粘5氖褂昧?xí)慣來說的,如果不是這樣的規(guī)律,就會(huì)對(duì)我們的編程造成麻煩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容