Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android機型適配之痛[干貨分享]

Android機型適配之痛[干貨分享]

編輯:關於Android編程

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

一、個性化十足的Launcher

快捷方式雖然看起來只是一個很小的功能點,但是它涉及到的機型適配問題很多。

快捷方式創建代碼:

Intent addShortCut = new Intent(com.android.launcher.action.INSTALL_SHORTCUT);
addShortCut.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
// 不允許重復創建
addShortCut.putExtra(duplicate, false);
addShortCut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
addShortCut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
sendBroadcast(addShortCut);

1. 無法創建快捷方式

越來越多的手機廠商取消了快捷方式的概念,導致我們無法通過代碼創建一個自己真實需要的快捷方式,數據顯示,這樣的手機約占13%。

/

2. 重復創建快捷方式

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

代表手機品牌為:華為、中興、HTC。

Android Launcher源碼:

/

2.1 重復創建快捷方式的解決方案V1.X

我們最早使用的解決快捷方式重復創建的方法是:在創建快捷方式前先執行刪除操作。這種方式其實很聰明,因為即使是在快捷方式不存在的情況下執行刪除操作也不會有任何異常。這樣看來問題解決得太輕松了,但是遺憾的是刪除快捷方式同樣存在適配問題,數據顯示大約21%的手機無法正常刪除快捷方式。

另外一種方法是:自行保存快捷方式的創建記錄,通過一個字段來記錄快捷方式是否已經創建過了,以此來決定是否創建新的快捷方式。這種做法也是因為出現快捷方式無法刪除情況後對解決方案進行了一個小的升級,雖然可以解決問題,但是如果程序被清除了數據,那麼一切都亂了,還是無法徹底的規避重復的問題。

2.2 重復創建快捷方式的解決方案V2.X

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

/

此外,我們也注意到,查詢數據庫的時候訪問地址URI是一個很重要的因素,問題是數據庫的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數據庫地址眾多,廠商自己定義的地址就更加豐富多彩,如OPPO R827T的訪問URI為:content://com.oppo.launcher.settings /favorites?notify=true;HTC Z715e的訪問地址為: content://com.htc.launcher.settings/favorites?notify=true。事實上 遠遠不止這些,還有不計其數的第三方Launcher應用,很多開發者也會修改數據庫訪問地址,目前僅我們掌握的不同訪問地址就有多達40種左右。

 

  • 通過權限查詢URI:

     

    通過數據庫的讀寫權限來查詢對應的URI相信大家也不陌生,感覺上像是找到了終極的解決方案,且看下去...

    /

     

    1. 問題一:如果使用完整的權限進行查詢--權限眾多,我們目前掌握的超過50種。

    2. 問題二:如果使用不完整的權限進行查詢(READ_SETTINGS)對應關系復雜,大約有 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

    二、多姿多彩的Camera

    1. Intent調用手機內相機程序

    /

    如果我們設置了照片的存儲路徑,那麼很可能會遇到一下三種問題:

     

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

      問題二:照片無法存儲。

       

      如果自定義存儲路徑是/mnt/sdcard/lowry/,而手機SD卡下在拍照前沒有名為lowry的文件夾,那麼部分手機拍照後圖片不會保存,導致我們無法獲得照片,大多數手機的相機遇到文件夾不存在的情況都會自己創建出不存在的文件夾,而個別手機卻不會創建,其代表機型為:三星I8258、華為H30-T00、紅米等。

      解決的方法就是在指定存儲路徑前先判斷路徑中的文件夾是否都存在,不存在先創建再調用相機。

       

      • 問題三:照片可以存儲,但是名字不對。

         

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

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

        /

        總結:

        (1)使用onActivityResult中的intent(data)前要做空判斷。
        (2)指定拍照路徑時,先檢查路徑中的文件夾是否都存在,不存在時先創建文件夾再調用 相機拍照。
        (3)指定拍照存儲路徑時,照片的命名中不要包含空格等特殊符號。

        2. 通過Camera的open方法調用手機攝像頭

        2.1 連續自動對焦crash

        原因:第一次對焦未結束,應用層又發起的第二次對焦,引起對焦失敗。

        /

        解決方案一:傳入AutoFocusCallback;

        /

        解決方案二:延時操作;

        解決方案三:異常捕獲。

        2.2 攝像頭個數判斷錯誤

        現象:當我們使用Camera.getNumberOfCameras()方法檢測攝像頭數量時返回的結果不准確,如果我們嘗試打開一個不存在的攝像頭肯定會拋出異常,這也提醒我們在開啟Camera攝像頭時需要加異常保護。

        代表機型:聯想278T、酷派8022

        /

        2.3 閃光燈的判斷

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

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

        /

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

        /

        方法一有3.7%的機器結果錯誤,無法准確地判斷出手機是否有閃光燈,主要的品牌包含:酷派、天語、聯想、三星等。方法二有9.7%的機器結果錯誤,主要品牌包含:VIVO、金立、酷派、天語、朵唯、三星等。

        我們建議在判斷手機是否有閃光燈的時候將這兩種方法聯合使用,出現錯誤的概率將大大降低。

        2.4 常亮狀態與其他狀態間的切換

        前提條件是我們設置閃光燈為常亮(Parameters.FLASH_MODE_TORCH),並且閃光燈成功常亮。此時我們在設置閃光燈模式為Parameters.FLASH_MODE_AUTO後閃光燈依然常亮,這樣的機型約占熱門機型的12%。遇到這種情況我們需要先設置閃光燈模式為Parameters.FLASH_MODE_OFF關閉閃光燈後再設置其他模式。

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

        既然開了,我們就要負責關,說實話,以前這個問題根本不在我的考慮范內,因為我們在使用Camera的時候都會在Activity被銷毀或者暫停時釋放Camera。這個時候無論閃光燈是什麼狀態,都會隨著Camera的釋放而關閉。直到我遇見了OPPO R815T,我的世界觀發生了變化,這貨如果設置了閃光燈常亮,即使釋放了Camera閃光燈依舊穩穩地亮著。

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

        2.6 CameraInfo的另類情況

        官方文檔中有關於調整相機預覽角度的例子:

        /

        在這個例子中CameraInfo非常重要,最終的角度計算就是根據CameraInfo中orientation值得到的,所以如果這個值不准確的話,那麼我們的角度就有可能出現錯誤。

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

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

        手機自帶的相機卻能很好的使用反編譯系統相機後果然發現系統相機並沒有像官方給出的例子來進行角度的矯正。

         

        /

        /

         

        解決方案:

         

        1. 按照此手機系統相機的做;

        2. 對該手機CameraInfo的orientation值寫死為90。

        三、不止是2的雙卡

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

         

        1. 推斷:手機內置的系統APP都可以正常使用這些功能,因此肯定存在廠商自定義API來實現這些功能;

        2. 反編譯:Framework、系統App、系統數據庫;

        3. 定位:TelephoneManager擴展、SMSManager擴展、電話服務擴展、短信服務擴展、數據庫字段擴展。

         

        四、UI適配

        說到UI適配其實很是讓人頭疼,下面的圖片是某個產品為了進行UI適配所做的工作,可以看出相當繁瑣。

        /

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

        /

        正確結果:

        /

        錯誤結果:

        /

        這就是因為Android 3.0以下版本在FrameLayout中使用layout_marginTo標簽,必須要設置gravity才能生效。

        那麼如何解決這個問題呢?在設置android:layout_marginTop的組件中再設置一下 android:layout_gravity=top即可。

        五、還有更奇葩的

        1. 廠商的抽象方法

        如果你需要實現InputConnection接口,那麼你一定要注意下面這個很奇葩的異常:

        /

        /

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

        /

        2. 距離傳感器

        /

        2.1 不同手機event.values[0]值簡直是千變萬化

        簡單說幾個有代表性的:

         

        1. 一部分手機比較正常,靠近時為0遠離時為1(0,1);

        2. 有點小個性的手機數值將變大,比如(0,100),(3,5),(3,100)等等;

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

         

        2.2 數值與遠近關系不統一

        既然我們是通過數值來判斷當前是否出於近耳狀態,那麼是不是應該這個數值的大小是有說道的?靠近時的數值小一點,遠離時的數值大一些,起碼我見過的99%的手機是這樣子的。但是就有幾款神經病手機(100W)偏偏是靠近時的數值比遠離時的數值大,這是個坑,開發者要注意~~!!

        2.3 getMaximumRange方法返回值不對

        有一句API:SensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY).getMaximumRange(),文檔解釋這個應該獲取的是傳感器數值變化的最大范圍,比如如果靠近時的值是0,遠離時的值是1。那麼getMaximumRange()的值應該是1才不會影響我們的判斷,我這裡僅僅是從API角度和我們日常的使用習慣來說的,如果不是這樣的規律,就會對我們的編程造成麻煩。

         

    1. 上一頁:
    2. 下一頁:
    熱門文章
    閱讀排行版
    Copyright © Android教程網 All Rights Reserved