Android targetSdkVersion28 适配

本文详细介绍了在将Android应用的targetSdkVersion升级到28时遇到的各种问题及解决方案,包括腾讯云语音识别错误、7.0以上设备的抓包问题、MTA崩溃、全屏活动请求方向限制、Android 9.0键盘不自动弹出、okhttp3上传文件名中文报错等。并提供了解决这些问题的具体步骤和代码示例。

平时csdn看资料的时候,都是游客模式,前两天要看个评论,不得不登一下,发现之前写的一个心得,有好几个朋友评论了。虽然是很小的问题,但能帮到别人,心里就很满足2333

正好近期把targetSdkVersion 升到了28,适配过程中踩到了各种坑,趁此机会写一篇个人总结与诸位一起分享。

 


1. 腾讯云-语音识别 code=-301 & 7.0无法抓包 & MTA崩溃

这几个其实本质是一个问题。详情参考官方文档网络安全配置。可以看到关键的一点是“从 Android 9(API 级别 28)开始,系统默认情况下已停用明文支持。”,即 cleartextTrafficPermitted="false"。

由此而知,腾讯云语音识别一直报错code=-301, message=server connect failed,其实是因为加了这个设置ClientConfiguration.setServerProtocolHttps(false); 导致接口都是http,自然无法使用了,去掉该配置就可以了,因为默认是用的https。因为demo中是这么设置的,所以之前同事就copy过来了,我升级target sdk后发现不能用也没多想就给腾讯云提了工单,那边一开始也接锅表示会修复的,结果升新版后还是不行,一来一回花了不少时间,以后用sdk还是得多看文档,但这语音识别的文档也太简陋了,连版本日志都没...

MTA崩溃也是同样的问题,一上报就崩了,CLEARTEXT communication to xxx not permitted by network security policy,看了下MTA已经是新版本了,再加上其他sdk中可能还有http的,所以索性修改networkSecurityConfig,允许明文。

7.0以上不能抓包则同样需要在networkSecurityConfig中配置信任的证书,我选择了debug调试时信任所有证书。

networkSecurityConfig配置如下:

在res/xml中新建 network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

在AndroidManifest里的<application>标签中,添加代码:

 android:networkSecurityConfig="@xml/network_security_config"

以上问题就一并解决啦,各位可以根据自己的需求来进行配置。 

 2. java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

这个问题发生在android 8.0 的机子上,app都没进就崩了,我的心也跟着一起崩了啊23333

这个问题中有较多讨论,问题来源是源码中这一段

  //Need to pay attention mActivityInfo.isFixedOrientation() and ActivityInfo.isTranslucentOrFloating(ta)
    if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) {
        final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
        final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
        ta.recycle();

        //Exception occurred
        if (isTranslucentOrFloating) {
            throw new IllegalStateException(
                    "Only fullscreen opaque activities can request orientation");
        }
    }

解决方法:项目中很多activity用的主题都是透明的

 <style name="CommonTheme" parent="AppTheme">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@color/white</item>
 </style>

将用到透明主题的acitivity在manifest中的

android:screenOrientation="portrait"

这行都去掉就可以了,经测试,如果透明activity后面有不透明的固定方向的activity,透明的是不会转动的,所以主页activity不要用透明主题就好。

3. android 9.0 pie windowSoftInputMode不管怎么设置 进入activity键盘不会自动弹出

这个问题也蛮妖的,就是在9.0上弹不出键盘,android:windowSoftInputMode="adjustResize|stateVisible"不起作用。

依旧是在stackoverflow找到了解决办法,在9.0上,没有自动获取焦点,那就只能手动代码中requestFocus,或者xml加上</requestFocus>,例如:

 <EditText
        android:id="@+id/et_envelope_post_script"
        android:layout_width="match_parent"
        android:layout_height="98dp"
        android:background="@color/white"
        android:gravity="left|top"
        android:padding="16dp"
        android:textSize="16sp">
        <requestFocus />
 </EditText>

4. okhttp3 通过multipart上传文件名为中文报错Caused by: java.lang.IllegalArgumentException: Unexpected char

顺便升级了一下okhttp3,结果bugly中几个上报都是通过multipart/form-data上传书封时的,是因为用到的本地图片文件名含中文,报错如下:

 Caused by: java.lang.IllegalArgumentException: Unexpected char 0x6211 at 39 in Content-Disposition value: form-data; name="bookCover"; filename="我推荐吗.jpg"

代码使用:

MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart(param, targetFile.getName(), RequestBody.create(MEDIA_TYPE, targetFile));

源码部分:

 /** Add a form data part to the body. */
    public Builder addFormDataPart(String name, @Nullable String filename, RequestBody body) {
      return addPart(Part.createFormData(name, filename, body));
    }
   public static Part createFormData(String name, @Nullable String filename, RequestBody body) {
      if (name == null) {
        throw new NullPointerException("name == null");
      }
      StringBuilder disposition = new StringBuilder("form-data; name=");
      appendQuotedString(disposition, name);

      if (filename != null) {
        disposition.append("; filename=");
        appendQuotedString(disposition, filename);
      }

      return create(Headers.of("Content-Disposition", disposition.toString()), body);
    }
  public static Headers of(String... namesAndValues) {
    if (namesAndValues == null) throw new NullPointerException("namesAndValues == null");
    if (namesAndValues.length % 2 != 0) {
      throw new IllegalArgumentException("Expected alternating header names and values");
    }

    // Make a defensive copy and clean it up.
    namesAndValues = namesAndValues.clone();
    for (int i = 0; i < namesAndValues.length; i++) {
      if (namesAndValues[i] == null) throw new IllegalArgumentException("Headers cannot be null");
      namesAndValues[i] = namesAndValues[i].trim();
    }

    // Check for malformed headers.
    for (int i = 0; i < namesAndValues.length; i += 2) {
      String name = namesAndValues[i];
      String value = namesAndValues[i + 1];
      checkName(name);
      checkValue(value, name);
    }

    return new Headers(namesAndValues);
  }
 static void checkName(String name) {
    if (name == null) throw new NullPointerException("name == null");
    if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
    for (int i = 0, length = name.length(); i < length; i++) {
      char c = name.charAt(i);
      if (c <= '\u0020' || c >= '\u007f') {
        throw new IllegalArgumentException(Util.format(
            "Unexpected char %#04x at %d in header name: %s", (int) c, i, name));
      }
    }
  }

  static void checkValue(String value, String name) {
    if (value == null) throw new NullPointerException("value for name " + name + " == null");
    for (int i = 0, length = value.length(); i < length; i++) {
      char c = value.charAt(i);
      if ((c <= '\u001f' && c != '\t') || c >= '\u007f') {
        throw new IllegalArgumentException(Util.format(
            "Unexpected char %#04x at %d in %s value: %s", (int) c, i, name, value));
      }
    }
  }

由此可知,文件名中含中文时,checkValue会抛出异常,解决办法就是上传前重命名文件名。同时注意,header头中也是不能带中文的。

5. 其他

有些项目中已经适配,或者没涉及的点,也一起提一下。

一个是7.0开始App对外无法暴露file://类型的URI了,在类似调用系统相机或是通过代码安装apk等需要暴露uri给其他app的情况下,会抛出FileUriExposedException异常,需要使用FileProvider来定义对外暴露的文件夹路径,再调用:

FileProvider.getUriForFile(this.mContext.getApplicationContext(), authority, file);

来获取content://类型的Uri。其中authority指在manifest的<provider>中配置的provider名,例如:

<provider
            android:name="me.iwf.photopicker.utils.PhotoFileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/__picker_provider_paths"/>
</provider>

其中__picker_provider_paths是在res/xml中定义的对外暴露的文件夹路径,如下:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_storage_root" path="."/>
    <files-path name="files" path="."/>
</paths>

关于paths配置比较详细的讲解可参考Android 7.0适配-应用之间共享文件(FileProvider)

二是android 8.0以上未知来源的应用是不可以通过代码来执行安装的,会提示“解析包时出现问题”。需要申明权限

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

并在代码中动态申请。 这么一想之前一些app更新时是碰到过这个问题,当时只当是apk有问题,现在看来是没有进行适配。我们的项目因为是直接跳到网页进行下载的,所以没有碰到这个问题。

目前为止碰到的问题大概就这些,如果之后还有的话,会在此更新的,也希望各位能留言自己在升级sdk中碰到的坑,我也会一并整理进来的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值