跨程序共享數(shù)據(jù)——Content Provider 之 運(yùn)行時權(quán)限解析以及申請的實(shí)現(xiàn)(可完美解決java.lang.SecurityException:Permission Denial 問題)


本模塊共有四篇文章,參考郭神的《第一行代碼》,對Content Provider的學(xué)習(xí)做一個詳細(xì)的筆記,大家可以一起交流一下:


關(guān)于內(nèi)容提供器:
內(nèi)容提供器(Content Provider)主要用于在不同的應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)共享的功能,它提供了一套完整的機(jī)制,允許一個程序訪問另一個程序中的數(shù)據(jù),同時還能保證被訪數(shù)據(jù)的安全性。目前,使用內(nèi)容提供器是Android實(shí)現(xiàn)跨程序共享數(shù)據(jù)的標(biāo)準(zhǔn)方式。
不同于文件存儲和SharedPreferences存儲中的兩種全局可讀寫操作模式,內(nèi)容提供器可以選擇只對哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,從而保證我們程序中的隱私數(shù)據(jù)不會有泄漏的風(fēng)險。

在正式開始學(xué)習(xí)內(nèi)容提供器之前,我們需要先掌握待會兒需要用到的運(yùn)行時權(quán)限。

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



1.運(yùn)行時權(quán)限

首先,Android所有的權(quán)限可以歸成兩類:一類是普通權(quán)限,一類是危險權(quán)限;
普通權(quán)限是指那些不會直接威脅到用戶的安全和隱私的權(quán)限,對于這部分權(quán)限申請,系統(tǒng)會自動幫我們進(jìn)行授權(quán),而不需要用戶再去手動操作了,比如在BroadcastTest項目中申請的兩個權(quán)限就是普通權(quán)限。
危險權(quán)限則表示那些可能會觸及用戶隱私,或者對設(shè)備安全性造成影響的權(quán)限,如獲取設(shè)備聯(lián)系人信息、定
位設(shè)備的地理位置等,對于這部分權(quán)限,我們當(dāng)在程序中申請,并必須要由用戶手動點(diǎn)擊授權(quán)才可以,否則程序就無法使用相應(yīng)的功能。

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


使用這張表格:
這張表格里面的權(quán)限可能全都是我們沒使用過的。
不過沒事,我們并不需要了解表格中每個權(quán)限的作用,只要把它當(dāng)成一個參照表來查看就行了:
每當(dāng)要使用一個權(quán)限時,可以先到這張表中來查一下:
如果是屬于這張表中的權(quán)限,那么就需要進(jìn)行運(yùn)行時權(quán)限處理;
如果不在這張表中,則在AndroidManifest.xml文件中添加權(quán)限聲明即可;
以及有的時候是關(guān)于特殊權(quán)限的申請,那就當(dāng)查看API了;

另外注意一下,表格中每個危險權(quán)限都屬于一個權(quán)限組,我們在進(jìn)行運(yùn)行時權(quán)限處理時使用的是權(quán)限名,而用戶一旦同意授權(quán)了一個權(quán)限組中的任意一個權(quán)限的話,則該權(quán)限所對應(yīng)的權(quán)限組中所有的其他權(quán)限也會同時被授權(quán)。

訪問https://developer.android.google.cn/reference/android/Manifest.permission可以查看Android系統(tǒng)中完整的權(quán)限列表。

1.1 在程序運(yùn)行時申請權(quán)限

首先新建一個RuntimePermissionTest項目;
本例子將嘗試對CALL_PHONE這個權(quán)限的申請;

CALL_PHONE這個權(quán)限是編寫撥打電話功能的時候需要聲明的,在Android6.0系統(tǒng)出現(xiàn)之前,撥打電話功能的實(shí)現(xiàn)其實(shí)非常簡單,Android6.0之后因?yàn)閾艽螂娫挄婕坝脩羰謾C(jī)的資費(fèi)問題,因而被列為了危險權(quán)限,需要聲明才可使用。

修改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,添加監(jiān)聽,觸發(fā)打電話的邏輯:

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();
                }
            }
        });
    }
}

可以看到,在按鈕的點(diǎn)擊事件中,我們構(gòu)建了一個隱式Intent, Intent 的 action 指定為 Intent.ACTION_CALL ,這是一個系統(tǒng)內(nèi)置的打電話的動作,然后在data部分指定了協(xié)議是 tel , 號碼是10086
接下來還需在AndroidManifest中聲明權(quán)限:

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

當(dāng)然到此為止運(yùn)行的時候,會出現(xiàn)報錯,下面需要最后一步,進(jìn)行權(quán)限申請?。?/strong>
我們把剛剛的打電話邏輯封裝在call()中:
PS:onRequestPermissionsResult所在的包

代碼簡析:

@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:
        }
    }

代碼詳細(xì)講解:
上面的代碼是申請運(yùn)行時權(quán)限的完整流程,下面我們來具體解析一下;

運(yùn)行時權(quán)限的核心就是在程序運(yùn)行過程中由用戶授權(quán)我們?nèi)?zhí)行某些危險操作,程序是不可以擅自做主去執(zhí)行這些危險操作的;

因此,第一步就是要先借助于ContextCompa.checkSelfPermission()方法,判斷用戶是不是已經(jīng)給過我們授權(quán)了。
checkSelfPermission()方法接收兩個參數(shù),第一個參數(shù)是Context,第二個參數(shù)是具體的權(quán)限名;

  • 比如打電話的權(quán)限名就是Manifest.permission.CALLPHONE,
    然后我們使用方法的返回值和PackageManager.PERMISSION_GRANTED做比較:
    相等就說明用戶已經(jīng)授權(quán),
    不等就表示用戶沒有授權(quán)。

  • 如果已經(jīng)授權(quán),就直接執(zhí)行撥打電話的封裝方法call();
    如果沒有授權(quán),則需要調(diào)用ActivityCompat.requestpermissions()方法來向用戶申請授權(quán),
    requestpermissions()方法接收3個參數(shù),
    第一個參數(shù)要求是Activity的實(shí)例,
    第二個參數(shù)是一個String數(shù)組,把要申請的權(quán)限名放在數(shù)組中即可,
    第三個參數(shù)是請求碼,只要是唯一值就可以了,這里傳入了1。

  • 調(diào)用完了requestpermissions()方法之后,系統(tǒng)會彈出一個權(quán)限申請的對話框,然后用戶可以選擇同意或拒絕我們的權(quán)限申請,
    不論是哪種結(jié)果,最終都會回調(diào)到onRequestPermissionsResult()方法中,
    而授權(quán)的結(jié)果則會封裝在grantResuIts參數(shù)當(dāng)中。
    到這里判斷一下最后的授權(quán)結(jié)果,
    如果用戶同意就調(diào)用call()方法來撥打電話,
    如果用戶拒絕則放棄操作并彈出一條失敗提示;

下面運(yùn)行程序,點(diǎn)擊按鈕,會彈出對話框:


如果點(diǎn)擊拒絕,則會彈出Toast:

如果點(diǎn)擊允許,則成功進(jìn)入到撥打電話界面:

在這之后,我們就成功進(jìn)入撥打電話界面了,并且至此用戶已經(jīng)完成了授權(quán),之后再點(diǎn)擊MakeCall按鈕就不會再彈出權(quán)限申請對話框了,而是可以直接撥打電話。
所以注意一下這里,用戶隨時都可以手動將授予程序的危險權(quán)限進(jìn)行關(guān)閉,
進(jìn)人Settings→Apps→RuntimePermissionTest→Permissions進(jìn)行操作即可,界面如圖:


在這里便可以手動開關(guān)危險權(quán)限了:

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

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評論 25 709
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 7,335評論 0 17
  • Intent組件雖然不是四大組件,但卻是連接四大組件的橋梁,學(xué)習(xí)好這個知識,也非常的重要。 一、什么是Intent...
    困惑困惑困惑閱讀 1,718評論 0 0
  • 說實(shí)話,剛有孩子的時候真的花了一段時間適應(yīng)這個新角色。想要在各方面都做的好,做一個好媽媽,不光是自己認(rèn)可的,更是對...
    OliviaTu閱讀 516評論 0 0
  • 厲時二十天,讀完《你是我不能說的秘密》。此書給予的感受是: 伴著女主子蔚生活的律歷,同受著怒視、厭惡、驚恐、掙扎到...
    吉羊玉奕v閱讀 259評論 1 3

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