您的位置:首页 > 移动开发 > Android开发

基于MQTT实现Android消息推送(Push…

2014-12-16 20:44 495 查看
基于MQTT实现Android消息推送(Push)

How to Implement Push Notifications for Android
中文版本:/article/8638991.html

在开发Android和iPhone应用程序时,我们往往需要从服务器不定的向手机客户端即时推送各种通知消息,iPhone上已经有了比较简单的和完美的推送通知解决方案,可是Android平台上实现起来却相对比较麻烦,最近利用几天的时间对Android的推送通知服务进行初步的研究。

在Android手机平台上,Google提供了C2DM(Cloudto
Device
Messaging)服务,起初我就是准备采用这个服务来实现自己手机上的推送功能。

Android Cloud to Device Messaging
(C2DM)是一个用来帮助开发者从服务器向Android应用程序发送数据的服务。该服务提供了一个简单的、轻量级的机制,允许服务器可以通知移动应用程序直接与服务器进行通信,以便于从服务器获取应用程序更新和用户数据。C2DM服务负责处理诸如消息排队等事务并向运行于目标设备上的应用程序分发这些消息。

但是经过一番研究发现,这个服务存在很大的问题:

1)C2DM内置于Android的2.2系统上,无法兼容老的1.6到2.1系统;

2)C2DM需要依赖于Google官方提供的C2DM服务器,由于国内的网络环境,这个服务经常不可用,如果想要很好的使用,我们的App
Server必须也在国外,这个恐怕不是每个开发者都能够实现的;

有了上述两个使用上的制约,导致我最终放弃了这个方案,不过我想利用另外一篇文章来详细的介绍C2DM的框架以及客户端和App Server的相应设置方法,可以作为学习与参考之用。

即然C2DM无法满足我们的要求,那么我们就需要自己来实现Android手机客户端与App Server之间的通信协议,保证在App Server想向指定的Android设备发送消息时,Android设备能够及时的收到。下面我来介绍几种常见的方案:

1)轮询:应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等。而且你还要考虑轮询的频率,如果太慢可能导致某些消息的延迟,如果太快,则会大量消耗网络带宽和电池。

2)SMS:在Android平台上,你可以通过拦截SMS消息并且解析消息内容来了解服务器的意图。这是一个不错的想法,我就见过采用这个方案的应用程序。这个方案的好处是,可以实现完全的实时操作。但是问题是这个方案的成本相对比较高,你很难找到免费的短消息发送网关,关于这个方案的实现,可以参考如下链接:https://labs.ericsson.com/apis/mobile-java-push/

3)持久连接:这个方案可以解决由轮询带来的性能问题,但是还是会消耗手机的电池。Apple的推送服务之所以工作的很好,是因为每一台手机仅仅保持一个与服务器之间的连接,事实上C2DM也是这么工作的。不过这个方案也存在不足,就是我们很难在手机上实现一个可靠的服务。Android操作系统允许在低内存情况下杀死系统服务,所以你的通知服务很可能被操作系统Kill掉了。

前两个方案存在明显的不足,第三个方案也有不足,不过我们可以通过良好的设计来弥补,以便于让该方案可以有效的工作。毕竟,我们要知道GMail,GTalk以及GoogleVoice都可以实现实时更新的。

Ø 采用MQTT协议实现Android推送

MQTT是一个轻量级的消息发布/订阅协议,它是实现基于手机客户端的消息推送服务器的理想解决方案。

我们可以从这里下载该项目的实例代码,并且可以找到一个采用PHP书写的服务器端实现

Hello everyone. In
this post I will try to provide you with a quick example on how to
implement push notifications for your Android app using
M
QTT protocol.
I will NOT discuss here why an application might need push
notifications or the advantages of Push over Pull. I assume that
you know exactly

what I mean
by push notifications are and why you might need them. However,
before jumping in straight to the good stuff, let’s go over how it
all started.

Introduction



It’s been around 4
months now since I’ve started developing apps on the Android
platform. It began with me scoring a free Nexus One phone at one of
the Android Developer
Labs. Obviously, I couldn’t
resist trying to hack around with some code, so I downloaded the
SDK and dove in. I guess in some sense, that’s exactly what Google
was hoping for when they starting giving out free phones. While it
might sound like I got lucky, in the end Google is the one who
won.

Anyway, developing
for the Android platform turned out to the a pleasure. The SDK was
easy to setup, easy to use and and easy to understand. Putting
together your first app was a breeze. I was very
impressed.

Unfortunately, I
soon realized that Android is not perfect. One of the things that
really disappointed me was the lack of a native method for
performing push notifications. Over the past year push
notifications became almost a standard in the mobile space thanks
to Apple. Even though BlackBerry utlilized Push since god knows
when, it was Apple that really brought Push mainstream. Obviously,
lack of native Push on Android seems like a huge

drawback.
Naturally, I started looking around for a solution. After Googling
through dozens and dozens of blogs and message boards, I’ve
realized that there are 3 generally accepted ways to implement push
notifications for your Android app. All of which are non-trivial,
hacky and have their own disadvantages. Let’s go over the
list:

Poll? The
name obviously tells you that it’s really not even push. The idea
here is to periodically poll the server for new messages
from

a background
local or remote service. The more often you poll the closer you get
to the real-time push.

Advantages: easy
to implement. no cost solution
Disadvantages: Obviously,
you will never be actually real-time. If you polling interval is 30
min, you can get a message that is 29 minutes and 59 seconds late.
Moreover, polling more often than every 15-30 min will kill your
battery pretty quickly: https://labs.ericsson.com/apis/mobile-java-push/blog/save-device-battery-mobile-java-push

SMS Android
allows you to intercept SMS messages. Your server sends a specially
encoded SMS to your phone, whenever there is something new. Your
app intercepts all messages, looks for the ones from the server,
then pops up a notification.

Advantages: easy
to implement. Fully real-time updates. Known drop-in solutions
exist such as one provided
by Ericsson Labs: https://labs.ericsson.com/apis/mobile-java-push/
Disadvantages: Can
be costly to you and the user. There are only a few services that
allow you send around free SMS and even those are often limited to
North America. If you want to have a reliable SMS-based service
that is available worldwide, you will likely need to pay. Similar
goes for the users. Not everyone has an SMS plan and you don’t want
your users getting charged by 3rd party for using your
app.

Persistent
TCP/IP
The phone initiates a long-lived
mostly idle TCP/IP connection with the server and maintains it
by occasionally sending keepalive
messages. Whenever there is something new on the server, it sends a
messages to the phone over the TCP connection.

Advantages:
Fully real-time updates.
Disadvantages: Hard to
implement a reliable service on both the phone and the server side.
The Android OS is known to be able to kill services when it’s
running low on memory, so your notifications service can
easily disappear. What happens when your phone
goes to sleep? Some people complain about battery life issues
related to maintaining an active connection.

The first two
methods have significant disadvantages that we cannot do anything
about. However, the third method’s drawbacks are not as severe. It
seems like with enough work and a good design, the persistent
TCP/IP method can work. After all, that’s how GMail, GTalk and
Google Voice implement their real-time updates. In fact, many
developers out there agree that it is probably the best way to go
until Google actually takes the matter in their own
hands.

Persistent
TCP/IP


After more
Googling around I was able to come across
three reasonable efforts to
implement push notifications using a persistent TCP/IP
connection:

Josh
Guilfoyl
e talks
about how to create a most-idle TCP/IP connection with a long
keep-alive timer based on the AlarmManager. He provides some really cool
sample code with a service that runs in the background and makes
connections. http://devtcg.blogspot.com/2009/01/push-services-implementing-persistent.html

Dave
Rea
recently started the Deacon
project, which is aimed at developing a 3rd party library for
Android push notifications using comet technology based
on the Meteor server. The project is still in a very
early stage (at the time of this post), but looks quite
promising. http://deacon.daverea.com/

Dale
Lane
had done a number of
presentations, where he talked about using the IBM
developed MQTT protocol for implementing
Android push notifications. He also provides some really useful
sample Android code. http://dalelane.co.uk/blog/?p=938

While all of the
work done by these guys is incredible, none of their results are
quite ready for drop-in use by other developers. In my effort to
implement push notifications, I decided to put the pieces of the
puzzle together and combine their results to produce a relatively
stable way of implementing push. The example that I provide you
with further, is a combination of Josh
Guilfoyle’s TestKeepAlive project and Dale
Lane’s MQTT work. I borrow quite a bit of code from those guys, so
they should get most of the credit. Anyways,
enough for the introduction, let’s get to the good
stuff.

My
Idea


The problem with
the TestKeepAlive project is that it creates a raw TCP connection,
which means that you need write your own server to take care of
push on the other side. While it’s, without a question, doable, it
is exactly why TestKeepAlive is far from a working solution. On the
other hand, the MQTT example shown by Dale Lane uses the IBM’s MQTT
broker to handle the server work. To backup a little, MQTT stands
for MQ Telemetry Transport, which is a protocol
developed by IBM. Let’s take a quick look at the man
page:

mqtt is
a publish/subscribe messaging protocol intended that is designed to
be lightweight. It is useful for use with low power sensors, but is
applicable to many scenarios.

Did you see the
part about ‘low power’? So did I. Basically, the reason why one
might consider using MQTT is that it was designed to be very
lightweight, so that it doesn’t consume much power. This is ideal
for a mobile push solution as it addresses many battery life
related concerns about persistent TCP/IP connections. Obviously,
MQTT also has some disadvantages such as privacy, but we can talk
about that later.

So, my idea
consists of taking a KeepAliveService and replacing the raw TCP/IP
connection with an MQTT connection. In this case, each device can
simply subscribe to a unique topic which is based on its device ID.
Now, assuming that your server knows the device ID, it can push
data to the device over MQTT by publishing to that unique
topic.

Architecture

In my example,
I utilize a PHP script as a
server. This uses the Simple Asynchronous
Messaging library (see project
SAM http://project-sam.awardspace.com/)
to publish MQTT messages to the broker on which I host on my
server. Let’s have a look at the overall system diagram:





wmqtt.jar is
a simple drop-in implementation of MQTT protocol provided by IBM.
It can be downloaded from http://www-01.ibm.com/support/docview.wss?rs=171&uid=swg24006006.
The file that you download has a bunch of different stuff. Just
look for the right jar file. You can include this jar as a part of
your Android app.

Really
Small Message Broker (RSMB)
is a simple
MQTT broker also provided by IBM http://www.alphaworks.ibm.com/tech/rsmb.
It runs on port 1883 by default. In our architecture it accepts
messages from the server and passes them on to the right devices.
RSMB can also be replaced by the Mosquitto
server http://mosquitto.atchoo.org/.

SAM is
a drop-in PHP library for MQTT and other stuff. You can either get
it as PECL extension or download the source
online http://pecl.php.net/package/sam/download/0.2.0.

send_mqtt.php is
a simple PHP script that accepts messages over POST and uses SAM to
pass-on messages to the broker.

Sample
Code and Demo






The goal of my
work on push notifications was to develop a working demo, which is
what all other examples out there lack. I’m happy to say that I
accomplished my objective. You can download the sample android app
on GitHub.

This app (shown on
the left) has a TextView and two buttons. The TextView contains
your device ID and the buttons are used to start and stop the push
notifications service. Once you have the app on your phone, start
the service. Then go to http://tokudu.com/demo/android-push/ and
enter the device ID in the first text box and enter the message
text in the textarea below. Press “Send Push Message” and you
should get a notification on your phone. It’s as easy as
that.

You can see the
source code
for andoid-push inthis GitHub project. It
contains the
aforementioned send_mqtt.php script.

If you didn’t
get a notification, make sure you have network connectivity. It can
also be that the broker is down on my server (see server status on
the page). If that’s the case, please post a comment and I will
look into it bringing the broker back up.

Final
Thoughts and Comments


MQTT is definitely
not the best way to implement push for Android, but it does work.
One of the main drawbacks of MQTT is that anyone who knows the IP
and the PORT at which the broker is running can connect and
intercept your Push messages. So it’s probably a good idea to
encrypt them. Alternatively, you could write your own broker and
introduce some sort of authentication to MQTT.

The code I provide
here for the push service still needs more testing. Reliability is
definitely the main question. I think the code can definitely be
improved to better handle connectivity loss and
other erroneous situations. You are welcome to
post your comments here regarding how it can be
improved.

Also let me know
if you find any bad bugs. Good luck testing!

Anton
Lopyrev

Follow me on
twitter @tokudu

简要总结:

1)client端

作者给出了android上面的client端程序见: AndroidPushNotificationsDemo

2)server端

首先需要自己安装Mosquitto http://mosquitto.org/download/,
在MQTT
Wiki上可以找到更多关于API,关于如何使用MQTT的介绍。

3)发送消息

使用PHP发送消息的示例程序,见:PhpMQTTClient

用上面给出的例子代码,就可以自己搭建一个环境进行测试。

上面就是对上面文章的简要总结。

上述步骤需要用PHP,然后搭建webserver来发送消息。但是,有时候我们并不方便搭建webserver,或者我们只想通过程序发消息,应该怎么办呢?可以用python脚本发送。

(Above tutorials need setup
webserver, however, sometimes it is not so convenient for us to set
up one web server and config corresponding environgments,
we just need to send message to our android
client, so I use python to solve this problem.
a)
install
mosquitto.
b)
write a python script, send message to mosquitto, mosquitto will
send message to android
client.
)

4)python脚本发送消息的程序

可以考虑使用python。下面给出使用python的例子程序。(参见:http://mqtt.org/wiki/doku.php/python_examples

我安装了python27,并且安装了Mosquitto目录下面python目录下面的库(python
setup.py install)。

给出代码:




#!/usr/bin/env python

#coding=utf-8

import mosquitto

import os

import time

broker = "127.0.0.1"

port = 1883

mypid = os.getpid()

client_uniq = "pubclient_"+str(mypid)

mqttc = mosquitto.Mosquitto(client_uniq)

#connect to broker
mqttc.connect(broker, port, 60, True)

#remain connected and publish
while mqttc.loop() == 0:

msg = "test message "+time.ctime()

mqttc.publish("topic", msg)

print "message published"

time.sleep(1)

pass






在执行上面脚本时,会提示“TypeError:
expected string or Unicode object, NoneType found”

解决方法:把mosquitto.dll所在目录加入path环境变量。参见:https://lists.launchpad.net/mqtt-users/msg00043.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: