CSDN移動將持續(xù)為您優(yōu)選移動開發(fā)的精華內(nèi)容,共同探討移動開發(fā)的技術(shù)熱點話題,涵蓋移動應用、開發(fā)工具、移動游戲及引擎、智能硬件、物聯(lián)網(wǎng)等方方面面。如果您想投稿、參與內(nèi)容翻譯工作,或?qū)で蠼硤蟮?,請發(fā)送郵件至tangxy#csdn.net(請把#改成@)。
Android平臺的誕生為手機智能化的普及立下汗馬功勞,但其最大的缺點也越來越凸顯,那就是碎片化嚴重:設(shè)備繁多、品牌眾多、版本各異,芯片、攝像頭、分辨率不統(tǒng)一等等,這些都逐漸成為Android系統(tǒng)發(fā)展的障礙,碎片化嚴重不僅造成Android系統(tǒng)混亂,也導致Android應用隱形開發(fā)成本的增多。本文中詳細介紹了Android琳瑯滿目的適配問題。
快捷方式雖然看起來只是一個很小的功能點,但是它涉及到的機型適配問題很多。
快捷方式創(chuàng)建代碼:
[java]view plaincopy
ntent?addShortCut?=newIntent("com.android.launcher.action.INSTALL_SHORTCUT");
addShortCut.putExtra(Intent.EXTRA_SHORTCUT_NAME,?title);
//?不允許重復創(chuàng)建
addShortCut.putExtra("duplicate",false);
addShortCut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,?icon);
addShortCut.putExtra(Intent.EXTRA_SHORTCUT_INTENT,?intent);
sendBroadcast(addShortCut);
越來越多的手機廠商取消了快捷方式的概念,導致我們無法通過代碼創(chuàng)建一個自己真實需要的快捷方式,數(shù)據(jù)顯示,這樣的手機約占13%。

通常情況下,我們是不希望自己的快捷方式被重復創(chuàng)建。使用addShortCut.putExtra("duplicate", false);方法就能達到目的,但是市面上至少有8%的手機,即使設(shè)置了duplicate為false,還是可以重復創(chuàng)建快捷方式。
代表手機品牌為:華為、中興、HTC。
Android Launcher源碼:

2.1 重復創(chuàng)建快捷方式的解決方案V1.X
我們最早使用的解決快捷方式重復創(chuàng)建的方法是:在創(chuàng)建快捷方式前先執(zhí)行刪除操作。這種方式其實很聰明,因為即使是在快捷方式不存在的情況下執(zhí)行刪除操作也不會有任何異常。這樣看來問題解決得太輕松了,但是遺憾的是刪除快捷方式同樣存在適配問題,數(shù)據(jù)顯示大約21%的手機無法正常刪除快捷方式。
另外一種方法是:自行保存快捷方式的創(chuàng)建記錄,通過一個字段來記錄快捷方式是否已經(jīng)創(chuàng)建過了,以此來決定是否創(chuàng)建新的快捷方式。這種做法也是因為出現(xiàn)快捷方式無法刪除情況后對解決方案進行了一個小的升級,雖然可以解決問題,但是如果程序被清除了數(shù)據(jù),那么一切都亂了,還是無法徹底的規(guī)避重復的問題。
2.2 重復創(chuàng)建快捷方式的解決方案V2.X
遇到難解的問題還是看看源碼吧,Android的Launcher源碼在創(chuàng)建快捷方式的時候不僅會判斷duplicate的值,還會在數(shù)據(jù)庫中查詢一下將要被創(chuàng)建的快捷方式是否已經(jīng)存在,我們也照做就OK了。

此外,我們也注意到,查詢數(shù)據(jù)庫的時候訪問地址URI是一個很重要的因素,問題是數(shù)據(jù)庫的URI比較多,Android標準的URI就有3個:
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ù)的第三方Launcher應用,很多開發(fā)者也會修改數(shù)據(jù)庫訪問地址,目前僅我們掌握的不同訪問地址就有多達40種左右。
通過權(quán)限查詢URI:
通過數(shù)據(jù)庫的讀寫權(quán)限來查詢對應的URI相信大家也不陌生,感覺上像是找到了終極的解決方案,且看下去...

問題一:如果使用完整的權(quán)限進行查詢--權(quán)限眾多,我們目前掌握的超過50種。
問題二:如果使用不完整的權(quán)限進行查詢(READ_SETTINGS)對應關(guān)系復雜,大約有 32% 的手機會對應兩個以上的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

如果我們設(shè)置了照片的存儲路徑,那么很可能會遇到一下三種問題:
問題一:onActivityResult方法中的data返回為空(數(shù)據(jù)表明,93%的機型的data將會是Null,所以如果我們指定了路徑,就不要使用data來獲取照片,起碼在使用前要做空判斷)。
問題二:照片無法存儲。
如果自定義存儲路徑是/mnt/sdcard/lowry/,而手機SD卡下在拍照前沒有名為lowry的文件夾,那么部分手機拍照后圖片不會保存,導致我們無法獲得照片,大多數(shù)手機的相機遇到文件夾不存在的情況都會自己創(chuàng)建出不存在的文件夾,而個別手機卻不會創(chuàng)建,其代表機型為:三星I8258、華為H30-T00、紅米等。
解決的方法就是在指定存儲路徑前先判斷路徑中的文件夾是否都存在,不存在先創(chuàng)建再調(diào)用相機。
問題三:照片可以存儲,但是名字不對。
file:///mnt/sdcard/123 1.jpg,由于URI的fromFile方法會將路徑中的空格用“%20”取代。
其實對于大多數(shù)的手機這都不算事,手機在解析存儲路徑的時候都會將“%20”替換為空格,這樣實際上最終的照片名字還是我們當初指定的名字:123 1.jpg,遺憾的是個別手機(如酷派7260)系統(tǒng)自帶的相機沒有將“%20”讀成空格,拍照后的照片的名字是123%201.jpg,我們用路徑“file:///mnt/sdcard/123 1.jpg”能找到照片才怪!



總結(jié):
(1)使用onActivityResult中的intent(data)前要做空判斷。
(2)指定拍照路徑時,先檢查路徑中的文件夾是否都存在,不存在時先創(chuàng)建文件夾再調(diào)用 ? 相機拍照。
(3)指定拍照存儲路徑時,照片的命名中不要包含空格等特殊符號。
2. 通過Camera的open方法調(diào)用手機攝像頭
2.1 連續(xù)自動對焦crash
原因:第一次對焦未結(jié)束,應用層又發(fā)起的第二次對焦,引起對焦失敗。

解決方案一:傳入AutoFocusCallback;

解決方案二:延時操作;
解決方案三:異常捕獲。
2.2 攝像頭個數(shù)判斷錯誤
現(xiàn)象:當我們使用Camera.getNumberOfCameras()方法檢測攝像頭數(shù)量時返回的結(jié)果不準確,如果我們嘗試打開一個不存在的攝像頭肯定會拋出異常,這也提醒我們在開啟Camera攝像頭時需要加異常保護。
代表機型:聯(lián)想278T、酷派8022

2.3 閃光燈的判斷
我們常用的判斷手機是否有閃光燈的方法應該有以下兩種:
判斷是否支持閃光燈方法一:使用getSupportedFlashModes方法;

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

方法一有3.7%的機器結(jié)果錯誤,無法準確地判斷出手機是否有閃光燈,主要的品牌包含:酷派、天語、聯(lián)想、三星等。方法二有9.7%的機器結(jié)果錯誤,主要品牌包含:VIVO、金立、酷派、天語、朵唯、三星等。
我們建議在判斷手機是否有閃光燈的時候?qū)⑦@兩種方法聯(lián)合使用,出現(xiàn)錯誤的概率將大大降低。
2.4 常亮狀態(tài)與其他狀態(tài)間的切換
前提條件是我們設(shè)置閃光燈為常亮(Parameters.FLASH_MODE_TORCH),并且閃光燈成功常亮。此時我們在設(shè)置閃光燈模式為Parameters.FLASH_MODE_AUTO后閃光燈依然常亮,這樣的機型約占熱門機型的12%。遇到這種情況我們需要先設(shè)置閃光燈模式為Parameters.FLASH_MODE_OFF關(guān)閉閃光燈后再設(shè)置其他模式。
2.5 釋放Camera后閃光燈依舊閃亮
既然開了,我們就要負責關(guān),說實話,以前這個問題根本不在我的考慮范內(nèi),因為我們在使用Camera的時候都會在Activity被銷毀或者暫停時釋放Camera。這個時候無論閃光燈是什么狀態(tài),都會隨著Camera的釋放而關(guān)閉。直到我遇見了OPPO R815T,我的世界觀發(fā)生了變化,這貨如果設(shè)置了閃光燈常亮,即使釋放了Camera閃光燈依舊穩(wěn)穩(wěn)地亮著。
而且由于Camera被釋放掉了,你再也沒辦法關(guān)閉閃光燈了,關(guān)閉App、卸載App,你還是扣電池關(guān)機吧.....所以,如果你的程序中有設(shè)置閃光燈為常亮狀態(tài)的操作,建議在釋放Camera前先將閃光燈設(shè)置為關(guān)閉(Parameters.FLASH_MODE_OFF)狀態(tài)。
2.6 CameraInfo的另類情況
官方文檔中有關(guān)于調(diào)整相機預覽角度的例子:

在這個例子中CameraInfo非常重要,最終的角度計算就是根據(jù)CameraInfo中orientation值得到的,所以如果這個值不準確的話,那么我們的角度就有可能出現(xiàn)錯誤。
VIVO V1手機第一次獲取CameraInfo的orientation值是90,而當執(zhí)行了mCamera = Camera.open();之后再獲取CameraInfo的orientation值就是0,而且以后獲取的都是 0 ,除非重啟手機。
無論是這款手機上的哪個應用,只要執(zhí)行了一次Camera.open()之后,其他所有程序中獲取CameraInfo的orientation都是是0。
手機自帶的相機卻能很好的使用反編譯系統(tǒng)相機后果然發(fā)現(xiàn)系統(tǒng)相機并沒有像官方給出的例子來進行角度的矯正。




解決方案:
按照此手機系統(tǒng)相機的做;
對該手機CameraInfo的orientation值寫死為90。
雙卡的問題解決的基本思路:
推斷:手機內(nèi)置的系統(tǒng)APP都可以正常使用這些功能,因此肯定存在廠商自定義API來實現(xiàn)這些功能;
反編譯:Framework、系統(tǒng)App、系統(tǒng)數(shù)據(jù)庫;
定位:TelephoneManager擴展、SMSManager擴展、電話服務擴展、短信服務擴展、數(shù)據(jù)庫字段擴展。
說到UI適配其實很是讓人頭疼,下面的圖片是某個產(chǎn)品為了進行UI適配所做的工作,可以看出相當繁瑣。

除了分辨率的適配,有時候布局文件中的某個標簽還會引起一些問題,我們先看下面一段布局代碼:

正確結(jié)果:

錯誤結(jié)果:

這就是因為Android 3.0以下版本在FrameLayout中使用layout_marginTo標簽,必須要設(shè)置gravity才能生效。
那么如何解決這個問題呢?在設(shè)置android:layout_marginTop的組件中再設(shè)置一下 android:layout_gravity="top"即可。
如果你需要實現(xiàn)InputConnection接口,那么你一定要注意下面這個很奇葩的異常:


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


2.1 不同手機event.values[0]值簡直是千變?nèi)f化
簡單說幾個有代表性的:
一部分手機比較正常,靠近時為0遠離時為1(0,1);
有點小個性的手機數(shù)值將變大,比如(0,100),(3,5),(3,100)等等;
213手機的數(shù)值就比較莫名其妙,(1.001,5.003),你是表明精確度高?
2.2 數(shù)值與遠近關(guān)系不統(tǒng)一
既然我們是通過數(shù)值來判斷當前是否出于近耳狀態(tài),那么是不是應該這個數(shù)值的大小是有說道的?靠近時的數(shù)值小一點,遠離時的數(shù)值大一些,起碼我見過的99%的手機是這樣子的。但是就有幾款神經(jīng)病手機(100W)偏偏是靠近時的數(shù)值比遠離時的數(shù)值大,這是個坑,開發(fā)者要注意~~??!
2.3 getMaximumRange方法返回值不對
有一句API:SensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY).getMaximumRange(),文檔解釋這個應該獲取的是傳感器數(shù)值變化的最大范圍,比如如果靠近時的值是0,遠離時的值是1。那么getMaximumRange()的值應該是1才不會影響我們的判斷,我這里僅僅是從API角度和我們?nèi)粘5氖褂昧晳T來說的,如果不是這樣的規(guī)律,就會對我們的編程造成麻煩。