上周我一直在处理类似的问题,并且可以提出类似于 Tom Tache 的解决方案(使用反射),但可能更容易理解。以下方法将梳理任何存在的文本的通知,并在可能的情况下在 ArrayList 中返回该文本。
public static List<String> getText(Notification notification)
{
// We have to extract the information from the view
RemoteViews views = notification.bigContentView;
if (views == null) views = notification.contentView;
if (views == null) return null;
// Use reflection to examine the m_actions member of the given RemoteViews object.
// It's not pretty, but it works.
List<String> text = new ArrayList<String>();
try
{
Field field = views.getClass().getDeclaredField("mActions");
field.setAccessible(true);
@SuppressWarnings("unchecked")
ArrayList<Parcelable> actions = (ArrayList<Parcelable>) field.get(views);
// Find the setText() and setTime() reflection actions
for (Parcelable p : actions)
{
Parcel parcel = Parcel.obtain();
p.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
// The tag tells which type of action it is (2 is ReflectionAction, from the source)
int tag = parcel.readInt();
if (tag != 2) continue;
// View ID
parcel.readInt();
String methodName = parcel.readString();
if (methodName == null) continue;
// Save strings
else if (methodName.equals("setText"))
{
// Parameter type (10 = Character Sequence)
parcel.readInt();
// Store the actual string
String t = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel).toString().trim();
text.add(t);
}
// Save times. Comment this section out if the notification time isn't important
else if (methodName.equals("setTime"))
{
// Parameter type (5 = Long)
parcel.readInt();
String t = new SimpleDateFormat("h:mm a").format(new Date(parcel.readLong()));
text.add(t);
}
parcel.recycle();
}
}
// It's not usually good style to do this, but then again, neither is the use of reflection...
catch (Exception e)
{
Log.e("NotificationClassifier", e.toString());
}
return text;
}
因为这可能看起来有点像黑魔法,让我更详细地解释一下。我们首先从通知本身中提取 RemoteViews 对象。这表示实际通知中的视图。为了访问这些视图,我们要么必须膨胀 RemoteViews 对象(仅当存在活动上下文时才会起作用)或使用反射。反射在任何一种情况下都可以工作,并且是这里使用的方法。
如果您检查 RemoteViews here 的来源,您会看到其中一个私有成员是 Action 对象的 ArrayList。这表示在视图膨胀后将对视图执行什么操作。例如,在创建视图之后,setText() 将在作为层次结构一部分的每个 TextView 上的某个时间点调用,以分配适当的字符串。我们所做的是获得对这个动作列表的访问权并遍历它。动作定义如下:
private abstract static class Action implements Parcelable
{
...
}
RemoteViews 中定义了许多具体的 Action 子类。我们感兴趣的是ReflectionAction,定义如下:
private class ReflectionAction extends Action
{
String methodName;
int type;
Object value;
}
此操作用于为视图分配值。此类的单个实例可能具有值 {"setText", 10, "content of textview"}。因此,我们只对作为“ReflectionAction”对象并以某种方式分配文本的 mAction 元素感兴趣。我们可以通过检查 Action 中的“TAG”字段来判断特定的“Action”是“ReflectionAction”,该字段始终是从包裹中读取的第一个值。 2 的 TAG 代表 ReflectionAction 对象。
之后,我们只需按照写入的顺序从包裹中读取值(如果您好奇,请参阅源链接)。我们找到使用 setText() 设置的任何字符串并将其保存在列表中。 (还包括setTime(),以防还需要通知时间。如果不需要,可以安全地删除这些行。)
虽然我通常反对在这种情况下使用反射,但有时它是必要的。除非有可用的活动上下文,否则“标准”方法将无法正常工作,因此这是唯一的选择。