Android動態權限

和以前在安裝 APP 的是就申請了權限不同,Google 在 API 23,也就 6.0 之后加入了動態權限。對于一些敏感的權限,決定權交還給了用戶,不再是強制申請了。因為這個原因,如果 APP 需要支持 6.0 以上的系統,就需要進行一下適配,否則 APP 就會崩潰。

比如,下面撥打電話.。已經在在 AndroidManifest.xml 中申明權限了

<uses-permission android:name="android.permission.CALL_PHONE"/>

這是撥打電話的代碼實現

private void callPhone() {
    Intent intent = new Intent(Intent.ACTION_CALL);
    Uri data = Uri.parse("tel:" + "12312341234");
    intent.setData(data);
    startActivity(intent);
}

但是程序如果是在 Android 6.0 的設備中運行的話,一運行就崩潰了,報錯信息如下

java.lang.SecurityException: Permission Denial: ... 后面的信息省略

那么要怎么才能適配呢?

動態權限有哪些?

首先要先知道動態權限有哪些?

Andriod 中的動態權限是按組來分的,下面的表格來自官網

runtime_permissions

每個組中的權限不用全部申請,申請了其中一個,組中其他權限也就一起申請到了。

怎么申請動態權限?

有個前提,我們的 Activity 比如 MainActivity 不再是繼承于 Acticity 了,而是繼承于 AppCompatActivity,因為動態權限的一些方法是只有 AppCompatActivity 才有的。

首先是檢測是否已經賦予了權限,可以調用 ContextCompat.checkSelfPermission(Context, permissionName) 方法,通過方法的返回值來判斷

if (ContextCompat.checkSelfPermission(this,
        Manifest.permission.CALL_PHONE)
        == PackageManager.PERMISSION_GRANTED) {
    // 已經賦予權限,直接調用撥打電話的代碼
    callPhone();
} else {
    // 沒有賦予權限,那就去申請權限
}

要申請權限可以使用 requestPermissions (Activity activity, String[] permissions, int requestCode) 進行申請

private static final int REQUEST_CALL_PHONE = 456;

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE},
                                  REQUEST_CALL_PHONE);

于是在界面上就會彈出一個請求撥打電話權限的對話框(這個對話框是系統彈出來的,樣式不可修改)

request_call_phone_permission

不管用戶選擇同意還是拒絕,都會在 onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法中接收到信息

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CALL_PHONE:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 權限請求成功
                callPhone();
            } else {
                // 用戶拒絕了
                showTipDialog();
            }
            break;

        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

private void showTipDialog() {
    new AlertDialog.Builder(this)
            .setMessage("該程序需要電話權限,否則無法正常運行")
            .setPositiveButton(android.R.string.ok, null)
            .create()
            .show();
}

如果用戶選擇”同意“,就可以調用撥打電話的按鈕,同時這個對話框之后都不會出現了。

如果用戶拒絕了,那就無法調用撥打電話的代碼了。為了用戶體驗,可以彈出一個對話框,告知用戶我需要這個權限,沒有這個權限程序無法正常運行

用戶看到了這個信息,于是再次點擊“撥打電話”的按鈕,程序又進行了權限檢查,彈出申請權限的對話框,這時候的對話框的和之前第一次進行權限申請時彈出的對話框的樣式不同,多出了一個“不再提醒”的勾選項

permission_not_ask_agin

如果用戶勾選“不再詢問”,對話框是這樣的

choose_not_ask_agin

意味著用戶永遠地拒絕撥打電話的權限,同時這個對話框不會再彈出來了,這就造成程序無法正常運行。還有辦法可以補救一下,就是跳轉到設置頁面,讓用戶手動開啟權限。Anroid 提供了一個方法 ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE) 可以用來判斷是否選擇了“不再提示”

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CALL_PHONE:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 權限請求成功
                callPhone();
            } else {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
                    // 用戶選擇了“拒絕”
                    showTipDialog();
                } else {
                    // 用戶勾選了“不再提示”
                    goToSetting();
                }
            }
            break;

        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

private void goToSetting() {
    new AlertDialog.Builder(this)
            .setMessage("該程序需要電話權限,否則無法正常運行")
            .setPositiveButton("去打開權限", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                    intent.setData(uri);
                    startActivity(intent);
                }
            })
            .create()
            .show();
}

完整代碼

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CALL_PHONE = 456;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_capture).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callPhoneWithCheck();
            }
        });
    }

    private void callPhone() {
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + "12312341234");
        intent.setData(data);
        startActivity(intent);
    }

    private void callPhoneWithCheck() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                == PackageManager.PERMISSION_GRANTED) {
            // 已經賦予權限
            callPhone();
        } else {
             ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE},
                        REQUEST_CALL_PHONE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CALL_PHONE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // 權限請求成功
                    callPhone();
                } else {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
                        // 用戶選擇了“拒絕”
                        showTipDialog();
                    } else {
                        // 用戶勾選了“不再提示”
                        goToSetting();
                    }
                }
                break;

            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    private void goToSetting() {
        new AlertDialog.Builder(this)
                .setMessage("該程序需要電話權限,否則無法正常運行")
                .setPositiveButton("去打開權限", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivity(intent);
                    }
                })
                .create()
                .show();
    }

    private void showTipDialog() {
        new AlertDialog.Builder(this)
                .setMessage("該程序需要電話權限,否則無法正常運行")
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},
                                REQUEST_CALL_PHONE);
                    }
                })
                .create()
                .show();
    }
}

開源庫

為了申請一個撥打電話的權限,要寫好多代碼。網上有個一個流行的開源庫 PermissionsDispatcher,采用注解來快速實現以上的邏輯。用法很簡單,就是需要注意到是,寫完代碼是需要編譯一下 project,這樣才能生成相應的代碼。

參考來源

http://blog.csdn.net/zhangqinghuazhangzhe/article/details/52801202
http://www.lxweimin.com/p/d6b3e16cc1d9
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1110/3670.html
http://www.lxweimin.com/p/b4a8b3d4f587
http://blog.csdn.net/quan356270259/article/details/50876272
http://blog.csdn.net/u010483016/article/details/50401605
http://www.lxweimin.com/p/e1ab1a179fbb#
http://blog.csdn.net/u011200604/article/details/52874599
http://blog.csdn.net/yangqingqo/article/details/48371123

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,368評論 25 708
  • 最近APP里面要添加動態權限,網上找了不少例子并經過一定的測試,基本流程有了一定的認識,需要注意的地方記錄下來以備...
    Tom老兄_baaa閱讀 1,068評論 0 1
  • 6.0以后分出了危險權限,危險權限需要動態申請,他們分了組,如果申請了組內的某個權限,那么整個組的權限都會被授予 ...
    達峰a閱讀 4,904評論 0 9
  • 21.3 兼容性問題 新的運行時權限僅當APP的targetSdkVersion 為23,且APP運行在安裝了An...
    xjbclz閱讀 384評論 0 1
  • 上班的路上,很經常會遇到一個女子。我走路的這個方向,她走路的那個方向。 剛開始留意她,是因為她的衣服。 淡藍色牛仔...
    修女Maria閱讀 254評論 0 0