Android9适配

转载Android 笔记:sdk升级为28遇到的问题_牛客博客

Android 9.0 以上修改

1.当在 Android 9.0 加载网络请求数据时,有时会抛出如下异常:

1

Cause (1 of 1): class java.io.IOException: Cleartext HTTP traffic to xxxx.xxxx.xxxx not permitted

这是因为Android 9.0版本系统默认支持一个网络访问协议:Https协议的网络,所以不支持网络访问:Http协议的网络面对这样的问题,解决办法:

方法1

直接通过在AnroidManifest.xml中的<application>标签内添加</application>

1

<application android:usesCleartextTraffic="true"/>

原来默认为 true,但在 Android 9.0 中默认值改为了 false,因此将配置手动设为 true 即可解决明文传输被限制的问题

方法2

在res文件夹下创建一个xml文件夹,然后创建一个network_security_config.xml文件,文件内容如下:

1

2

3

4

<?xml version="1.0" encoding="utf-8"?>

<network-security-config>

    <base-config cleartextTrafficPermitted="true" />

</network-security-config>

然后在<application>标签内添加以下代码:</application>

1

android:networkSecurityConfig="@xml/network_security_config"

2. Android 9.0 弃用 Apache HTTP Client

由于官方在 Android 9.0 中移除了所有 Apache HTTP Client 相关的类,因此我们的应用或是一些第三方库如果使用了这些类,就会抛出找不到类的异常:

1

java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/conn/scheme/SchemeRegistry;

若需要继续使用 Apache HTTP Client ,可通过以下方法进行适配:

在 AndroidManifest.xml 中的<application>标签内添加以下内容:</application>

1

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

3. Android 9.0 Build.SERIAL 被弃用

Android 9.0 之前,开发者可以使用 Build.SERIAL 获取设备的序列号。现在这个方法被弃用了,Build.SERIAL 将始终设置为 "UNKNOWN" 以保护用户的隐私。适配的方法为先请求READ_PHONE_STATE权限,然后调用Build.getSerial()方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/**

 * 获取android设备唯一标识码

 */

fun getClientId(context: Context): String {

    val clientId: String

    val androidID = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)

    clientId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

        androidID + Build.getSerial()

    else {

        androidID + Build.SERIAL

    }

    Log.e("""ClientId: $clientId")

    return clientId

}

4. Android 9.0 使用Intent卸载应用无反应

使用下面代码在安卓9.0并不管用

1

2

3

4

5

6

//卸载APP

fun uninstallApp(activity: Activity, packageName: String) {

    val intent = Intent(Intent.ACTION_DELETE)

    intent.data = Uri.parse("package:$packageName")

    activity.startActivity(intent)

}

原因是没有添加权限,解决办法:
在AnroidManifest.xml中加入

1

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

5. Android 9.0 取消 BottomNavigationView 动画效果的实现

在 API 28 之前,取消 BottomNavigationView 动画效果的实现方法为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//使用

BottomNavHelper.disableShiftMode(bottomNavigationView);

/**

 * 处理BottomNavigationView控件底部按钮超过3个文字不显示

 */

public class BottomNavHelper {

    @SuppressLint("RestrictedApi")

    public static void disableShiftMode(BottomNavigationView bottomNavView) {

        BottomNavigationMenuView menuView = (BottomNavigationMenuView) bottomNavView.getChildAt(0);

        try {

            Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");

            shiftingMode.setAccessible(true);

            shiftingMode.setBoolean(menuView, false);

            shiftingMode.setAccessible(false);

            for (int i = 0; i < menuView.getChildCount(); i++) {

                BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);

                itemView.setShiftingMode(false);

                itemView.setChecked(itemView.getItemData().isChecked());

            }

        catch (NoSuchFieldException | IllegalAccessException e) {

            e.printStackTrace();

        }

    }

}

然而升级 API 28 后,BottomNavigationView 的 setShiftingMode(boolean) 不能用了。

解决方法:直接设置labelVisibilityMode属性即可。

1

2

<android.support.design.widget.BottomNavigationView

    app:labelVisibilityMode="labeled" />

labelVisibilityMode 用于设置图标下面的文字显示,该属性对应的值为:

  • auto : 当 item 小于等于3时,显示文字,item 大于3个默认不显示,选中显示文字
  • labeled : 始终显示文字
  • selected : 选中时显示
  • unlabeled : 选中时显示

该属性对应的方法是setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED);

Android 8.0 以上修改

1. Android 8.0 无法通过 "application/vnd.android.package-archive" 安装应用

原因:targetsdkversion 大于 25 必须声明 REQUEST_INSTALL_PACKAGES 权限

解决办法:在AnroidManifest.xml中加入

1

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

2. Android 8.0 的 Notification 不显示问题

原因:NotificationChannel 是 Android 8.0 新增的特性,如果 targetSdkVersion >= 26,没有设置 channel 通知渠道的话,就会导致通知无法展示。

Android O 引入了通知渠道(Notification Channels),以提供统一的系统来帮助用户管理通知,如果是针对 Android O 为目标平台时,必须实现一个或者多个通知渠道,以向用户显示通知。比如聊天软件,为每个聊天组设置一个通知渠道,指定特定声音、灯光等配置。

示例代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

/**

 * Notification通知设置

 */

fun pushMsg(context: Context) {

    val channelId = "channel_id"

    val channelName = "channel_name"

    val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {

        val channel = NotificationChannel(

            channelId, channelName, NotificationManager.IMPORTANCE_HIGH)

            manager.createNotificationChannel(channel)

    }

    val builder = NotificationCompat.Builder(context, channelId)

    builder.setContentTitle("新消息来了"//标题

        .setContentText("周末到了,不用上班了"//文本内容

        .setSmallIcon(R.mipmap.ic_launcher) //小图标

        .setAutoCancel(true//设置点击信息后自动清除通知

    manager.notify(1, builder.build())

}

3. Android 8.0 启动后台 service 问题

Android P 上应用在后台启动 service 时报了个异常:

1

java.lang.IllegalStateException, Not allowed to start service Intent

原因:Android 8.0+ 对后台服务进行了限制,如果依然采用之前startService()方式会导致问题。

Android 8.0 对特定函数做出了以下变更:

  • 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
  • 新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。

解决办法:原来的 startService() 需要根据sdk版本进行兼容

1

2

3

4

5

6

Intent intent = new Intent(context, SampleService.class);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

    context.startForegroundService(intent);

else {

    context.startService(intent);

}

之后在被启动的 Service 创建服务后的五秒内需要调用 startForeground(1, notification) ,如果不调用或调用时间超过5秒则会抛出一个 ANR 错误。

所以需要在 service 的 onCreat() 方法中按如下代码设置:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

public class SampleService extends Service {

    private static final String CHANNEL_ID = "channel_id";

    private static final String CHANNEL_NAME = "channel_name";

    @Override

    public void onCreate() {

        super.onCreate();

        //适配 8.0 service

        NotificationManager notificationManager = (NotificationManager) App.getInstance()

                .getSystemService(Context.NOTIFICATION_SERVICE);

        NotificationChannel mChannel;

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {

            mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);

            if (notificationManager != null) {

                notificationManager.createNotificationChannel(mChannel);

            }

            Notification notification = new Notification

                    .Builder(getApplicationContext(), CHANNEL_ID)

                    .build();

            startForeground(1, notification);

        }

    }

    @Nullable

    @Override

    public IBinder onBind(Intent intent) {

        return null;

    }

}

除此之外,Android 9.0 要求创建一个前台服务需要请求 FOREGROUND_SERVICE 权限,否则系统会引发 SecurityException。

解决方法:AndroidManifest.xml中添加FOREGROUND_SERVICE权限:

1

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

4. Android 8.0 静态注册广播 行为变更

Android 8.0 在 AndroidManifest 中注册的 receiver 不能收到广播

原因:Android 8.0 引入了新的广播接收器限制,加强对匿名 receiver 的控制,以至于在 manifest 中注册的隐式 receiver 都失效了。

不过 8.0 以后,静态注册的广播接收器还是可以接收到广播的,只要广播是通过显示方式发送的。

当广播接收器使用静态注册方式时,除了一些例外,这个接收器接收不到隐式广播。注意这个“隐式”是重点

先定义一个简单广播:

1

2

3

4

5

6

public class MyReceiver extends BroadcastReceiver {

    @Override

    public void onReceive(Context context, Intent intent) {

        Toast.makeText(context,context.toString(),Toast.LENGTH_SHORT).show();

    }

}

然后注册到AndroidManifest.xml中

1

2

3

4

5

<receiver android:name=".broadcast.MyReceiver">

    <intent-filter>

        <action android:name="com.demo.recriver"/>

    </intent-filter>

</receiver>

最后,在Activity中发送一个广播,intent通过设置Action为com.demo.recriver的形式发送隐式广播。

1

2

Intent intent = Intent intent = new Intent("com.demo.recriver");//隐式intent,发送隐式广播

sendBroadcast(intent);

运行后会发现在8.0以下的手机上,会有Toast显示,8.0以上的手机不会弹出,说明没有接收到广播。

原因在于这个广播 是“隐式” 发送的,8.0中,静态注册的广播接收器无法接受 隐式广播。

有两种解决方法:

  1. 在Activity或其他组件中动态注册广播

  2. 发送显式广播

发送显式广播写法:

复制代码

1

2

Intent intent = new Intent(MainActivity.this,MyReceiver.class);//显示指定组件名

sendBroadcast(intent);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值