1俯萎、權(quán)限的動(dòng)態(tài)申請(qǐng)
這個(gè)是targetSdkVersion為23時(shí),對(duì)于一些比較危險(xiǎn)的權(quán)限运杭,需要?jiǎng)討B(tài)申請(qǐng)夫啊,網(wǎng)上很多資料,庫(kù)也很多辆憔。
2撇眯、隨著Android版本越來(lái)越高,Android對(duì)隱私的保護(hù)力度也越來(lái)越大虱咧。
比如:Android6.0引入的動(dòng)態(tài)權(quán)限控制(Runtime Permissions)熊榛,
Android7.0又引入“私有目錄被限制訪問(wèn)”,“StrictMode API 政策”腕巡。
這些更改在為用戶帶來(lái)更加安全的操作系統(tǒng)的同時(shí)也為開(kāi)發(fā)者帶來(lái)了一些新的任務(wù)玄坦。如何讓你的APP能夠適應(yīng)這些改變而不是crash,是擺在每一位Android開(kāi)發(fā)者身上的責(zé)任绘沉。
“私有目錄被限制訪問(wèn)“ 是指在Android7.0中為了提高私有文件的安全性煎楣,面向 Android N 或更高版本的應(yīng)用私有目錄將被限制訪問(wèn)。這點(diǎn)類(lèi)似iOS的沙盒機(jī)制车伞。
" StrictMode API 政策" 是指禁止向你的應(yīng)用外公開(kāi) file:// URI择懂。 如果一項(xiàng)包含文件 file:// URI類(lèi)型 的 Intent 離開(kāi)你的應(yīng)用,應(yīng)用失敗另玖,并出現(xiàn) FileUriExposedException 異常困曙。
接下來(lái)就用FileProvider來(lái)解決這一問(wèn)題
1)在AndroidManifest.xml清單文件中注冊(cè)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)在項(xiàng)目的res文件夾下新建xml文件夾,然后新建filepaths.xml日矫,名字要與清單文件中的一致
android:resource="@xml/filepaths"
3)編寫(xiě)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" /> 對(duì)應(yīng)Context.getFilesDir() + “/path/”赂弓,即/data/data/<package-name>/files/text/绑榴。
-->
<!--<files-path-->
<!--name="my_files"-->
<!--path="text/" />-->
<!--
<cache-path name="name" path="path" /> 對(duì)應(yīng)Context.getCacheDir() + “/path/”哪轿,即/data/data/<package-name>/cache/text/。
-->
<!--<cache-path-->
<!--name="my_cache"-->
<!--path="text/" />-->
<!--
<external-path name="name" path="path" /> 對(duì)應(yīng)Environment.getExternalStorageDirectory() + “/path/”翔怎,即/storage/emulated/0/path/
-->
<external-path
name="myFile"
path="" />
</paths>
path=""窃诉,它代碼根目錄杨耙,也就是說(shuō)你可以向其它的應(yīng)用共享根目錄及其子目錄下任何一個(gè)文件了。
如果你將path設(shè)為path="text"飘痛,那么它代表著根目錄下的text目錄(/storage/emulated/0/text)珊膜,如果你向其它應(yīng)用分享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以上
//參數(shù)1 上下文, 參數(shù)2 Provider主機(jī)地址 和清單文件中保持一致 參數(shù)3 共享的文件
Uri apkUri = FileProvider.getUriForFile(context,"com.dh.test.fileprovider", file);
//添加這一句表示對(duì)目標(biāo)應(yīng)用臨時(shí)授權(quán)該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其中一項(xiàng)行為變更是Notification,Android 8.0 引入了通知渠道塑猖,其允許您為要顯示的每種通知類(lèi)型創(chuàng)建用戶可自定義的渠道竹祷。用戶界面將通知渠道稱(chēng)之為通知類(lèi)別。targeSdk升級(jí)到26之后羊苟,所有的通知的實(shí)現(xiàn)都需要提供通知渠道塑陵,如果不提供通知渠道的話,所有通知在8.0系統(tǒng)上面都不能正常展示蜡励。
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); //是否在久按桌面圖標(biāo)時(shí)顯示此渠道的通知
//是否在桌面icon右上角展示小紅點(diǎn)
// 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實(shí)例
// NotificationManager notifyManager = (NotificationManager) MyApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
//實(shí)例化NotificationCompat.Builde并設(shè)置相關(guān)屬性
Notification notification = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(MyApplication.getInstance(), channelId);
//設(shè)置小圖標(biāo)
builder.setSmallIcon(R.mipmap.ic_launcher)
//設(shè)置通知標(biāo)題
.setContentTitle(title)
//設(shè)置通知內(nèi)容
.setContentText(content)
.setAutoCancel(true);//用戶觸摸時(shí)令花,自動(dòng)關(guān)閉
notification = builder.build();
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(MyApplication.getContext())
//點(diǎn)擊通知后自動(dòng)清除
.setAutoCancel(true)
//設(shè)置小圖標(biāo)
.setSmallIcon(R.mipmap.ic_launcher)
//設(shè)置通知標(biāo)題
.setContentTitle(title)
//設(shè)置通知內(nèi)容
.setContentText(content);
// .setDefaults(Notification.DEFAULT_SOUND);// 設(shè)置通知響應(yīng)方式 ;
// .setContentIntent(mainPendingIntent);
//設(shè)置通知時(shí)間,默認(rèn)為系統(tǒng)發(fā)出通知的時(shí)間凉倚,通常不用設(shè)置
//.setWhen(System.currentTimeMillis());
//通過(guò)builder.build()方法生成Notification對(duì)象,并發(fā)送通知,id=1
notification = builder.build();
//這通知的其他屬性兼都,比如:聲音和振動(dòng)
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
}
getManager().notify(count, notification);
count++;
}
4、8.0上版本升級(jí)無(wú)法跳轉(zhuǎn)安裝頁(yè)面
Android 8.0強(qiáng)化了權(quán)限管理稽寒,變得更加安全俯抖。在Android 8.0以前,只要在設(shè)置中打開(kāi)允許未知應(yīng)用的安裝瓦胎,則所有的未知來(lái)源應(yīng)用都可以被安裝芬萍,如此設(shè)計(jì)雖然方便,但是若被引誘安裝了惡意軟件搔啊,安裝"未知來(lái)源"的應(yīng)用有可能會(huì)對(duì)手機(jī)系統(tǒng)帶來(lái)潛在的危害柬祠;
而在Android 8.0的系統(tǒng)中,未知來(lái)源應(yīng)用權(quán)限的開(kāi)關(guān)被移除掉了负芋,取而代之的是未知來(lái)源應(yīng)用的管理列表漫蛔,如果你想要安裝某個(gè)被自己所信任的開(kāi)發(fā)者的app,則需要在每一次都手動(dòng)授權(quán)"安裝未知應(yīng)用"的許可。
坑就在這里了旧蛾,下面是解決辦法:
1)清單文件中添加
<!--android8.0版本更新跳轉(zhuǎn)安裝頁(yè)面 -->
<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以上
//參數(shù)1 上下文, 參數(shù)2 Provider主機(jī)地址 和清單文件中保持一致 參數(shù)3 共享的文件
Uri apkUri = FileProvider.getUriForFile(context,"com.dh.test.fileprovider", file);
//添加這一句表示對(duì)目標(biāo)應(yīng)用臨時(shí)授權(quán)該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);
/**
* 跳轉(zhuǎn)到設(shè)置-允許安裝未知來(lái)源-頁(yè)面
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity(final Context context) {
//注意這個(gè)是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);
}
當(dāng)用戶打開(kāi)了允許安裝未知應(yīng)用后莽龟,返回再次點(diǎn)擊安裝按鈕,就可以跳轉(zhuǎn)到安裝頁(yè)面了锨天。