前言
權(quán)限申請(qǐng)效果

GitHub地址:PermissionUtils
Android8.0昨天已經(jīng)發(fā)布,但是關(guān)于Android版本的最新統(tǒng)計(jì),來看看圖

很明顯,6.0版本目前比重最高,從6.0開始的運(yùn)行時(shí)權(quán)限,是每個(gè)Android開發(fā)者繞不過的問題
正文
雖然Google大佬允許我們將targetSdkVersion 設(shè)置為22及以下,但是向來緊跟Google大佬步伐的我們,怎么會(huì)用這種投機(jī)取消的方式呢。
關(guān)于Android權(quán)限,Google分為兩類,一類為普通權(quán)限,默認(rèn)在AndroidManifest中注冊(cè)即可,主要為以下,這類權(quán)限默認(rèn)不需要用戶授權(quán),比如震動(dòng)、訪問網(wǎng)絡(luò)權(quán)限:
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)權(quán)限,涉及用戶隱私或影響設(shè)備安全
根據(jù)表中可以看出,危險(xiǎn)權(quán)限都是成組出現(xiàn)的,那么在授權(quán)時(shí),如果申請(qǐng)一組中某個(gè)權(quán)限,權(quán)限申請(qǐng)彈窗會(huì)提示整組權(quán)限的說明,而且如果一組權(quán)限中的某個(gè)權(quán)限已經(jīng)被授權(quán),那么在申請(qǐng)同組其他權(quán)限時(shí),系統(tǒng)立即授權(quán),不需要通過用戶允許。
注意: 運(yùn)行時(shí)權(quán)限依然要在AndroidManifest中注冊(cè)
開始適配
這里舉例適配Manifest.permission.ACCESS_FINE_LOCATION位置權(quán)限
- 1、在AndroidManifest中注冊(cè)權(quán)限
- 2、檢查權(quán)限
ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
- 3、申請(qǐng)權(quán)限
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
myRequestCode);
- 4、申請(qǐng)回調(diào)的處理
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case myRequestCode: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//申請(qǐng)通過
} else {
//申請(qǐng)失敗
//是否需要向用戶解釋申請(qǐng)的原因,在用戶點(diǎn)擊拒絕后,彈出dialog,給用戶解釋
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.ACCESS_FINE_LOCATION))
}
}
}
}
}
封裝
運(yùn)行時(shí)權(quán)限的流程很清晰,那么開發(fā)中每次寫這么多固定代碼,為何不進(jìn)行封裝一下呢?那就來分析申請(qǐng)的流程和特點(diǎn),進(jìn)行處理
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權(quán)限申請(qǐng)的封裝,并在內(nèi)部實(shí)現(xiàn)申請(qǐng)結(jié)果的處理}
*/
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;
}
/**
* 權(quán)限檢查
*
* @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 頁面申請(qǐng)權(quán)限
*
* @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頁面申請(qǐng)權(quán)限
*
* @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);
}
/**
* 權(quán)限申請(qǐng)結(jié)果的回調(diào)
*
* @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();
}
}
}
}
/**
* 用戶點(diǎn)擊拒絕,彈出申請(qǐng)權(quán)限的說明彈窗,也可以自定義實(shí)現(xiàn)
*
* @param context Context
* @param title 彈窗標(biāo)題
* @param message 申請(qǐng)權(quán)限解釋說明
* @param confirm 確認(rèn)按鈕的文字,默認(rèn)OK
* @param cancel 取消按鈕呢的文字,默認(rèn)不顯示取消按鈕
*/
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();
}
}
/**
* 用戶勾選不再顯示并點(diǎn)擊拒絕,彈出打開設(shè)置頁面申請(qǐng)權(quán)限,也可以自定義實(shí)現(xiàn)
*
* @param context Context
* @param title 彈窗標(biāo)題
* @param message 申請(qǐng)權(quán)限解釋說明
* @param confirm 確認(rèn)按鈕的文字,默認(rèn)OK
* @param cancel 取消按鈕呢的文字,默認(rèn)不顯示取消按鈕
*/
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();
}
}
/**
* 打開設(shè)置頁面打開權(quán)限
*
* @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();
}
}
/**
* 打開設(shè)置頁面的回調(diào)
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 10: //這里值是打開設(shè)置頁面申請(qǐng)權(quán)限的RequestCode,默認(rèn)為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;
}
}
/**
* 權(quán)限申請(qǐng)回調(diào)
*/
public interface RequestPermissionListener {
void requestConfirm(); //申請(qǐng)成功
void requestCancel(); //拒絕
void requestCancelAgain(); //勾選不再提示并拒絕
void requestFailed(); //在設(shè)置頁面申請(qǐng)權(quán)限失敗
}
}
封裝的工具類使用 (分為四步)
檢查權(quán)限
PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)
申請(qǐng)權(quán)限
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申請(qǐng)成功
toast(null);
}
@Override
public void requestCancel() {
//用戶拒絕,對(duì)用戶解釋申請(qǐng)理由
//如果想使用封裝好的彈窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申請(qǐng)權(quán)限", "需要位置權(quán)限", null, null);
}
@Override
public void requestCancelAgain() {
//用戶勾選不再提示并拒絕,申請(qǐng)打開應(yīng)用設(shè)置頁面申請(qǐng)權(quán)限,具體邏輯自己寫
//使用默認(rèn)封裝好的提示,并打開設(shè)置頁面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申請(qǐng)權(quán)限", "去設(shè)置頁面打開位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申請(qǐng)失敗
toast("對(duì)不起,沒有權(quán)限,退出");
}
});
權(quán)限回調(diào)的處理
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
打開設(shè)置頁面申請(qǐng)權(quán)限的處理
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);
根據(jù)封裝好的工具類,寫個(gè)簡(jiǎn)單例子
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() {
//申請(qǐng)成功
toast(null);
}
@Override
public void requestCancel() {
//用戶拒絕,對(duì)用戶解釋申請(qǐng)理由
//如果想使用封裝好的彈窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申請(qǐng)權(quán)限", "需要位置權(quán)限", null, null);
}
@Override
public void requestCancelAgain() {
//用戶勾選不再提示并拒絕,申請(qǐng)打開應(yīng)用設(shè)置頁面申請(qǐng)權(quán)限,具體邏輯自己寫
//使用默認(rèn)封裝好的提示,并打開設(shè)置頁面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申請(qǐng)權(quán)限", "去設(shè)置頁面打開位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申請(qǐng)失敗
toast("對(duì)不起,沒有權(quán)限,退出");
}
});
}
}
//一定要對(duì)權(quán)限回調(diào)進(jìn)行處理
@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);
}
//如果打開了設(shè)置頁面申請(qǐng)權(quán)限,一定要對(duì)回調(diào)進(jìn)行處理
@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 ? "權(quán)限申請(qǐng)成功" : message, Toast.LENGTH_SHORT).show();
}
}
結(jié)束
封裝后的權(quán)限申請(qǐng),結(jié)構(gòu)更加清晰,根據(jù)回調(diào)結(jié)果的不同可以做出相應(yīng)處理,也封裝了權(quán)限申請(qǐng)的說明彈窗,如果沒有特殊的設(shè)計(jì)要求,即可滿足日常開發(fā)的權(quán)限申請(qǐng)工作。
GitHub地址:PermissionUtils