Android6.0權限處理問題

Android6.0運行時權限處理

Android6.0的發佈介紹了一種新的權限機制。用戶可以在運行時直接管理應用程序的權限,這個功能提升了權限控制的可見性和可控性,同時簡化了安裝和自動升級過程,用戶可以單獨撤銷或者授予應用程序某項權限,對應用擁有更多的控制權。

應用程序target是Android 6.0及以上(API level 23),要確保在運行時檢查和請求權限,在6.0以後有些權限屬於危險權限,清單中聲明權限,不能保證權限授權通過,爲了確定你的app是否授予某個權限,通過checkSelfPermission()方法判斷,請求權限使用requestPermissions()方法。即使你的app不是target Android 6.0,你也應該在新的權限機制下測試你的應用。

本文參考博文:
Hongyang:Android 6.0 運行時權限處理完全解析

1. Android權限管理

Google將權限分爲兩類,一類是普通權限(Normal Permissions),這類權限一般不涉及用戶隱私,也不需要用戶進行授權,如網絡訪問,手機震動等,另外一類是危險權限(Dangerous Permission),涉及用戶隱私,需要用戶授權,如對sd卡讀取、訪問用戶手機通訊錄、撥打電話等。

關於Android的危險權限:Dangerous Permissions:

group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:com.google.android.gms.permission.CAR_SPEED
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

危險權限我們可以通過adb命令查看:

adb shell pm list permissions -d -g

查看權限以及詳細信息:

adb shell pm list permissions -d -g -f

查看到危險的權限是一組一組的,如果某一組裏面的某個危險權限已被用戶授權,那麼系統會立即授權其他的權限,而不需要再向用戶授權,比如你的app已經授權READ_SMS,那麼當你的app需要SEND_SMS,系統會自動授權。彈出的dialog的文本是對權限組的說明,而非單個權限說明。且dialog不可以自定義。

2. 權限相關處理

2.1 添加權限

在AndroidManifest.xml中正常添加權限。

2.2 檢查權限

if (ContextCompat.checkSelfPermission(this,
        Manifest.permission.CALL_PHONE)
        != PackageManager.PERMISSION_GRANTED) {

       // 向用戶申請權限

} else {

    // 執行權限通過後的事件

}

這裏涉及到一個API,ContextCompat.checkSelfPermission(),主要用於檢測某個權限是否已經被授予,方法返回值爲PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。當返回DENIED就需要進行申請授權了。查看源碼:

源碼:
/**
 * Determine whether <em>you</em> have been granted a particular permission.
 *  確定你是否獲得了一個特定的權限
 *  
 * @param permission The name of the permission being checked.
 *  參數permission:你要檢測的權限
 *
 * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
 * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
 *  返回值PackageManager.PERMISSION_GRANTED授權已成功
 *       PackageManager.PERMISSION_DENIED 授權被拒絕
 *
 * @see android.content.pm.PackageManager#checkPermission(String, String)
 */

public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}

關於返回值,同樣到源碼裏面可以查看:

/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has been granted to the given package.
 */
public static final int PERMISSION_GRANTED = 0;

/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has not been granted to the given package.
 */
public static final int PERMISSION_DENIED = -1;

檢查完權限之後,如果系統沒有授權成功,需要向用戶申請權限。

2.3 申請權限

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

該方法是異步的,第一個參數是Context;第二個參數是需要申請的權限的字符串數組;第三個參數爲requestCode,主要用於回調的時候檢測。

2.4 處理權限申請回調

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // 權限申請成功,可以執行事件了

        } else {
            // Permission Denied 權限被拒絕
            // 提示對話框等
        }
        return;
    }
}

對於權限的申請結果,首先驗證requestCode定位到你的申請,然後驗證grantResults對應於申請的結果,這裏的數組對應於申請時的第二個權限字符串數組。如果你同時申請兩個權限,那麼grantResults的length就爲2,分別記錄你兩個權限的申請結果。

2.5 權限徹底禁止

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

當第一次向用戶詢問權限時,用戶拒絕,而後再申請的彈窗會提示以後不再提示選項,如果用戶勾選並拒絕權限,表示用戶徹底拒絕權限,那麼ActivityCompat.shouldShowRequestPermissionRationale將返回false,一般我們進行判斷用戶是徹底拒絕,那麼想要再申請權限,可以提示用戶到設置去修改。

3. 案例實踐

public class MainActivity extends AppCompatActivity {

    private static final int CALL_PHONE_RESQUESTCODE = 1;

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

        Button btnCall = (Button) findViewById(R.id.btnCall);
        btnCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testCall();
            }
        });
    }

    public void testCall() {

        // 判斷權限是否未被系統授權
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED) {

            Log.i("TAG", "is" + ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE));

            // 重新請求權限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CALL_PHONE},
                    CALL_PHONE_RESQUESTCODE);
        } else {
            // 否則直接執行事件了
            callPhone();
        }
    }

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

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

        if (requestCode == CALL_PHONE_RESQUESTCODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                callPhone();
            } else {
                // Permission Denied,權限被拒絕
//                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();

                // 完全拒絕,可以提示用戶跳到設置頁面
                if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
                    showDialog();
                }
            }
            return;
        }
    }

    public void showDialog() {
        new AlertDialog.Builder(this)
                .setMessage("權限被拒絕,請到權限管理裏面設置!")
                .setPositiveButton("去設置", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent();
                        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null);
                        intent.setData(uri);
                        MainActivity.this.startActivity(intent);
                    }
                })
                .setNegativeButton("取消", null)
                .create().show();
    }
}

基本的處理過程到此結束。

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