前言
權限申請效果
GitHub地址:PermissionUtils
Android8.0昨天已經發(fā)布棉安,但是關于Android版本的最新統(tǒng)計底扳,來看看圖
很明顯,6.0版本目前比重最高贡耽,從6.0開始的運行時權限衷模,是每個Android開發(fā)者繞不過的問題
正文
雖然Google大佬允許我們將targetSdkVersion 設置為22及以下,但是向來緊跟Google大佬步伐的我們蒲赂,怎么會用這種投機取消的方式呢阱冶。
關于Android權限,Google分為兩類滥嘴,一類為普通權限木蹬,默認在AndroidManifest中注冊即可,主要為以下若皱,這類權限默認不需要用戶授權镊叁,比如震動尘颓、訪問網絡權限:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
而另一類就是危險權限,涉及用戶隱私或影響設備安全
根據表中可以看出晦譬,危險權限都是成組出現(xiàn)的疤苹,那么在授權時,如果申請一組中某個權限敛腌,權限申請彈窗會提示整組權限的說明卧土,而且如果一組權限中的某個權限已經被授權,那么在申請同組其他權限時像樊,系統(tǒng)立即授權夸溶,不需要通過用戶允許。
注意: 運行時權限依然要在AndroidManifest中注冊
開始適配
這里舉例適配Manifest.permission.ACCESS_FINE_LOCATION位置權限
- 1、在AndroidManifest中注冊權限
- 2拣宏、檢查權限
ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
- 3沈贝、申請權限
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
myRequestCode);
- 4、申請回調的處理
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case myRequestCode: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//申請通過
} else {
//申請失敗
//是否需要向用戶解釋申請的原因勋乾,在用戶點擊拒絕后宋下,彈出dialog,給用戶解釋
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.ACCESS_FINE_LOCATION))
}
}
}
}
}
封裝
運行時權限的流程很清晰辑莫,那么開發(fā)中每次寫這么多固定代碼学歧,為何不進行封裝一下呢?那就來分析申請的流程和特點各吨,進行處理
package com.ddz.lifestyle.utils;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.SparseArray;
import com.ddz.lifestyle.LifeStyle;
import com.ddz.lifestyle.http.GankApiStores;
import com.ddz.lifestyle.http.bean.BookBean;
import com.tencent.smtt.sdk.QbSdk;
import org.jetbrains.annotations.NotNull;
/**
* @Author: ddz
* Creation time: 17.8.11 17:11
* describe:{Android權限申請的封裝枝笨,并在內部實現(xiàn)申請結果的處理}
*/
public class PermissionUtils {
private static PermissionUtils permission;
private String[] permissions;
private Activity mActivity;
private RequestPermissionListener PermissionListener;
public static int requestCode = 100; //requestCode傳值為100
public static PermissionUtils getInstance() {
if (null == permission) {
synchronized (PermissionUtils.class) {
if (null == permission) {
permission = new PermissionUtils();
}
}
}
return permission;
}
/**
* 權限檢查
*
* @param permission
* @return
*/
public boolean checkPermission(@NonNull String permission) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
} else {
return ContextCompat.checkSelfPermission(LifeStyle.getContext(), permission) == PackageManager.PERMISSION_GRANTED;
}
}
/**
* Activity 頁面申請權限
*
* @param activity
* @param permissions
* @param requestCode
* @param requestPermissionListener
*/
public void requestPermissiion(final @NonNull Activity activity,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode, @NonNull RequestPermissionListener requestPermissionListener) {
this.mActivity = activity;
PermissionListener = requestPermissionListener;
this.permissions = permissions;
ActivityCompat.requestPermissions(activity, permissions, requestCode);
}
/**
* Fragment頁面申請權限
*
* @param fragment
* @param permissions
* @param requestCode
* @param requestPermissionListener
*/
public void requestFragmentPermission(final @NonNull Fragment fragment,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode, @NonNull RequestPermissionListener requestPermissionListener) {
PermissionListener = requestPermissionListener;
this.permissions = permissions;
fragment.requestPermissions(permissions, requestCode);
}
/**
* 權限申請結果的回調
*
* @param activity
* @param requestCode
* @param permissions
* @param grantResults
*/
public void onRequestPermissionResult(final @NonNull Activity activity, int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
this.mActivity = activity;
if (null != PermissionListener) {
if (requestCode == PermissionUtils.requestCode && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
PermissionListener.requestConfirm();
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permissions[0])) {
PermissionListener.requestCancel();
} else {
PermissionListener.requestCancelAgain();
}
}
}
}
/**
* 用戶點擊拒絕,彈出申請權限的說明彈窗,也可以自定義實現(xiàn)
*
* @param context Context
* @param title 彈窗標題
* @param message 申請權限解釋說明
* @param confirm 確認按鈕的文字伺帘,默認OK
* @param cancel 取消按鈕呢的文字昭躺,默認不顯示取消按鈕
*/
public void requestDialog(Context context, @NonNull String title, @NonNull String message, String confirm, String cancel) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle(title).setMessage(message);
builder.setPositiveButton(confirm == null ? "OK" : confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissiion(mActivity, permissions, requestCode, PermissionListener);
dialog.dismiss();
}
});
if (null != cancel) {
builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
builder.setCancelable(false);
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用戶勾選不再顯示并點擊拒絕忌锯,彈出打開設置頁面申請權限伪嫁,也可以自定義實現(xiàn)
*
* @param context Context
* @param title 彈窗標題
* @param message 申請權限解釋說明
* @param confirm 確認按鈕的文字,默認OK
* @param cancel 取消按鈕呢的文字偶垮,默認不顯示取消按鈕
*/
public void requestDialogAgain(Context context, @NonNull String title, @NonNull String message, String confirm, String cancel) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle(title).setMessage(message);
builder.setPositiveButton(confirm == null ? "OK" : confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startSettingActivity(mActivity);
dialog.dismiss();
}
});
if (null != cancel) {
builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
builder.setCancelable(false);
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打開設置頁面打開權限
*
* @param context
*/
public void startSettingActivity(@NonNull Activity context) {
try {
Intent intent =
new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" +
context.getPackageName()));
intent.addCategory(Intent.CATEGORY_DEFAULT);
context.startActivityForResult(intent, 10); //這里的requestCode和onActivityResult中requestCode要一致
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打開設置頁面的回調
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 10: //這里值是打開設置頁面申請權限的RequestCode张咳,默認為10
try {
if (null != PermissionListener) {
if (null != permissions && permissions.length > 0) {
for (String permission : permissions) {
if (checkPermission(permission)) {
PermissionListener.requestConfirm();
} else {
PermissionListener.requestFailed();
}
}
} else {
PermissionListener.requestFailed();
}
} else {
PermissionListener.requestFailed();
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
/**
* 權限申請回調
*/
public interface RequestPermissionListener {
void requestConfirm(); //申請成功
void requestCancel(); //拒絕
void requestCancelAgain(); //勾選不再提示并拒絕
void requestFailed(); //在設置頁面申請權限失敗
}
}
封裝的工具類使用 (分為四步)
檢查權限
PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)
申請權限
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申請成功
toast(null);
}
@Override
public void requestCancel() {
//用戶拒絕,對用戶解釋申請理由
//如果想使用封裝好的彈窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申請權限", "需要位置權限", null, null);
}
@Override
public void requestCancelAgain() {
//用戶勾選不再提示并拒絕似舵,申請打開應用設置頁面申請權限脚猾,具體邏輯自己寫
//使用默認封裝好的提示,并打開設置頁面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申請權限", "去設置頁面打開位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申請失敗
toast("對不起,沒有權限砚哗,退出");
}
});
權限回調的處理
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
打開設置頁面申請權限的處理
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);
根據封裝好的工具類龙助,寫個簡單例子
package com.ddz.lifestyle.view.activity;
import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.ddz.lifestyle.R;
import com.ddz.lifestyle.utils.PermissionUtils;
/**
* @Author: ddz
* Creation time: 17.8.11 16:07
* describe:()
*/
public class TestActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPremission();
}
private void initPremission() {
if (PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
toast(null);
} else {
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申請成功
toast(null);
}
@Override
public void requestCancel() {
//用戶拒絕,對用戶解釋申請理由
//如果想使用封裝好的彈窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申請權限", "需要位置權限", null, null);
}
@Override
public void requestCancelAgain() {
//用戶勾選不再提示并拒絕蛛芥,申請打開應用設置頁面申請權限提鸟,具體邏輯自己寫
//使用默認封裝好的提示,并打開設置頁面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申請權限", "去設置頁面打開位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申請失敗
toast("對不起,沒有權限仅淑,退出");
}
});
}
}
//一定要對權限回調進行處理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
//如果打開了設置頁面申請權限称勋,一定要對回調進行處理
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
private void toast(String message) {
Toast.makeText(TestActivity.this, message == null ? "權限申請成功" : message, Toast.LENGTH_SHORT).show();
}
}
結束
封裝后的權限申請,結構更加清晰涯竟,根據回調結果的不同可以做出相應處理赡鲜,也封裝了權限申請的說明彈窗,如果沒有特殊的設計要求庐船,即可滿足日常開發(fā)的權限申請工作银酬。
GitHub地址:PermissionUtils