天天看點

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

本子產品共有四篇文章,參考郭神的《第一行代碼》,對Content Provider的學習做一個詳細的筆記,大家可以一起交流一下:

關于内容提供器:

内容提供器(Content Provider)主要用于在不同的應用程式之間實作資料共享的功能,它提供了一套完整的機制,允許一個程式通路另一個程式中的資料,同時還能保證被訪資料的安全性。目前,使用内容提供器是Android實作跨程式共享資料的标準方式。

不同于檔案存儲和SharedPreferences存儲中的兩種全局可讀寫操作模式,内容提供器可以選擇隻對哪一部分資料進行共享,進而保證我們程式中的隐私資料不會有洩漏的風險。

在正式開始學習内容提供器之前,我們需要先掌握待會兒需要用到的運作時權限。

完美解決java.lang.SecurityException:Permission Denial 問題

1.運作時權限

首先,Android所有的權限可以歸成兩類:一類是普通權限,一類是危險權限;

普通權限是指那些不會直接威脅到使用者的安全和隐私的權限,對于這部分權限申請,系統會自動幫我們進行授權,而不需要使用者再去手動操作了,比如在BroadcastTest項目中申請的兩個權限就是普通權限。

危險權限則表示那些可能會觸及使用者隐私,或者對裝置安全性造成影響的權限,如擷取裝置聯系人資訊、定

位裝置的地理位置等,對于這部分權限,我們當在程式中申請,并必須要由使用者手動點選授權才可以,否則程式就無法使用相應的功能。

Android中有一共上百種權限,危險權限主要為以下9組24個權限,剩餘的都是普通權限:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

使用這張表格:

這張表格裡面的權限可能全都是我們沒使用過的。

不過沒事,我們并不需要了解表格中每個權限的作用,隻要把它當成一個參照表來檢視就行了:

每當要使用一個權限時,可以先到這張表中來查一下:

如果是屬于這張表中的權限,那麼就需要進行運作時權限處理;

如果不在這張表中,則在AndroidManifest.xml檔案中添權重限聲明即可;

以及有的時候是關于特殊權限的申請,那就當檢視API了;

另外注意一下,表格中每個危險權限都屬于一個權限組,我們在進行運作時權限處理時使用的是權限名,而使用者一旦同意授權了一個權限組中的任意一個權限的話,則該權限所對應的權限組中所有的其他權限也會同時被授權。

通路 https://developer.android.google.cn/reference/android/Manifest.permission 可以檢視Android系統中完整的權限清單。
1.1 在程式運作時申請權限

首先建立一個RuntimePermissionTest項目;

本例子将嘗試對CALL_PHONE這個權限的申請;

CALL_PHONE這個權限是編寫撥打電話功能的時候需要聲明的,在Android6.0系統出現之前,撥打電話功能的實作其實非常簡單,Android6.0之後因為撥打電話會涉及使用者手機的資費問題,因而被列為了危險權限,需要聲明才可使用。

修改activity_main.xml,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.runtimepermissiontest.MainActivity">

    <Button
        android:id="@+id/make_call"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Make Call"/>

</LinearLayout>
           

MainActivity,添加監聽,觸發打電話的邏輯:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button makeCall = (Button) findViewById(R.id.make_call);
        makeCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    Intent intent = new Intent(Intent.ACTION_CALL);
                    intent.setData(Uri.parse("tel:10086"));
                    startActivity(intent);
                }catch (SecurityException e){
                    e.printStackTrace();
                }
            }
        });
    }
}
           

可以看到,在按鈕的點選事件中,我們建構了一個隐式Intent, Intent 的 action 指定為 Intent.ACTION_CALL ,這是一個系統内置的打電話的動作,然後在data部分指定了協定是 tel , 号碼是10086

接下來還需在AndroidManifest中聲明權限:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)
<uses-permission android:name="android.permission.CALL_PHONE" />
           

當然到此為止運作的時候,會出現報錯,下面需要最後一步,進行權限申請!:

我們把剛剛的打電話邏輯封裝在call()中:

PS:onRequestPermissionsResult所在的包

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

代碼簡析:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button makeCall = (Button) findViewById(R.id.make_call);
        makeCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.
                permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(MainActivity.this, new
                        String[]{ Manifest.permission.CALL_PHONE}, 1);
                }else {
                    call();
                }
            }
        });
    }

    private void call() {
        try{
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:10086"));
            startActivity(intent);
        }catch (SecurityException e){
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    call();
                }else{
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }
           

代碼詳細講解:

上面的代碼是申請運作時權限的完整流程,下面我們來具體解析一下;

運作時權限的核心就是在程式運作過程中由使用者授權我們去執行某些危險操作,程式是不可以擅自做主去執行這些危險操作的;

是以,第一步就是要先借助于ContextCompa.checkSelfPermission()方法,判斷使用者是不是已經給過我們授權了。

checkSelfPermission()方法接收兩個參數,第一個參數是Context,第二個參數是具體的權限名;

  • 比如打電話的權限名就是Manifest.permission.CALLPHONE,

    然後我們使用方法的傳回值和PackageManager.PERMISSION_GRANTED做比較:

    相等就說明使用者已經授權,

    不等就表示使用者沒有授權。

  • 如果已經授權,就直接執行撥打電話的封裝方法call();

    如果沒有授權,則需要調用ActivityCompat.requestpermissions()方法來向使用者申請授權,

    requestpermissions()方法接收3個參數,

    第一個參數要求是Activity的執行個體,

    第二個參數是一個String數組,把要申請的權限名放在數組中即可,

    第三個參數是請求碼,隻要是唯一值就可以了,這裡傳入了1。

  • 調用完了requestpermissions()方法之後,系統會彈出一個權限申請的對話框,然後使用者可以選擇同意或拒絕我們的權限申請,

    不論是哪種結果,最終都會回調到onRequestPermissionsResult()方法中,

    而授權的結果則會封裝在grantResuIts參數當中。

    到這裡判斷一下最後的授權結果,

    如果使用者同意就調用call()方法來撥打電話,

    如果使用者拒絕則放棄操作并彈出一條失敗提示;

下面運作程式,點選按鈕,會彈出對話框:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

如果點選拒絕,則會彈出Toast:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

如果點選允許,則成功進入到撥打電話界面:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

在這之後,我們就成功進入撥打電話界面了,并且至此使用者已經完成了授權,之後再點選MakeCall按鈕就不會再彈出權限申請對話框了,而是可以直接撥打電話。

是以注意一下這裡,使用者随時都可以手動将授予程式的危險權限進行關閉,

進人Settings→Apps→RuntimePermissionTest→Permissions進行操作即可,界面如圖:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

在這裡便可以手動開關危險權限了:

跨程式共享資料——Content Provider 之 運作時權限解析以及申請的實作(可完美解決java.lang.SecurityException:Permission Denial 問題)

繼續閱讀