在鴻蒙系統(tǒng)上實現(xiàn)權(quán)限請求框架—桃夭權(quán)限請求框架
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
本文正在參與優(yōu)質(zhì)創(chuàng)作者激勵
一、簡介
桃夭是鴻蒙系統(tǒng)上的一款權(quán)限請求框架,對請求權(quán)限的代碼進(jìn)行高度封裝,極大的簡化了申請權(quán)限的代碼邏輯,同時支持在Ability、FractionAbility、AbilitySlice、Fractiion里面申請權(quán)限。
二、源碼
建議大家把源碼下載下來看看。
三、申請權(quán)限的一般步驟
- 判斷是否有權(quán)限,如果有權(quán)限,直接進(jìn)行下一步。
- 如果沒有權(quán)限,可以彈窗告知用戶申請權(quán)限的原因。
- 彈窗告知用戶后,如果用戶同意申請權(quán)限,則判斷用戶是否點擊了不再提醒。
- 如果用戶沒有點擊不再提醒,則開始申請權(quán)限。
- 如果用戶點擊了不再提醒,則彈窗告知用戶去設(shè)置頁面開啟權(quán)限,用戶點擊彈窗后,跳轉(zhuǎn)到設(shè)置頁面。
- 重寫onRequestPermissionsFromUserResult方法,判斷用戶是否授予權(quán)限。
每次申請權(quán)限的時候,都需要經(jīng)過以上幾個步驟,當(dāng)申請的權(quán)限越來越多,大量的重復(fù)代碼就出現(xiàn)了。為了減少重復(fù)代碼,我封裝了一個權(quán)限請求框架,權(quán)限請求框架取名為桃夭。
四、桃夭的使用方式
如下代碼,先添加依賴,然后你只需要告知權(quán)限請求框架你需要什么權(quán)限,權(quán)限請求框架就會告知你權(quán)限申請成功還是失敗。你不需要手動判斷是否有權(quán)限,不需要彈窗告知用戶申請權(quán)限的原因,不需要判斷用戶是否點擊了不再提醒,不需要跳轉(zhuǎn)設(shè)置頁面讓用戶開啟權(quán)限,不需要重寫onRequestPermissionsFromUserResult方法??蚣馨堰@些代碼邏輯都給做了,你只需要關(guān)注權(quán)限申請成功還是失敗。
申請權(quán)限變得如此之簡單。
添加依賴
- api 'io.gitee.zhongte:taoyao:1.0.1'
申請權(quán)限
- // 申請多設(shè)備協(xié)同權(quán)限
- EasyPermission.requestPermission(this, EasyPermission.DISTRIBUTED_DATASYNC, new PermissionAction() {
- @Override
- public void onGranted(List<String> permissions) {
- // 權(quán)限申請成功
- }
- @Override
- public void onDenied(List<String> permissions) {
- // 權(quán)限申請失敗
- }
- }, SystemPermission.DISTRIBUTED_DATASYNC);
申請權(quán)限的時候可能會涉及到兩個彈窗,一個彈窗是用來告知用戶申請權(quán)限的原因,另一個彈窗是用來告知用戶去設(shè)置頁面開啟權(quán)限,這兩個彈窗在不同的應(yīng)用里面可能長得不一樣,所以這兩個彈窗并沒有被封裝到桃夭框架里面,而是需要使用者根據(jù)你的彈窗樣式對桃夭進(jìn)行二次封裝,我在源碼里面對桃夭框架進(jìn)行了二次封裝,大家可以把源碼下載下來,參考下我是如何對桃夭框架進(jìn)行二次封裝的。二次封裝完成后,就可以愉快的使用上面的代碼申請權(quán)限了。
五、實現(xiàn)原理
5、1 檢測申請的權(quán)限是否在配置文件中聲明
申請的權(quán)限必須在配置文件中聲明,否則桃夭會直接拋異常。如何檢測申請的權(quán)限是否在配置文件中聲明。
如下代碼,獲取bundleManager對象,通過bundleManager對象獲取應(yīng)用信息,之后就可以獲取應(yīng)用在配置文件中聲明的權(quán)限了。
- /**
- * 獲取在配置文件中聲明的權(quán)限
- *
- * @param context 上下文
- * @return 在配置文件中聲明的權(quán)限
- */
- private List<String> getConfigPermissions(Context context) {
- // 獲取bundleManager對象
- IBundleManager bundleManager = context.getBundleManager();
- String bundleName = context.getBundleName();
- try {
- // 獲取應(yīng)用信息
- BundleInfo bundleInfo = bundleManager.getBundleInfo(bundleName, IBundleManager.GET_BUNDLE_WITH_REQUESTED_PERMISSION);
- // 獲取應(yīng)用在配置文件中聲明的權(quán)限
- List<String> reqPermissionDetails = bundleInfo.reqPermissions;
- if (reqPermissionDetails == null || reqPermissionDetails.isEmpty()) {
- throw new IllegalStateException("請在配置文件中聲明要申請的權(quán)限");
- }
- return reqPermissionDetails;
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return new ArrayList<>();
- }
獲取到在配置文件中聲明的權(quán)限后,就可以判斷申請的權(quán)限是否在配置文件中了
- /**
- * 檢查申請的權(quán)限是否在配置文件中聲明
- *
- * @param permissions 要申請的權(quán)限
- */
- private void checkPermissions(String... permissions) {
- if (permissions == null || permissions.length == 0) {
- throw new IllegalArgumentException("請至少申請一個權(quán)限");
- }
- // 獲取在配置文件中聲明的權(quán)限
- mReqPermissions = getConfigPermissions(mOrigin.getContext());
- if (mReqPermissions.isEmpty()) {
- throw new IllegalStateException("請在配置文件中聲明要申請的權(quán)限");
- }
- for (String target : permissions) {
- if (!mReqPermissions.contains(target)) {
- // 沒有在配置中聲明要申請的權(quán)限,直接拋異常
- throw new IllegalStateException(String.format("%1$s權(quán)限沒有配置文件中聲明", target));
- }
- }
- }
5、2 判斷是否有權(quán)限
檢測完權(quán)限是否在配置中聲明后,就可以判斷是否有權(quán)限了。這里就是通過上下文對象的verifySelfPermission方法來判斷是否有權(quán)限,如果沒有權(quán)限,可以彈窗告知用戶申請的原因。
- /**
- * 是否有權(quán)限
- *
- * @param context
- * @param permissions
- * @return
- */
- @Override
- public boolean hasPermission(Context context, List<String> permissions) {
- for (String permission : permissions) {
- int result = context.verifySelfPermission(permission);
- if (result == IBundleManager.PERMISSION_DENIED) {
- // 沒有權(quán)限
- return false;
- }
- }
- return true;
- }
5、3 判斷用戶是否點擊了不再提醒
通過上下文對象的canRequestPermission方法來判斷用戶是否點擊了不再提醒。
- /**
- * 用戶是否點擊了不在提醒
- *
- * @param permission 權(quán)限
- * @return
- */
- @Override
- public boolean canRequestPermission(String permission) {
- return mContext.canRequestPermission(permission);
- }
5、4 跳轉(zhuǎn)到設(shè)置頁面
如果用戶點擊了不再提醒,則可以跳轉(zhuǎn)到設(shè)置頁面讓用戶開啟權(quán)限
- /**
- * 跳轉(zhuǎn)到設(shè)置頁面
- */
- @Override
- public void gotoSetting() {
- try {
- Intent intent = new Intent();
- intent.setAction(IntentConstants.ACTION_APPLICATION_DETAILS_SETTINGS);
- intent.setUri(Uri.parse("package:" + mOrigin.getContext().getBundleName()));
- mOrigin.startAbility(intent);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
5、5 啟動透明的Ability申請權(quán)限
如果沒有權(quán)限,用戶頁面沒有點擊不再提醒,那就可以申請權(quán)限了。為了不讓調(diào)用者重寫onRequestPermissionsFromUserResult方法,桃夭內(nèi)部啟動了一個Ability。如下代碼。
- /**
- * 開啟一個透明的Ability來申請權(quán)限,這樣外界就不需要重寫onRequestPermissionsFromUserResult方法
- */
- public class PermissionAbility extends Ability {
- private static final int REQUEST_CODE = 0X10;
- public static final String KEY_PERMISSION = "key_permission";
- public static final String KEY_TYPE = "key_type";
- public static final String SENSITIVE_PERMISSION = "sensitive_permission";
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- getWindow().setTransparent(true);
- super.setUIContent(ResourceTable.Layout_ability_permission);
- List<String> permissions = intent.getSerializableParam(KEY_PERMISSION);
- String permissionType = intent.getStringParam(KEY_TYPE);
- // 請求權(quán)限
- requestPermissionsFromUser(permissions.toArray(new String[0]), REQUEST_CODE);
- }
- @Override
- public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
- super.onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
- // 權(quán)限的回調(diào)方法
- Postman.send(permissions, grantResults);
- terminateAbility();
- }
- @Override
- protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
- super.onAbilityResult(requestCode, resultCode, resultData);
- }
- }
直接啟動一個Ability會發(fā)生頁面跳轉(zhuǎn),為了不讓頁面發(fā)生跳轉(zhuǎn),這里啟動了一個透明的Ability。如何將Ability設(shè)置透明,如下代碼。在abilities節(jié)點添加metaData,這里最關(guān)鍵的是Translucent,也就是透明。
- "abilities": [
- {
- "orientation": "unspecified",
- "name": "com.poetry.taoyao.ability.PermissionAbility",
- "icon": "$media:icon",
- "description": "$string:permissionability_description",
- "label": "$string:taoyao_PermissionAbility",
- "type": "page",
- "launchType": "standard",
- "metaData": {
- "customizeData": [
- {
- "name": "hwc-theme",
- "value": "androidhwext:style/Theme.Emui.Translucent.NoTitleBar"
- }
- ]
- }
- }
僅僅有上面的步驟好不夠,需要在Ability或者AbilitySlice里面將窗口設(shè)置成透明。如下代碼。
- getWindow().setTransparent(true);
經(jīng)過上面兩步,也就是將Ability的主題和窗口都設(shè)置成透明,這樣就能將Ability變成透明的了,同時也不會發(fā)生頁面跳轉(zhuǎn)。
5、6 判斷用戶是否授予權(quán)限
判斷用戶是否授予權(quán)限,可以使用標(biāo)準(zhǔn)的方式來判斷。也就是通過grantResult來判斷用戶是否授予權(quán)限。
- @Override
- public boolean hasPermission(int[] grantResults, String... permissions) {
- if (grantResults == null || grantResults.length <= 0) {
- return false;
- }
- for (int grantResult : grantResults) {
- if (grantResult == IBundleManager.PERMISSION_DENIED) {
- return false;
- }
- }
- return true;
- }
其實還有另外的方式來判斷用戶是否授予權(quán)限。也就是不管用戶是否授權(quán),直接訪問相關(guān)業(yè)務(wù)。比如,申請錄音權(quán)限,當(dāng)系統(tǒng)回調(diào)onRequestPermissionsFromUserResult方法時,直接去錄音,如果發(fā)生異常,捕獲異常說明沒有權(quán)限。如下代碼:
- /**
- * 通過直接錄音的方式來判斷是否有錄音權(quán)限
- *
- * @param context
- * @return
- * @throws Throwable
- */
- @Override
- public boolean test(Context context) throws Throwable {
- AudioStreamInfo audioStreamInfo = new AudioStreamInfo.Builder().encodingFormat(
- AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
- .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_IN_STEREO)
- .sampleRate(AUDIO_SAMPLE_RATE)
- .build();
- AudioCapturerInfo audioCapturerInfo = new AudioCapturerInfo.Builder().audioStreamInfo(audioStreamInfo).build();
- try {
- AudioCapturer capturer = new AudioCapturer(audioCapturerInfo);
- // 錄音
- capturer.start();
- new Timer().schedule(new TimerTask() {
- @Override
- public void run() {
- capturer.stop();
- }
- }, AUDIO_RECORDING_TIME);
- // 沒有發(fā)生異常,有權(quán)限
- return true;
- } catch (Exception e) {
- // 發(fā)生異常,無權(quán)限
- return false;
- }
- }
桃夭在判斷用戶是否授權(quán)時,上面的兩種方式都使用了。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)