本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布
Android 6.0
-
運(yùn)行時(shí)權(quán)限
相機(jī)缘回,圖庫(kù)剂公,下載颜武,語(yǔ)音,定位....
此版本引入了一種新的權(quán)限模式香璃,如今同云,用戶(hù)可直接在運(yùn)行時(shí)管理應(yīng)用權(quán)限禁偎。這種模式讓用戶(hù)能夠更好地了解和控制權(quán)限,同時(shí)為應(yīng)用開(kāi)發(fā)者精簡(jiǎn)了安裝和自動(dòng)更新過(guò)程。用戶(hù)可為所安裝的各個(gè)應(yīng)用分別授予或撤銷(xiāo)權(quán)限痪伦。
對(duì)于以 Android 6.0(API 級(jí)別 23)或更高版本為目標(biāo)平臺(tái)的應(yīng)用涣达,請(qǐng)務(wù)必在運(yùn)行時(shí)檢查和請(qǐng)求權(quán)限寇窑。要確定您的應(yīng)用是否已被授予權(quán)限,請(qǐng)調(diào)用新增的 checkSelfPermission()方法无午。要請(qǐng)求權(quán)限梦皮,請(qǐng)調(diào)用新增的requestPermissions() 方法牡借。即使您的應(yīng)用并不以 Android 6.0(API 級(jí)別 23)為目標(biāo)平臺(tái)沈矿,您也應(yīng)該在新權(quán)限模式下測(cè)試您的應(yīng)用醒颖。如需了解有關(guān)在您的應(yīng)用中支持新權(quán)限模式的詳情榛丢,請(qǐng)參閱使用系統(tǒng)權(quán)限。如需了解有關(guān)如何評(píng)估新模式對(duì)應(yīng)用的影響的提示挺庞,請(qǐng)參閱權(quán)限最佳做法晰赞。
權(quán)限管理工具類(lèi)
package cn.loveshow.live.util;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Created by Fungo_Xiaoke on 2017/5/4 14:18.
* eamil:luoxiaoke@yuntutv.net
* 權(quán)限工具類(lèi)
*/
public class PermissionUtils {
/**
* @param context 上下文
* @param activity activity
* @param permissions 權(quán)限數(shù)組
* @param requestCode 申請(qǐng)碼
* @return true 有權(quán)限 false 無(wú)權(quán)限
*/
public static boolean checkAndApplyfPermissionActivity(Activity activity, String[] permissions, int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permissions = checkPermissions(activity, permissions);
if (permissions != null && permissions.length > 0) {
ActivityCompat.requestPermissions(activity, permissions, requestCode);
return false;
} else {
return true;
}
} else {
return true;
}
}
/**
* @param context 上下文
* @param mFragment fragment
* @param permissions 權(quán)限數(shù)組
* @param requestCode 申請(qǐng)碼
* @return true 有權(quán)限 false 無(wú)權(quán)限
*/
public static boolean checkAndApplyfPermissionFragment( Fragment mFragment, String[] permissions, int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permissions = checkPermissions(mFragment.getActivity(), permissions);
if (permissions != null && permissions.length > 0) {
if (mFragment.getActivity() != null) {
mFragment.requestPermissions(permissions, requestCode);
}
return false;
} else {
return true;
}
} else {
return true;
}
}
/**
* @param context 上下文
* @param permissions 權(quán)限數(shù)組
* @return 還需要申請(qǐng)的權(quán)限
*/
private static String[] checkPermissions(Context context, String[] permissions) {
if (permissions == null || permissions.length == 0) {
return new String[0];
}
ArrayList<String> permissionLists = new ArrayList<>();
permissionLists.addAll(Arrays.asList(permissions));
for (int i = permissionLists.size() - 1; i >= 0; i--) {
if (ContextCompat.checkSelfPermission(context, permissionLists.get(i)) == PackageManager.PERMISSION_GRANTED) {
permissionLists.remove(i);
}
}
String[] temps = new String[permissionLists.size()];
for (int i = 0; i < permissionLists.size(); i++) {
temps[i] = permissionLists.get(i);
}
return temps;
}
/**
* 檢查申請(qǐng)的權(quán)限是否全部允許
*/
public static boolean checkPermission(int[] grantResults) {
if (grantResults == null || grantResults.length == 0) {
return true;
} else {
int temp = 0;
for (int i : grantResults) {
if (i == PackageManager.PERMISSION_GRANTED) {
temp++;
}
}
return temp == grantResults.length;
}
}
/**
* 沒(méi)有獲取到權(quán)限的提示
*
* @param permissions 權(quán)限名字?jǐn)?shù)組
*/
public static void showPermissionsToast(Activity activity, @NonNull String[] permissions) {
if (permissions.length > 0) {
for (String permission : permissions) {
showPermissionToast(activity, permission);
}
}
}
/**
* 沒(méi)有獲取到權(quán)限的提示
*
* @param permission 權(quán)限名字
*/
private static void showPermissionToast(Activity activity, @NonNull String permission) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
//用戶(hù)勾選了不再詢(xún)問(wèn),提示用戶(hù)手動(dòng)打開(kāi)權(quán)限
switch (permission) {
case Manifest.permission.CAMERA:
ToastUtils.showShort("相機(jī)權(quán)限已被禁止,請(qǐng)?jiān)趹?yīng)用管理中打開(kāi)權(quán)限");
break;
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
ToastUtils.showShort("文件權(quán)限已被禁止挠阁,請(qǐng)?jiān)趹?yīng)用管理中打開(kāi)權(quán)限");
break;
case Manifest.permission.RECORD_AUDIO:
ToastUtils.showShort("錄制音頻權(quán)限已被禁止宾肺,請(qǐng)?jiān)趹?yīng)用管理中打開(kāi)權(quán)限");
break;
case Manifest.permission.ACCESS_FINE_LOCATION:
ToastUtils.showShort("位置權(quán)限已被禁止,請(qǐng)?jiān)趹?yīng)用管理中打開(kāi)權(quán)限");
break;
}
} else {
//用戶(hù)沒(méi)有勾選了不再詢(xún)問(wèn),拒絕了權(quán)限申請(qǐng)
switch (permission) {
case Manifest.permission.CAMERA:
ToastUtils.showShort("沒(méi)有相機(jī)權(quán)限");
break;
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
ToastUtils.showShort("沒(méi)有文件讀取權(quán)限");
break;
case Manifest.permission.RECORD_AUDIO:
ToastUtils.showShort("沒(méi)有錄制音頻權(quán)限");
break;
case Manifest.permission.ACCESS_FINE_LOCATION:
ToastUtils.showShort("沒(méi)有位置權(quán)限");
break;
}
}
}
}
用法
if (PermissionUtils.checkAndApplyfPermissionActivity(this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUESRCARMEA)) {
//獲取到權(quán)限的操作 沒(méi)有權(quán)限會(huì)申請(qǐng)權(quán)限 然后在onRequestPermissionsResult處理申請(qǐng)的結(jié)果
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (PermissionUtils.checkPermission(grantResults)) {
//申請(qǐng)權(quán)限成功
switch (requestCode) {
case 0x001:
//dosomething
break;
case 0x002:
//dosomething
break;
...
}
} else {
//提示沒(méi)有什么權(quán)限
PermissionUtils.showPermissionsToast(activity, permissions);
//or 去權(quán)限管理界面
//gotoPermissionManager(mContext);
}
}
沒(méi)有權(quán)限去權(quán)限管理界面
/**
* 去應(yīng)用權(quán)限管理界面
*/
public static void gotoPermissionManager(Context context) {
Intent intent;
ComponentName comp;
//防止刷機(jī)出現(xiàn)的問(wèn)題
try {
switch (Build.MANUFACTURER) {
case "Huawei":
intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");
intent.setComponent(comp);
context.startActivity(intent);
break;
case "Meizu":
intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
context.startActivity(intent);
break;
case "Xiaomi":
String rom = getSystemProperty("ro.miui.ui.version.name");
if ("v5".equals(rom)) {
Uri packageURI = Uri.parse("package:" + context.getApplicationInfo().packageName);
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
} else {//if ("v6".equals(rom) || "v7".equals(rom)) {
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
intent.putExtra("extra_pkgname", context.getPackageName());
}
context.startActivity(intent);
break;
case "Sony":
intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity");
intent.setComponent(comp);
context.startActivity(intent);
break;
case "OPPO":
intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity");
intent.setComponent(comp);
context.startActivity(intent);
break;
case "LG":
intent = new Intent("android.intent.action.MAIN");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity");
intent.setComponent(comp);
context.startActivity(intent);
break;
case "Letv":
intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps");
intent.setComponent(comp);
context.startActivity(intent);
break;
default:
getAppDetailSettingIntent(context);
break;
}
} catch (Exception e) {
getAppDetailSettingIntent(context);
}
}
/**
* 獲取系統(tǒng)屬性值
*/
public static String getSystemProperty(String propName) {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop " + propName);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
Log.e(TAG, "Unable to read sysprop " + propName, ex);
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
Log.e(TAG, "Exception while closing InputStream", e);
}
}
}
return line;
}
//以下代碼可以跳轉(zhuǎn)到應(yīng)用詳情侵俗,可以通過(guò)應(yīng)用詳情跳轉(zhuǎn)到權(quán)限界面(6.0系統(tǒng)測(cè)試可用)
public static void getAppDetailSettingIntent(Context context) {
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", context.getPackageName(), null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
}
launchApp(context, localIntent);
}
/**
* 安全的啟動(dòng)APP
*/
public static boolean launchApp(Context ctx, Intent intent) {
if (ctx == null)
throw new NullPointerException("ctx is null");
try {
ctx.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
Logger.e(e);
return false;
}
}
-
取消支持 Apache HTTP 客戶(hù)端
Android 6.0 版移除了對(duì) Apache HTTP 客戶(hù)端的支持锨用。如果您的應(yīng)用使用該客戶(hù)端,并以 Android 2.3(API 級(jí)別 9)或更高版本為目標(biāo)平臺(tái)隘谣,請(qǐng)改用HttpURLConnection 類(lèi)增拥。此 API 效率更高啄巧,因?yàn)樗梢酝ㄟ^(guò)透明壓縮和響應(yīng)緩存減少網(wǎng)絡(luò)使用,并可最大限度降低耗電量掌栅。要繼續(xù)使用 Apache HTTP API秩仆,您必須先在 build.gradle 文件中聲明以下編譯時(shí)依賴(lài)項(xiàng):
android {
useLibrary 'org.apache.http.legacy'
}
-
BoringSSL
Android 正在從使用 OpenSSL 庫(kù)轉(zhuǎn)向使用 BoringSSL 庫(kù)。如果您要在應(yīng)用中使用 Android NDK猾封,請(qǐng)勿鏈接到并非 NDK API 組成部分的加密庫(kù)澄耍,如libcrypto.so和 libssl.so。這些庫(kù)并非公共 API晌缘,可能會(huì)在不同版本和設(shè)備上毫無(wú)征兆地發(fā)生變化或出現(xiàn)故障齐莲。此外,您還可能讓自己暴露在安全漏洞的風(fēng)險(xiǎn)之下磷箕。請(qǐng)改為修改原生代碼选酗,以通過(guò) JNI 調(diào)用 Java 加密 API,或靜態(tài)鏈接到您選擇的加密庫(kù)岳枷。
-
通知
此版本移除了 Notification.setLatestEventInfo()方法芒填。請(qǐng)改用 Notification.Builder 類(lèi)來(lái)構(gòu)建通知。要重復(fù)更新通知空繁,請(qǐng)重復(fù)使用Notification.Builder 實(shí)例殿衰。調(diào)用 build() 方法可獲取更新后的 Notification 實(shí)例。
adb shell dumpsys notification 命令不再打印輸出您的通知文本家厌。請(qǐng)改用 adb shell dumpsys notification --noredact 命令打印輸出 notification 對(duì)象中的文本播玖。build()方法在4.1以上(16+)的系統(tǒng)才能用。
-
音頻管理器變更
不再支持通過(guò) AudioManager 類(lèi)直接設(shè)置音量或?qū)⑻囟ㄒ纛l流 靜音饭于。[setStreamSolo()](https://developer.android.google.cn/reference/android/media/AudioManager.html#setStreamSolo(int, boolean)) 方法已棄用蜀踏,您應(yīng)該改為調(diào)用 [requestAudioFocus()](https://developer.android.google.cn/reference/android/media/AudioManager.html#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int)) 方法。類(lèi)似地掰吕,[setStreamMute()](https://developer.android.google.cn/reference/android/media/AudioManager.html#setStreamMute(int, boolean)) 方法也已棄用果覆,請(qǐng)改為調(diào)用 [adjustStreamVolume()](https://developer.android.google.cn/reference/android/media/AudioManager.html#adjustStreamVolume(int, int, int)) 方法并傳入方向值 ADJUST_MUTE 或 ADJUST_UNMUTE。
-
Android 密鑰庫(kù)變更
從此版本開(kāi)始殖熟,Android 密鑰庫(kù)提供程序不再支持 DSA局待。但仍支持 ECDSA。
停用或重置安全鎖定屏幕時(shí)(例如菱属,由用戶(hù)或設(shè)備管理員執(zhí)行此類(lèi)操作時(shí))钳榨,系統(tǒng)將不再刪除需要閑時(shí)加密的密鑰,但在上述事件期間會(huì)刪除需要閑時(shí)加密的密鑰纽门。
-
APK 驗(yàn)證
該平臺(tái)現(xiàn)在執(zhí)行的 APK 驗(yàn)證更為嚴(yán)格薛耻。如果在清單中聲明的文件在 APK 中并不存在,該 APK 將被視為已損壞赏陵。移除任何內(nèi)容后必須重新簽署 APK饼齿。
http://www.reibang.com/p/95790125b7f4
http://blog.csdn.net/lxk_1993/article/details/73784883
Android7.0
-
系統(tǒng)權(quán)限更改
為了提高私有文件的安全性饲漾,面向 Android 7.0 或更高版本的應(yīng)用私有目錄被限制訪問(wèn) (0700)。此設(shè)置可防止私有文件的元數(shù)據(jù)泄漏缕溉,如它們的大小或存在性考传。此權(quán)限更改有多重副作用:
私有文件的文件權(quán)限不應(yīng)再由所有者放寬,為使用 MODE_WORLD_READABLE 和/或 MODE_WORLD_WRITEABLE 而進(jìn)行的此類(lèi)嘗試將觸發(fā) SecurityException
注:迄今為止证鸥,這種限制尚不能完全執(zhí)行僚楞。應(yīng)用仍可能使用原生 API 或 File API 來(lái)修改它們的私有目錄權(quán)限。但是枉层,我們強(qiáng)烈反對(duì)放寬私有目錄的權(quán)限镜硕。傳遞軟件包網(wǎng)域外的 file://URI 可能給接收器留下無(wú)法訪問(wèn)的路徑。因此返干,嘗試傳遞 file://URI 會(huì)觸發(fā)FileUriExposedException。分享私有文件內(nèi)容的推薦方法是使用 FileProvider血淌。
DownloadManager不再按文件名分享私人存儲(chǔ)的文件矩欠。舊版應(yīng)用在訪問(wèn) COLUMN_LOCAL_FILENAME時(shí)可能出現(xiàn)無(wú)法訪問(wèn)的路徑。面向 Android 7.0 或更高版本的應(yīng)用在嘗試訪問(wèn) COLUMN_LOCAL_FILENAME時(shí)會(huì)觸發(fā) SecurityException悠夯。通過(guò)使用 [DownloadManager.Request.setDestinationInExternalFilesDir()](https://developer.android.google.cn/reference/android/app/DownloadManager.Request.html#setDestinationInExternalFilesDir(android.content.Context, java.lang.String, java.lang.String)) 或 [DownloadManager.Request.setDestinationInExternalPublicDir()](https://developer.android.google.cn/reference/android/app/DownloadManager.Request.html#setDestinationInExternalPublicDir(java.lang.String, java.lang.String))將下載位置設(shè)置為公共位置的舊版應(yīng)用仍可以訪問(wèn) COLUMN_LOCAL_FILENAME 中的路徑癌淮,但是我們強(qiáng)烈反對(duì)使用這種方法。對(duì)于由 DownloadManager 公開(kāi)的文件沦补,首選的訪問(wèn)方式是使用[ContentResolver.openFileDescriptor()](https://developer.android.google.cn/reference/android/content/ContentResolver.html#openFileDescriptor(android.net.Uri, java.lang.String))乳蓄。
-
在應(yīng)用間共享文件
對(duì)于面向 Android 7.0 的應(yīng)用,Android 框架執(zhí)行的 StrictModeAPI 政策禁止在您的應(yīng)用外部公開(kāi) file://URI夕膀。如果一項(xiàng)包含文件 URI 的 intent 離開(kāi)您的應(yīng)用虚倒,則應(yīng)用出現(xiàn)故障,并現(xiàn) FileUriExposedException产舞。異常魂奥。要在應(yīng)用間共享文件,您應(yīng)發(fā)送一項(xiàng) content://URI易猫,并授予 URI 臨時(shí)訪問(wèn)權(quán)限耻煤。進(jìn)行此授權(quán)的最簡(jiǎn)單方式是使用FileProvider類(lèi)。如需了解有關(guān)權(quán)限和共享文件的詳細(xì)信息准颓,請(qǐng)參閱共享文件哈蝇。
-
FileProvider用法
AndroidManiFest.xml添加
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
...
</application>
res目錄下新建xml文件夾,創(chuàng)建provider_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<!-- 前面兩個(gè)是bugly的 -->
<!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
<external-path
name="beta_external_path"
path="Download/" />
<!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
<external-path
name="beta_external_files_path"
path="Android/data/" />
<external-path
name="sdcard_files"
path="" />
<!--相機(jī)相冊(cè)裁剪-->
<external-files-path
name="camera_has_sdcard"
path="" />
<files-path
name="camera_no_sdcard"
path="" />
</paths>
<!--<paths>-->
<!-- xml文件是唯一設(shè)置分享的目錄 攘已,不能用代碼設(shè)置-->
<!--1.<files-path> getFilesDir() /data/data//files目錄-->
<!--2.<cache-path> getCacheDir() /data/data//cache目錄-->
<!--3.<external-path> Environment.getExternalStorageDirectory() -->
<!--4.<external-files-path>
Context.getExternalFilesDir(String) Context.getExternalFilesDir(null)
== SDCard/Android/data/你的應(yīng)用的包名/files/ 目錄-->
<!--5.<external-cache-path> Context.getExternalCacheDir().-->
<!-- path :代表設(shè)置的目錄下一級(jí)目錄 eg:<external-path path="images/"-->
<!--整個(gè)目錄為Environment.getExternalStorageDirectory()+"/images/"-->
<!--name: 代表定義在Content中的字段 eg:name = "myimages" 炮赦,并且請(qǐng)求的內(nèi)容的文件名為default_image.jpg-->
<!--則 返回一個(gè)URI content://com.example.myapp.fileprovider/myimages/default_image.jpg-->
<!--</paths>-->
</resources>
確認(rèn)下路徑名
FileProvider 頭部設(shè)置的對(duì)應(yīng)標(biāo)簽
FileProvider 獲取對(duì)應(yīng)路徑邏輯 解析xml文件 對(duì)比對(duì)應(yīng)的標(biāo)簽 獲取對(duì)應(yīng)的路徑
**修改所有用到Uri的地方 圖中的 BuildConfig.APPLICATION_ID 最好還是改成 context.getPackageName() **
官方鏈接:FileProvider
-
APK Signature Scheme v2
Android 7.0引入了全新的 APK Signature Scheme v2。這是加強(qiáng)對(duì)包的校驗(yàn)贯被,啟動(dòng)了新的簽名后眼五,像美團(tuán)的多渠道打包方案在7.0機(jī)器上就會(huì)報(bào)錯(cuò)了妆艘。
解決的辦法也很簡(jiǎn)單,官方提供了關(guān)閉v2簽名的方法看幼,只需要在gradle上配置一下即可:
signingConfigs {
release {
.......
v2SigningEnabled false
}
}
參考:Android7.0適配
鏈接: APK Signature Scheme v2詳細(xì)介紹
其他
Android6.0
-
USB 連接
默認(rèn)情況下批旺,現(xiàn)在通過(guò) USB 端口進(jìn)行的設(shè)備連接設(shè)置為僅充電模式。要通過(guò) USB 連接訪問(wèn)設(shè)備及其內(nèi)容诵姜,用戶(hù)必須明確地為此類(lèi)交互授予權(quán)限汽煮。如果您的應(yīng)用支持用戶(hù)通過(guò) USB 端口與設(shè)備進(jìn)行交互,請(qǐng)將必須顯式啟用交互考慮在內(nèi)棚唆。
-
瀏覽器書(shū)簽變更
此版本移除了對(duì)全局書(shū)簽的支持暇赤。android.provider.Browser.getAllBookmarks() 和 android.provider.Browser.saveBookmark() 方法現(xiàn)已移除。同樣宵凌,READ_HISTORY_BOOKMARKS 權(quán)限和 WRITE_HISTORY_BOOKMARKS 權(quán)限也已移除鞋囊。如果您的應(yīng)用以 Android 6.0(API 級(jí)別 23)或更高版本為目標(biāo)平臺(tái),請(qǐng)勿從全局提供程序訪問(wèn)書(shū)簽或使用書(shū)簽權(quán)限瞎惫。您的應(yīng)用應(yīng)改為在內(nèi)部存儲(chǔ)書(shū)簽數(shù)據(jù)溜腐。
-
硬件標(biāo)識(shí)符訪問(wèn)權(quán)
為給用戶(hù)提供更嚴(yán)格的數(shù)據(jù)保護(hù),從此版本開(kāi)始瓜喇,對(duì)于使用 WLAN API 和 Bluetooth API 的應(yīng)用挺益,Android 移除了對(duì)設(shè)備本地硬件標(biāo)識(shí)符的編程訪問(wèn)權(quán)。WifiInfo.getMacAddress()方法和 BluetoothAdapter.getAddress()方法現(xiàn)在會(huì)返回常量值 02:00:00:00:00:00乘寒。
現(xiàn)在望众,要通過(guò)藍(lán)牙和 WLAN 掃描訪問(wèn)附近外部設(shè)備的硬件標(biāo)識(shí)符,您的應(yīng)用必須擁有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 權(quán)限伞辛。
注:當(dāng)運(yùn)行 Android 6.0(API 級(jí)別 23)的設(shè)備發(fā)起后臺(tái) WLAN 或藍(lán)牙掃描時(shí)烂翰,在外部設(shè)備看來(lái),該操作的發(fā)起來(lái)源是一個(gè)隨機(jī)化 MAC 地址始锚。
-
WLAN 和網(wǎng)絡(luò)連接變更
此版本對(duì) WLAN API 和 Networking API 引入了以下行為變更刽酱。
現(xiàn)在,您的應(yīng)用只能更改由您創(chuàng)建的 WifiConfiguration 對(duì)象的狀態(tài)瞧捌。系統(tǒng)不允許您修改或刪除由用戶(hù)或其他應(yīng)用創(chuàng)建的 WifiConfiguration 對(duì)象棵里。
在之前的版本中,如果應(yīng)用利用帶有 disableAllOthers=true
設(shè)置的 [enableNetwork()](https://developer.android.google.cn/reference/android/net/wifi/WifiManager.html#enableNetwork(int, boolean)) 強(qiáng)制設(shè)備連接特定 WLAN 網(wǎng)絡(luò)姐呐,設(shè)備將會(huì)斷開(kāi)與移動(dòng)數(shù)據(jù)網(wǎng)絡(luò)等其他網(wǎng)絡(luò)的連接殿怜。在此版本中,設(shè)備不再斷開(kāi)與上述其他網(wǎng)絡(luò)的連接曙砂。如果您的應(yīng)用的 targetSdkVersion
為 “20” 或更低头谜,則會(huì)固定連接所選 WLAN 網(wǎng)絡(luò)。如果您的應(yīng)用的 targetSdkVersion 為 “21”
或更高鸠澈,請(qǐng)使用多網(wǎng)絡(luò) API(如 openConnection()柱告、bindSocket() 和新增的bindProcessToNetwork()
方法)來(lái)確保通過(guò)所選網(wǎng)絡(luò)傳送網(wǎng)絡(luò)流量截驮。-
相機(jī)服務(wù)變更
在此版本中,相機(jī)服務(wù)中共享資源的訪問(wèn)模式已從之前的“先到先得”訪問(wèn)模式更改為高優(yōu)先級(jí)進(jìn)程優(yōu)先的訪問(wèn)模式际度。對(duì)服務(wù)行為的變更包括:
根據(jù)客戶(hù)端應(yīng)用進(jìn)程的“優(yōu)先級(jí)”授予對(duì)相機(jī)子系統(tǒng)資源的訪問(wèn)權(quán)葵袭,包括打開(kāi)和配置相機(jī)設(shè)備。帶有對(duì)用戶(hù)可見(jiàn) Activity 或前臺(tái) Activity 的應(yīng)用進(jìn)程一般會(huì)被授予較高的優(yōu)先級(jí)乖菱,從而使相機(jī)資源的獲取和使用更加可靠坡锡;
當(dāng)高優(yōu)先級(jí)的應(yīng)用嘗試使用相機(jī)時(shí),系統(tǒng)可能會(huì)“驅(qū)逐”正在使用相機(jī)客戶(hù)端的低優(yōu)先級(jí)應(yīng)用窒所。在已棄用的 Camera API 中鹉勒,這會(huì)導(dǎo)致系統(tǒng)為被驅(qū)逐的客戶(hù)端調(diào)用 [onError()](https://developer.android.google.cn/reference/android/hardware/Camera.ErrorCallback.html#onError(int, android.hardware.Camera))。在 Camera2 API 中吵取,這會(huì)導(dǎo)致系統(tǒng)為被驅(qū)逐的客戶(hù)端調(diào)用 onDisconnected()禽额;
在配備相應(yīng)相機(jī)硬件的設(shè)備上,不同的應(yīng)用進(jìn)程可同時(shí)獨(dú)立打開(kāi)和使用不同的相機(jī)設(shè)備皮官。但現(xiàn)在绵疲,如果在多進(jìn)程用例中同時(shí)訪問(wèn)相機(jī)會(huì)造成任何打開(kāi)的相機(jī)設(shè)備的性能或能力嚴(yán)重下降,相機(jī)服務(wù)會(huì)檢測(cè)到這種情況并禁止同時(shí)訪問(wèn)臣疑。即使并沒(méi)有其他應(yīng)用直接嘗試訪問(wèn)同一相機(jī)設(shè)備,此變更也可能導(dǎo)致低優(yōu)先級(jí)客戶(hù)端被“驅(qū)逐”徙菠。
更改當(dāng)前用戶(hù)會(huì)導(dǎo)致之前用戶(hù)帳戶(hù)擁有的應(yīng)用內(nèi)活動(dòng)相機(jī)客戶(hù)端被驅(qū)逐讯沈。對(duì)相機(jī)的訪問(wèn)僅限于訪問(wèn)當(dāng)前設(shè)備用戶(hù)擁有的用戶(hù)個(gè)人資料。舉例來(lái)說(shuō)婿奔,這意味著缺狠,當(dāng)用戶(hù)切換到其他帳戶(hù)后,“來(lái)賓”帳戶(hù)實(shí)際上無(wú)法讓使用相機(jī)子系統(tǒng)的進(jìn)程保持運(yùn)行狀態(tài)萍摊。
-
運(yùn)行時(shí)
ART 運(yùn)行時(shí)環(huán)境現(xiàn)在可正確實(shí)現(xiàn) newInstance() 方法的訪問(wèn)規(guī)則挤茄。此變更修正了之前版本中 Dalvik 無(wú)法正確檢查訪問(wèn)規(guī)則的問(wèn)題。如果您的應(yīng)用使用newInstance() 方法冰木,并且您想重寫(xiě)訪問(wèn)檢查穷劈,請(qǐng)調(diào)用 setAccessible() 方法(將輸入?yún)?shù)設(shè)置為 true)。如果您的應(yīng)用使用 v7 appcompat 庫(kù)或 v7 recyclerview 庫(kù)踊沸,則您必須更新應(yīng)用以使用這些庫(kù)的最新版本歇终。否則,請(qǐng)務(wù)必更新從 XML 引用的任何自定義類(lèi)逼龟,以便能夠訪問(wèn)它們的類(lèi)構(gòu)造函數(shù)评凝。此版本更新了動(dòng)態(tài)鏈接程序的行為。動(dòng)態(tài)鏈接程序現(xiàn)在可以識(shí)別庫(kù)的 soname 與其路徑之間的差異(公開(kāi)錯(cuò)誤 6670)腺律,并且現(xiàn)在已實(shí)現(xiàn)了按 soname 搜索奕短。之前包含錯(cuò)誤的 DT_NEEDED 條目(通常是開(kāi)發(fā)計(jì)算機(jī)文件系統(tǒng)上的絕對(duì)路徑)卻仍工作正常的應(yīng)用宜肉,如今可能會(huì)出現(xiàn)加載失敗。現(xiàn)已正確實(shí)現(xiàn) dlopen(3) RTLD_LOCAL 標(biāo)記翎碑。請(qǐng)注意谬返,RTLD_LOCAL 是默認(rèn)值,因此不顯式使用 RTLD_LOCAL 的 dlopen(3) 調(diào)用將受到影響(除非您的應(yīng)用顯式使用 RTLD_GLOBAL)杈女。使用 RTLD_LOCAL 時(shí)朱浴,在隨后通過(guò)調(diào)用 dlopen(3) 加載的庫(kù)中并不能使用這些符號(hào)(這與由 DT_NEEDED 條目引用的情況截然不同)。
在之前版本的 Android 上达椰,如果您的應(yīng)用請(qǐng)求系統(tǒng)加載包含文本重定位信息的共享庫(kù)翰蠢,系統(tǒng)會(huì)顯示警告,但仍允許加載共享庫(kù)啰劲。從此版本開(kāi)始梁沧,如果您的應(yīng)用的目標(biāo) SDK 版本為 23 或更高,則系統(tǒng)會(huì)拒絕加載該庫(kù)蝇裤。為幫助您檢測(cè)庫(kù)是否加載失敗廷支,您的應(yīng)用應(yīng)該記錄 dlopen(3) 失敗日志,并在日志中加入dlerror(3) 調(diào)用返回的問(wèn)題描述文本栓辜。要詳細(xì)了解如何處理文本重定位恋拍,請(qǐng)參閱此指南。
-
低電耗模式和應(yīng)用待機(jī)模式
此版本引入了針對(duì)空閑設(shè)備和應(yīng)用的最新節(jié)能優(yōu)化技術(shù)藕甩。這些功能會(huì)影響所有應(yīng)用施敢,因此請(qǐng)務(wù)必在這些新模式下測(cè)試您的應(yīng)用。
- 低電耗模式:如果用戶(hù)拔下設(shè)備的電源插頭狭莱,并在屏幕關(guān)閉后的一段時(shí)間內(nèi)使其保持不活動(dòng)狀態(tài)僵娃,設(shè)備會(huì)進(jìn)入低電耗模式,在該模式下設(shè)備會(huì)嘗試讓系統(tǒng)保持休眠狀態(tài)腋妙。在該模式下默怨,設(shè)備會(huì)定期短時(shí)間恢復(fù)正常工作,以便進(jìn)行應(yīng)用同步骤素,還可讓系統(tǒng)執(zhí)行任何掛起的操作匙睹。
- 應(yīng)用待機(jī)模式:應(yīng)用待機(jī)模式允許系統(tǒng)判定應(yīng)用在用戶(hù)未主動(dòng)使用它時(shí)處于空閑狀態(tài)。當(dāng)用戶(hù)有一段時(shí)間未觸摸應(yīng)用時(shí)济竹,系統(tǒng)便會(huì)作出此判定垃僚。如果拔下了設(shè)備電源插頭,系統(tǒng)會(huì)為其視為空閑的應(yīng)用停用網(wǎng)絡(luò)訪問(wèn)以及暫停同步和作業(yè)规辱。
要詳細(xì)了解這些節(jié)能變更谆棺,請(qǐng)參閱對(duì)低電耗模式和應(yīng)用待機(jī)模式進(jìn)行針對(duì)性?xún)?yōu)化。
-
文本選擇
現(xiàn)在,當(dāng)用戶(hù)在您的應(yīng)用中選擇文本時(shí)改淑,您可以在一個(gè)浮動(dòng)工具欄中顯示“剪切”碍岔、“復(fù)制”和“粘貼”等文本選擇操作。其在用戶(hù)交互實(shí)現(xiàn)上與為單個(gè)視圖啟用上下文操作模式中所述的上下文操作欄類(lèi)似朵夏。
要實(shí)現(xiàn)可用于文本選擇的浮動(dòng)工具欄忘分,請(qǐng)?jiān)谀默F(xiàn)有應(yīng)用中做出以下更改:
- 在 View 對(duì)象或 Activity 對(duì)象中嗜逻,將 ActionMode 調(diào)用從 startActionMode(Callback)更改為 startActionMode(Callback, ActionMode.TYPE_FLOATING)善茎。
- 改為使用 ActionMode.Callback 的現(xiàn)有實(shí)現(xiàn)擴(kuò)展 ActionMode.Callback2神得。
- 替代 [onGetContentRect()](https://developer.android.google.cn/reference/android/view/ActionMode.Callback2.html#onGetContentRect(android.view.ActionMode, android.view.View, android.graphics.Rect)) 方法,用于提供 Rect 內(nèi)容對(duì)象(如文本選擇矩形)在視圖中的坐標(biāo)饥侵。
- 如果矩形的定位不再有效鸵赫,并且這是唯一需要聲明為無(wú)效的元素,請(qǐng)調(diào)invalidateContentRect() 方法躏升。
請(qǐng)注意辩棒,如果您使用 Android 支持庫(kù) 22.2 修訂版,浮動(dòng)工具欄不向后兼容膨疏,默認(rèn)情況下 appcompat 會(huì)獲得對(duì) ActionMode 對(duì)象的控制權(quán)一睁。這會(huì)禁止顯示浮動(dòng)工具欄。要在ActionMode 中啟用 AppCompatActivity 支持佃却,請(qǐng)調(diào)用 getDelegate()者吁,然后對(duì)返回的setHandleNativeActionModesEnabled() 對(duì)象調(diào)用 AppCompatDelegate,并將輸入?yún)?shù)設(shè)置為 false饲帅。此調(diào)用會(huì)將 ActionMode 對(duì)象的控制權(quán)交還給框架砚偶。在運(yùn)行 Android 6.0(API 級(jí)別 23)的設(shè)備上,框架可以支持 ActionBar 模式或浮動(dòng)工具欄模式洒闸;而在運(yùn)行 Android 5.1(API 級(jí)別 22)或之前版本的設(shè)備上,框架僅支持 ActionBar 模式均芽。
-
Android for Work 變更
此版本包含下列針對(duì) Android for Work 的行為變更:
- 個(gè)人上下文中的工作聯(lián)系人丘逸。Google 撥號(hào)器通話記錄現(xiàn)在會(huì)在用戶(hù)查看通話記錄時(shí)顯示工作聯(lián)系人。將 [setCrossProfileCallerIdDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean)) 設(shè)置為 true 可在 Google 撥號(hào)器通話記錄中隱藏托管配置文件聯(lián)系人掀宋。僅當(dāng)將 [setBluetoothContactSharingDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setBluetoothContactSharingDisabled(android.content.ComponentName, boolean)) 設(shè)置為 false 時(shí)深纲,才可以通過(guò)藍(lán)牙將工作聯(lián)系人隨個(gè)人聯(lián)系人一起顯示給設(shè)備。默認(rèn)情況下劲妙,它設(shè)置為 true湃鹊。
-
WLAN 配置刪除:現(xiàn)在,當(dāng)刪除某個(gè)托管配置文件時(shí)镣奋,將會(huì)移除由配置文件所有者添加的 WLAN 配置(例如币呵,通過(guò)調(diào)用 addNetwork()
方法添加的配置)。 - WLAN 配置鎖定:如果 WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN 不為零侨颈,則用戶(hù)無(wú)法再修改或刪除任何由活動(dòng)設(shè)備所有者創(chuàng)建的 WLAN 配置余赢。用戶(hù)仍可創(chuàng)建和修改其自己的 WLAN 配置芯义。活動(dòng)設(shè)備所有者擁有編輯或刪除任何 WLAN 配置(包括并非由其創(chuàng)建的配置)的權(quán)限妻柒。
- 通過(guò)添加 Google 帳戶(hù)下載設(shè)備規(guī)范控制器:向托管環(huán)境以外的設(shè)備添加需要通過(guò)設(shè)備規(guī)范控制器 (DPC) 應(yīng)用管理的 Google 帳戶(hù)時(shí)扛拨,帳戶(hù)添加流程現(xiàn)在會(huì)提示用戶(hù)安裝相應(yīng)的 WPC。在設(shè)備初始設(shè)置向?qū)е型ㄟ^(guò) Settings > Accounts 添加帳戶(hù)時(shí)举塔,也會(huì)出現(xiàn)此行為绑警。
-
對(duì)特定 DevicePolicyManager API 行為的變更:
- 調(diào)用 [setCameraDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setCameraDisabled(android.content.ComponentName, boolean)) 方法只會(huì)影響調(diào)用該方法的用戶(hù)的相機(jī);從托管配置文件調(diào)用它不會(huì)影響主用戶(hù)運(yùn)行的相機(jī)應(yīng)用央渣。
- 此外计盒,[setKeyguardDisabledFeatures()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setKeyguardDisabledFeatures(android.content.ComponentName, int)) 方法現(xiàn)在除了可供設(shè)備所有者使用外,還可供配置文件所有者使用痹屹。
- 配置文件所有者可設(shè)置以下鍵盤(pán)鎖限制:
- KEYGUARD_DISABLE_TRUST_AGENTS 和 KEYGUARD_DISABLE_FINGERPRINT章郁,它們影響配置文件上級(jí)用戶(hù)的鍵盤(pán)鎖設(shè)置。
- KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS志衍,它只影響應(yīng)用在托管配置文件中生成的通知暖庄。
- DevicePolicyManager.createAndInitializeUser() 方法和 DevicePolicyManager.createUser() 方法已棄用。
- 當(dāng)給定用戶(hù)的應(yīng)用在前臺(tái)運(yùn)行時(shí)楼肪,[setScreenCaptureDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setScreenCaptureDisabled(android.content.ComponentName, boolean)) 方法現(xiàn)在也會(huì)屏蔽輔助結(jié)構(gòu)培廓。
- EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM 現(xiàn)在默認(rèn)為 SHA-256。出于向后兼容性考慮春叫,仍然支持 SHA-1肩钠,但未來(lái)將會(huì)取消該支持。
-
EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM 現(xiàn)在只接受 SHA-256暂殖。
Android 6.0(API 級(jí)別 23)中曾經(jīng)存在的 Device initializer API 現(xiàn)已刪除
EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS
已刪除价匠,因此 NFC 占位配置無(wú)法通過(guò)編程解鎖受恢復(fù)出廠設(shè)置保護(hù)的設(shè)備。 - 您現(xiàn)在可以使用 EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE extra 在對(duì)托管設(shè)備進(jìn)行 NFC 配置期間向設(shè)備所有者應(yīng)用傳遞數(shù)據(jù)呛每。
- Android for Work API 針對(duì) M 運(yùn)行時(shí)權(quán)限(包括 Work 配置文件踩窖、輔助層及其他內(nèi)容)進(jìn)行了優(yōu)化。新增的 DevicePolicyManager 權(quán)限 API 不會(huì)影響 M 之前版本的應(yīng)用晨横。
- 當(dāng)用戶(hù)退出通過(guò) ACTION_PROVISION_MANAGED_PROFILE 或 ACTION_PROVISION_MANAGED_DEVICE intent 發(fā)起的設(shè)置流程的同步部分時(shí)洋腮,系統(tǒng)現(xiàn)在會(huì)返回RESULT_CANCELED 結(jié)果代碼。
- 對(duì)其他 API 的變更:
- 流量消耗:android.app.usage.NetworkUsageStats 類(lèi)已重命名為 NetworkStats手形。
- 對(duì)全局設(shè)置的變更:
- 這些設(shè)置不再通過(guò) [setGlobalSettings()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String)) 進(jìn)行設(shè)置:
- BLUETOOTH_ON
- DEVELOPMENT_SETTINGS_ENABLED
- MODE_RINGER
- NETWORK_PREFERENCE
- WIFI_ON
- 這些全局設(shè)置現(xiàn)在可通過(guò) [setGlobalSettings()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String)) 進(jìn)行設(shè)置:
- WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN
Android7.0
-
無(wú)障礙改進(jìn)
為提高平臺(tái)對(duì)于視力不佳或視力受損用戶(hù)的易用性啥供,Android 7.0 做出了一些更改。這些更改一般并不要求更改您的應(yīng)用代碼库糠,不過(guò)您應(yīng)仔細(xì)檢查并使用您的應(yīng)用測(cè)試這些功能伙狐,以評(píng)估它們對(duì)用戶(hù)體驗(yàn)的潛在影響。
-
電池和內(nèi)存
Android 7.0 包括旨在延長(zhǎng)設(shè)備電池壽命和減少 RAM 使用的系統(tǒng)行為變更。這些變更可能會(huì)影響您的應(yīng)用訪問(wèn)系統(tǒng)資源鳞骤,以及您的應(yīng)用通過(guò)特定隱式 intent 與其他應(yīng)用交互的方式窒百。
-
屏幕縮放
Android 7.0 支持用戶(hù)設(shè)置顯示尺寸,以放大或縮小屏幕上的所有元素豫尽,從而提升設(shè)備對(duì)視力不佳用戶(hù)的可訪問(wèn)性篙梢。用戶(hù)無(wú)法將屏幕縮放至低于最小屏幕寬度 sw320dp,該寬度是 Nexus 4 的寬度美旧,也是常規(guī)中等大小手機(jī)的寬度渤滞。
當(dāng)設(shè)備密度發(fā)生更改時(shí)榴嗅,系統(tǒng)會(huì)以如下方式通知正在運(yùn)行的應(yīng)用:
- 如果是面向 API 級(jí)別 23 或更低版本系統(tǒng)的應(yīng)用妄呕,系統(tǒng)會(huì)自動(dòng)終止其所有后臺(tái)進(jìn)程。這意味著如果用戶(hù)切換離開(kāi)此類(lèi)應(yīng)用嗽测,轉(zhuǎn)而打開(kāi) Settings 屏幕并更改 Display size 設(shè)置绪励,則系統(tǒng)會(huì)像處理內(nèi)存不足的情況一樣終止該應(yīng)用。如果應(yīng)用具有任何前臺(tái)進(jìn)程唠粥,則系統(tǒng)會(huì)如處理運(yùn)行時(shí)更改中所述將配置變更通知給這些進(jìn)程疏魏,就像對(duì)待設(shè)備屏幕方向變更一樣。
- 如果是面向 Android 7.0 的應(yīng)用晤愧,則其所有進(jìn)程(前臺(tái)和后臺(tái))都會(huì)收到有關(guān)配置變更的通知大莫,如處理運(yùn)行時(shí)更改中所述。
大多數(shù)應(yīng)用并不需要進(jìn)行任何更改即可支持此功能官份,不過(guò)前提是這些應(yīng)用遵循 Android 最佳做法只厘。具體要檢查的事項(xiàng):
- 在屏幕寬度為 sw320dp 的設(shè)備上測(cè)試您的應(yīng)用,并確保其充分運(yùn)行舅巷。
- 當(dāng)設(shè)備配置發(fā)生變更時(shí)羔味,更新任何與密度相關(guān)的緩存信息,例如緩存位圖或從網(wǎng)絡(luò)加載的資源钠右。當(dāng)應(yīng)用從暫停狀態(tài)恢復(fù)運(yùn)行時(shí)赋元,檢查配置變更。
注:如果您要緩存與配置相關(guān)的數(shù)據(jù)爬舰,則最好也包括相關(guān)元數(shù)據(jù),例如該數(shù)據(jù)對(duì)應(yīng)的屏幕尺寸或像素密度寒瓦。保存這些元數(shù)據(jù)便于您在配置變更后決定是否需要刷新緩存數(shù)據(jù)情屹。 - 避免用像素單位指定尺寸,因?yàn)橄袼夭粫?huì)隨屏幕密度縮放杂腰。應(yīng)改為使用與密度無(wú)關(guān)像素 (dp) 單位指定尺寸垃你。
-
NDK 應(yīng)用鏈接至平臺(tái)庫(kù)
從 Android 7.0 開(kāi)始,系統(tǒng)將阻止應(yīng)用動(dòng)態(tài)鏈接非公開(kāi) NDK 庫(kù),這種庫(kù)可能會(huì)導(dǎo)致您的應(yīng)用崩潰惜颇。此行為變更旨在為跨平臺(tái)更新和不同設(shè)備提供統(tǒng)一的應(yīng)用體驗(yàn)皆刺。即使您的代碼可能不會(huì)鏈接私有庫(kù),但您的應(yīng)用中的第三方靜態(tài)庫(kù)可能會(huì)這么做凌摄。因此羡蛾,所有開(kāi)發(fā)者都應(yīng)進(jìn)行相應(yīng)檢查,確保他們的應(yīng)用不會(huì)在運(yùn)行 Android 7.0 的設(shè)備上崩潰锨亏。如果您的應(yīng)用使用原生代碼痴怨,則只能使用公開(kāi) NDK API。
-
低電耗模式
Android 6.0(API 級(jí)別 23)引入了低電耗模式器予,當(dāng)用戶(hù)設(shè)備未插接電源浪藻、處于靜止?fàn)顟B(tài)且屏幕關(guān)閉時(shí),該模式會(huì)推遲 CPU 和網(wǎng)絡(luò)活動(dòng)乾翔,從而延長(zhǎng)電池壽命爱葵。而 Android 7.0 則通過(guò)在設(shè)備未插接電源且屏幕關(guān)閉狀態(tài)下、但不一定要處于靜止?fàn)顟B(tài)(例如用戶(hù)外出時(shí)把手持式設(shè)備裝在口袋里)時(shí)應(yīng)用部分 CPU 和網(wǎng)絡(luò)限制反浓,進(jìn)一步增強(qiáng)了低電耗模式萌丈。
當(dāng)設(shè)備處于充電狀態(tài)且屏幕已關(guān)閉一定時(shí)間后勾习,設(shè)備會(huì)進(jìn)入低電耗模式并應(yīng)用第一部分限制:關(guān)閉應(yīng)用網(wǎng)絡(luò)訪問(wèn)浓瞪、推遲作業(yè)和同步。如果進(jìn)入低電耗模式后設(shè)備處于靜止?fàn)顟B(tài)達(dá)到一定時(shí)間巧婶,系統(tǒng)則會(huì)對(duì)PowerManager.WakeLock
乾颁、AlarmManager 鬧鈴、GPS 和 WLAN 掃描應(yīng)用余下的低電耗模式限制艺栈。無(wú)論是應(yīng)用部分還是全部低電耗模式限制英岭,系統(tǒng)都會(huì)喚醒設(shè)備以提供簡(jiǎn)短的維護(hù)時(shí)間窗口,在此窗口期間湿右,應(yīng)用程序可以訪問(wèn)網(wǎng)絡(luò)并執(zhí)行任何被推遲的作業(yè)/同步诅妹。
請(qǐng)注意毅人,激活屏幕或插接設(shè)備電源時(shí)吭狡,系統(tǒng)將退出低電耗模式并移除這些處理限制。此項(xiàng)新增的行為不會(huì)影響有關(guān)使您的應(yīng)用適應(yīng) Android 6.0(API 級(jí)別 23)中所推出的舊版本低電耗模式的建議和最佳做法丈莺,如對(duì)低電耗模式和應(yīng)用待機(jī)模式進(jìn)行針對(duì)性?xún)?yōu)化中所討論划煮。您仍應(yīng)遵循這些建議(例如使用 Google 云消息傳遞 (GCM) 發(fā)送和接收消息)并開(kāi)始安排更新計(jì)劃以適應(yīng)新增的低電耗模式行為。
-
Project Svelte:后臺(tái)優(yōu)化
Android 7.0 移除了三項(xiàng)隱式廣播缔俄,以幫助優(yōu)化內(nèi)存使用和電量消耗弛秋。此項(xiàng)變更很有必要器躏,因?yàn)殡[式廣播會(huì)在后臺(tái)頻繁啟動(dòng)已注冊(cè)偵聽(tīng)這些廣播的應(yīng)用。刪除這些廣播可以顯著提升設(shè)備性能和用戶(hù)體驗(yàn)蟹略。
移動(dòng)設(shè)備會(huì)經(jīng)歷頻繁的連接變更登失,例如在 WLAN 和移動(dòng)數(shù)據(jù)之間切換時(shí)。目前挖炬,可以通過(guò)在應(yīng)用清單中注冊(cè)一個(gè)接收器來(lái)偵聽(tīng)隱式 CONNECTIVITY_ACTION
廣播揽浙,讓?xiě)?yīng)用能夠監(jiān)控這些變更。由于很多應(yīng)用會(huì)注冊(cè)接收此廣播茅茂,因此單次網(wǎng)絡(luò)切換即會(huì)導(dǎo)致所有應(yīng)用被喚醒并同時(shí)處理此廣播捏萍。
同理,在之前版本的 Android 中空闲,應(yīng)用可以注冊(cè)接收來(lái)自其他應(yīng)用(例如相機(jī))的隱式 ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO 廣播令杈。當(dāng)用戶(hù)使用相機(jī)應(yīng)用拍攝照片時(shí),這些應(yīng)用即會(huì)被喚醒以處理廣播碴倾。
為緩解這些問(wèn)題逗噩,Android 7.0 應(yīng)用了以下優(yōu)化措施:
- 面向 Android 7.0 開(kāi)發(fā)的應(yīng)用不會(huì)收到 CONNECTIVITY_ACTION 廣播,即使它們已有清單條目來(lái)請(qǐng)求接受這些事件的通知跌榔。在前臺(tái)運(yùn)行的應(yīng)用如果使用 BroadcastReceiver 請(qǐng)求接收通知异雁,則仍可以在主線程中偵聽(tīng)CONNECTIVITY_CHANGE
- 應(yīng)用無(wú)法發(fā)送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 廣播。此項(xiàng)優(yōu)化會(huì)影響所有應(yīng)用僧须,而不僅僅是面向 Android 7.0 的應(yīng)用纲刀。
如果您的應(yīng)用使用任何 intent,您仍需要盡快移除它們的依賴(lài)關(guān)系担平,以正確適配 Android 7.0 設(shè)備示绊。Android 框架提供多個(gè)解決方案來(lái)緩解對(duì)這些隱式廣播的需求。例如暂论,JobScheduler API 提供了一個(gè)穩(wěn)健可靠的機(jī)制來(lái)安排滿足指定條件(例如連入無(wú)限流量網(wǎng)絡(luò))時(shí)所執(zhí)行的網(wǎng)絡(luò)操作面褐。您甚至可以使用 JobScheduler 來(lái)適應(yīng)內(nèi)容提供程序變化。如需了解有關(guān) Android N 中后臺(tái)優(yōu)化以及如何改寫(xiě)應(yīng)用的詳細(xì)信息取胎,請(qǐng)參閱后臺(tái)優(yōu)化展哭。
更多信息請(qǐng)猛戳下面的官方鏈接
轉(zhuǎn)載請(qǐng)以鏈接形式標(biāo)明出處:
http://www.reibang.com/p/95790125b7f4
本文出自:103style
or
csdn
http://blog.csdn.net/lxk_1993/article/details/73784883
本文出自:lxk_1993