摘要:是權(quán)限被拒絕,但是沒(méi)有勾選不再提醒。這樣被拒絕后再次申請(qǐng)權(quán)限是不會(huì)彈框提醒的。用戶(hù)點(diǎn)擊拒絕,并勾選不再提示,下次請(qǐng)求權(quán)限時(shí),系統(tǒng)彈窗不會(huì)再出現(xiàn),而且為,此時(shí)你的權(quán)限申請(qǐng)被用戶(hù)徹底拒絕,需要跳轉(zhuǎn)到系統(tǒng)設(shè)置頁(yè)手動(dòng)允許權(quán)限。
版權(quán)聲明:本文已授權(quán)微信公眾號(hào):Android必修課,轉(zhuǎn)載請(qǐng)申明出處官方權(quán)限申請(qǐng)示例:Android6.0以上的系統(tǒng)中,引入了運(yùn)行時(shí)權(quán)限檢查,運(yùn)行時(shí)權(quán)限分為正常權(quán)限和危險(xiǎn)權(quán)限,當(dāng)我們的App調(diào)用了需要危險(xiǎn)權(quán)限的api時(shí),需要向系統(tǒng)申請(qǐng)權(quán)限,系統(tǒng)會(huì)彈出一個(gè)對(duì)話框讓用戶(hù)感知,只有當(dāng)用戶(hù)授權(quán)以后,App才能正常調(diào)用api。
關(guān)于危險(xiǎn)權(quán)限的說(shuō)明,請(qǐng)參閱官方文檔:https://developer.android.goo...
這里采用googleSamples中的權(quán)限申請(qǐng)框架EasyPermissions作為例子:
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks,EasyPermissions.RationaleCallbacks{ private static final int RC_CAMERA_PERM = 123; private static final int RC_LOCATION_CONTACTS_PERM = 124; @AfterPermissionGranted(RC_CAMERA_PERM) public void cameraTask() { EasyPermissions.requestPermissions( this, getString(R.string.rationale_camera), RC_CAMERA_PERM, Manifest.permission.CAMERA); } @AfterPermissionGranted(RC_LOCATION_CONTACTS_PERM) public void locationAndContactsTask() { EasyPermissions.requestPermissions( this, getString(R.string.rationale_location_contacts), RC_LOCATION_CONTACTS_PERM, LOCATION_AND_CONTACTS); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } @Override public void onPermissionsGranted(int requestCode, @NonNull Listperms) { Log.d(TAG, "onPermissionsGranted:" + requestCode + ":" + perms.size()); } @Override public void onPermissionsDenied(int requestCode, @NonNull List perms) { if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { new AppSettingsDialog.Builder(this).build().show(); } } }
官方權(quán)限申請(qǐng)的例子,代碼量相當(dāng)多,每個(gè)涉及危險(xiǎn)權(quán)限的地方都得寫(xiě)這么一堆代碼。
*
改造既然官方例子無(wú)法滿(mǎn)足我們,那只能自己改造了,首先看看我們最后要實(shí)現(xiàn)的效果:
GPermisson.with(this) .permisson(new String[] {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CAMERA}) .callback(new PermissionCallback() { @Override public void onPermissionGranted() {} @Override public void shouldShowRational(String permisson) {} @Override public void onPermissonReject(String permisson) {} }).request();
onPermissionGranted是權(quán)限申請(qǐng)通過(guò)回調(diào)。
shouldShowRational是權(quán)限被拒絕,但是沒(méi)有勾選“不再提醒"。
onPermissonReject是權(quán)限被拒絕,并且勾選了"不再提醒",即徹底被拒絕
可以看到,相對(duì)于官方例子,我們的api簡(jiǎn)潔了很多,并且流式調(diào)用可以讓邏輯更容易接受。
怎么實(shí)現(xiàn)呢?慢慢看
1.編寫(xiě)權(quán)限申請(qǐng)Activity首先,我們封裝一個(gè)透明的Activity,在該Activity中進(jìn)行權(quán)限申請(qǐng)
/* * 權(quán)限申請(qǐng)回調(diào) */ public interface PermissionCallback { void onPermissionGranted(); void shouldShowRational(String permisson); void onPermissonReject(String permisson); } public class PermissionActivity extends Activity { public static final String KEY_PERMISSIONS = "permissions"; private static final int RC_REQUEST_PERMISSION = 100; private static PermissionCallback CALLBACK; /* * 添加一個(gè)靜態(tài)方法方便使用 */ public static void request(Context context, String[] permissions, PermissionCallback callback) { CALLBACK = callback; Intent intent = new Intent(context, PermissionActivity.class); intent.putExtra(KEY_PERMISSIONS, permissions); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); if (!intent.hasExtra(KEY_PERMISSIONS)) { return; } // 當(dāng)api大于23時(shí),才進(jìn)行權(quán)限申請(qǐng) String[] permissions = getIntent().getStringArrayExtra(KEY_PERMISSIONS); if (Build.VERSION.SDK_INT >= 23) { requestPermissions(permissions, RC_REQUEST_PERMISSION); } } @TargetApi(23) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode != RC_REQUEST_PERMISSION) { return; } // 處理申請(qǐng)結(jié)果 boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length]; for (int i = 0; i < permissions.length; ++i) { shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]); } this.onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale); } @TargetApi(23) void onRequestPermissionsResult(String[] permissions, int[] grantResults, boolean[] shouldShowRequestPermissionRationale) { int length = permissions.length; int granted = 0; for (int i = 0; i < length; i++) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { if (shouldShowRequestPermissionRationale[i] == true){ CALLBACK.shouldShowRational(permissions[i]); } else { CALLBACK.onPermissonReject(permissions[i]); } } else { granted++; } } if (granted == length) { CALLBACK.onPermissionGranted(); } finish(); } }
添加一個(gè)透明的主題:
2.封裝一個(gè)門(mén)面類(lèi),提供api調(diào)用public class GPermisson { // 權(quán)限申請(qǐng)回調(diào) private PermissionCallback callback; // 需要申請(qǐng)的權(quán)限 private String[] permissions; private Context context; public GPermisson(Context context) { this.context = context; } public static GPermisson with(Context context) { GPermisson permisson = new GPermisson(context); return permisson; } public GPermisson permisson(String[] permissons) { this.permissions = permissons; return this; } public GPermisson callback(PermissionCallback callback) { this.callback = callback; return this; } public void request() { if (permissions == null || permissions.length <= 0) { return; } PermissionActivity.request(context, permissions, callback); } }
至此,我們就簡(jiǎn)單封裝好了一個(gè)權(quán)限請(qǐng)求庫(kù),達(dá)到上述效果。
等等,這種方式足夠優(yōu)雅了嗎?
想想,每個(gè)涉及權(quán)限的地方,我們還是需要寫(xiě)一段權(quán)限請(qǐng)求代碼,還能簡(jiǎn)化嗎?
上一篇我們通過(guò)AOP封裝了按鈕點(diǎn)擊的優(yōu)雅實(shí)現(xiàn),這里一樣可以用AOP來(lái)簡(jiǎn)化我們的權(quán)限請(qǐng)求。
我們希望一個(gè)注解完成權(quán)限申請(qǐng),例如:
@Permission(permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CONTACTS}) private void initView() {}
這樣比上面的方法又簡(jiǎn)化了很多,但是,有個(gè)問(wèn)題:
大家知道,權(quán)限申請(qǐng)是會(huì)被拒絕的,甚至是會(huì)被勾選上“不再提示”,然后再拒絕。這樣被拒絕后再次申請(qǐng)權(quán)限是不會(huì)彈框提醒的。因此,我們需要處理:
用戶(hù)點(diǎn)擊拒絕,但不勾選“不再提示”,下次請(qǐng)求權(quán)限時(shí),系統(tǒng)彈窗依然會(huì)出現(xiàn),而且shouldShowRequestPermissionRationale(permission)為true,意思是,用戶(hù)拒絕了你,你應(yīng)該顯示一段文字或者其他信息,來(lái)說(shuō)服用戶(hù)允許你的權(quán)限申請(qǐng)。
用戶(hù)點(diǎn)擊拒絕,并勾選“不再提示”,下次請(qǐng)求權(quán)限時(shí),系統(tǒng)彈窗不會(huì)再出現(xiàn),而且shouldShowRequestPermissionRationale(permission)為false,此時(shí)你的權(quán)限申請(qǐng)被用戶(hù)徹底拒絕,需要跳轉(zhuǎn)到系統(tǒng)設(shè)置頁(yè)手動(dòng)允許權(quán)限。
ok,我們知道了@Permission注解里,只有一個(gè)權(quán)限數(shù)組是不夠的,我們還需要有一個(gè)rationale信息和被徹底拒絕后讓用戶(hù)跳轉(zhuǎn)到設(shè)置頁(yè)的信息。
升級(jí) 1.定義注解/** 注意,@Retention需要為RUNTIME,否則運(yùn)行時(shí)時(shí)沒(méi)有這個(gè)注解的 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Permission { /* Permissions */ String[] permissions(); /* Rationales */ int[] rationales() default {}; /* Rejects */ int[] rejects() default {}; }
使用int[]而不使用String[],是因?yàn)镾tring[]傳入的字串無(wú)法適配多語(yǔ)言。
2.改寫(xiě)GPermissionpublic class GPermisson { private static PermissionGlobalConfigCallback globalConfigCallback; private PermissionCallback callback; private String[] permissions; private Context context; public GPermisson(Context context) { this.context = context; } public static void init(PermissionGlobalConfigCallback callback) { globalConfigCallback = callback; } static PermissionGlobalConfigCallback getGlobalConfigCallback() { return globalConfigCallback; } public static GPermisson with(Context context) { GPermisson permisson = new GPermisson(context); return permisson; } public GPermisson permisson(String[] permissons) { this.permissions = permissons; return this; } public GPermisson callback(PermissionCallback callback) { this.callback = callback; return this; } public void request() { if (permissions == null || permissions.length <= 0) { return; } PermissionActivity.request(context, permissions, callback); } /** * 寫(xiě)一個(gè)接口,將申請(qǐng)被拒絕的上述兩種情況交給調(diào)用者自行處理,框架內(nèi)不處理 */ public abstract class PermissionGlobalConfigCallback { abstract public void shouldShowRational(String permission, int ration); abstract public void onPermissonReject(String permission, int reject); } }3.Aspect切面處理類(lèi)
@Aspect public class PermissionAspect { @Around("execution(@me.baron.gpermission.Permission * *(..))") public void aroundJoinPoint(final ProceedingJoinPoint joinPoint) { try { // 獲取方法注解 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); Permission annotation = method.getAnnotation(Permission.class); // 獲取注解參數(shù),這里我們有3個(gè)參數(shù)需要獲取 final String[] permissions = annotation.permissions(); final int[] rationales = annotation.rationales(); final int[] rejects = annotation.rejects(); final List使用 1.引入Aspectj依賴(lài),依賴(lài)方式見(jiàn)上一篇:permissionList = Arrays.asList(permissions); // 獲取上下文 Object object = joinPoint.getThis(); Context context = null; if (object instanceof FragmentActivity) { context = (FragmentActivity) object; } else if (object instanceof Fragment) { context = ((Fragment) object).getContext(); } else if (object instanceof Service) { context = (Service) object; } // 申請(qǐng)權(quán)限 GPermisson.with(context) .permisson(permissions) .callback(new PermissionCallback() { @Override public void onPermissionGranted() { try { // 權(quán)限申請(qǐng)通過(guò),執(zhí)行原方法 joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } @Override public void shouldShowRational(String permisson) { // 申請(qǐng)被拒絕,但沒(méi)有勾選“不再提醒”,這里我們讓外部自行處理 int index = permissionList.indexOf(permisson); int rationale = -1; if (rationales.length > index) { rationale = rationales[index]; } GPermisson.getGlobalConfigCallback().shouldShowRational(permisson, rationale); } @Override public void onPermissonReject(String permisson) { // 申請(qǐng)被拒絕,且勾選“不再提醒”,這里我們讓外部自行處理 int index = permissionList.indexOf(permisson); int reject = -1; if (rejects.length > index) { reject = rejects[index]; } GPermisson.getGlobalConfigCallback().onPermissonReject(permisson, reject); } }).request(); } catch (Exception e) { e.printStackTrace(); } } }
Android優(yōu)雅地處理按鈕重復(fù)點(diǎn)擊
2.設(shè)置全局權(quán)限請(qǐng)求結(jié)果監(jiān)聽(tīng)GPermisson.init(new PermissionGlobalConfigCallback() { @Override public void shouldShowRational(String permission, int ration) { showRationaleDialog(ration); } @Override public void onPermissonReject(String permission, int reject) { showRejectDialog(reject); } }); private void showRationaleDialog(int ration) { new AlertDialog.Builder(MainActivity.this) .setTitle("權(quán)限申請(qǐng)") .setMessage(getString(ration)) .show(); } private void showRejectDialog(int reject) { new AlertDialog.Builder(MainActivity.this) .setTitle("權(quán)限申請(qǐng)") .setMessage(getString(reject)) .setPositiveButton("跳轉(zhuǎn)到設(shè)置頁(yè)", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 本人魅族手機(jī),其他品牌的設(shè)置頁(yè)跳轉(zhuǎn)邏輯不同,請(qǐng)百度解決 Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); startActivity(intent); dialog.dismiss(); } }) .setNegativeButton("取消", null) .show(); }3.在需要權(quán)限的地方添加注解:
@Permission(permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CONTACTS}, rationales = {R.string.location_rationale, R.string.contact_rationale}, rejects = {R.string.location_reject, R.string.contact_reject}) private void initView() {}
一旦權(quán)限申請(qǐng)被拒絕,將會(huì)回調(diào)到全局監(jiān)聽(tīng)中,這里我們只彈窗提醒,若需要其他形式的提醒,自行實(shí)現(xiàn)ui即可。運(yùn)行效果:
如果你們有過(guò)組件化開(kāi)發(fā),就應(yīng)該馬上了解到,我們?cè)谏厦媸褂聾Permission注解傳入的rationale和reject的字符串id,在Module中是會(huì)報(bào)錯(cuò)的,原因是Module中的R.string.xxx不是final常量,而注解值需要final常量值。
@Permission(permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CONTACTS}, rationales = {R.string.location_rationale, R.string.contact_rationale}, rejects = {R.string.location_reject, R.string.contact_reject}) private void initView() {}
那么,如何處理在Module中的情況呢,這里我想到了一個(gè)思路:
既然R.string.xxx不是常量,我們就給注解值傳入我們自定義的常量:
public class Permissions { public static final int LOCATION_RATIONALE = 100; public static final int LOCATION_REJECT= 101; public static final int CONTACT_RATIONALE= 102; public static final int CONTACT_REJECT= 103; }
@Permission(permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CONTACTS}, rationales = {Permissions.LOCATION_RATIONALE, Permissions.CONTACT_RATIONALE}, rejects = {Permissions.LOCATION_REJECT, Permissions.CONTACT_REJECT}) private void initView() {}
然后在全局的監(jiān)聽(tīng)中修改:
GPermisson.init(new PermissionGlobalConfigCallback() { @Override public void shouldShowRational(String permission, int ration) { if (ration == Permissions.LOCATION_RATIONALE) { showRationaleDialog(R.string.location_rationale); } else if (ration == Permissions.CONTACT_RATIONALE) { showRationaleDialog(R.string.contact_rationale); } else { showRationaleDialog(ration); } } @Override public void onPermissonReject(String permission, int reject) { if (reject == Permissions.LOCATION_RATIONALE) { showRejectDialog(R.string.location_reject); } else if (reject == Permissions.CONTACT_RATIONALE) { showRejectDialog(R.string.contact_reject); } else { showRejectDialog(reject); } } });
可能不是那么優(yōu)雅,如果有好的方式,請(qǐng)留言告知,讓大家學(xué)習(xí)學(xué)習(xí)……感謝。
源碼地址:(https://github.com/DevBraon/G...)
想解鎖更多姿勢(shì),請(qǐng)關(guān)注微信公眾號(hào):Android必修課
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/77166.html
摘要:和以前在安裝的是就申請(qǐng)了權(quán)限不同,在,也就之后加入了動(dòng)態(tài)權(quán)限。對(duì)于一些敏感的權(quán)限,決定權(quán)交還給了用戶(hù),不再是強(qiáng)制申請(qǐng)了。還有辦法可以補(bǔ)救一下,就是跳轉(zhuǎn)到設(shè)置頁(yè)面,讓用戶(hù)手動(dòng)開(kāi)啟權(quán)限。網(wǎng)上有個(gè)一個(gè)流行的開(kāi)源庫(kù),采用注解來(lái)快速實(shí)現(xiàn)以上的邏輯。 和以前在安裝 APP 的是就申請(qǐng)了權(quán)限不同,Google 在 API 23,也就 6.0 之后加入了動(dòng)態(tài)權(quán)限。對(duì)于一些敏感的權(quán)限,決定權(quán)交還給了用戶(hù)...
閱讀 3027·2021-11-16 11:42
閱讀 3683·2021-09-08 09:36
閱讀 959·2019-08-30 12:52
閱讀 2494·2019-08-29 14:12
閱讀 784·2019-08-29 13:53
閱讀 3601·2019-08-29 12:16
閱讀 655·2019-08-29 12:12
閱讀 2480·2019-08-29 11:16