版權聲明:本文為部落客原創文章(部分引用他人博文,已加上引用說明),未經部落客允許不得轉載。 https://www.jianshu.com/p/f9c67a4b908e 轉載請标明出處: 本文出自 AWeiLoveAndroid的部落格 第一篇文章講了 Android适配全面總結(一)----螢幕适配 上一篇文章講了 Android适配全面總結(二)----版本适配
這一篇文章講一下 ROM适配。
Android是開源的,不同的手機廠商都有自己定制的系統,是以這就給開發者帶來了ROM适配難題。在一些群裡面經常看到有人因為手機适配問題,說這個手機坑,那個手機坑,其實那是沒有對ROM定制系統的一些變更了解,導緻了盲目的說出這些指責的話。如果你熟悉了,也就會少走很多彎路。下面這篇文章就來講一下幾個主流手機的ROM适配問題。
一、手機平台相關文檔
(一)小米
1、
小米開發者文檔2、
開發人員必看:《小米應用開發者文檔》在這裡可以找到在小米手機上開發、分發應用的相關文檔~
3、
常見問題4、
小米帳号場景化登入5、
技術文檔(二)華為
華為開發者文檔
2、華為部分裝置不列印Log
部分的華為裝置工程模式下log是關閉的,華為部分裝置不列印Log的解決方案:
1.如果是華為手機,進入撥号界面輸入:*#*#2846579#*#*進入頁面設定。
2.如果是華為pad,進入電腦輸入:()()2846579()()= 進入頁面設定。
3、華為手機擷取拍照權限後拍照,傳回值為空
問題起源:
開發中遇到了需要拍照和從圖庫中選擇圖檔展示并上傳的功能,其他手機測試沒問題,華為手機擷取拍照權限後拍照,傳回值為空。
問題分析:
原來是華為在7.0以後的系統中,對于拍照後傳回的圖檔也做了權限處理。是以說,華為7.0在拍照的時候,不僅要拿到拍照
CAMERA
的權限,還要拿到讀寫檔案的權限
WRITE_EXTERNAL_STORAGE
。
解決方法部分代碼如下:
//聲明兩個常量
public static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 0x0001;
public static final int MY_PERMISSIONS_REQUEST_CALL_PHONE2 = 0x0002;
//設定權限
public void setTakePhonePermissions(Context context) {
if (Build.VERSION.SDK_INT >= 23) {
int checkCallPhonePermission = ContextCompat.checkSelfPermission(context,
Manifest.permission.CAMERA);
if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.CAMERA,}
, MY_PERMISSIONS_REQUEST_CALL_PHONE);
} else if (ContextCompat.checkSelfPermission(context,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(context,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST_CALL_PHONE2);
} else {
takePhoto();
}
//小于23的不需要動态權限
} else {
takePhoto();
}
}
4、華為Android7.0手機打開攝像頭拍照閃退問題。
5、華為手機Android8.0 使用代碼安裝APK閃退問題
更新版本APK自動安裝的時候,在安卓6.0、7.0下都OK,唯獨在華為安卓8.0手機閃退。
解決方案: 隻要在Mainfest.xml 中加入請求安裝權限就OK了
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
- 關于閃退的小結:
-
- 在Android 7.0(及以上)手機開啟拍照功能,既要申請
權限,還要申請CAMERA
權限。WRITE_EXTERNAL_STORAGE
- 在Android 7.0(及以上)手機開啟拍照功能,既要申請
-
- Android 7.0(API24)以及以上版本不支援file://這種類型的URI,而是使用content://這種類型的URI。不然會報
這個錯,使用Android 7.0(及以上)手機拍照功能時,一定要注意這個api的變化。android.os.FileUriExposedException
- Android 7.0(API24)以及以上版本不支援file://這種類型的URI,而是使用content://這種類型的URI。不然會報
-
- 使用Android 8.0(及以上)手機更新安裝apk時,在Mainfest.xml 中請求安裝權限
android.permission.REQUEST_INSTALL_PACKAGES
- 使用Android 8.0(及以上)手機更新安裝apk時,在Mainfest.xml 中請求安裝權限
6、華為手機app閃退重新開機清空log日志問題
解決方案:
◆ 方式1(最全面的解決方案):
找到手機設定 ---> 最後的開發人員選項 ---> 在調試子產品,打開USB調試。
還是調試子產品内,找到日志記錄器緩沖區大小,改為1M(也可選擇更大),
然後進入撥号界面輸入:*#*#2846579#*#* ----> 選擇USB端口設定 ----> 選擇Google模式。
◆ 方式2:撥号鍵盤 + 快捷鍵設定(這種方式不是所有log都能顯示)
進入撥号界面輸入:*#*#2846579#*#*
依次選擇:背景設定 ---> LOG設定 ---> AP 日志,重新啟動手機。
◆ 方式3:錯誤出現後,迅速拔掉USB線,這是一個拼手速的方法,成功率不敢保證。
7、關于華為手機App權限更改導緻應用重新開機的坑(暫且我還沒有很好的解決方式)
問題重制:
- 1.當我們在華為手機上打開一個應用,将應用退至背景程序中。
- 2.打開 “設定”去更改該應用的權限(比如将“存儲”權限由授權狀态改為非授權狀态)。
- 3.再将該應用重新切換到前台,會發現應用進行了重新啟動。 (在該app中,啟動的時候,FragmentManager仍然會持有原有的fragment。)
網上有人說出了一種原因和一種 解決方案:當應用的權限發生變化的時候,華為手機發出廣播,導緻應用重新啟動。 解決辦法(比較笨):在Activity的onCreate()方法中,根據FragmentManager擷取到已經存在的fragment,并将它們移除掉。重新再建立一下需要展示的fragment
但是我想知道framework層是如何操作的?不知道有沒有大佬能夠分析一下源碼?
魅族開發者文檔(三)魅族
錘子開發者文檔(四)錘子
oppo開發者文檔(五)oppo
2、關于 開發者選項
:
開發者選項
oppo手機的開發者模式很惡心,開啟“設定”》其他設定》開發者選項》USB調試 待機,然後狀态欄有個黃色的提醒視窗,提示10分鐘後自動關閉開發者選項。
3、關于 驗證碼
驗證碼
裝個應用要驗證碼,打開開發者選項需要驗證碼。。很惡心。。
4、oppo手機的R9系列和A系列的5.1系統存在嚴重的bug,類似以下這種的gc導緻的釋放逾時很多。
vivo開發者文檔(六)vivo
- 關于as項目無法在vivo中安裝的問題:
最近适配vivo手機 用的是vivo x9 發現應用無法在手機上安裝 已經打開了開發者模式還是不行,報以下錯:
【解決方案】
關掉Android Studio的Instant Run功能,然後把開發者模式中的USB安全模式(在USB調試下面)和USB調試一起打開。(其他手機遇到同樣問題,也可以用這個方式解決。)
二、開發中遇到的問題在不同手機上的處理方式
(一)沉浸式狀态欄适配
- 這裡講一下華為手機沉浸式狀态欄和虛拟鍵盤沖突問題怎麼解決:
由于指數限制,詳細代碼請看我的github
https://github.com/AweiLoveAndroid/Solve-StatusBar-VirtualKeyBoard(二)沉浸式狀态欄圖示的适配
- 2.2.1 小米MIUI系統适配
之前做沉浸式狀态欄,由于公司APP底色是白色,是以對MIUI進行特殊處理。在MIUI V6及以上版本,調用MIUI的方法将狀态欄圖示改為黑色。發現部分小米手機,這樣的設定不管用,導緻頭上一片白,狀态欄上的東西基本看不到。
調整過程中發現以下情況:
手機型号 | MIUI版本 | Android版本 | 系統方法是否生效 | MIUI的方法是否生效 |
---|---|---|---|---|
紅米 NOTE 1LTE | MIUI 8 8.2.1穩定版 | 4.4 | 否 | 生效 |
小米5 | MIUI 8 8.5.3穩定版 | 7.0 | ||
MI 3W | MIUI 9 7.9.14開發版 | 6.0.1 |
參考官方文檔:
http://www.miui.com/thread-8946673-1-1.html(三)應用解除安裝然後安裝更新的适配
-
2.3.1 華為适配
華為手機程式解除安裝,安裝更新包,還是提醒更新包與安裝應用簽名不一緻。
- 2.3.2 魅族适配
- 問題1. 測試的簽名和你正式出包的簽名如果不一緻就不能安裝,解除安裝應用也沒用。
- 問題2. 用as安裝過應用,解除安裝後安裝正式的apk就安裝不了,用adb指令解除安裝後就行了。
(四)改變狀态欄字型顔色為黑色的适配
- 2.4.1 小米适配
/**
* 改變小米的狀态欄字型顔色為黑色,要求MIUI6以上
* tested on: MIUI V7 5.0 Redmi-Note3
*/
private void processMIUI(boolean lightStatusBar) throws Exception{
Window window = getWindow();
Class<? extends Window> clazz = window.getClass();
int darkModeFlag;
Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
extraFlagField.invoke(window,lightStatusBar ? darkModeFlag : 0, darkModeFlag);
}
- 2.4.2 魅族适配
/**
* 改變魅族的狀态欄字型為黑色,要求FlyMe4以上
*/
private void processFlyMe(boolean isLightStatusBar) throws Exception{
Window window = getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
Class<?> instance = Class.forName("android.view.MiuiWindowManager$LayoutParams");
int value = instance.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON").getInt(layoutParams);
Field field = instance.getDeclaredField("meizuFlags");
field.setAccessible(true);
int origin = field.getInt(layoutParams);
if(isLightStatusBar){
field.set(layoutParams, origin | value);
}else{
field.set(layoutParams, -value | origin);
}
}
下面來一張示例圖:
(五)螢幕圓角實作和适配
示例圖
- 實作原理:利用WindowManager将我們的圓角加到螢幕的四個角,圓角顔色設定為黑色,形成視覺圓角螢幕。
下面簡單的把一些核心代碼講一下:
- ** 自定義圓角View,這裡以左上角為例:**
// top left
case Gravity.TOP | Gravity.LEFT:
path.moveTo(0.0f, 0.0f);
path.lineTo(0.0f, (float) h);
path.arcTo(new RectF(0.0f, 0.0f,
((float) w) * 2.0f, ((float) h) * 2.0f), 180.0f, 90.0f, true);
path.lineTo((float) w, 0.0f);
path.lineTo(0.0f, 0.0f);
path.close();
break;
- windowmanager在添加view的時候需要設定一個WindowManager.LayoutParams。下面初始化這個Params:
// window manager
manager = (WindowManager) this.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams();
/*
系統提示類型:7.0以前可以直接用TOAST的類型,不用申請權限,直接添加
7.0以後不行了,需要申請SYSTEM_ALERT_WINDOW權限,window type最好
設定為ERROR 或者 PHONE
*/
if (Utilities.isCanUseToastType()) {
params.type = WindowManager.LayoutParams.TYPE_TOAST;
} else {
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
}
params.format = 1;
params.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN // 全屏
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS // 覆寫到status bar
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION // 覆寫到導航欄
// 以下屬性設定加載我們圓角window 不搶焦點,不攔截事件
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.alpha = 1.0f;
params.x = 0;
params.y = 0;
// 設定 大小為全屏
params.width = ViewUtil.getScreenSize(this).x;
params.height = ViewUtil.getScreenSize(this).y;
- 圓角加到螢幕上:
public void addCornerViewByPosition(String position){
boolean enable = true;
switch (position) {
case LEFT_TOP:
enable = leftTopEnable;
params.gravity = Gravity.TOP | Gravity.LEFT;
break;
case RIGHT_TOP:
enable = rightTopEnable;
params.gravity = Gravity.TOP | Gravity.RIGHT;
break;
case LEFT_BOTTOM:
enable = leftBottomEnable;
params.gravity = Gravity.BOTTOM | Gravity.LEFT;
break;
case RIGHT_BOTTOM:
enable = rightBottomEnable;
params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
break;
}
CornerView corner = buildCorner(enable,params.gravity);
if(!corners.containsValue(corner)) {
corners.put(position, corner);
manager.addView(corner, params);
}
}
螢幕圓角實作和适配,詳細的可以點選這裡:
http://mp.weixin.qq.com/s/h5qRvfgVj04f_xExTtrIHg(六)在帶虛拟按鍵的手機上,虛拟按鍵會遮擋全屏圖檔的底部的解決。
在做splash頁面的時候,通過windowBackground設定背景圖檔,在帶虛拟按鍵的手機上,虛拟按鍵會遮擋圖檔的底部,這個問題的解決方式:
參考:
http://blog.csdn.net/c15522627353/article/details/52452490究竟如何适配Android底部虛拟按鍵,可以參考這篇博文:
https://www.jianshu.com/p/b499628e0ae0(七)懸浮窗權限設定了,dialog還是不提示。
(八)在Nexus 手機,原生Android 8.0上,使用掃碼的時候顯示的拍照預覽方向不正,有180度的旋轉并且變形的,解決方案:
private void surfaceIsChanged() {
if (mHolder.getSurface() == null) {
System.out.println("getSurface,nullnull");
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
e.printStackTrace();
}
try {
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
int dataBufferSize = (int) (previewSize.height * previewSize.width
* (ImageFormat.getBitsPerPixel(mCamera.getParameters()
.getPreviewFormat()) / 8.0));
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
// 核心代碼:根據照相的内容進行設定顯示方向。
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
// DO your logic to get front or back camera...or loop through all avaialable.
int camIdx = 0;
Camera.getCameraInfo(camIdx, cameraInfo);
try {
// If using back camera then simply rotate what CameraInfo tells you.
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK){
mCamera.setDisplayOrientation(cameraInfo.orientation);
}else{
// If using front camera note that image might be flipped
// to give users the impresion the are looking at a mirror.
mCamera.setDisplayOrientation( (360 - cameraInfo.orientation) % 360);
}
} catch (Exception e) {
e.printStackTrace();
}
//開始掃描
// Toast.makeText(QRZbarActivity.this, "開始掃描",Toast.LENGTH_SHORT).show();
// 打開閃光燈,這個方法自己去實作,這裡不是重點,就不寫了。
autoOpenLight();
} catch (Exception e) {
Toast.makeText(BaseScanActivity.this, R.string.account_toast_not_open_camera,
Toast.LENGTH_SHORT).show();
// showTip("您未允許" + getResources().getString(R.string.app_name)
// + "通路您的相冊\n請在“安全中心 -授權管理”中更改設定");
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
這個解決方案來自:
https://stackoverflow.com/questions/12017148/android-camera-setdisplayorientation90-fails-in-different-devices#(九)擷取手機裡所有儲存設備盤符,不同廠商手機的路徑可能不一樣。
問題描述:華為手機很變态,存儲路徑跟原生系統的不一樣,是以需要對其做特别處理。
解決方案:需要用到一個被系統隐藏的方法,即StorageManager下的getVolumePaths()方法。具體通過反射可以得到,其中mPath、mRemovable、mEmulated、mState這幾個屬性是我們需要關注的。
具體代碼,可以參看部落格
Android判斷是否存在外置SD卡(擷取手機所有儲存設備的路徑)【好消息】我的微信公衆号正式開通了,關注一下吧!
關注一下我的公衆号吧