初探Android6.0權(quán)限庫(kù)PermissionsDispatcher

關(guān)于Android6.0運(yùn)行時(shí)權(quán)限問題,大家應(yīng)該不會(huì)陌生,這個(gè)坑我早就傻傻的跳進(jìn)去過,說一個(gè)笑話,api23剛出來不久,在沒搞清新特性之前,我就在項(xiàng)目中用上了,發(fā)到線上的包因?yàn)橐粋€(gè)權(quán)限忘了判斷而崩潰,后來緊急熱修復(fù),還特意寫了一篇博客來總結(jié),言歸正傳,那么這個(gè)PermissionsDispatcher什么東西;

PermissionsDispatcher是一個(gè)用注解方式來處理Android6.0運(yùn)行時(shí)權(quán)限的庫(kù),旨在高效處理權(quán)限問題。

用它一張圖
用它一張圖

使用對(duì)比

首先看看PermissionsDispatcher怎么用,我們拿普通的權(quán)寫法來和它對(duì)比:

  • 普通寫法
//發(fā)起一個(gè)權(quán)限檢測(cè)
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
    // 我們需要向用戶解釋為什么需要這個(gè)授權(quán),主要是用戶已經(jīng)拒絕過一次了
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

    } else {
        //不需要解釋,
        //請(qǐng)求權(quán)限,請(qǐng)求了權(quán)限一個(gè)數(shù)組
        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);   
        // MY_PERMISSIONS_REQUEST_READ_CONTACTS 是程序員自己定義的一個(gè)常量,在結(jié)果回調(diào)時(shí)判斷,類似于startActivityForResult()方法中的requestCode
        
    }
}

//結(jié)果回調(diào)
@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
     //grantResults對(duì)應(yīng)于請(qǐng)求權(quán)限的那個(gè)String[]
    switch (requestCode) {
        //對(duì)應(yīng)剛才那個(gè)請(qǐng)求code
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // 如果權(quán)限取消,數(shù)組是空的
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
              //獲取權(quán)限成功
            } else {
                //獲取權(quán)限失敗
            }
            return;
        }
      //其他請(qǐng)求
    }
}

  • PermissionsDispatcher寫法
//類注解,標(biāo)注這個(gè)類需要權(quán)限
@RuntimePermissions
public class MainActivity extends AppCompatActivity {

  //需要檢查權(quán)限的方法
    @NeedsPermission(Manifest.permission.CAMERA)
    void showCamera() {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
                .addToBackStack("camera")
                .commitAllowingStateLoss();
    }

    //對(duì)這個(gè)權(quán)限的解釋
    @OnShowRationale(Manifest.permission.CAMERA)
    void showRationaleForCamera(final PermissionRequest request) {
        new AlertDialog.Builder(this)
            .setMessage(R.string.permission_camera_rationale)
            .setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
            .setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
            .show();
    }

    //權(quán)限被拒絕
    @OnPermissionDenied(Manifest.permission.CAMERA)
    void showDeniedForCamera() {
        Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
    }
      //用戶勾選了不再詢問且被拒
    @OnNeverAskAgain(Manifest.permission.CAMERA)
    void showNeverAskForCamera() {
        Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show();
    }
   @Override
   public void onRequestPermissionsResult(int requestCode,       @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode,  permissions, grantResults);
        MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    
}
}

    //真正調(diào)用showCamera方法:
    MainActivityPermissionsDispatcher.showCameraWithCheck(this);

兩者寫法的對(duì)比可以看出,用PermissionsDispatcher寫的邏輯更加清晰,避免各種if判斷,顯得清爽;

如何引入

  • 當(dāng)你項(xiàng)目中使用的Android Gradle Plugin版本大于2.2時(shí)
// module build.gradle
ependencies {
  compile 'com.github.hotchemi:permissionsdispatcher:${latest.version}'
  annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:${latest.version}'
}
  • 當(dāng)Android Gradle Plugin版本小于2.2時(shí)
// project build.gradle
buildscript {
  dependencies {
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

// module build.gradle

apply plugin: 'android-apt'

dependencies {
  compile 'com.github.hotchemi:permissionsdispatcher:${latest.version}'
  apt 'com.github.hotchemi:permissionsdispatcher-processor:${latest.version}'
}

至于為什么要區(qū)分gradle plugin版本2.2,可以查看android-apt這個(gè)庫(kù)的作者寫的聲明,和Android官方對(duì)Android Studio2.2新功能的介紹;你如果感興趣翻看,你會(huì)了解到jack編譯這個(gè)詞匯,這個(gè)有機(jī)會(huì)再深入了解吧;

源碼初探

1.down代碼

代碼在github上,我是先fork然后clone到本地,我選擇的是tag分支下2.2.0,并不是master;

打開編譯后的工程

小提示,這個(gè)工程依賴的包比較多,默認(rèn)都是從jcenter下,慢成狗,建議改為國(guó)內(nèi)鏡像,比如我的

// project  build.gradle
allprojects {
    repositories {
//        jcenter()
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public'
        }
    }
}

2.模塊概況

可以看出該項(xiàng)目由四個(gè)模塊構(gòu)成,分別是'library', 'processor', 'sample', 'lint'

include ':library', ':processor', ':sample', ':lint'
  • sample是android app模塊,主要是使用的示例
  • library是android lib模塊,主要定義了注解類和調(diào)用工具
  • processor是java lib模塊,實(shí)現(xiàn)了編譯注解的邏輯
  • lint是一個(gè)lint模塊,應(yīng)該是定義lint規(guī)則

3.library模塊

這個(gè)模塊主要就一堆注解的定義和一個(gè)操作權(quán)限的工具類:PermissionUtils.java
library中一共定義了五個(gè)注解:

  • RuntimePermissions | 在一個(gè)Class上注冊(cè)權(quán)限
  • NeedsPermission | 作用在需要檢測(cè)權(quán)限的方法上
  • OnShowRationale | 作用在需要做權(quán)限解釋的方法上
  • OnPermissionDenied | 作用在權(quán)限被拒的方法上
  • OnNeverAskAgain | 作用在權(quán)限被拒且不再提示的方法上

再看PermissionUtils:

PermissionUtils.java是一個(gè)工具類,它的關(guān)鍵方法

 //驗(yàn)證權(quán)限返回的結(jié)果是不是GRANTED
 public static boolean verifyPermissions(int... grantResults) {
        if (grantResults.length == 0) {
            return false;
        }
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

//目標(biāo)權(quán)限在當(dāng)前版本是否存在
    private static boolean permissionExists(String permission) {
        // Check if the permission could potentially be missing on this device
        Integer minVersion = MIN_SDK_PERMISSIONS.get(permission);
        // If null was returned from the above call, there is no need for a device API level check for the permission;
        // otherwise, we check if its minimum API level requirement is met
        return minVersion == null || Build.VERSION.SDK_INT >= minVersion;
    }
//是否已獲取該權(quán)限
    public static boolean hasSelfPermissions(Context context, String... permissions) {
        for (String permission : permissions) {
            if (permissionExists(permission) && !hasSelfPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }

    //是否需要作出解釋
    public static boolean shouldShowRequestPermissionRationale(Activity activity, String... permissions) {
        for (String permission : permissions) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                return true;
            }
        }
        return false;
    }

//獲取當(dāng)前targetSdk版本
    @TargetApi(Build.VERSION_CODES.DONUT)
    public static int getTargetSdkVersion(Context context) {
        if (targetSdkVersion != -1) {
            return targetSdkVersion;
        }
        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            targetSdkVersion = packageInfo.applicationInfo.targetSdkVersion;
        } catch (PackageManager.NameNotFoundException ignored) {
        }
        return targetSdkVersion;
    }

4.processor模塊

processor是一個(gè)java模塊,是用來處理編譯時(shí)注解,如果對(duì)編譯時(shí)注解android-apt不明白的,那么久需要補(bǔ)習(xí)一下了,具體可以參考這篇博客;

processor模塊

processor模塊是用kotlin寫的,現(xiàn)在就不講該模塊了,我準(zhǔn)備看一下kotlin語法,然后再單獨(dú)寫一篇來講解,期待哈;

其他

還有一個(gè)lint模塊和sample,sample不打算拿出來講,太simple了,lint我暫時(shí)講不動(dòng),等我補(bǔ)補(bǔ)相關(guān)知識(shí),到時(shí)候再做一個(gè)總結(jié)。

總結(jié)

PermissionsDispatcher這個(gè)庫(kù),很值得大家去使用,國(guó)內(nèi)的知乎在用,未來應(yīng)該會(huì)加普及吧;最后預(yù)告一下,我近期準(zhǔn)備把processor模塊用java實(shí)現(xiàn)一遍,期待!最后感謝作者hotchemi,感謝閱讀的朋友?。?!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,176評(píng)論 25 708
  • 太長(zhǎng)了,還是轉(zhuǎn)載吧...今天在看博客的時(shí)候,無意中發(fā)現(xiàn)了@Trinea在GitHub上的一個(gè)項(xiàng)目Android開源...
    龐哈哈哈12138閱讀 20,394評(píng)論 3 283
  • 市場(chǎng)部和銷售部的關(guān)系好比“頭”和“手”,市場(chǎng)部提供方向和支持,銷售部完成任務(wù)和指標(biāo),在此,將市場(chǎng)部針對(duì)于項(xiàng)目的工作...
    史蒂分孫閱讀 5,016評(píng)論 0 1
  • 希望畢業(yè)兩年后找到自己喜歡的工作,有一份穩(wěn)定的收入和一個(gè)很愛很愛我的男朋友~當(dāng)然我也很愛他呀~
    云從足下生閱讀 348評(píng)論 0 0

友情鏈接更多精彩內(nèi)容