Android7.0、8.0适配

技术分享

7.0

provider 问题

FileUriExposedException

image

  • 三步曲

首先在manifest中声明provider

1
2
3
4
5
6
7
8
9
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.cmcm.shorts.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>

然后在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<root-path
name="aa"
path="" />
<!--<external-path-->
<!--name="camera_photos"-->
<!--path="shorts_photos" />-->
<!--<external-path-->
<!--name="movies"-->
<!--path="Movies" />-->
</paths>
</resources>
1
2
3
4
5
6
7
fun pathToUri(path: String): Uri {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return FileProvider.getUriForFile(BloodEyeApplication.getInstance(), "com.cmcm.shorts.fileprovider", File(path))
} else {
return Uri.fromFile(File(path))
}
}

移除三项广播

CONNECTIVITY_ACTION
ACTION_NEW_PICTURE
ACTION_NEW_VIDEO

在应用间共享文件

Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。

StickerMode

会自动检测file开头的限制

8.0

后台服务限制

startService不受影响
但是会有一个时间
超过这个时间会自动停止

  • IME
  • 壁纸服务
  • 通知侦听器
  • 语音或文本服务

使用startForeground()方法来启动前台服务
如果没有使用这个来启动的话,将会ANR

使用JobScheduler,来持有设备

image

Service.stopSelf()方法

隐式广播限制

将项目中所有的静态广播注册动态广播

自定义广播也要弄成动态的

不受限制的静态广播https://developer.android.com/guide/components/broadcast-exceptions

静态广播不设置IntentFilter的时候也可以用Intent指定Receiver去接收广播

后台位置限制

主要是针对GP服务 Location Manager

提醒窗口

使用SYSTEM_ALERT_WINDOW的权限时候都会在

  • TYPE_PHONE
  • TYPE_PRIORITY_PHONE
  • TYPE_SYSTEM_ALERT
  • TYPE_SYSTEM_OVERLAY
  • TYPE_SYSTEM_ERROR

都会在TYPE_APPLICATION_OVERLAY类型的下方显示

应当使用TYPE_APPLICATION_OVERLAY

HTTPS请求

HttpURLConnection
必须将http://xxx.com 转化为 http://xxx.com/

集合的处理

AbstractCollection.removeAll() 和 AbstractCollection.retainAll() 始终引发 NullPointerException

在之前当集合为空时不会引发 NullPointerException

1
2
3
4
5
6
7
8
9
10
11
12
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}

1
2
3
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false);
}
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
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
谢谢您的鼓励~