簡述
Android是一個(gè)權(quán)限分離的操作系統(tǒng),每一個(gè)應(yīng)用程序運(yùn)行時(shí)都會(huì)有一個(gè)明確地系統(tǒng)身份標(biāo)識(shí)(Linux的user ID和group ID)。部分系統(tǒng)也同樣被特定身份標(biāo)識(shí)而隔開。因此,Linux才能將應(yīng)用程序與其他程序和系統(tǒng)隔離開來。
這樣的機(jī)制可以說是相當(dāng)安全,但是也阻斷了各個(gè)應(yīng)用程序之間或者和系統(tǒng)之間的“交流”。因此,Android通過一種“permission”機(jī)制強(qiáng)力限制某些特定地操作來達(dá)到細(xì)粒度的安全能力。
進(jìn)程沙箱
Android進(jìn)程沙箱機(jī)制是借鑒Linux中用戶組的原理,其限制了不同應(yīng)用程序之間的資源和數(shù)據(jù)的互訪。當(dāng)應(yīng)用首次安裝的時(shí),系統(tǒng)會(huì)向其分配一個(gè)UID。如果該應(yīng)用程序是第三方的,那么其UID值大于10000,如果是系統(tǒng)應(yīng)用程序則小于10000。如果應(yīng)用程序卸載后又重新安裝,那么其UID值是會(huì)改變的。
//獲取應(yīng)用程序UID方法
public void getApplicationUid() {
PackageManager pm = getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
Log.d(getClass().getSimpleName(), "uid = " + ai.uid);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
不同UID的應(yīng)用程序是不能進(jìn)行資源互訪,從而有效達(dá)到進(jìn)程隔離目的。
此外,你可以每個(gè)應(yīng)用程序的AndroidManifest.xml文件中使用ShareUserID屬性來使他們擁有同一UserID。UserID相同的應(yīng)用程序?qū)?huì)被系統(tǒng)當(dāng)做同一應(yīng)用程序,擁有相同的UserID和文件權(quán)限。
注意:為了保留系統(tǒng)安全性,只有簽名相同(并且需要相同的shareUserId)的應(yīng)用程序才會(huì)被分配相同的UserID。
一個(gè)應(yīng)用程序存儲(chǔ)的任何數(shù)據(jù)都會(huì)被分配應(yīng)用程序的UserID,通常是不能被其他應(yīng)用程序所訪問。當(dāng)使用getSharedPreferences(String, int),openFileOutput(String, int),或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory),你可以使用MODE_WORLD_READABLE或者M(jìn)ODE_WORLD_WRITEABLE標(biāo)記來允許其他應(yīng)用程序讀或?qū)懳募?/p>
權(quán)限使用
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.myapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>
權(quán)限的使用是相當(dāng)簡單的,某功能需要申請(qǐng)權(quán)限時(shí),只需在AndroidManifest.xml文件中申明對(duì)應(yīng)權(quán)限就行。如上述代碼。
如果你的App在其manifest文件中聲明一系列normal permissions(不會(huì)對(duì)用戶隱私或者設(shè)備運(yùn)行構(gòu)成威脅的權(quán)限),系統(tǒng)會(huì)自動(dòng)準(zhǔn)許這些權(quán)限申請(qǐng)。如果你的App在其manifest文件中聲明一系列dangerous permissions(對(duì)用戶隱私或者設(shè)備運(yùn)行構(gòu)成潛在威脅的權(quán)限),系統(tǒng)將會(huì)詢問用戶是否同意這些權(quán)限申請(qǐng)。
詢問的方式根據(jù)系統(tǒng)的版本而有所不同。
1.靜態(tài)權(quán)限申請(qǐng)?jiān)儐柦缑?br>
若設(shè)備運(yùn)行的系統(tǒng)版本為Android5.1(API版本22)或更低,或者App的targetSdkVersion是22或更低,Android提供的是靜態(tài)權(quán)限申請(qǐng)?jiān)儐柦缑妗?br>
這種詢問方式只要玩過Android手機(jī)的應(yīng)該都見過,當(dāng)應(yīng)用程序首次安裝時(shí),會(huì)彈出以下類似界面,出現(xiàn)在圖標(biāo)列表中的權(quán)限都是dangerous permissions。
這種詢問方式相當(dāng)霸道,如果想要安裝該應(yīng)用,我們只有同意其申請(qǐng)的所有權(quán)限。當(dāng)應(yīng)用程序安裝更新時(shí),如果該應(yīng)用程序有新申請(qǐng)的權(quán)限,那么該權(quán)限詢問界面會(huì)將新申請(qǐng)的權(quán)限列出。你廢除這些權(quán)限申請(qǐng)的唯一方式就是卸載它們!
-
動(dòng)態(tài)權(quán)限申請(qǐng)?jiān)儐柦缑?br> 如果設(shè)備運(yùn)行的系統(tǒng)版本為Android6.0(API版本23)或更高,或者App的targetSdkVersion是23或更高,Android提供了動(dòng)態(tài)權(quán)限申請(qǐng)?jiān)儐柦缑妗?br> 其實(shí)這種方式,早在Android6.0之前就有大批國產(chǎn)ROM提供動(dòng)態(tài)權(quán)限管理方式,市面上主流的安全軟件也提供這種功能。Google終于在Android6.0提供了動(dòng)態(tài)權(quán)限管理功能(不過對(duì)我大天朝來說然并卵)。
動(dòng)態(tài)權(quán)限申請(qǐng)?jiān)儐柦缑?/div>
這種交互方式更加的人性化,也更加安全。在應(yīng)用程序運(yùn)行的過程中,如果需要申請(qǐng)網(wǎng)絡(luò)連接權(quán)限,那么系統(tǒng)會(huì)彈出權(quán)限詢問對(duì)話框供用戶選擇。
當(dāng)然,權(quán)限并不僅僅局限于此。我們也可以自定義某些權(quán)限來保證安全性。比如,啟動(dòng)Activity或者Service時(shí),增加權(quán)限控制,防止被外部應(yīng)用程序胡亂啟動(dòng)。
權(quán)限組
對(duì)普通第三方應(yīng)用程序來說,權(quán)限一般分為normal permission和dangerous permission。Android系統(tǒng)所有的dangerous permissions都屬于某一權(quán)限組。如果設(shè)備運(yùn)行的系統(tǒng)版本為Android6.0(API版本23)或更高,或者App的targetSdkVersion是23或更高,當(dāng)你的應(yīng)用程序需要一個(gè)dangerous permission時(shí),那么:
- 如果應(yīng)用程序在manifest中聲明了一個(gè)dangerous permission,并且它目前沒有該權(quán)限組中的任一權(quán)限,那么系統(tǒng)會(huì)彈出一個(gè)將要申請(qǐng)權(quán)限組的對(duì)話框。但是該對(duì)話框不會(huì)具體描述是該權(quán)限組中的哪一個(gè)權(quán)限。比如應(yīng)用程序需要READ_CONTACTS權(quán)限,那么該對(duì)話框僅僅只描述為該應(yīng)用程序需要訪問聯(lián)系人。
2.如果應(yīng)用程序在manifest中聲明了一個(gè)dangerous permission,并且它已經(jīng)擁有該權(quán)限組的其他權(quán)限,那么系統(tǒng)將直接允許其訪問該權(quán)限,不與用戶產(chǎn)生交互。
若設(shè)備運(yùn)行的系統(tǒng)版本為Android5.1(API版本22)或更低,或者App的targetSdkVersion是22或更低,系統(tǒng)將會(huì)在應(yīng)用程序安裝的時(shí)候讓用戶同意權(quán)限申請(qǐng)。系統(tǒng)僅僅只告訴用戶哪些權(quán)限組被申請(qǐng),而不是單獨(dú)某一個(gè)權(quán)限。
Android6.0動(dòng)態(tài)權(quán)限管理
國產(chǎn)ROM和各類安全軟件早已提供了動(dòng)態(tài)權(quán)限管理功能,實(shí)現(xiàn)方式上大同小異,雖然對(duì)用戶來說這是相當(dāng)利好的消息,但是對(duì)我們開發(fā)者來說,還是很麻煩的,各種ROM的兼容性讓我們很頭疼。終于在棉花糖上,Android提供了動(dòng)態(tài)權(quán)限管理的相關(guān)API,我們在處理權(quán)限問題上方便了很多。
當(dāng)你的需要申請(qǐng)一個(gè)dangerous permission時(shí)候,你必須在每次申請(qǐng)之前進(jìn)行權(quán)限檢查。權(quán)限檢查的方法如下。int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE);
如果該方法的返回值為PackageManager.PERMISSION_GRANTED,應(yīng)用程序就可以繼續(xù)后續(xù)操作。如果應(yīng)用程序沒有該權(quán)限,那么方法的返回值為PERMISSION_DENIED,并且將會(huì)詢問用戶是否允許該權(quán)限。
我們在Manifest中申請(qǐng)的任何dangerous permission,都會(huì)詢問用戶是否允許該權(quán)限,Android提供了幾個(gè)申請(qǐng)權(quán)限的方法,調(diào)用之后,會(huì)彈出一個(gè)標(biāo)準(zhǔn)的系統(tǒng)對(duì)話框供用戶選擇,該對(duì)話框是不能自定義的。如果一個(gè)圖像類軟件申請(qǐng)發(fā)短信權(quán)限,用戶可能會(huì)產(chǎn)生懷疑,是不是扣費(fèi)短信。那么我們?nèi)绾谓档陀脩舻牟乱赡兀緼ndroid提供了一個(gè)比較實(shí)用的方法shouldShowRequestPermissionRationale(),該方法給了我們一個(gè)解釋的機(jī)會(huì)來增加權(quán)限申請(qǐng)通過的概率。如果該權(quán)限之前已被申請(qǐng)過但是被用戶拒絕,那么shouldShowRequestPermissionRationale()方法返回true。
如果你的應(yīng)用程序沒有所需要的權(quán)限,那么你必須要通過調(diào)用requestPermissions()方法來申請(qǐng)權(quán)限,該方法調(diào)用后,系統(tǒng)會(huì)立刻彈出權(quán)限申請(qǐng)?jiān)儐枌?duì)話框供供用戶選擇,在用戶交互后,系統(tǒng)會(huì)立刻通過onRequestPermissionsResult()將結(jié)果返回給應(yīng)用程序。這里直接將官方文檔中相關(guān)演示代碼貼出來供參考。
// 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. } } @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 } }
再一次,系統(tǒng)權(quán)限對(duì)話框僅僅只描述你所申請(qǐng)權(quán)限所在權(quán)限組的描述,而不是針對(duì)某一特定權(quán)限。對(duì)于同一權(quán)限組的權(quán)限,用戶只需同意一次即可。這種方案的好壞見仁見智,但是有時(shí)候感覺會(huì)把一個(gè)很小的問題給擴(kuò)大了,比如我們只是需要簡單的獲取設(shè)備的IMEI碼,那么這時(shí)候系統(tǒng)對(duì)話框的描述為應(yīng)用程序?qū)⒃L問設(shè)備信息。這時(shí)候用戶肯定會(huì)想,你訪問我設(shè)備信息作甚!然后你的申請(qǐng)被無情拒絕了!
注意:你的應(yīng)用程序需要明確地申請(qǐng)每一個(gè)你需要的權(quán)限,即使用戶已經(jīng)同意了該權(quán)限所在權(quán)限組的另外一個(gè)權(quán)限。此外,隨著Android版本的更新,權(quán)限組中所含的權(quán)限可能會(huì)改變。因此,不要偷懶,該顯示申請(qǐng)權(quán)限的地方還是要乖乖申請(qǐng)吧。
合理申請(qǐng)權(quán)限
曾幾何時(shí),權(quán)限的濫用導(dǎo)致用戶隱私泄露頻發(fā),而今,用戶對(duì)隱私也愈發(fā)敏感,過渡的權(quán)限申請(qǐng)會(huì)給用戶造成不良的印象。因此,作為有節(jié)操的程序員,我們在權(quán)限申請(qǐng)上應(yīng)該慎重,而不是一股腦把所有權(quán)限都給申請(qǐng)。
隨著Android版本的更新,相應(yīng)的權(quán)限也會(huì)更新,因此我們一定要注意不同targetSdkVersion屬性所帶來的權(quán)限變化,并盡可能的提高targetSdkVersion。在權(quán)限使用上Google也給了我們一些建議。- 考慮使用Intent來完成權(quán)限相關(guān)的操作
這點(diǎn)建議,我覺得可以作為一個(gè)比較好的參考。在Manifest中,我們申請(qǐng)了SEND_SMS權(quán)限,那么可以通過下面代碼完成發(fā)送短信功能。
SmsManager sm = SmsManager.getDefault(); sm.sendTextMessage(address, null, message, null, null);
如果發(fā)送短信時(shí)候,用戶選擇拒絕該權(quán)限申請(qǐng),那么你的功能也就Over了。
如果我們換intent方式進(jìn)行發(fā)送短信,則不會(huì)出現(xiàn)權(quán)限被拒絕的情況,代碼如下。Intent intent = new Intent(Intent.ACTION_SENDTO); intent.setData(Uri.parse("smsto:" + number)); intent.putExtra("sms_body", body); context.startActivity(sendIntent);
該方法會(huì)跳轉(zhuǎn)到發(fā)送短信界面(如果系統(tǒng)裝有多個(gè)短信類應(yīng)用,那么系統(tǒng)會(huì)彈出一個(gè)選擇應(yīng)用對(duì)話框,讓用戶選擇使用何種應(yīng)用來完成發(fā)送短信功能),并填充好相應(yīng)的內(nèi)容。類似的,撥打電話和使用照相機(jī)等都可以使用intent來完成相應(yīng)的功能,降低了用戶拒絕權(quán)限的風(fēng)險(xiǎn)。
最后,權(quán)限方式和intent方式各有千秋,根據(jù)不同的業(yè)務(wù)情景,我們可以選擇不同的方式。- 只申請(qǐng)你所需要的權(quán)限
不想讓用戶覺得你的應(yīng)用程序是一個(gè)“流氓應(yīng)用”,最好不要過度申請(qǐng)權(quán)限。
3.不要“吞噬”用戶
在Android6.0中,不要在同一時(shí)刻申請(qǐng)多種權(quán)限。因?yàn)橄到y(tǒng)可能會(huì)彈出多個(gè)系統(tǒng)權(quán)限詢問對(duì)話框,這種情況:
第一,用戶可能覺得很煩鎖,并退出你的應(yīng)用程序。
第二,用戶可能由于誤操作,拒絕了你的某些權(quán)限申請(qǐng)。
因此,最好的方式還是在你需要的時(shí)候進(jìn)行申請(qǐng)吧。
4.給出你為什么使用權(quán)限的原因
為了降低權(quán)限申請(qǐng)被拒絕的風(fēng)險(xiǎn),最好在調(diào)用requestPermissions()之前,進(jìn)行權(quán)限申請(qǐng)的說明,使用戶覺得你不是在做“壞事”。
動(dòng)態(tài)權(quán)限申請(qǐng)的一種解決方案
雖然目前Android6.0市場占有率相當(dāng)?shù)停请S著時(shí)間的推移,關(guān)于動(dòng)態(tài)權(quán)限管理這一塊,我們遲早要接觸的。這里我參考Android官方開發(fā)文檔,封裝了動(dòng)態(tài)權(quán)限管理所需的方法。雖然自己的項(xiàng)目中還未用到動(dòng)態(tài)權(quán)限管理,但作為工作之余的學(xué)習(xí)還是大有裨益!
權(quán)限申請(qǐng)流程
權(quán)限申請(qǐng)流程圖BaseActivity中完成權(quán)限申請(qǐng)
這里我沒有將權(quán)限申請(qǐng)相關(guān)方法封裝成一個(gè)類,而是在BaseActivity中添加相關(guān)方法。
public class BaseActivity extends AppCompatActivity { //申請(qǐng)請(qǐng)求的request code private final static int YZT_PERMISSION_REQUEST = 12; public final String TAG = getClass().getSimpleName(); //是否跳轉(zhuǎn)過應(yīng)用程序信息詳情頁 private boolean mIsJump2Settings = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void onResume() { super.onResume(); if (mIsJump2Settings) { onRecheckPermission(); mIsJump2Settings = false; } } //單個(gè)權(quán)限的檢查 public void checkPermission(@NonNull final String permission, @Nullable String reason) { if (Build.VERSION.SDK_INT < 23) return; int permissionCheck = ContextCompat.checkSelfPermission(this, permission); if (permissionCheck == PackageManager.PERMISSION_GRANTED) { //權(quán)限已經(jīng)申請(qǐng) onPermissionGranted(permission); } else { if (!TextUtils.isEmpty(reason)) { //判斷用戶先前是否拒絕過該權(quán)限申請(qǐng),如果為true,我們可以向用戶解釋為什么使用該權(quán)限 if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { //這里的dialog可以自定義 new AlertDialog.Builder(this).setCancelable(false).setTitle("溫馨提示").setMessage(reason). setNegativeButton("我知道了", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestPermission(new String[]{permission}); dialog.dismiss(); } }).show(); } else { requestPermission(new String[]{permission}); } } else { requestPermission(new String[]{permission}); } } } //多個(gè)權(quán)限的檢查 public void checkPermissions(@NonNull String... permissions) { if (Build.VERSION.SDK_INT < 23) return; //用于記錄權(quán)限申請(qǐng)被拒絕的權(quán)限集合 List<String> permissionDeniedList = new ArrayList<>(); for (String permission : permissions) { int permissionCheck = ContextCompat.checkSelfPermission(this, permission); if (permissionCheck == PackageManager.PERMISSION_GRANTED) { onPermissionGranted(permission); } else { permissionDeniedList.add(permission); } } if (!permissionDeniedList.isEmpty()) { String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]); requestPermission(deniedPermissions); } } //調(diào)用系統(tǒng)API完成權(quán)限申請(qǐng) private void requestPermission(String[] permissions) { ActivityCompat.requestPermissions(this, permissions, YZT_PERMISSION_REQUEST); } //申請(qǐng)權(quán)限被允許的回調(diào) public void onPermissionGranted(String permission) { } //申請(qǐng)權(quán)限被拒絕的回調(diào) public void onPermissionDenied(String permission) { } //申請(qǐng)權(quán)限的失敗的回調(diào) public void onPermissionFailure() { } //如果從設(shè)置界面返回,則重新申請(qǐng)權(quán)限 public void onRecheckPermission() { } //彈出系統(tǒng)權(quán)限詢問對(duì)話框,用戶交互后的結(jié)果回調(diào) @Override public final void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case YZT_PERMISSION_REQUEST: if (grantResults.length > 0) { //用于記錄是否有權(quán)限申請(qǐng)被拒絕的標(biāo)記 boolean isDenied = false; for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { onPermissionGranted(permissions[i]); } else { isDenied = true; onPermissionDenied(permissions[i]); } } if (isDenied) { isDenied = false; //如果有權(quán)限申請(qǐng)被拒絕,則彈出對(duì)話框提示用戶去修改權(quán)限設(shè)置。 showPermissionSettingsDialog(); } } else { onPermissionFailure(); } break; } } private void showPermissionSettingsDialog() { new AlertDialog.Builder(this).setCancelable(false).setTitle("溫馨提示"). setMessage("缺少必要權(quán)限\n不然將導(dǎo)致部分功能無法正常使用").setNegativeButton("下次吧", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }).setPositiveButton("去設(shè)置", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { jump2PermissionSettings(); } }).show(); } /** * 跳轉(zhuǎn)到應(yīng)用程序信息詳情頁面 */ private void jump2PermissionSettings() { mIsJump2Settings = true; Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } }
使用方法
public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //權(quán)限檢查 String[] permissionArray = {Manifest.permission.SEND_SMS, Manifest.permission.CALL_PHONE}; checkPermissions(permissionArray); // checkPermission(Manifest.permission.SEND_SMS, "YZT將要發(fā)生短信進(jìn)行身份驗(yàn)證"); } @Override public void onRecheckPermission() { super.onRecheckPermission(); String[] permissionArray = {Manifest.permission.SEND_SMS, Manifest.permission.CALL_PHONE}; checkPermissions(permissionArray); } @Override public void onPermissionGranted(String permission) { super.onPermissionGranted(permission); switch (permission) { case Manifest.permission.SEND_SMS: //TODO:發(fā)送短信 Toast.makeText(this, "發(fā)短信咯", Toast.LENGTH_LONG).show(); break; case Manifest.permission.CALL_PHONE: //TODO:打電話 Toast.makeText(this, "電話咯", Toast.LENGTH_LONG).show(); break; } } @Override public void onPermissionDenied(String permission) { super.onPermissionDenied(permission); switch (permission) { case Manifest.permission.SEND_SMS: //TODO: break; case Manifest.permission.CALL_PHONE: //TODO: break; } } @Override public void onPermissionFailure() { super.onPermissionFailure(); Toast.makeText(this, "權(quán)限獲取失敗", Toast.LENGTH_LONG).show(); }
隨著用戶安全意識(shí)的提升,我們在權(quán)限的使用上也應(yīng)該更加趨于合理和謹(jǐn)慎。雖然目前Android6.0的占有率很低,但是我們也應(yīng)該未雨綢繆,盡快引入動(dòng)態(tài)權(quán)限管理機(jī)制。
最后編輯于 :?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。推薦閱讀更多精彩內(nèi)容
- Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
- 總結(jié)整理了一下android權(quán)限相關(guān)的知識(shí),由于篇幅過長,分為兩篇博客來寫,上篇博客主要是詳解權(quán)限和安全,下篇主要...
- 剛建了一個(gè)QQ群,感興趣的大家一起多多交流:544645972 在 android permission權(quán)限與安全...
- 如果應(yīng)用程序在manifest中聲明了一個(gè)dangerous permission,并且它目前沒有該權(quán)限組中的任一權(quán)限,那么系統(tǒng)會(huì)彈出一個(gè)將要申請(qǐng)權(quán)限組的對(duì)話框。但是該對(duì)話框不會(huì)具體描述是該權(quán)限組中的哪一個(gè)權(quán)限。比如應(yīng)用程序需要READ_CONTACTS權(quán)限,那么該對(duì)話框僅僅只描述為該應(yīng)用程序需要訪問聯(lián)系人。