博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android ROM适配
阅读量:5214 次
发布时间:2019-06-14

本文共 9480 字,大约阅读时间需要 31 分钟。

Android是开源的,不同的手机厂商都有自己定制的系统,所以这就给开发者带来了ROM适配难题。在一些群里面经常看到有人因为手机适配问题,说这个手机坑,那个手机坑,其实那是没有对ROM定制系统的一些变更了解,导致了盲目的说出这些指责的话。如果你熟悉了,也就会少走很多弯路。下面这篇文章就来讲一下几个主流手机的ROM适配问题。

 
 

一、手机平台相关文档

(一)小米

1、
2、

在这里可以找到在小米手机上开发、分发应用的相关文档~

3、
4、
5、

(二)华为

1、 华为开发者文档
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了


  • ★★ 关于闪退的小结:
    1. 在Android 7.0(及以上)手机开启拍照功能,既要申请CAMERA 权限,还要申请 WRITE_EXTERNAL_STORAGE 权限。
    1. Android 7.0(API24)以及以上版本不支持file://这种类型的URI,而是使用content://这种类型的URI。不然会报android.os.FileUriExposedException这个错,使用Android 7.0(及以上)手机拍照功能时,一定要注意这个api的变化。
    1. 使用Android 8.0(及以上)手机更新安装apk时,在Mainfest.xml 中请求安装权限android.permission.REQUEST_INSTALL_PACKAGES

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

1、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

(二)沉浸式状态栏图标的适配

  • 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 生效

参考官方文档:

(三)应用卸载然后安装更新的适配

  • 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
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 leftcase 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 managermanager = (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); } }

屏幕圆角实现和适配,详细的可以点击这里:

(六)在带虚拟按键的手机上,虚拟按键会遮挡全屏图片的底部的解决。

在做splash页面的时候,通过windowBackground设置背景图片,在带虚拟按键的手机上,虚拟按键会遮挡图片的底部,这个问题的解决方式:

参考:

究竟如何适配Android底部虚拟按键,可以参考这篇博文:

(七)悬浮窗权限设置了,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()); } }

这个解决方案来自:


(九)获取手机里所有存储设备盘符,不同厂商手机的路径可能不一样。

问题描述:华为手机很变态,存储路径跟原生系统的不一样,所以需要对其做特别处理。

解决方案:需要用到一个被系统隐藏的方法,即StorageManager下的getVolumePaths()方法。具体通过反射可以得到,其中mPath、mRemovable、mEmulated、mState这几个属性是我们需要关注的。

作者:AWeiLoveAndroid
链接:https://www.jianshu.com/p/f9c67a4b908e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/chenxibobo/p/9623923.html

你可能感兴趣的文章
jQuery ajax从后台取不到数据
查看>>
GetTypes Unable to load one or more of the requested types
查看>>
Python基础练习
查看>>
工业控制系统之葡萄酒保温发酵控制系统
查看>>
Xcode加入应用图标以及启动界面
查看>>
HDU-5387 Clock
查看>>
目标识别AI资料
查看>>
Zabbix网络自动发现规则和自动添加hosts及link模板
查看>>
页面清空。
查看>>
RDO Stack: Install newton in the dashboard can't create images
查看>>
IE6不支持position:fixed的解决方法
查看>>
springmvc学习(一)helloworld实例
查看>>
Java中static修饰符
查看>>
Object-C,遍历目录
查看>>
小雷:我的核心定位和远大志向(上次更新2013年11月9日)
查看>>
文件统计
查看>>
Thread类
查看>>
HTTP要点概述:一,TCP/IP协议族
查看>>
揭秘应用程序池帐号ApplicationPoolIdentity
查看>>
CURLOPT_FOLLOWLOCATION
查看>>