关于Android9.0适配(SDK28)

本文详细介绍了Android 9 Pie的重要更新,包括全新手势操作、Android仪表板、Adaptive Battery智能电池管理等功能。此外,文章还深入探讨了通知管理、隐私保护、刘海屏适配等关键改进。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

##Android 9 Pie重要功能

1、Android 9 Pie 是 Android 的一次重大更新,首先 Pie 带来了一个全新的 Android 仪表板,可以让用户知道其在设备上花费的时间,这与 Apple 的屏幕时间功能类似。应用程序屏幕被重新设计,支持 iPhone X 等带有刘海的显示器设备。Pie 也使用了一种新的基于手势的系统界面,具有类似 iPhone 的滑动功能,可以在操作系统中导航。名为 Shush 的勿扰功能在手机屏幕朝下放置时会使 Android 设备静音,而 Wind Down 功能可让手机界面变为灰色,以减少 Android 用户在特定睡眠时间使用手机的可能性。

2、Android 9 Pie 还带来 AI 驱动的 Adaptive Battery,可通过优先处理用户最有可能使用的应用程序来优化电池电量,利用机器学习技术对系统资源进行有效分配,更专注于用户最常用的应用。如果用户已经针对低耗电模式、应用待机模式以及后台限制对您的应用进行过相关优化,那么它就应该已经能够和动态电量管理特性完美配合。

3、与其它版本的 Android 一样,Android 9 Pie 仅限于部分手机升级使用,每个手机厂商都需要经过定制才能对用户推出相应的版本更新。目前的情况是 Android Pie 已经面向 Pixel 手机推出,接下来几个月内将更新包括 Sony Mobile、Xiaomi、Oppo、Vivo、OnePlus 和 Essential 等厂商的最新设备。此外,适用于 Pixel 设备的系统映像现可供下载,支持手动刷机。

4、请前往 Android开源项目库 资源库中的 Android 9 板块,获取更多 Android 9 的相关资源。

##简要说明Android 9 Pie重要功能

1、手势:Android Pie中加入全新的全面屏操作手势,与iPhone X的操作类似,但屏幕底部依然有导航键,不过只剩返回主菜单和后退。

2、Android Dashboard:这是一种查看APP使用时间的软件,这一点与iOS 12的屏幕使用时间功能类似。

3、Wind Down:在夜间使用手机时能将界面调整为灰色,也就是夜间模式。

4、Adaptive Battery(自适应电池用量):对用户常用的软件进行优化,控制CPU资源占用。

5、Shush勿扰功能:开启该功能后,当手机屏幕朝下,设备可以自动静音。

6、Actions和Slices:这两项功能是为了节省用户操作,比如Actions,检测到手机插入耳机后,会自动跳出音乐相关的App,方便用户选择;而Slices则能在App里节省用户操作,比如搜索打车软件,会自动跳选出叫车回家、目的地等。这类似于Smartisan OS的“一步”、MIUI的“传送门”,主要目的是为了简化用户对手机的操作。

7、界面调整:通知栏、顶部图标等一些功能也有着改变。

##Channel settings, broadcasts, and Do Not Disturb
Android 8.0 引入了通知渠道,允许您为要显示的每种通知类型创建可由用户自定义的渠道。 Android 9 通过下列变更简化通知渠道设置:

屏蔽渠道组:现在,用户可以针对某个应用在通知设置中屏蔽整个渠道组。 您可以使用 isBlocked()函数确定何时屏蔽一个渠道组,从而不会向该组中的渠道发送任何通知。
此外,您的应用可以使用全新的 getNotificationChannelGroup()函数查询当前渠道组设置。

全新的广播 Intent 类型:现在,当通知渠道和渠道组的屏蔽状态发生变更时,Android 系统将发送广播 Intent。 拥有已屏蔽的渠道或渠道组的应用可以侦听这些 Intent 并做出相应的回应。 有关这些 Intent 操作和 extra 的更多信息,请参阅 NotificationManager 参考中更新的常量列表。 有关响应广播 Intent 的信息,请参阅广播。

NotificationManager.Policy 有 3 种新的“请勿打扰”优先级类别:

PRIORITY_CATEGORY_ALARMS优先处理警报。

PRIORITY_CATEGORY_MEDIA优先处理媒体源的声音,如媒体和语音导航。

PRIORITY_CATEGORY_SYSTEM优先处理系统声音。

NotificationManager.Policy 还有 7 种新的“请勿打扰”常量,可以用来抑制视觉中断:

SUPPRESSED_EFFECT_FULL_SCREEN_INTENT防止通知启动全屏 Activity。

SUPPRESSED_EFFECT_LIGHTS屏蔽通知灯。

SUPPRESSED_EFFECT_PEEK防止通知短暂进入视图(“滑出”)。

SUPPRESSED_EFFECT_STATUS_BAR防止通知显示在支持状态栏的设备的状态栏中。

SUPPRESSED_EFFECT_BADGE在支持标志的设备上屏蔽标志。 如需了解详细信息,请参阅修改通知标志。

SUPPRESSED_EFFECT_AMBIENT在支持微光显示的设备上屏蔽通知。

SUPPRESSED_EFFECT_NOTIFICATION_LIST防止通知显示在支持列表视图(如通知栏或锁屏)的设备的列表视图中。

##隐私权变更-现在收的原来越紧,安卓也越来越规范
为了增强用户隐私,Android 9 引入了若干行为变更,如限制后台应用访问设备传感器、限制通过 Wi-Fi 扫描检索到的信息,以及与通话、手机状态和 Wi-Fi 扫描相关的新权限规则和权限组。

无论采用哪一种目标 SDK 版本,这些变更都会影响运行于 Android 9 上的所有应用。

##后台对传感器的访问受限
Android 9 限制后台应用访问用户输入和传感器数据的能力。 如果您的应用在运行 Android 9 设备的后台运行,系统将对您的应用采取以下限制:

您的应用不能访问麦克风或摄像头。
使用连续报告模式的传感器(例如加速度计和陀螺仪)不会接收事件。
使用变化或一次性报告模式的传感器不会接收事件。
如果您的应用需要在运行 Android 9 的设备上检测传感器事件,请使用前台服务。

##限制访问通话记录
Android 9 引入 CALL_LOG 权限组并将 READ_CALL_LOG、WRITE_CALL_LOG和 PROCESS_OUTGOING_CALLS权限移入该组。 在之前的 Android 版本中,这些权限位于 PHONE 权限组。

如果应用需要访问通话记录或者需要处理去电,则您必须向 CALL_LOG权限组明确请求这些权限。 否则会发生 SecurityException。

##限制访问电话号码
在未首先获得 READ_CALL_LOG 权限的情况下,除了应用的用例需要的其他权限之外,运行于 Android 9 上的应用无法读取电话号码或手机状态。

与来电和去电关联的电话号码可在手机状态广播(比如来电和去电的手机状态广播)中看到,并可通过 PhoneStateListener 类访问。 但是,如果没有 READ_CALL_LOG 权限,则 PHONE_STATE_CHANGED 广播和 PhoneStateListener 提供的电话号码字段为空。

要从手机状态中读取电话号码,请根据您的用例更新应用以请求必要的权限:

要通过 PHONE_STATE Intent 操作读取电话号码,同时需要 READ_CALL_LOG 权限和 READ_PHONE_STATE 权限。
要从 onCallStateChanged() 中读取电话号码,只需要 READ_CALL_LOG 权限。 不需要 READ_PHONE_STATE 权限。

##电话信息现在依赖设备位置设置
如果用户在运行 Android 9 的设备上停用设备定位,则以下函数不提供结果:

TelephonyManager.getAllCellInfo()
TelephonyManager.listen()
TelephonyManager.getCellLocation()
TelephonyManager.getNeighboringCellInfo()

##Build.SERIAL 始终设置为 “UNKNOWN” 以保护用户的隐私。
如果您的应用需要访问设备的硬件序列号,您应改为请求 READ_PHONE_STATE权限,然后调用 getSerial()。

##多进程 webview 信息访问限制
在 Android P 中为了提升系统的安全性,用户无法在多进程的 webview 中共享数据目录,该目录下存储的是一些 cookies、Http 缓存和其他一些永久、临时的缓存。当下不少应用会把 webview 放在另一个进程中打开以避免内存泄漏,但是他们 cookies 的设置往往还是在主进程中,所以开发者需要仔细排查自己的应用是否有这么使用,webview 相关运行是否正常等。

##对使用非 SDK 接口的限制
为帮助确保应用稳定性和兼容性,此平台对某些非 SDK 函数和字段的使用进行了限制;无论您是直接访问这些函数和字段,还是通过反射或 JNI 访问,这些限制均适用。 在 Android 9 中,您的应用可以继续访问这些受限的接口;该平台通过 toast和日志条目提醒您注意这些接口。 如果您的应用显示这样的 toast,则必须寻求受限接口之外的其他实现策略。 如果您认为没有可行的替代策略,您可以提交错误以请求重新考虑此限制。

##对于非SDK 接口
浅灰名单:仍可以访问的非 SDK 函数/字段。
深灰名单:
对于目标 SDK 低于 API 级别 28 的应用,允许使用深灰名单接口。
对于目标 SDK 为 API 28 或更高级别的应用:行为与黑名单相同。
黑名单:受限,无论目标 SDK 如何
平台将提示接口并不存在。
例如,无论应用何时尝试使用接口,平台都会引发 NoSuchMethodError/NoSuchFieldException,即使应用想要了解某个特殊类别的字段/函数名单,平台也不会包含接口。

##检测是否使用了非SDK接口
工具veridex
下载工具,阅读README.txt
打包一个应用 APK,建议使用 release 包,排除一些未使用到的单元测试类或者其他因素的影响,取消混淆,将 APK 放到工具目录下;
执行命令 ./appcompat.sh --dex-file=test.apk,在终端上会输出三个名单每个 API 的详细调用处

##P版本三方适配挖孔屏方案
注意,以下接口都是要Build.VERSION.SDK_INT >= 28才能调用到。
1、 新增挖孔屏挖孔尺寸和位置接口

class WindowInsets {
DisplayCutout getDisplayCutout();
}
class DisplayCutout {
int getSafeInsetLeft();
int getSafeInsetTop();
int getSafeInsetRight();
int getSafeInsetBottom();
Region getBounds();
}

2、新窗口布局模式,允许应用程序请求是否在挖孔区域布局:

class WindowManager.LayoutParams {
int layoutInDisplayCutoutMode;
final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
    }

layoutInDisplayCutoutMode值说明:

a)LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默认情况下,全屏窗口不会使用到挖孔区域,非全屏窗口可正常使用挖孔区域。(
模式在全屏显示下跟LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER一样。
当刘海区域完全在系统的状态栏时,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT的显示效果与LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES一致。)

b)LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS(LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES):
窗口声明使用挖孔区域(模式会让屏幕到延申刘海区域中)

c)LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:窗口声明不使用挖孔区域(模式不会让屏幕到延申刘海区域中,会留出一片黑色区域。)

参数使用示例:

public class NotchActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去掉标题
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //开局就一张背景图
        setContentView(R.layout.notch);

        //全屏显示
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

        WindowManager.LayoutParams lp = getWindow().getAttributes();
        
        //1
        lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
        //2
//        lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        //3
//        lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
        getWindow().setAttributes(lp);
    }
}
  1. Android P提供提供的刘海屏适配方案
    a.对于有状态栏的页面,不会受到刘海屏特性的影响,因为刘海屏包含在状态栏中了;
    b.全屏显示的页面,系统刘海屏方案会对应用界面做下移处理,避开刘海区显示,这时会看到刘海区域变成一条黑边,完全看不到刘海了;
    c.已经适配Android P应用的全屏页面可以通过谷歌提供的适配方案使用刘海区,真正做到全屏显示。

目前Android支持了三类凹口屏幕类型:边角显示屏凹口(斜刘海)、双显示屏凹口(刘海+胡子)、长型显示屏凹口(刘海
Google提供刘海规格<=系统状态栏

双显示屏凹口接口测试代码:

public class NotchActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //开局就一张背景图
        setContentView(R.layout.notch);

        getNotchParams();
    }

    @TargetApi(28)
    public void getNotchParams() {
        final View decorView = getWindow().getDecorView();

        decorView.post(new Runnable() {
            @Override
            public void run() {
                WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
                if (rootWindowInsets == null) {
                    Log.e("TAG", "rootWindowInsets为空了");
                    return;
                }
                DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
                Log.e("TAG", "安全区域距离屏幕左边的距离 SafeInsetLeft:" + displayCutout.getSafeInsetLeft());
                Log.e("TAG", "安全区域距离屏幕右部的距离 SafeInsetRight:" + displayCutout.getSafeInsetRight());
                Log.e("TAG", "安全区域距离屏幕顶部的距离 SafeInsetTop:" + displayCutout.getSafeInsetTop());
                Log.e("TAG", "安全区域距离屏幕底部的距离 SafeInsetBottom:" + displayCutout.getSafeInsetBottom());
                
                List<Rect> rects = displayCutout.getBoundingRects();
                if (rects == null || rects.size() == 0) {
                    Log.e("TAG", "不是刘海屏");
                } else {
                    Log.e("TAG", "刘海屏数量:" + rects.size());
                    for (Rect rect : rects) {
                        Log.e("TAG", "刘海屏区域:" + rect);
                    }
                }
            }
        });
    }
}

输出结果:

E/TAG: 安全区域距离屏幕左边的距离 SafeInsetLeft:0
E/TAG: 安全区域距离屏幕右部的距离 SafeInsetRight:0
E/TAG: 安全区域距离屏幕顶部的距离 SafeInsetTop:112
E/TAG: 安全区域距离屏幕底部的距离 SafeInsetBottom:112
E/TAG: 刘海屏数量:2
E/TAG: 刘海屏区域:Rect(468, 0 - 972, 112)
E/TAG: 刘海屏区域:Rect(468, 2448 - 972, 2560)

##上面是Android P才有的解决方案,在P之前呢,上面的代码通通都没用。然而我们伟大的国产厂商在Android P之前(基本都是Android O)就用上了高档大气上档次的刘海屏,所以,这也造就了各大厂商在Android P之前的解决方案百花齐放。下面,我们来看下主流厂商:华为、vivo、OPPO、小米等所提供的方案。

##华为

a. 使用刘海区显示
使用新增的meta-data属性android.notch_support。
在应用的AndroidManifest.xml中增加meta-data属性,此属性不仅可以针对Application生效,也可以对Activity配置生效。
如下所示:

<meta-data android:name="android.notch_support" android:value="true"/>

对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理。
对Activity生效,意味着可以针对单个页面进行刘海屏适配,设置了该属性的Activity系统将不会做特殊处理。

实际上还有一种代码实现的方式,不过代码比较多,这里就不贴了,有兴趣的话可以在文末的链接中点进去看看。
b. 是否有刘海屏
通过以下代码即可知道华为手机上是否有刘海屏了,true为有刘海,false则没有。

    public static boolean hasNotchAtHuawei(Context context) {
        boolean ret = false;
        try {
            ClassLoader classLoader = context.getClassLoader();
            Class HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
            Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");
            ret = (boolean) get.invoke(HwNotchSizeUtil);
        } catch (ClassNotFoundException e) {
            Log.e("Notch", "hasNotchAtHuawei ClassNotFoundException");
        } catch (NoSuchMethodException e) {
            Log.e("Notch", "hasNotchAtHuawei NoSuchMethodException");
        } catch (Exception e) {
            Log.e("Notch", "hasNotchAtHuawei Exception");
        } finally {
            return ret;
        }
    }

c. 刘海尺寸
华为提供了接口获取刘海的尺寸,如下:

    //获取刘海尺寸:width、height
    //int[0]值为刘海宽度 int[1]值为刘海高度
    public static int[] getNotchSizeAtHuawei(Context context) {
        int[] ret = new int[]{0, 0};
        try {
            ClassLoader cl = context.getClassLoader();
            Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
            Method get = HwNotchSizeUtil.getMethod("getNotchSize");
            ret = (int[]) get.invoke(HwNotchSizeUtil);
        } catch (ClassNotFoundException e) {
            Log.e("Notch", "getNotchSizeAtHuawei ClassNotFoundException");
        } catch (NoSuchMethodException e) {
            Log.e("Notch", "getNotchSizeAtHuawei NoSuchMethodException");
        } catch (Exception e) {
            Log.e("Notch", "getNotchSizeAtHuawei Exception");
        } finally {
            return ret;
        }
    }

vivo

vivo在设置–显示与亮度–第三方应用显示比例中可以切换是否全屏显示还是安全区域显示。
a. 是否有刘海屏

    public static final int VIVO_NOTCH = 0x00000020;//是否有刘海
    public static final int VIVO_FILLET = 0x00000008;//是否有圆角

    public static boolean hasNotchAtVivo(Context context) {
        boolean ret = false;
        try {
            ClassLoader classLoader = context.getClassLoader();
            Class FtFeature = classLoader.loadClass("android.util.FtFeature");
            Method method = FtFeature.getMethod("isFeatureSupport", int.class);
            ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH);
        } catch (ClassNotFoundException e) {
            Log.e("Notch", "hasNotchAtVivo ClassNotFoundException");
        } catch (NoSuchMethodException e) {
            Log.e("Notch", "hasNotchAtVivo NoSuchMethodException");
        } catch (Exception e) {
            Log.e("Notch", "hasNotchAtVivo Exception");
        } finally {
            return ret;
        }
    }

b. 刘海尺寸
vivo不提供接口获取刘海尺寸,目前vivo的刘海宽为100dp,高为27dp。

##oppo
OPPO目前在设置 – 显示 – 应用全屏显示 – 凹形区域显示控制,里面有关闭凹形区域开关。
a. 是否有刘海屏

    public static boolean hasNotchAtOPPO(Context context) {
        return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
    }

b. 刘海尺寸
OPPO不提供接口获取刘海尺寸,目前其有刘海屏的机型尺寸规格都是统一的。不排除以后机型会有变化。
其显示屏宽度为1080px,高度为2280px。刘海区域则都是宽度为324px, 高度为80px。

##小米
a.是否有刘海屏
系统增加了 property ro.miui.notch,值为1时则是 Notch 屏手机。
手头上没有小米8的手机,暂时没法验证,这里就不贴代码了,免得误导大家。后面测试过再放出来。
b. 刘海尺寸
小米的状态栏高度会略高于刘海屏的高度,因此可以通过获取状态栏的高度来间接避开刘海屏,获取状态栏的高度代码如下:

    public static int getStatusBarHeight(Context context) {
        int statusBarHeight = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
        }
        return statusBarHeight;
    }

其他手机也可以通过这个方法来间接避开刘海屏,但是有可能有些手机的刘海屏高度会高于状态栏的高度,所以这个方法获取到的结果并不一定安全。

### 回答1: 这是一个编程类的问题,是在讨论特殊字符的转义问题。在编程中,有些字符是有特殊含义的,如果需要使用它们本身的字符,就需要进行转义。例如,"<"符号在HTML中表示标签的开始,如果需要在HTML中显示"<"符号本身,就需要将其转义为"<"。在其他编程语言中也有类似的转义规则。因此,如果你遇到了类似的问题,可以使用相应的转义字符来解决。 ### 回答2: “特殊字符必须进行转义”指的是在编程过程中,特定字符需要通过转义字符进行表示,以防止它们被错误地解释或执行。在给定的例子中,方括号“[”是一种特殊字符,具有特定的含义和功能。为了表示方括号本身,而不是其特定功能,我们需要对其进行转义。 转义字符是一个用来表明紧随其后的字符应该被当作字面意义而不是特殊含义来解释的字符。在许多编程语言中,通常使用的转义字符是反斜杠(\)。 因此,当我们需要表示方括号本身时,我们可以使用“\[”来进行转义。这样,编程语言会正确地将其解释为一个字面意义的方括号,而不是开始定义一个特殊功能或语法。 这种转义机制适用于许多编程语言和场景,以确保特殊字符的正确处理和解释。当我们需要在代码中使用具有特殊含义的特殊字符时,我们应该遵循转义规则,以确保我们的代码能够正确编译和执行。 总之,对于特殊字符,如方括号“[”,我们必须进行转义,以便正确地表达它们的字面含义,而不是其特定功能。这种转义机制是编程语言中的一项重要规则,以确保代码的正确性和可执行性。 ### 回答3: 特殊字符必须进行转义:<。 在编程中,特殊字符具有特殊的含义,如果我们想要表示特殊字符本身而不是其具有的含义,我们就需要对其进行转义。这意味着我们在特殊字符之前添加一个转义字符,以告诉程序将其当作普通字符对待。 在方括号内的字符“[”是一种特殊字符,常用于正则表达式或者其他编程语言中表示一组字符的范围。如果我们想要表达方括号自身的字符而不是范围的含义,就需要对其进行转义。 转义字符通常是反斜杠“\”,因此,在表示方括号字符“[”本身时,我们需要在其之前添加反斜杠进行转义,即“\[”。这样一来,编程语言就会将其解释为一个普通字符,而不是表示范围的特殊字符。 所以,如果我们想要在编程中使用方括号字符“[”而不是范围的含义,我们需要对其进行转义,写作“\[”,以确保它被正确识别为特殊字符本身。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值