本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發布
Android 6.0
-
運行時權限
相機,圖庫,下載,語音,定位....
此版本引入了一種新的權限模式,如今,用戶可直接在運行時管理應用權限。這種模式讓用戶能夠更好地了解和控制權限,同時為應用開發者精簡了安裝和自動更新過程。用戶可為所安裝的各個應用分別授予或撤銷權限。
對于以 Android 6.0(API 級別 23)或更高版本為目標平臺的應用,請務必在運行時檢查和請求權限。要確定您的應用是否已被授予權限,請調用新增的 checkSelfPermission()方法。要請求權限,請調用新增的requestPermissions() 方法。即使您的應用并不以 Android 6.0(API 級別 23)為目標平臺,您也應該在新權限模式下測試您的應用。如需了解有關在您的應用中支持新權限模式的詳情,請參閱使用系統權限。如需了解有關如何評估新模式對應用的影響的提示,請參閱權限最佳做法。
權限管理工具類
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
* 權限工具類
*/
public class PermissionUtils {
/**
* @param context 上下文
* @param activity activity
* @param permissions 權限數組
* @param requestCode 申請碼
* @return true 有權限 false 無權限
*/
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 權限數組
* @param requestCode 申請碼
* @return true 有權限 false 無權限
*/
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 權限數組
* @return 還需要申請的權限
*/
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;
}
/**
* 檢查申請的權限是否全部允許
*/
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;
}
}
/**
* 沒有獲取到權限的提示
*
* @param permissions 權限名字數組
*/
public static void showPermissionsToast(Activity activity, @NonNull String[] permissions) {
if (permissions.length > 0) {
for (String permission : permissions) {
showPermissionToast(activity, permission);
}
}
}
/**
* 沒有獲取到權限的提示
*
* @param permission 權限名字
*/
private static void showPermissionToast(Activity activity, @NonNull String permission) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
//用戶勾選了不再詢問,提示用戶手動打開權限
switch (permission) {
case Manifest.permission.CAMERA:
ToastUtils.showShort("相機權限已被禁止,請在應用管理中打開權限");
break;
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
ToastUtils.showShort("文件權限已被禁止,請在應用管理中打開權限");
break;
case Manifest.permission.RECORD_AUDIO:
ToastUtils.showShort("錄制音頻權限已被禁止,請在應用管理中打開權限");
break;
case Manifest.permission.ACCESS_FINE_LOCATION:
ToastUtils.showShort("位置權限已被禁止,請在應用管理中打開權限");
break;
}
} else {
//用戶沒有勾選了不再詢問,拒絕了權限申請
switch (permission) {
case Manifest.permission.CAMERA:
ToastUtils.showShort("沒有相機權限");
break;
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
ToastUtils.showShort("沒有文件讀取權限");
break;
case Manifest.permission.RECORD_AUDIO:
ToastUtils.showShort("沒有錄制音頻權限");
break;
case Manifest.permission.ACCESS_FINE_LOCATION:
ToastUtils.showShort("沒有位置權限");
break;
}
}
}
}
用法
if (PermissionUtils.checkAndApplyfPermissionActivity(this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUESRCARMEA)) {
//獲取到權限的操作 沒有權限會申請權限 然后在onRequestPermissionsResult處理申請的結果
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (PermissionUtils.checkPermission(grantResults)) {
//申請權限成功
switch (requestCode) {
case 0x001:
//dosomething
break;
case 0x002:
//dosomething
break;
...
}
} else {
//提示沒有什么權限
PermissionUtils.showPermissionsToast(activity, permissions);
//or 去權限管理界面
//gotoPermissionManager(mContext);
}
}
沒有權限去權限管理界面
/**
* 去應用權限管理界面
*/
public static void gotoPermissionManager(Context context) {
Intent intent;
ComponentName comp;
//防止刷機出現的問題
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);
}
}
/**
* 獲取系統屬性值
*/
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;
}
//以下代碼可以跳轉到應用詳情,可以通過應用詳情跳轉到權限界面(6.0系統測試可用)
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);
}
/**
* 安全的啟動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 客戶端
Android 6.0 版移除了對 Apache HTTP 客戶端的支持。如果您的應用使用該客戶端,并以 Android 2.3(API 級別 9)或更高版本為目標平臺,請改用HttpURLConnection 類。此 API 效率更高,因為它可以通過透明壓縮和響應緩存減少網絡使用,并可最大限度降低耗電量。要繼續使用 Apache HTTP API,您必須先在 build.gradle 文件中聲明以下編譯時依賴項:
android {
useLibrary 'org.apache.http.legacy'
}
-
BoringSSL
Android 正在從使用 OpenSSL 庫轉向使用 BoringSSL 庫。如果您要在應用中使用 Android NDK,請勿鏈接到并非 NDK API 組成部分的加密庫,如libcrypto.so和 libssl.so。這些庫并非公共 API,可能會在不同版本和設備上毫無征兆地發生變化或出現故障。此外,您還可能讓自己暴露在安全漏洞的風險之下。請改為修改原生代碼,以通過 JNI 調用 Java 加密 API,或靜態鏈接到您選擇的加密庫。
-
通知
此版本移除了 Notification.setLatestEventInfo()方法。請改用 Notification.Builder 類來構建通知。要重復更新通知,請重復使用Notification.Builder 實例。調用 build() 方法可獲取更新后的 Notification 實例。
adb shell dumpsys notification 命令不再打印輸出您的通知文本。請改用 adb shell dumpsys notification --noredact 命令打印輸出 notification 對象中的文本。build()方法在4.1以上(16+)的系統才能用。
-
音頻管理器變更
不再支持通過 AudioManager 類直接設置音量或將特定音頻流 靜音。[setStreamSolo()](https://developer.android.google.cn/reference/android/media/AudioManager.html#setStreamSolo(int, boolean)) 方法已棄用,您應該改為調用 [requestAudioFocus()](https://developer.android.google.cn/reference/android/media/AudioManager.html#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int)) 方法。類似地,[setStreamMute()](https://developer.android.google.cn/reference/android/media/AudioManager.html#setStreamMute(int, boolean)) 方法也已棄用,請改為調用 [adjustStreamVolume()](https://developer.android.google.cn/reference/android/media/AudioManager.html#adjustStreamVolume(int, int, int)) 方法并傳入方向值 ADJUST_MUTE 或 ADJUST_UNMUTE。
-
Android 密鑰庫變更
從此版本開始,Android 密鑰庫提供程序不再支持 DSA。但仍支持 ECDSA。
停用或重置安全鎖定屏幕時(例如,由用戶或設備管理員執行此類操作時),系統將不再刪除需要閑時加密的密鑰,但在上述事件期間會刪除需要閑時加密的密鑰。
-
APK 驗證
該平臺現在執行的 APK 驗證更為嚴格。如果在清單中聲明的文件在 APK 中并不存在,該 APK 將被視為已損壞。移除任何內容后必須重新簽署 APK。
http://www.lxweimin.com/p/95790125b7f4
http://blog.csdn.net/lxk_1993/article/details/73784883
Android7.0
-
系統權限更改
為了提高私有文件的安全性,面向 Android 7.0 或更高版本的應用私有目錄被限制訪問 (0700)。此設置可防止私有文件的元數據泄漏,如它們的大小或存在性。此權限更改有多重副作用:
私有文件的文件權限不應再由所有者放寬,為使用 MODE_WORLD_READABLE 和/或 MODE_WORLD_WRITEABLE 而進行的此類嘗試將觸發 SecurityException
注:迄今為止,這種限制尚不能完全執行。應用仍可能使用原生 API 或 File API 來修改它們的私有目錄權限。但是,我們強烈反對放寬私有目錄的權限。傳遞軟件包網域外的 file://URI 可能給接收器留下無法訪問的路徑。因此,嘗試傳遞 file://URI 會觸發FileUriExposedException。分享私有文件內容的推薦方法是使用 FileProvider。
DownloadManager不再按文件名分享私人存儲的文件。舊版應用在訪問 COLUMN_LOCAL_FILENAME時可能出現無法訪問的路徑。面向 Android 7.0 或更高版本的應用在嘗試訪問 COLUMN_LOCAL_FILENAME時會觸發 SecurityException。通過使用 [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))將下載位置設置為公共位置的舊版應用仍可以訪問 COLUMN_LOCAL_FILENAME 中的路徑,但是我們強烈反對使用這種方法。對于由 DownloadManager 公開的文件,首選的訪問方式是使用[ContentResolver.openFileDescriptor()](https://developer.android.google.cn/reference/android/content/ContentResolver.html#openFileDescriptor(android.net.Uri, java.lang.String))。
-
在應用間共享文件
對于面向 Android 7.0 的應用,Android 框架執行的 StrictModeAPI 政策禁止在您的應用外部公開 file://URI。如果一項包含文件 URI 的 intent 離開您的應用,則應用出現故障,并現 FileUriExposedException。異常。要在應用間共享文件,您應發送一項 content://URI,并授予 URI 臨時訪問權限。進行此授權的最簡單方式是使用FileProvider類。如需了解有關權限和共享文件的詳細信息,請參閱共享文件。
-
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文件夾,創建provider_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<!-- 前面兩個是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="" />
<!--相機相冊裁剪-->
<external-files-path
name="camera_has_sdcard"
path="" />
<files-path
name="camera_no_sdcard"
path="" />
</paths>
<!--<paths>-->
<!-- xml文件是唯一設置分享的目錄 ,不能用代碼設置-->
<!--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/你的應用的包名/files/ 目錄-->
<!--5.<external-cache-path> Context.getExternalCacheDir().-->
<!-- path :代表設置的目錄下一級目錄 eg:<external-path path="images/"-->
<!--整個目錄為Environment.getExternalStorageDirectory()+"/images/"-->
<!--name: 代表定義在Content中的字段 eg:name = "myimages" ,并且請求的內容的文件名為default_image.jpg-->
<!--則 返回一個URI content://com.example.myapp.fileprovider/myimages/default_image.jpg-->
<!--</paths>-->
</resources>
確認下路徑名
FileProvider 頭部設置的對應標簽
FileProvider 獲取對應路徑邏輯 解析xml文件 對比對應的標簽 獲取對應的路徑
**修改所有用到Uri的地方 圖中的 BuildConfig.APPLICATION_ID 最好還是改成 context.getPackageName() **
官方鏈接:FileProvider
-
APK Signature Scheme v2
Android 7.0引入了全新的 APK Signature Scheme v2。這是加強對包的校驗,啟動了新的簽名后,像美團的多渠道打包方案在7.0機器上就會報錯了。
解決的辦法也很簡單,官方提供了關閉v2簽名的方法,只需要在gradle上配置一下即可:
signingConfigs {
release {
.......
v2SigningEnabled false
}
}
參考:Android7.0適配
鏈接: APK Signature Scheme v2詳細介紹
其他
Android6.0
-
USB 連接
默認情況下,現在通過 USB 端口進行的設備連接設置為僅充電模式。要通過 USB 連接訪問設備及其內容,用戶必須明確地為此類交互授予權限。如果您的應用支持用戶通過 USB 端口與設備進行交互,請將必須顯式啟用交互考慮在內。
-
瀏覽器書簽變更
此版本移除了對全局書簽的支持。android.provider.Browser.getAllBookmarks() 和 android.provider.Browser.saveBookmark() 方法現已移除。同樣,READ_HISTORY_BOOKMARKS 權限和 WRITE_HISTORY_BOOKMARKS 權限也已移除。如果您的應用以 Android 6.0(API 級別 23)或更高版本為目標平臺,請勿從全局提供程序訪問書簽或使用書簽權限。您的應用應改為在內部存儲書簽數據。
-
硬件標識符訪問權
為給用戶提供更嚴格的數據保護,從此版本開始,對于使用 WLAN API 和 Bluetooth API 的應用,Android 移除了對設備本地硬件標識符的編程訪問權。WifiInfo.getMacAddress()方法和 BluetoothAdapter.getAddress()方法現在會返回常量值 02:00:00:00:00:00。
現在,要通過藍牙和 WLAN 掃描訪問附近外部設備的硬件標識符,您的應用必須擁有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 權限。
注:當運行 Android 6.0(API 級別 23)的設備發起后臺 WLAN 或藍牙掃描時,在外部設備看來,該操作的發起來源是一個隨機化 MAC 地址。
-
WLAN 和網絡連接變更
此版本對 WLAN API 和 Networking API 引入了以下行為變更。
現在,您的應用只能更改由您創建的 WifiConfiguration 對象的狀態。系統不允許您修改或刪除由用戶或其他應用創建的 WifiConfiguration 對象。
在之前的版本中,如果應用利用帶有 disableAllOthers=true
設置的 [enableNetwork()](https://developer.android.google.cn/reference/android/net/wifi/WifiManager.html#enableNetwork(int, boolean)) 強制設備連接特定 WLAN 網絡,設備將會斷開與移動數據網絡等其他網絡的連接。在此版本中,設備不再斷開與上述其他網絡的連接。如果您的應用的 targetSdkVersion
為 “20” 或更低,則會固定連接所選 WLAN 網絡。如果您的應用的 targetSdkVersion 為 “21”
或更高,請使用多網絡 API(如 openConnection()、bindSocket() 和新增的bindProcessToNetwork()
方法)來確保通過所選網絡傳送網絡流量。-
相機服務變更
在此版本中,相機服務中共享資源的訪問模式已從之前的“先到先得”訪問模式更改為高優先級進程優先的訪問模式。對服務行為的變更包括:
根據客戶端應用進程的“優先級”授予對相機子系統資源的訪問權,包括打開和配置相機設備。帶有對用戶可見 Activity 或前臺 Activity 的應用進程一般會被授予較高的優先級,從而使相機資源的獲取和使用更加可靠;
當高優先級的應用嘗試使用相機時,系統可能會“驅逐”正在使用相機客戶端的低優先級應用。在已棄用的 Camera API 中,這會導致系統為被驅逐的客戶端調用 [onError()](https://developer.android.google.cn/reference/android/hardware/Camera.ErrorCallback.html#onError(int, android.hardware.Camera))。在 Camera2 API 中,這會導致系統為被驅逐的客戶端調用 onDisconnected();
在配備相應相機硬件的設備上,不同的應用進程可同時獨立打開和使用不同的相機設備。但現在,如果在多進程用例中同時訪問相機會造成任何打開的相機設備的性能或能力嚴重下降,相機服務會檢測到這種情況并禁止同時訪問。即使并沒有其他應用直接嘗試訪問同一相機設備,此變更也可能導致低優先級客戶端被“驅逐”。
更改當前用戶會導致之前用戶帳戶擁有的應用內活動相機客戶端被驅逐。對相機的訪問僅限于訪問當前設備用戶擁有的用戶個人資料。舉例來說,這意味著,當用戶切換到其他帳戶后,“來賓”帳戶實際上無法讓使用相機子系統的進程保持運行狀態。
-
運行時
ART 運行時環境現在可正確實現 newInstance() 方法的訪問規則。此變更修正了之前版本中 Dalvik 無法正確檢查訪問規則的問題。如果您的應用使用newInstance() 方法,并且您想重寫訪問檢查,請調用 setAccessible() 方法(將輸入參數設置為 true)。如果您的應用使用 v7 appcompat 庫或 v7 recyclerview 庫,則您必須更新應用以使用這些庫的最新版本。否則,請務必更新從 XML 引用的任何自定義類,以便能夠訪問它們的類構造函數。此版本更新了動態鏈接程序的行為。動態鏈接程序現在可以識別庫的 soname 與其路徑之間的差異(公開錯誤 6670),并且現在已實現了按 soname 搜索。之前包含錯誤的 DT_NEEDED 條目(通常是開發計算機文件系統上的絕對路徑)卻仍工作正常的應用,如今可能會出現加載失敗。現已正確實現 dlopen(3) RTLD_LOCAL 標記。請注意,RTLD_LOCAL 是默認值,因此不顯式使用 RTLD_LOCAL 的 dlopen(3) 調用將受到影響(除非您的應用顯式使用 RTLD_GLOBAL)。使用 RTLD_LOCAL 時,在隨后通過調用 dlopen(3) 加載的庫中并不能使用這些符號(這與由 DT_NEEDED 條目引用的情況截然不同)。
在之前版本的 Android 上,如果您的應用請求系統加載包含文本重定位信息的共享庫,系統會顯示警告,但仍允許加載共享庫。從此版本開始,如果您的應用的目標 SDK 版本為 23 或更高,則系統會拒絕加載該庫。為幫助您檢測庫是否加載失敗,您的應用應該記錄 dlopen(3) 失敗日志,并在日志中加入dlerror(3) 調用返回的問題描述文本。要詳細了解如何處理文本重定位,請參閱此指南。
-
低電耗模式和應用待機模式
此版本引入了針對空閑設備和應用的最新節能優化技術。這些功能會影響所有應用,因此請務必在這些新模式下測試您的應用。
- 低電耗模式:如果用戶拔下設備的電源插頭,并在屏幕關閉后的一段時間內使其保持不活動狀態,設備會進入低電耗模式,在該模式下設備會嘗試讓系統保持休眠狀態。在該模式下,設備會定期短時間恢復正常工作,以便進行應用同步,還可讓系統執行任何掛起的操作。
- 應用待機模式:應用待機模式允許系統判定應用在用戶未主動使用它時處于空閑狀態。當用戶有一段時間未觸摸應用時,系統便會作出此判定。如果拔下了設備電源插頭,系統會為其視為空閑的應用停用網絡訪問以及暫停同步和作業。
要詳細了解這些節能變更,請參閱對低電耗模式和應用待機模式進行針對性優化。
-
文本選擇
現在,當用戶在您的應用中選擇文本時,您可以在一個浮動工具欄中顯示“剪切”、“復制”和“粘貼”等文本選擇操作。其在用戶交互實現上與為單個視圖啟用上下文操作模式中所述的上下文操作欄類似。
要實現可用于文本選擇的浮動工具欄,請在您的現有應用中做出以下更改:
- 在 View 對象或 Activity 對象中,將 ActionMode 調用從 startActionMode(Callback)更改為 startActionMode(Callback, ActionMode.TYPE_FLOATING)。
- 改為使用 ActionMode.Callback 的現有實現擴展 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 內容對象(如文本選擇矩形)在視圖中的坐標。
- 如果矩形的定位不再有效,并且這是唯一需要聲明為無效的元素,請調invalidateContentRect() 方法。
請注意,如果您使用 Android 支持庫 22.2 修訂版,浮動工具欄不向后兼容,默認情況下 appcompat 會獲得對 ActionMode 對象的控制權。這會禁止顯示浮動工具欄。要在ActionMode 中啟用 AppCompatActivity 支持,請調用 getDelegate(),然后對返回的setHandleNativeActionModesEnabled() 對象調用 AppCompatDelegate,并將輸入參數設置為 false。此調用會將 ActionMode 對象的控制權交還給框架。在運行 Android 6.0(API 級別 23)的設備上,框架可以支持 ActionBar 模式或浮動工具欄模式;而在運行 Android 5.1(API 級別 22)或之前版本的設備上,框架僅支持 ActionBar 模式。
-
Android for Work 變更
此版本包含下列針對 Android for Work 的行為變更:
- 個人上下文中的工作聯系人。Google 撥號器通話記錄現在會在用戶查看通話記錄時顯示工作聯系人。將 [setCrossProfileCallerIdDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean)) 設置為 true 可在 Google 撥號器通話記錄中隱藏托管配置文件聯系人。僅當將 [setBluetoothContactSharingDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setBluetoothContactSharingDisabled(android.content.ComponentName, boolean)) 設置為 false 時,才可以通過藍牙將工作聯系人隨個人聯系人一起顯示給設備。默認情況下,它設置為 true。
-
WLAN 配置刪除:現在,當刪除某個托管配置文件時,將會移除由配置文件所有者添加的 WLAN 配置(例如,通過調用 addNetwork()
方法添加的配置)。 - WLAN 配置鎖定:如果 WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN 不為零,則用戶無法再修改或刪除任何由活動設備所有者創建的 WLAN 配置。用戶仍可創建和修改其自己的 WLAN 配置。活動設備所有者擁有編輯或刪除任何 WLAN 配置(包括并非由其創建的配置)的權限。
- 通過添加 Google 帳戶下載設備規范控制器:向托管環境以外的設備添加需要通過設備規范控制器 (DPC) 應用管理的 Google 帳戶時,帳戶添加流程現在會提示用戶安裝相應的 WPC。在設備初始設置向導中通過 Settings > Accounts 添加帳戶時,也會出現此行為。
-
對特定 DevicePolicyManager API 行為的變更:
- 調用 [setCameraDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setCameraDisabled(android.content.ComponentName, boolean)) 方法只會影響調用該方法的用戶的相機;從托管配置文件調用它不會影響主用戶運行的相機應用。
- 此外,[setKeyguardDisabledFeatures()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setKeyguardDisabledFeatures(android.content.ComponentName, int)) 方法現在除了可供設備所有者使用外,還可供配置文件所有者使用。
- 配置文件所有者可設置以下鍵盤鎖限制:
- KEYGUARD_DISABLE_TRUST_AGENTS 和 KEYGUARD_DISABLE_FINGERPRINT,它們影響配置文件上級用戶的鍵盤鎖設置。
- KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS,它只影響應用在托管配置文件中生成的通知。
- DevicePolicyManager.createAndInitializeUser() 方法和 DevicePolicyManager.createUser() 方法已棄用。
- 當給定用戶的應用在前臺運行時,[setScreenCaptureDisabled()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setScreenCaptureDisabled(android.content.ComponentName, boolean)) 方法現在也會屏蔽輔助結構。
- EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM 現在默認為 SHA-256。出于向后兼容性考慮,仍然支持 SHA-1,但未來將會取消該支持。
-
EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM 現在只接受 SHA-256。
Android 6.0(API 級別 23)中曾經存在的 Device initializer API 現已刪除
EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS
已刪除,因此 NFC 占位配置無法通過編程解鎖受恢復出廠設置保護的設備。 - 您現在可以使用 EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE extra 在對托管設備進行 NFC 配置期間向設備所有者應用傳遞數據。
- Android for Work API 針對 M 運行時權限(包括 Work 配置文件、輔助層及其他內容)進行了優化。新增的 DevicePolicyManager 權限 API 不會影響 M 之前版本的應用。
- 當用戶退出通過 ACTION_PROVISION_MANAGED_PROFILE 或 ACTION_PROVISION_MANAGED_DEVICE intent 發起的設置流程的同步部分時,系統現在會返回RESULT_CANCELED 結果代碼。
- 對其他 API 的變更:
- 流量消耗:android.app.usage.NetworkUsageStats 類已重命名為 NetworkStats。
- 對全局設置的變更:
- 這些設置不再通過 [setGlobalSettings()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String)) 進行設置:
- BLUETOOTH_ON
- DEVELOPMENT_SETTINGS_ENABLED
- MODE_RINGER
- NETWORK_PREFERENCE
- WIFI_ON
- 這些全局設置現在可通過 [setGlobalSettings()](https://developer.android.google.cn/reference/android/app/admin/DevicePolicyManager.html#setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String)) 進行設置:
- WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN
Android7.0
-
無障礙改進
為提高平臺對于視力不佳或視力受損用戶的易用性,Android 7.0 做出了一些更改。這些更改一般并不要求更改您的應用代碼,不過您應仔細檢查并使用您的應用測試這些功能,以評估它們對用戶體驗的潛在影響。
-
電池和內存
Android 7.0 包括旨在延長設備電池壽命和減少 RAM 使用的系統行為變更。這些變更可能會影響您的應用訪問系統資源,以及您的應用通過特定隱式 intent 與其他應用交互的方式。
-
屏幕縮放
Android 7.0 支持用戶設置顯示尺寸,以放大或縮小屏幕上的所有元素,從而提升設備對視力不佳用戶的可訪問性。用戶無法將屏幕縮放至低于最小屏幕寬度 sw320dp,該寬度是 Nexus 4 的寬度,也是常規中等大小手機的寬度。
當設備密度發生更改時,系統會以如下方式通知正在運行的應用:
- 如果是面向 API 級別 23 或更低版本系統的應用,系統會自動終止其所有后臺進程。這意味著如果用戶切換離開此類應用,轉而打開 Settings 屏幕并更改 Display size 設置,則系統會像處理內存不足的情況一樣終止該應用。如果應用具有任何前臺進程,則系統會如處理運行時更改中所述將配置變更通知給這些進程,就像對待設備屏幕方向變更一樣。
- 如果是面向 Android 7.0 的應用,則其所有進程(前臺和后臺)都會收到有關配置變更的通知,如處理運行時更改中所述。
大多數應用并不需要進行任何更改即可支持此功能,不過前提是這些應用遵循 Android 最佳做法。具體要檢查的事項:
- 在屏幕寬度為 sw320dp 的設備上測試您的應用,并確保其充分運行。
- 當設備配置發生變更時,更新任何與密度相關的緩存信息,例如緩存位圖或從網絡加載的資源。當應用從暫停狀態恢復運行時,檢查配置變更。
注:如果您要緩存與配置相關的數據,則最好也包括相關元數據,例如該數據對應的屏幕尺寸或像素密度。保存這些元數據便于您在配置變更后決定是否需要刷新緩存數據。 - 避免用像素單位指定尺寸,因為像素不會隨屏幕密度縮放。應改為使用與密度無關像素 (dp) 單位指定尺寸。
-
NDK 應用鏈接至平臺庫
從 Android 7.0 開始,系統將阻止應用動態鏈接非公開 NDK 庫,這種庫可能會導致您的應用崩潰。此行為變更旨在為跨平臺更新和不同設備提供統一的應用體驗。即使您的代碼可能不會鏈接私有庫,但您的應用中的第三方靜態庫可能會這么做。因此,所有開發者都應進行相應檢查,確保他們的應用不會在運行 Android 7.0 的設備上崩潰。如果您的應用使用原生代碼,則只能使用公開 NDK API。
-
低電耗模式
Android 6.0(API 級別 23)引入了低電耗模式,當用戶設備未插接電源、處于靜止狀態且屏幕關閉時,該模式會推遲 CPU 和網絡活動,從而延長電池壽命。而 Android 7.0 則通過在設備未插接電源且屏幕關閉狀態下、但不一定要處于靜止狀態(例如用戶外出時把手持式設備裝在口袋里)時應用部分 CPU 和網絡限制,進一步增強了低電耗模式。
當設備處于充電狀態且屏幕已關閉一定時間后,設備會進入低電耗模式并應用第一部分限制:關閉應用網絡訪問、推遲作業和同步。如果進入低電耗模式后設備處于靜止狀態達到一定時間,系統則會對PowerManager.WakeLock
、AlarmManager 鬧鈴、GPS 和 WLAN 掃描應用余下的低電耗模式限制。無論是應用部分還是全部低電耗模式限制,系統都會喚醒設備以提供簡短的維護時間窗口,在此窗口期間,應用程序可以訪問網絡并執行任何被推遲的作業/同步。
請注意,激活屏幕或插接設備電源時,系統將退出低電耗模式并移除這些處理限制。此項新增的行為不會影響有關使您的應用適應 Android 6.0(API 級別 23)中所推出的舊版本低電耗模式的建議和最佳做法,如對低電耗模式和應用待機模式進行針對性優化中所討論。您仍應遵循這些建議(例如使用 Google 云消息傳遞 (GCM) 發送和接收消息)并開始安排更新計劃以適應新增的低電耗模式行為。
-
Project Svelte:后臺優化
Android 7.0 移除了三項隱式廣播,以幫助優化內存使用和電量消耗。此項變更很有必要,因為隱式廣播會在后臺頻繁啟動已注冊偵聽這些廣播的應用。刪除這些廣播可以顯著提升設備性能和用戶體驗。
移動設備會經歷頻繁的連接變更,例如在 WLAN 和移動數據之間切換時。目前,可以通過在應用清單中注冊一個接收器來偵聽隱式 CONNECTIVITY_ACTION
廣播,讓應用能夠監控這些變更。由于很多應用會注冊接收此廣播,因此單次網絡切換即會導致所有應用被喚醒并同時處理此廣播。
同理,在之前版本的 Android 中,應用可以注冊接收來自其他應用(例如相機)的隱式 ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO 廣播。當用戶使用相機應用拍攝照片時,這些應用即會被喚醒以處理廣播。
為緩解這些問題,Android 7.0 應用了以下優化措施:
- 面向 Android 7.0 開發的應用不會收到 CONNECTIVITY_ACTION 廣播,即使它們已有清單條目來請求接受這些事件的通知。在前臺運行的應用如果使用 BroadcastReceiver 請求接收通知,則仍可以在主線程中偵聽CONNECTIVITY_CHANGE
- 應用無法發送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 廣播。此項優化會影響所有應用,而不僅僅是面向 Android 7.0 的應用。
如果您的應用使用任何 intent,您仍需要盡快移除它們的依賴關系,以正確適配 Android 7.0 設備。Android 框架提供多個解決方案來緩解對這些隱式廣播的需求。例如,JobScheduler API 提供了一個穩健可靠的機制來安排滿足指定條件(例如連入無限流量網絡)時所執行的網絡操作。您甚至可以使用 JobScheduler 來適應內容提供程序變化。如需了解有關 Android N 中后臺優化以及如何改寫應用的詳細信息,請參閱后臺優化。
更多信息請猛戳下面的官方鏈接
轉載請以鏈接形式標明出處:
http://www.lxweimin.com/p/95790125b7f4
本文出自:103style
or
csdn
http://blog.csdn.net/lxk_1993/article/details/73784883
本文出自:lxk_1993