转载请注明出处:王亟亟的大牛之路
之前一旦时间觉得不知道看些什么学些什么还打游戏,有点颓废。然后想想总得继续学习,正好I/O大会刚结束,那就来看一些新东西
大篇幅安利:https://github.com/ddwhan0123/Useful-Open-Source-Android(我每天都在更啊!!)
平时总有一些小伙子们都在问,如何做到iOS的那些桌面图标显示未读,这次的官方Smaple给出了解决方式!!!看下图!!!!
乍看之下就是普通的Notification
然后点了查阅了这条Message返回桌面之后
看着Icon上面的数字,是不是很开熏?是不是就是一直想要的?
OK,我们来看下这东西在哪!
这里给出URL:https://developer.android.com/samples/MessagingService/index.html
这里给出下载地址:https://developer.android.com/downloads/samples/MessagingService.zip
如果Google的不行,用这个:https://github.com/googlesamples/android-MessagingService/archive/master.zip
下载好build之后是这样子的
我们来看下这个功能室如何使用的(还是老风格先学会用,暂时不太详细的看实现)
先解释下这个Sample程序是干嘛的?
This sample shows a simple service that sends notifications using NotificationCompat. It also extends the notification with Remote Input to allow Android N devices to reply via text directly from the notification without having to open an App. The same Remote Input object also allows Android Auto users to respond by voice when the notification is presented there. Note: Each unread conversation from a user is sent as a distinct notification.
机械翻译一下
此示例显示了一个简单的服务,使用NotificationCompat发送通知。它还扩展了远程输入的通知,让Android的N个器件通过文字直接从该通知,而无需打开应用答复。相同的远程输入对象还允许时通报提出有Android的汽车用户通过语音响应。注意:从用户的每个未读会话作为一个单独发送通知。
Sample业务流程大致如下:
绑定一个Service然后点击按钮发送消息然后创建了消息通知,当通知被处理ReplyReceiver接收conversationId再ReadReceiver通过MessageLogger记录每个事件,并将其显示TextView上。
来看下manifest文件的内容
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.messagingservice">
<application android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme">
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MessagingService">
</service>
<receiver
android:name=".MessageReadReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.example.android.messagingservice.ACTION_MESSAGE_READ"/>
</intent-filter>
</receiver>
<receiver
android:name=".MessageReplyReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.example.android.messagingservice.ACTION_MESSAGE_REPLY"/>
</intent-filter>
</receiver>
</application>
</manifest>
2个receiver ,1个activity,1个service。
receiver用于用户操作接收
service作为通知发送
activity前台UI
这里要提一点的是以下部分
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
这里应用了一个资源,我们点进去看看是什么
<automotiveApp>
<uses name="notification"/>
</automotiveApp>
那加了这段东西能干吗呢?
应用程序要显示Android的自动概览屏幕上的通知(简单地说就是通知用)
我们来看一下具体是怎么操作的
先是UI(作者是activity里面贴了个Fragment)
public class MessagingFragment extends Fragment implements View.OnClickListener {
private static final String TAG = MessagingFragment.class.getSimpleName();
private Button mSendSingleConversation;
private Button mSendTwoConversations;
private Button mSendConversationWithThreeMessages;
private TextView mDataPortView;
private Button mClearLogButton;
private Messenger mService;
private boolean mBound;
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mService = new Messenger(service);
mBound = true;
setButtonsState(true);
Log.d(TAG,"--->onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mService = null;
mBound = false;
setButtonsState(false);
Log.d(TAG,"--->onServiceDisconnected");
}
};
//根据SharedPreference的变化来刷新UI
private final SharedPreferences.OnSharedPreferenceChangeListener listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (MessageLogger.LOG_KEY.equals(key)) {
mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
}
}
};
public MessagingFragment() {
}
//获取控件
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_message_me, container, false);
mSendSingleConversation = (Button) rootView.findViewById(R.id.send_1_conversation);
mSendSingleConversation.setOnClickListener(this);
mSendTwoConversations = (Button) rootView.findViewById(R.id.send_2_conversations);
mSendTwoConversations.setOnClickListener(this);
mSendConversationWithThreeMessages =
(Button) rootView.findViewById(R.id.send_1_conversation_3_messages);
mSendConversationWithThreeMessages.setOnClickListener(this);
mDataPortView = (TextView) rootView.findViewById(R.id.data_port);
mDataPortView.setMovementMethod(new ScrollingMovementMethod());
mClearLogButton = (Button) rootView.findViewById(R.id.clear);
mClearLogButton.setOnClickListener(this);
setButtonsState(false);
return rootView;
}
//模拟消息发送行为
@Override
public void onClick(View view) {
if (view == mSendSingleConversation) {
sendMsg(1, 1);
} else if (view == mSendTwoConversations) {
sendMsg(2, 1);
} else if (view == mSendConversationWithThreeMessages) {
sendMsg(1, 3);
} else if (view == mClearLogButton) {
MessageLogger.clear(getActivity());
mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
}
}
//绑定MessagingService
@Override
public void onStart() {
super.onStart();
getActivity().bindService(new Intent(getActivity(), MessagingService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
//注销监听
@Override
public void onPause() {
super.onPause();
MessageLogger.getPrefs(getActivity()).unregisterOnSharedPreferenceChangeListener(listener);
}
//注册监听SharedPreference
@Override
public void onResume() {
super.onResume();
mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
MessageLogger.getPrefs(getActivity()).registerOnSharedPreferenceChangeListener(listener);
}
//解绑监听
@Override
public void onStop() {
super.onStop();
if (mBound) {
getActivity().unbindService(mConnection);
mBound = false;
}
}
//具体发送消息,传入2个参数,一个是通知条数,一个是通知内消息的条数
private void sendMsg(int howManyConversations, int messagesPerConversation) {
if (mBound) {
Message msg = Message.obtain(null, MessagingService.MSG_SEND_NOTIFICATION,
howManyConversations, messagesPerConversation);
try {
mService.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Error sending a message", e);
MessageLogger.logMessage(getActivity(), "Error occurred while sending a message.");
}
}
}
private void setButtonsState(boolean enable) {
mSendSingleConversation.setEnabled(enable);
mSendTwoConversations.setEnabled(enable);
mSendConversationWithThreeMessages.setEnabled(enable);
}
}
UI(Fragment)这边定义了一个ServiceConnection作为连接等行为的监听,在刚创建时就已经进行了相应的绑定,日志如下,其他就看注解吧
05-25 10:59:53.874 24203-24203/com.example.android.messagingservice D/MessagingFragment: --->onServiceConnected
用户点击发送某一种消息业务交由MessagingService处理
public class MessagingService extends Service {
private static final String TAG = MessagingService.class.getSimpleName();
private static final String EOL = "\n";
private static final String READ_ACTION =
"com.example.android.messagingservice.ACTION_MESSAGE_READ";
public static final String REPLY_ACTION =
"com.example.android.messagingservice.ACTION_MESSAGE_REPLY";
public static final String CONVERSATION_ID = "conversation_id";
public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply";
public static final int MSG_SEND_NOTIFICATION = 1;
private NotificationManagerCompat mNotificationManager;
private final Messenger mMessenger = new Messenger(new IncomingHandler(this));
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
mNotificationManager = NotificationManagerCompat.from(getApplicationContext());
}
//初始化时,将Messenger的行为交由了Handler处理
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mMessenger.getBinder();
}
// Creates an intent that will be triggered when a message is marked as read.
private Intent getMessageReadIntent(int id) {
return new Intent()
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
.setAction(READ_ACTION)
.putExtra(CONVERSATION_ID, id);
}
// Creates an Intent that will be triggered when a voice reply is received.
private Intent getMessageReplyIntent(int conversationId) {
return new Intent()
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
.setAction(REPLY_ACTION)
.putExtra(CONVERSATION_ID, conversationId);
}
//发送通知
private void sendNotification(int howManyConversations, int messagesPerConversation) {
Conversations.Conversation[] conversations = Conversations.getUnreadConversations(
howManyConversations, messagesPerConversation);
for (Conversations.Conversation conv : conversations) {
sendNotificationForConversation(conv);
}
}
//发送通知谈话
private void sendNotificationForConversation(Conversations.Conversation conversation) {
// A pending Intent for reads
PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
conversation.getConversationId(),
getMessageReadIntent(conversation.getConversationId()),
PendingIntent.FLAG_UPDATE_CURRENT);
// Build a RemoteInput for receiving voice input in a Car Notification or text input on
// devices that support text input (like devices on Android N and above).
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_REMOTE_REPLY)
.setLabel(getString(R.string.reply))
.build();
// Building a Pending Intent for the reply action to trigger
PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(),
conversation.getConversationId(),
getMessageReplyIntent(conversation.getConversationId()),
PendingIntent.FLAG_UPDATE_CURRENT);
// 建立一个Android N兼容的远程输入启用行为。
NotificationCompat.Action actionReplyByRemoteInput = new NotificationCompat.Action.Builder(
R.drawable.notification_icon, getString(R.string.reply), replyIntent)
.addRemoteInput(remoteInput)
.build();
// Create the UnreadConversation and populate it with the participant name
// read and reply intents.
UnreadConversation.Builder unreadConvBuilder =
new UnreadConversation.Builder(conversation.getParticipantName())
.setLatestTimestamp(conversation.getTimestamp())
.setReadPendingIntent(readPendingIntent)
.setReplyAction(replyIntent, remoteInput);
// Note: Add messages from oldest to newest to the UnreadConversation.Builder
StringBuilder messageForNotification = new StringBuilder();
for (Iterator<String> messages = conversation.getMessages().iterator();
messages.hasNext(); ) {
String message = messages.next();
unreadConvBuilder.addMessage(message);
messageForNotification.append(message);
if (messages.hasNext()) {
messageForNotification.append(EOL);
}
}
//构建NotificationCompat
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.notification_icon)
.setLargeIcon(BitmapFactory.decodeResource(
getApplicationContext().getResources(), R.drawable.android_contact))
.setContentText(messageForNotification.toString())
.setWhen(conversation.getTimestamp())
.setContentTitle(conversation.getParticipantName())
.setContentIntent(readPendingIntent)
.extend(new CarExtender()
.setUnreadConversation(unreadConvBuilder.build())
.setColor(getApplicationContext().getResources()
.getColor(R.color.default_color_light)))
.addAction(actionReplyByRemoteInput);
MessageLogger.logMessage(getApplicationContext(), "Sending notification "
+ conversation.getConversationId() + " conversation: " + conversation);
//刷新通知内容
mNotificationManager.notify(conversation.getConversationId(), builder.build());
}
/**
* 处理来自客户端的消息。
*/
private static class IncomingHandler extends Handler {
private final WeakReference<MessagingService> mReference;
IncomingHandler(MessagingService service) {
mReference = new WeakReference<>(service);
}
@Override
public void handleMessage(Message msg) {
MessagingService service = mReference.get();
switch (msg.what) {
case MSG_SEND_NOTIFICATION:
int howManyConversations = msg.arg1 <= 0 ? 1 : msg.arg1;
int messagesPerConversation = msg.arg2 <= 0 ? 1 : msg.arg2;
if (service != null) {
service.sendNotification(howManyConversations, messagesPerConversation);
}
break;
default:
super.handleMessage(msg);
}
}
}
}
事件进行一系列的分发传给了2个Receiver
一个是我们点击Notification就会有回应的MessageReadReceiver
这里很明显一点,用户点了就清除这一条了
NotificationManagerCompat.from(context)
.cancel(conversationId);
还有个是刷新Notification以及处理可能出现的交互行为的MessageReplyReceiver
主要实现行为如下
notificationManager.notify(conversationId, repliedNotification);
然后SP内容变化了UI就变化了,这一个环路的一圈就走完了,主要实现就是Service里的一系列事件分发。
当然这里只是一个规范性的Demo,你自己的项目也许并不需要定义这么多东西,搞点内部类就好了。
总结:
读的过程中可能有点乱,但是大家主要看明白以下几点就好:
1 如何创建,初始化,使用一个NotificationManagerCompat
2 如何传递消息(Intent Messenger)
3 如何反馈行为 (各类Receiver)
当然还有Handler 以及其他一些刷新UI的方法,这里用的SP,你也可以不用SP,特别是异步行为时。
MessagingService里有英文注解的我就没翻译了,看原版的更好点(毕竟我翻译错了,就误人子弟了)
如果有时间的话我会去找一下别人封装好的Git内容,如果没得话我找时间造轮子吧。
这一篇就讲到这里,接下来一段时间都会去看 Android N的一些官方Sample,有疑问和意见的可以 微信我,微信在下面。
工作愉快!!