1、权限的动态申请
这个是targetSdkVersion为23时,对于一些比较危险的权限,需要动态申请,网上很多资料,库也很多。
2、随着Android版本越来越高,Android对隐私的保护力度也越来越大。
比如:Android6.0引入的动态权限控制(Runtime Permissions),
Android7.0又引入“私有目录被限制访问”,“StrictMode API 政策”。
这些更改在为用户带来更加安全的操作系统的同时也为开发者带来了一些新的任务。如何让你的APP能够适应这些改变而不是crash,是摆在每一位Android开发者身上的责任。
“私有目录被限制访问“ 是指在Android7.0中为了提高私有文件的安全性,面向 Android N 或更高版本的应用私有目录将被限制访问。这点类似iOS的沙盒机制。
" StrictMode API 政策" 是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,应用失败,并出现 FileUriExposedException 异常。
接下来就用FileProvider来解决这一问题
1)在AndroidManifest.xml清单文件中注册provider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.dh.test.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
2)在项目的res文件夹下新建xml文件夹,然后新建filepaths.xml,名字要与清单文件中的一致
android:resource="@xml/filepaths"
3)编写filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--
<files-path name="name" path="path" /> 对应Context.getFilesDir() + “/path/”,即/data/data/<package-name>/files/text/。
-->
<!--<files-path-->
<!--name="my_files"-->
<!--path="text/" />-->
<!--
<cache-path name="name" path="path" /> 对应Context.getCacheDir() + “/path/”,即/data/data/<package-name>/cache/text/。
-->
<!--<cache-path-->
<!--name="my_cache"-->
<!--path="text/" />-->
<!--
<external-path name="name" path="path" /> 对应Environment.getExternalStorageDirectory() + “/path/”,即/storage/emulated/0/path/
-->
<external-path
name="myFile"
path="" />
</paths>
path="",它代码根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了。
如果你将path设为path="text",那么它代表着根目录下的text目录(/storage/emulated/0/text),如果你向其它应用分享text目录范围之外的文件是不行的。
4)代码修改,以版本更新为例:
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
if (Utils.isAndroid7()) { //判读版本是否在7.0以上
//参数1 上下文, 参数2 Provider主机地址 和清单文件中保持一致 参数3 共享的文件
Uri apkUri = FileProvider.getUriForFile(context,"com.dh.test.fileprovider", file);
//添加这一句表示对目标应用临时授权该Uri所代表的文件installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
installIntent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
}
context.startActivity(installIntent);
3、通知Notification
Android8.0其中一项行为变更是Notification,Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。targeSdk升级到26之后,所有的通知的实现都需要提供通知渠道,如果不提供通知渠道的话,所有通知在8.0系统上面都不能正常展示。
private NotifyManager() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel =
new NotificationChannel("channel_id1", "channel_name1", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableVibration(true);
// channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
//是否在桌面icon右上角展示小红点
// channel.enableLights(true);
// Uri mUri = Settings.System.DEFAULT_NOTIFICATION_URI;
// channel.setSound(mUri, Notification.AUDIO_ATTRIBUTES_DEFAULT);
// NotificationManager notifyManager = (NotificationManager) MyApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
getManager().createNotificationChannel(channel);
}
}
public void sendNotification(String title, String content) {
//获取NotificationManager实例
// NotificationManager notifyManager = (NotificationManager) MyApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
//实例化NotificationCompat.Builde并设置相关属性
Notification notification = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(MyApplication.getInstance(), channelId);
//设置小图标
builder.setSmallIcon(R.mipmap.ic_launcher)
//设置通知标题
.setContentTitle(title)
//设置通知内容
.setContentText(content)
.setAutoCancel(true);//用户触摸时,自动关闭
notification = builder.build();
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(MyApplication.getContext())
//点击通知后自动清除
.setAutoCancel(true)
//设置小图标
.setSmallIcon(R.mipmap.ic_launcher)
//设置通知标题
.setContentTitle(title)
//设置通知内容
.setContentText(content);
// .setDefaults(Notification.DEFAULT_SOUND);// 设置通知响应方式 ;
// .setContentIntent(mainPendingIntent);
//设置通知时间,默认为系统发出通知的时间,通常不用设置
//.setWhen(System.currentTimeMillis());
//通过builder.build()方法生成Notification对象,并发送通知,id=1
notification = builder.build();
//这通知的其他属性,比如:声音和振动
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
}
getManager().notify(count, notification);
count++;
}
4、8.0上版本升级无法跳转安装页面
Android 8.0强化了权限管理,变得更加安全。在Android 8.0以前,只要在设置中打开允许未知应用的安装,则所有的未知来源应用都可以被安装,如此设计虽然方便,但是若被引诱安装了恶意软件,安装"未知来源"的应用有可能会对手机系统带来潜在的危害;
而在Android 8.0的系统中,未知来源应用权限的开关被移除掉了,取而代之的是未知来源应用的管理列表,如果你想要安装某个被自己所信任的开发者的app,则需要在每一次都手动授权"安装未知应用"的许可。
坑就在这里了,下面是解决办法:
1)清单文件中添加
<!--android8.0版本更新跳转安装页面 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
2)代码:
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
if (Utils.isAndroid7()) { //判读版本是否在7.0以上
//参数1 上下文, 参数2 Provider主机地址 和清单文件中保持一致 参数3 共享的文件
Uri apkUri = FileProvider.getUriForFile(context,"com.dh.test.fileprovider", file);
//添加这一句表示对目标应用临时授权该Uri所代表的文件installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
//兼容8.0
if (Utils.isAndroid8()) {
boolean hasInstallPermission = context.getPackageManager().canRequestPackageInstalls();
if (!hasInstallPermission) {
startInstallPermissionSettingActivity(context);
return;
}
}
} else {
installIntent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
}
context.startActivity(installIntent);
/**
* 跳转到设置-允许安装未知来源-页面
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity(final Context context) {
//注意这个是8.0新API
Uri packageURI = Uri.parse("package:" + context.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
当用户打开了允许安装未知应用后,返回再次点击安装按钮,就可以跳转到安装页面了。