Android Runtime Permission

參考資料

Android 6.0 新特性

http://www.jianshu.com/p/d299f22dfbdb
http://blog.csdn.net/qq_34767498/article/details/51814669

Android Runtime Permission

Android 6.0(API 級別 23) 引入了一種新的權限模式,用戶可以在運行時對管理應用權限。有點類似 iOS 。即使你的 APP 不去適配 Android 6.0,但是官方建議還是使用新的權限 API 去開發你的應用。

大致的使用流程如下:

  1. 在 AndroidManifest.xml 申明權限,添加 uses-permission ,這一步和以前的權限申請一致

  2. 檢查權限,ContextCompat.checkSelfPermission() 方法。如果應用具有此權限,方法將返回 PackageManager.PERMISSION_GRANTED,並且應用可以繼續操作。如果應用不具有此權限,方法將返回 PERMISSION_DENIED,且應用必須明確向用戶要求權限。比如
    int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
    Manifest.permission.WRITE_CALENDAR);

  3. 請求權限,如果你的 APP 權限裏有危險的權限(拍照,短信,麥克風),那麼必須要要讓用戶授予權限,調用 shouldShowRequestPermissionRationale() 會彈出對話框讓用戶選擇,這個對話框是不是能修改的! 來看下大致的代碼:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

4 . 處理權限請求回調,看代碼

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

大致的流程就是上面的幾步,怎麼區分你的權限是否需要用戶授權,可以查看https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous

當然也可以使用命令 adb shell pm list permissions -d -g 查看Dangerous Permission

下面是完整的代碼,檢測相機的權限,原生的 permission 實現

public class PermissionActivity extends AppCompatActivity {

    private String sdPath;//SD卡的路徑
    private String picPath;//圖片存儲路徑

    public static final int REQUEST_CUST_CAMERA = 0X12;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);
        findViewById(R.id.readSDCard).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                checkPermission(Manifest.permission.CAMERA);
            }
        });
    }


    // 原生的permission check
    public void checkPermission(String permission) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
                // 這裏很奇怪,基本進不到這步
                Toast.makeText(this, "show permission detail", Toast.LENGTH_SHORT).show();

            } else {
                ActivityCompat.requestPermissions(this, new String[]{permission}, REQUEST_CUST_CAMERA);

            }
        } else {
            showReadFile();
        }
    }

    void showReadFile() {
        sdPath = Environment.getExternalStorageDirectory().getPath();
        picPath = sdPath + "/" + "temp.png";

        Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        Uri uri = Uri.fromFile(new File(picPath));
        //爲拍攝的圖片指定一個存儲的路徑
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(intent2, 0x12);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CUST_CAMERA) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                showReadFile();
            } else {
                //用戶取消了授權
                //用戶在安全中心關閉了權限(測試的小米手機)
                Toast.makeText(PermissionActivity.this, "permission denied", Toast.LENGTH_SHORT).show();
                //如果實在很重要,可以引導用戶去設置界面,開啓
            }
        }
    }
}

## 下面來介紹下第三方的庫: PermissionsDispatcher 這個庫有以下的特點:

  • 100% 自由反射
  • 支持特殊的權限
  • 支持小米
  • 支持 kotlin

首先來看下怎麼使用

1. 下載

dependencies {
    // 這裏注意替換掉 {latest.version}  去github 查詢最新的版本號,自行替換
  compile("com.github.hotchemi:permissionsdispatcher:${latest.version}") {
      // if you don't use android.app.Fragment you can exclude support for them
      exclude module: "support-v13"
  }
  annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:${latest.version}"
}
    repositories {
        jcenter()
        // 項目根目錄的 build.gradle repositories 添加 maven 支持
        maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' }

    }

添加好後,build 下就可以下載該庫了

2. AndroidManifest.xml 申明權限,添加 uses-permission ,這一步和以前的權限申請一致

    // 比如
    <uses-permission android:name="android.permission.CAMERA" />

3. 在相應的頁面添加下面的代碼,看註釋

// 這裏註解是必須的, 表明這個Activity 要申請權限, 可以在 Activity 和 Fragment 上使用這個註解
@RuntimePermissions
public class PermissionActivity extends AppCompatActivity {

    private String sdPath;//SD卡的路徑
    private String picPath;//圖片存儲路徑
    public static final int REQUEST_CUST_CAMERA = 0X12;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);
        findViewById(R.id.readSDCard).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 // PermissionActivityPermissionsDispatcher 這個類是由庫自動生成的,一個封裝好權限檢測的類,其實就是在當前類的後面加上PermissionsDispatcher,所以初學者千萬不要照抄這裏
 // showCameraWithCheck 則是根據NeedsPermission 註解生成的,可能有點不好理解,具體看 showCamera()     
        PermissionActivityPermissionsDispatcher.showCameraWithCheck(PermissionActivity.this);
            }
        });
    }

    // 這個註解也是必須的,指的是請求權限的方法,後面跟上對應的權限
    // 這個註解修飾方法會在PermissionsDispatcher 類生成 (方法名+WithCheck)的方法,看上面的showCameraWithCheck
    @NeedsPermission(Manifest.permission.CAMERA)
    void showCamera() {
        sdPath = Environment.getExternalStorageDirectory().getPath();
        picPath = sdPath + "/" + "temp.png";

        Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        Uri uri = Uri.fromFile(new File(picPath));
        //爲拍攝的圖片指定一個存儲的路徑
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(intent2, 0x12);
    }

    // 這裏和原生的一致
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 調用 PermissionActivityPermissionsDispatcher 的靜態方法,然後會回調 @NeedsPermission,@OnPermissionDenied 和 @OnNeverAskAgain,分別對應爲 用戶通過了權限,權限不通過和 用戶點了不再詢問。
        PermissionActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    }

    // 權限不通過 這個註解也是必須的
    @OnPermissionDenied(Manifest.permission.CAMERA)
    void showDeniedForRead() {
        Toast.makeText(this, "showDeniedForRead", Toast.LENGTH_SHORT).show();
    }
    // 用戶點了不再詢問  這個註解也是必須的
    @OnNeverAskAgain(Manifest.permission.CAMERA)
    void showNeverAskForRead() {
        Toast.makeText(this, "showNeverAskForRead", Toast.LENGTH_SHORT).show();
    }




}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章