Android中Notification的PendingIntent无效问题解决

下面来一段代码,引出今天的梗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent();
intent.setClass(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("aa","bb");
PendingIntent mPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification mNotification;
if (android.os.Build.VERSION.SDK_INT < 16) &#123;
mNotification = new Notification();
mNotification.icon = R.drawable.ic_launcher;// icon 的id
mNotification.defaults = Notification.DEFAULT_ALL;
mNotification.flags = Notification.FLAG_AUTO_CANCEL;
&#125; else &#123;
mNotification = new Notification.Builder(context)
.setContentTitle(title).setContentText(message)
.setSmallIcon(R.drawable.ic_launcher)
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true).setContentIntent(mPendingIntent).build();
&#125;
nm.notify(0, mNotification);

上面的代码就是最基本的消息通知,然后启动相应的界面,现在世面上大多数也都是这么写的,但是最近却栽在这个坑上了,为啥这么说,因为当我们消息推送的时候在其他机型这样写都是OK的,唯独在华为上面,点击消息启动栏没有任何反应,也不闪退,也不给我们答案,就是通知状态没有了。纠结了一阵子,今天终于。。。。
我们先来看一下,作用于此事件的幕后黑手究竟是谁,上面的代码一目了然,PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags),nm.notify(0, mNotification);就是这两个方法在搞怪,我们来看看里面的参数,PendingIntent.getActivity)官方的解释是这样的

Retrieve a PendingIntent that will start a new activity, like calling Context.startActivity(Intent). Note that the activity will be started outside of the context of an existing activity, so you must use the Intent.FLAG_ACTIVITY_NEW_TASK launch flag in the Intent.

主要的意思就是使用PendingIntent来启动一个Activity,就像用Context.startActivity(Intent)来启动一个Activity一样,注意这里的activity将是上下文之外现有的activity,所以你必须使用Intent.FLAG_ACTIVITY_NEW_TASK标记位来启动一个intent。

这里面的第一个,第三个参数这里不打算细说了。

1
2
3
4
5
6
7
8
9
* @param context The Context in which this PendingIntent should start
* the activity.
* @param requestCode Private request code for the sender
* @param intent Intent of the activity to be launched.
* @param flags May be &#123;@link #FLAG_ONE_SHOT&#125;, &#123;@link #FLAG_NO_CREATE&#125;,
* &#123;@link #FLAG_CANCEL_CURRENT&#125;, &#123;@link #FLAG_UPDATE_CURRENT&#125;,
* or any of the flags as supported by
* &#123;@link Intent#fillIn Intent.fillIn()&#125; to control which unspecified parts
* of the intent that can be supplied when the actual send happens.

摘自源码,当我们点进去getActivity方法时是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static PendingIntent getActivity(Context context, int requestCode,
Intent intent, @Flags int flags) &#123;
return getActivity(context, requestCode, intent, flags, null);
&#125;
public static PendingIntent getActivity(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags, @Nullable Bundle options) &#123;
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try &#123;
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
null, null, requestCode, new Intent[] &#123; intent &#125;,
resolvedType != null ? new String[] &#123; resolvedType &#125; : null,
flags, options, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
&#125; catch (RemoteException e) &#123;
&#125;
return null;
&#125;

第四个参数Flag我挨个换了一遍还是不行,排除法,最后剩下requestCode了,可是网上都是这么写的啊,不然,就是他的原因。我们可以从源码中可以看出来,我们把requestCode传进去之后,重新传给了IIntentSender对象,看源码涉及到IPC机制,贴出getIntentSender()源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle options, int userId) throws RemoteException &#123;
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(type);
data.writeString(packageName);
data.writeStrongBinder(token);
data.writeString(resultWho);
data.writeInt(requestCode);
if (intents != null) &#123;
data.writeInt(1);
data.writeTypedArray(intents, 0);
data.writeStringArray(resolvedTypes);
&#125; else &#123;
data.writeInt(0);
&#125;
data.writeInt(flags);
if (options != null) &#123;
data.writeInt(1);
options.writeToParcel(data, 0);
&#125; else &#123;
data.writeInt(0);
&#125;
data.writeInt(userId);
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
IIntentSender res = IIntentSender.Stub.asInterface(
reply.readStrongBinder());
data.recycle();
reply.recycle();
return res;
&#125;

这里首先获取Parcel对象,Parcel.obtain(),然后将我们传递过来的参数打包到Parcel对象中,parcel.write(),最后将我们打包好的数据发送出去 mRemote.transact(),最后回收parcel对象,data.recycle();reply.recycle();
猜测应该是sender用requestCode来区分不同的PendingIntent对象,然后对应。因为之前我们传递的都是0,所以这里我们将requestCode的值改为每条消息都不一样就好了。这里也要记住,getActivity中requestCode和notiy中的id要保持一致,我将这里改成了

1
2
PendingIntent mPendingIntent = PendingIntent.getActivity(context, (int)System.currentTimeMillis()/1000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
nm.notify((int) (System.currentTimeMillis()/1000), mNotification);

这样就解决问题了。

版权声明:



除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

坚持原创技术分享,您的支持将鼓励我继续创作!