理解PendingIntent的作用
官方文档:https://developer.android.google.cn/reference/android/app/PendingIntent
PendingIntent是什么
PendingIntent实际上包含了一个意图(Intent),以及这个意图的行为(action)。
通俗来说,就是把想要的操作(调用broadcast、打开activity、打开service)保存到PendingIntent中,在合适的时候执行这个操作。
按照官方文档,使用PendingIntent,可以赋予其他应用足够的权限(as if yourself)来执行包含的Intent的行为。
有四个方法可以获得PendingIntent的实例:
- getActivity(Context, int, Intent, int)
- getActivities(Context, int, Intent[] int)
- getBroadcast(Context, int, Intent, int)
- getService(Context, int, Intent, int)
对应着
- context.startActivity
- context.sendBroadcast
- context.startService
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application’s process is killed, the PendingIntent itself will remain usable from other processes that have been given it.
文档上还说:PendingIntent指向了一个系统维护的令牌(App Token),同时描述了原始的数据(Context..)。据我理解,App Token是代表原应用的执行权限,Context代表原应用的上下文环境。
非暴露组件的权限问题
非暴露组件是指exported=false的组件,设置了intent-filter的组件默认export=true,否则默认为false。
非暴露组件不能被外部唤起(会报出权限拒绝异常):
C:\Users\mingC\AndroidStudioProjects\NewsApp>adb shell am start -n com.newsapp/com.newsapp.main.UnexportedActivity
Starting: Intent { cmp=com.newsapp/.main.UnexportedActivity }
java.lang.SecurityException: Permission Denial: starting Intent { flg=0x10000000 cmp=com.newsapp/.main.UnexportedActivity } from null (pid=661, uid=2000) not exported from uid 10094
at android.os.Parcel.readException(Parcel.java:1683)
at android.os.Parcel.readException(Parcel.java:1636)
at android.app.ActivityManagerProxy.startActivityAsUser(ActivityManagerNative.java:3107)
at com.android.commands.am.Am.runStart(Am.java:635)
at com.android.commands.am.Am.onRun(Am.java:388)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
at com.android.commands.am.Am.main(Am.java:121)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:262)
同样的,在其他应用使用context.startActivity也是无法唤醒该活动;如果Service设置了exported=false,意味着无法跨进程调用。
实例:让其他应用打开本应用的非暴露组件
现在有一个需求,就是在我的app已经关闭的情况下,如何调起我的app里的设置为export=false的组件呢?
答案是使用PendingIntent。因为PendingIntent可以将本应用的权限临时赋予其他应用,这些权限只能用于执行PendingIntent的行为。
设置非暴露组件UnexportedActivity:
<activity android:name=".main.UnexportedActivity" android:exported="false">
以下可以实现在当前应用关闭,5秒后打开UnexportedActivity:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(Util.getContext(), UnexportedActivity.class);
PendingIntent restartIntent = PendingIntent.getActivity(Util.getContext(), 0, intent, 0);
AlarmManager mgr = (AlarmManager) Util.getContext().getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 5000, restartIntent);
finish();
}
}