PendingIntent.getActivity创建两个Activity实例的问题

一、前言

该问题不存在,正常去写代码不会出现这个问题,之所以出现两个Activity,是因为同时使用了
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

如果应用内有个功能页面,可以通过正常流程使用,也可以通过通知栏进入,那么就会发现当这个页面存在的时候,同时通过通知栏进入,会出现两个实例。之所以出现这个问题是因为使用了PendingIntent.getActivity函数的原因。另外需要注意的是使用TaskStackBuilder也不能解决该问题,不过这里会把相关代码保留,作为参考。需要注意的是使用android:launchMode="singleInstance"也可以解决,但是这种方式可能会在桌面出现两个图标。目前来说较为正确的解决方式是通过广播或者服务作为中转,然后跳转到Activity才可以。为什么会出现两个实例,据官网所说是会创建一个新的Activity栈。所以才会导致启动模式失效。不过实际测试的话并没有出现新的栈。本文只是解决了问题,但是对于为什么会出现问题倒是不明白。
正常来说启动一个页面,然后再通过通知栏启动这个页面,走的是启动模式设置的流程,普通的就创建,单例的就共用,但是假如,这时候第一个页面也就是ManActivity使用的是单例singleTask,然后进入第二个页面。通知栏有个功能要进入第二个页面,由于某些业务问题,需要先进入MainActivity,再进入该页面。处于某些原因,这个页面不能同时存在两个,比如有一个查询数据库的功能。否则会出问题,那么这个页面设置singleTasksingleTop会出现一个问题。就是会有一个时间同时存在两个页面,然后稍后第一个页面会销毁。所以里面的内容需要延迟加载。为什么会有实效的问题,这就要结合生命周期和启动模式来解释了,只有在一个页面onPause的时候,另外一个页面才会onCreate,当这个页面onResume时候,之前的页面才会onStop()->onDestory(),所以就会有这个时间段段问题。其实之所以这样也可以理解,毕竟不能另外一个页面还没有开始显示,这个页面就要销毁掉,也不合理。
这里重新说下启动模式,standard就不说了,这里记录下singleTopsingleTasksingleTop必须是在栈顶时候启动才会是同一个实例,不在栈顶就会创建新的。singleTask则是不管在哪里,只要是在同一个栈里面,只要存在,那么启动就会将其调到最上层,在此之上的页面会被销毁,即使其是singleTask。所以倘若有A->B->C->三个页面都为singleTask,这时候启动B,就会变成A->B。C被移除掉。

二、TaskStackBuilder

相关代码

       <activity
           ...
            android:exported="false"
            android:launchMode="singleTask"
            android:parentActivityName=".function.main.MainActivity"
            .../>
 Intent intent = new Intent(context, GarbageCleanActivity.class);
        //有启动处不走冷却期逻辑
        intent.putExtra(KEY_FLAG, FLAG_RECALL_NEED);
        intent.putExtra(KEY_ENABLE_FLAG, enable);
        intent.putExtra(KEY_NOTIFY_ID, notifyId);

        //创建返回栈
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
        //添加Activity到返回栈
        stackBuilder.addParentStack(GarbageCleanActivity.class);//这一行可有可无
        stackBuilder.addNextIntentWithParentStack(intent);
        //添加Intent到栈顶
        stackBuilder.addNextIntent(intent);
        int flag = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
            flag = PendingIntent.FLAG_IMMUTABLE;
        }else{
            flag = PendingIntent.FLAG_UPDATE_CURRENT;
        }
        PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, flag);
//拿到PendingIntent后绑定通知栏即可

三、广播

广播分为静态广播、动态广播。这里使用静态广播

   <receiver
            android:name=".function.util.NotificationReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="${notifictionReceiver}"/>
            </intent-filter>
        </receiver>
class NotificationReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val activityIntent = Intent(Intent.ACTION_VIEW)
        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        activityIntent.setClass(context!!, MainActivity::class.java)
        context.startActivity(activityIntent)
    }
}
   private fun getContentIntent(cxt: Context, type: Function): PendingIntent {
        val intent = Intent(cxt, NotificationReceiver::class.java)
        intent.action = BuildConfig.NOTIFICTION_RECEIVER
        return PendingIntent.getBroadcast(cxt, UUID.randomUUID().hashCode(), intent, PendingIntent.FLAG_CANCEL_CURRENT)

为了保证是Activity是一个实例,需要 android:launchMode="singleTask"

四、服务

参考Android 点击通知栏消息打开activity,并判断app是否运行

五、收起通知栏

上述代码会导致另外一个问题,就是通知栏无法自动收起,可以使用以下代码关闭通知栏

context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))

但是这个代码在Android12上会有问题,建议使用另外一种方式,在Intent.ACTION_CLOSE_SYSTEM_DIALOGS的源码中会有提示,然后根据源码进行修改即可,不过这种方式需要权限。对权限要求严格的话可能无法使用

六、参考链接

  1. 从通知启动 Activity
  2. Android知识点——TaskStackBuilder(类似于微信、QQ等点击通知栏)
  3. 短视频带货源码,android 自定义常驻通知栏
  4. Android中在通知栏内常驻应用程序消息
  5. ANDROID常驻通知栏点击事件
  6. Android 点击通知栏消息打开activity,并判断app是否运行
  7. 求助一个收起通知栏的问题
  8. 了解任务和返回堆栈
  9. 关于 PendingIntent 您需要知道的那些事
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值