問題描述: 想在APP1中的Activity中發(fā)送一個廣播,在APP2中定義一個Receiver然后接收這個廣播,為了安全,這個廣播聲明了一個自定義權限。但是在6.x的系統(tǒng)上怎么都接收不到廣播。
APP2代碼如下
- 定義一個自定義的
BroadcastReceiver用于接收廣播消息代碼如下:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Q_M:", "成功接收廣播");
}
}
-
AndroidManifest.xml聲明自定義權限,靜態(tài)注冊MyReceiver
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.febsky.myapplication">
<!-- 權限聲明 注意最后兩行-->
<permission
android:name="me.febsky.PPPP"
android:label="@string/app_name"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="dangerous"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 靜態(tài)注冊 -->
<receiver
android:name=".MyReceiver"
android:permission="me.febsky.PERMISSION">
<intent-filter>
<action android:name="me.febsky.ACTION" />
</intent-filter>
</receiver>
</application>
</manifest>
APP1 的主界面就一個Button,功能是發(fā)送廣播,不再給出。
- button的點擊事件如下:
private static final String ACTION = "me.febsky.ACTION";
//button的點擊事件
public void onClick(View view) {
Intent intent = new Intent(ACTION);
// 發(fā)送廣播
sendBroadcast(intent);
}
-
AndroidManifest.xml聲明使用權限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.febsky.aidltest">
<!-- 權限使用聲明 -->
<uses-permission android:name="me.febsky.PERMISSION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
注意這兩個APP的啟動順序,先啟動APP2,然后啟動APP1
到這里為止,其實兩個APP之間已經(jīng)可以通信了(6.0以下的手機)。為什么6.0以上的手機不行,這就涉及到一個問題,【運行時權限】。
Android6.0 運行時權限
對于6.0以下的權限及在安裝的時候,根據(jù)權限聲明產(chǎn)生一個權限列表,用戶只有在同意之后才能完成app的安裝,造成了我們想要使用某個app,就要默默忍受其一些不必要的權限(比如是個app都要訪問通訊錄、短信等)。而在6.0以后,我們可以直接安裝,當app需要我們授予不恰當?shù)臋嘞薜臅r候,我們可以予以拒絕(比如:單機的象棋對戰(zhàn),請求訪問任何權限,我都是不同意的)。當然你也可以在設置界面對每個app的權限進行查看,以及對單個權限進行授權或者解除授權。
系統(tǒng)權限分為兩類:正常權限和危險權限:
- 正常權限Normal Permissions不會直接給用戶隱私權帶來風險。如果您的應用在其清單中列出了正常權限,系統(tǒng)將自動授予該權限。
- 危險權限會授予應用訪問用戶機密數(shù)據(jù)的權限。如果您的應用在其清單中列出了正常權限,系統(tǒng)將自動授予該權限。如果您列出了危險權限,則用戶必須明確批準您的應用使用這些權限。
Dangerous Permission,是按照權限組分配使用的。如果app運行在Android 6.x的機器上,對于授權機制是這樣的。如果你申請某個危險的權限,假設你的app早已被用戶授權了同一組的某個危險權限,那么系統(tǒng)會立即授權,而不需要用戶去點擊授權。比如你的app對READ_CONTACTS已經(jīng)授權了,當你的app申請WRITE_CONTACTS時,系統(tǒng)會直接授權通過。此外,對于申請時彈出的dialog上面的文本說明也是對整個權限組的說明,而不是單個權限(ps:這個dialog是不能進行定制的)。
任何權限都可屬于一個權限組,包括正常權限和應用定義的權限。但權限組僅當權限危險時才影響用戶體驗。可以忽略正常權限的權限組。
根據(jù)上面的介紹,我們又兩種解決方式
- 聲明權限為Normal Permissions,也就是在(APP2中的
AndroidManifest.xml)聲明權限的時候
<!-- 權限聲明 最后一行-->
<permission
android:name="me.febsky.PPPP"
android:label="@string/app_name"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"/>
- 在運行時檢查是否獲得了這個自定義的dangerous權限,如果沒有那么動態(tài)申請一個。如何動態(tài)申請權限
這時候要改造下發(fā)起廣播的app中代碼(APP1)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view) {
if (ContextCompat.checkSelfPermission(this, p) == PackageManager.PERMISSION_GRANTED) {
sendBroadCast();
} else {
ActivityCompat.requestPermissions(this, new String[]{p}, 11);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 11) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
sendBroadCast();
} else {
Toast.makeText(this, "未授權!", Toast.LENGTH_LONG).show();
}
}
}
private static final String p = "me.febsky. PERMISSION";
private static final String ACTION = "me.febsky.ACTION";
private void sendBroadCast() {
Intent shortcut = new Intent(ACTION);
// 發(fā)送廣播
sendBroadcast(shortcut);
}
}
注:羅里吧嗦的說了這么多廢話,其實就是一點,如果在Android6.0上聲明的自定義權限的level是dangerous的,那么在使用這個自定義的權限的app中要動態(tài)的申請這個權限。(也就是要彈出一個請求授權的Dialog,讓用戶來授權)