android4.0以上实现Mqtt客户端

来源:互联网 发布:能看杂志的软件 编辑:程序博客网 时间:2024/06/05 16:25

由于wmqtt.jar库在android4.0以上实现有问题会报MqttException Null异常,原因是该库只支持4.0以下版本。无奈只有寻找其他解决方案,最后选择的是Paho库中的client版本,org.eclipse.paho.client.mqttv3.jar。利用该库可以在android4.0以上正常连接Mqtt的服务器,博主用的android5.1进行实验的。
博主利用mqtt实现的主要功能是android端要与特定的智能硬件进行通信,而且是双向通信,android要给智能硬件发控制信息,智能硬件要给android返回状态信息;关于订阅与发布的id问题是其中的关键,android端要获取智能硬件的id,作为android发布信息的主题,在绑定完智能硬件之后,智能硬件发布以智能硬件的id+后缀作为智能硬件的发布信息id,而android端订阅智能硬件的id+后缀的主题。(启动的时候默认订阅服务器主题,服务器发送目前已经绑定了的智能硬件的主题,拉取主题列表。主题列表中有智能硬件在线、离线状态,更新客户端状态。外部设置进行其他智能硬件的主题订阅,在接口中添加主题订阅操作,在接口中添加自定义主题发布操作。)
android端为了每个活动都可以更方便得对确定主题的发布,与订阅主题的接收。在android建立一个基础活动类MqttBaseActivity,而其他需要进行mqtt操作的活动都继承该活动,减少活动中非主要业务代码冗余,提高代码可维护性。
要实现上述设想,需要进行3方面的工作:
1.MqttService类实现;
2.MqttBaseActivity类实现;
3.主活动调用示例;
MqttService提供给外界的接口主要包括内容订阅,自定义主题消息发送,全局广播消息接收。

package com.splxtech.powermanagor.engine;import java.util.Locale;import org.eclipse.paho.client.mqttv3.MqttCallback;import org.eclipse.paho.client.mqttv3.MqttClient;import org.eclipse.paho.client.mqttv3.MqttConnectOptions;import org.eclipse.paho.client.mqttv3.MqttDefaultFilePersistence;import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;import org.eclipse.paho.client.mqttv3.MqttException;import org.eclipse.paho.client.mqttv3.MqttMessage;import org.eclipse.paho.client.mqttv3.MqttPersistenceException;import org.eclipse.paho.client.mqttv3.MqttTopic;import org.eclipse.paho.client.mqttv3.internal.MemoryPersistence;import android.app.AlarmManager;import android.app.PendingIntent;import android.app.Service;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.os.Handler;import android.os.HandlerThread;import android.os.IBinder;import android.provider.Settings.Secure;import android.support.v4.content.LocalBroadcastManager;import android.util.Log;import com.splxtech.powermanagor.IMqttService;public class MqttService extends Service implements MqttCallback{    public static final String DEBUG_TAG = "MqttService"; // Debug TAG    private static final String MQTT_THREAD_NAME = "MqttService[" + DEBUG_TAG + "]"; // Handler Thread ID    private static final String MQTT_BROKER = ""; // Broker URL or IP Address    private static final int MQTT_PORT = 1883; // Broker Port    public static final int MQTT_QOS_0 = 0; // QOS Level 0 ( Delivery Once no confirmation )    public static final int MQTT_QOS_1 = 1; // QOS Level 1 ( Delevery at least Once with confirmation )    public static final int MQTT_QOS_2 = 2; // QOS Level 2 ( Delivery only once with confirmation with handshake )    private static final int MQTT_KEEP_ALIVE = 240000; // KeepAlive Interval in MS    private static final String MQTT_KEEP_ALIVE_TOPIC_FORAMT = "/users/%s/keepalive"; // Topic format for KeepAlives    private static final byte[]     MQTT_KEEP_ALIVE_MESSAGE = { 0 }; // Keep Alive message to send    private static final int MQTT_KEEP_ALIVE_QOS = MQTT_QOS_0; // Default Keepalive QOS    private static final boolean MQTT_CLEAN_SESSION = true; // Start a clean session?    private static final String MQTT_URL_FORMAT = "tcp://%s:%d"; // URL Format normally don't change    private static final String ACTION_START  = DEBUG_TAG + ".START"; // Action to start    private static final String ACTION_STOP   = DEBUG_TAG + ".STOP"; // Action to stop    private static final String ACTION_KEEPALIVE= DEBUG_TAG + ".KEEPALIVE"; // Action to keep alive used by alarm manager    private static final String ACTION_RECONNECT= DEBUG_TAG + ".RECONNECT"; // Action to reconnect    private static final String DEVICE_ID_FORMAT = "andr_%s"; // Device ID Format, add any prefix you'd like    // Note: There is a 23 character limit you will get    // An NPE if you go over that limit    private boolean mStarted = false;   // Is the Client started?    private String mDeviceId;       // Device ID, Secure.ANDROID_ID    private Handler mConnHandler;     // Seperate Handler thread for networking    private MqttDefaultFilePersistence mDataStore; // Defaults to FileStore    private MemoryPersistence mMemStore; // On Fail reverts to MemoryStore    private MqttConnectOptions mOpts; // Connection Options    private MqttTopic mKeepAliveTopic; // Instance Variable for Keepalive topic    private MqttClient mClient; // Mqtt Client    private AlarmManager mAlarmManager; // Alarm manager to perform repeating tasks    private ConnectivityManager mConnectivityManager; // To check for connectivity changes    private LocalBroadcastManager localBroadcastManager;    public static final String MQTT_RECE_MESSAGE_ACTION = "com.splxtech.powermanagor.engine.mqttservice.recemessage";    /**     * Start MQTT Client     * @param     * @return void     */    public static void actionStart(Context ctx) {        Intent i = new Intent(ctx,MqttService.class);        i.setAction(ACTION_START);        ctx.startService(i);    }    /**     * Stop MQTT Client     * @param     * @return void     */    public static void actionStop(Context ctx) {        Intent i = new Intent(ctx,MqttService.class);        i.setAction(ACTION_STOP);        ctx.startService(i);    }    /**     * Send a KeepAlive Message     * @param     * @return void     */    public static void actionKeepalive(Context ctx) {        Intent i = new Intent(ctx,MqttService.class);        i.setAction(ACTION_KEEPALIVE);        ctx.startService(i);    }    /**     * Initalizes the DeviceId and most instance variables     * Including the Connection Handler, Datastore, Alarm Manager     * and ConnectivityManager.     */    @Override    public void onCreate() {        super.onCreate();        mDeviceId = String.format(DEVICE_ID_FORMAT,                Secure.getString(getContentResolver(), Secure.ANDROID_ID));        HandlerThread thread = new HandlerThread(MQTT_THREAD_NAME);        thread.start();        mConnHandler = new Handler(thread.getLooper());        try {            mDataStore = new MqttDefaultFilePersistence(getCacheDir().getAbsolutePath());        } catch(MqttPersistenceException e) {            e.printStackTrace();            mDataStore = null;            mMemStore = new MemoryPersistence();        }        mOpts = new MqttConnectOptions();        mOpts.setCleanSession(MQTT_CLEAN_SESSION);        // Do not set keep alive interval on mOpts we keep track of it with alarm's        mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);        mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);        localBroadcastManager = LocalBroadcastManager.getInstance(this);    }    /**     * Service onStartCommand     * Handles the action passed via the Intent     *     * @return START_REDELIVER_INTENT     */    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        super.onStartCommand(intent, flags, startId);        String action = intent.getAction();        Log.i(DEBUG_TAG,"Received action of " + action);        if(action == null) {            Log.i(DEBUG_TAG,"Starting service with no action\n Probably from a crash");        } else {            if(action.equals(ACTION_START)) {                Log.i(DEBUG_TAG,"Received ACTION_START");                start();            } else if(action.equals(ACTION_STOP)) {                stop();            } else if(action.equals(ACTION_KEEPALIVE)) {                keepAlive();            } else if(action.equals(ACTION_RECONNECT)) {                if(isNetworkAvailable()) {                    reconnectIfNecessary();                }            }        }        return START_REDELIVER_INTENT;    }    /**     * Attempts connect to the Mqtt Broker     * and listen for Connectivity changes     * via ConnectivityManager.CONNECTVITIY_ACTION BroadcastReceiver     */    private synchronized void start() {        if(mStarted) {            Log.i(DEBUG_TAG,"Attempt to start while already started");            return;        }        if(hasScheduledKeepAlives()) {            stopKeepAlives();        }        connect();        registerReceiver(mConnectivityReceiver,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));    }    /**     * Attempts to stop the Mqtt client     * as well as halting all keep alive messages queued     * in the alarm manager     */    private synchronized void stop() {        if(!mStarted) {            Log.i(DEBUG_TAG,"Attemtpign to stop connection that isn't running");            return;        }        if(mClient != null) {            mConnHandler.post(new Runnable() {                @Override                public void run() {                    try {                        mClient.disconnect();                    } catch(MqttException ex) {                        ex.printStackTrace();                    }                    mClient = null;                    mStarted = false;                    stopKeepAlives();                }            });        }        unregisterReceiver(mConnectivityReceiver);    }    /**     * Connects to the broker with the appropriate datastore     */    private synchronized void connect() {        String url = String.format(Locale.US, MQTT_URL_FORMAT, MQTT_BROKER, MQTT_PORT);        Log.i(DEBUG_TAG,"Connecting with URL: " + url);        try {            if(mDataStore != null) {                Log.i(DEBUG_TAG,"Connecting with DataStore");                mClient = new MqttClient(url,mDeviceId,mDataStore);            } else {                Log.i(DEBUG_TAG,"Connecting with MemStore");                mClient = new MqttClient(url,mDeviceId,mMemStore);            }        } catch(MqttException e) {            e.printStackTrace();        }        mConnHandler.post(new Runnable() {            @Override            public void run() {                try {                    mClient.connect(mOpts);                    mClient.subscribe("hello", 0);                    mClient.setCallback(MqttService.this);                    mStarted = true; // Service is now connected                    Log.i(DEBUG_TAG,"Successfully connected and subscribed starting keep alives");                    startKeepAlives();                } catch(MqttException e) {                    e.printStackTrace();                }            }        });    }    /**     * Schedules keep alives via a PendingIntent     * in the Alarm Manager     */    private void startKeepAlives() {        Intent i = new Intent();        i.setClass(this, MqttService.class);        i.setAction(ACTION_KEEPALIVE);        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);        mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,                System.currentTimeMillis() + MQTT_KEEP_ALIVE,                MQTT_KEEP_ALIVE, pi);    }    /**     * Cancels the Pending Intent     * in the alarm manager     */    private void stopKeepAlives() {        Intent i = new Intent();        i.setClass(this, MqttService.class);        i.setAction(ACTION_KEEPALIVE);        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);        mAlarmManager.cancel(pi);    }    /**     * Publishes a KeepALive to the topic     * in the broker     */    private synchronized void keepAlive() {        if(isConnected()) {            try {                sendKeepAlive();                return;            } catch(MqttConnectivityException ex) {                ex.printStackTrace();                reconnectIfNecessary();            } catch(MqttPersistenceException ex) {                ex.printStackTrace();                stop();            } catch(MqttException ex) {                ex.printStackTrace();                stop();            }        }    }    /**     * Checkes the current connectivity     * and reconnects if it is required.     */    private synchronized void reconnectIfNecessary() {        if(mStarted && mClient == null) {            connect();        }    }    /**     * Query's the NetworkInfo via ConnectivityManager     * to return the current connected state     * @return boolean true if we are connected false otherwise     */    private boolean isNetworkAvailable() {        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();        return (info == null) ? false : info.isConnected();    }    /**     * Verifies the client State with our local connected state     * @return true if its a match we are connected false if we aren't connected     */    private boolean isConnected() {        if(mStarted && mClient != null && !mClient.isConnected()) {            Log.i(DEBUG_TAG,"Mismatch between what we think is connected and what is connected");        }        if(mClient != null) {            return (mStarted && mClient.isConnected()) ? true : false;        }        return false;    }    /**     * Receiver that listens for connectivity chanes     * via ConnectivityManager     */    private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            Log.i(DEBUG_TAG,"Connectivity Changed...");        }    };    /**     * Sends a Keep Alive message to the specified topic     * @return MqttDeliveryToken specified token you can choose to wait for completion     */    private synchronized MqttDeliveryToken sendKeepAlive()            throws MqttConnectivityException, MqttPersistenceException, MqttException {        if(!isConnected())            throw new MqttConnectivityException();        if(mKeepAliveTopic == null) {            mKeepAliveTopic = mClient.getTopic(                    String.format(Locale.US, MQTT_KEEP_ALIVE_TOPIC_FORAMT,mDeviceId));        }        Log.i(DEBUG_TAG,"Sending Keepalive to " + MQTT_BROKER);        MqttMessage message = new MqttMessage(MQTT_KEEP_ALIVE_MESSAGE);        message.setQos(MQTT_KEEP_ALIVE_QOS);        return mKeepAliveTopic.publish(message);    }    /**     * Query's the AlarmManager to check if there is     * a keep alive currently scheduled     * @return true if there is currently one scheduled false otherwise     */    private synchronized boolean hasScheduledKeepAlives() {        Intent i = new Intent();        i.setClass(this, MqttService.class);        i.setAction(ACTION_KEEPALIVE);        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_NO_CREATE);        return (pi != null) ? true : false;    }    @Override    public IBinder onBind(Intent arg0) {        return iMqttService;    }    /**     * Connectivity Lost from broker     */    @Override    public void connectionLost(Throwable arg0) {        stopKeepAlives();        mClient = null;        if(isNetworkAvailable()) {            reconnectIfNecessary();        }    }    /**     * Publish Message Completion     */    @Override    public void deliveryComplete(MqttDeliveryToken arg0) {    }    /**     * Received Message from broker     */    @Override    public void messageArrived(MqttTopic topic, MqttMessage message)            throws Exception {        Intent intent = new Intent(MQTT_RECE_MESSAGE_ACTION);        intent.putExtra("Topic",topic.getName());        intent.putExtra("Message",message.getPayload());        localBroadcastManager.sendBroadcast(intent);        Log.i(DEBUG_TAG,"  Topic:\t" + topic.getName() +                "  Message:\t" + new String(message.getPayload()) +                "  QoS:\t" + message.getQos());    }    /**     * MqttConnectivityException Exception class     */    private class MqttConnectivityException extends Exception {        private static final long serialVersionUID = -7385866796799469420L;    }    private IMqttService.Stub iMqttService = new IMqttService.Stub(){        @Override        public boolean mqttSubscribe(String topic,int mqttQOS)        {            if(isConnected()) {                try {                    mClient.subscribe(topic, mqttQOS);                    return true;                } catch (MqttException e) {                    e.printStackTrace();                    return false;                }            }            else            {                return false;            }        }        @Override        public boolean mqttPubMessage(String topic,String Message,int mqttQOS)        {            if(isConnected())            {                MqttTopic mqttTopic = mClient.getTopic(topic);                MqttMessage message = new MqttMessage(Message.getBytes());                message.setQos(mqttQOS);                try{                    mqttTopic.publish(message);                    return true;                }                catch (MqttException e)                {                    e.printStackTrace();                    return false;                }            }            else {                return false;            }        }    };}

2.MqttBaseActivity.class,里面包含iMqtt_Service接口绑定,广播接收器消息注册,调用基本思路是如果继承子类中有receiver的实现,则会调用接口绑定与广播消息接收器注册,如果没有receiver实现则认为在该activity中不需要实现mqtt的订阅、发布、接收任务。下面是具体实现代码:

package com.splxtech.powermanagor.Base;import android.content.BroadcastReceiver;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.content.ServiceConnection;import android.os.IBinder;import android.support.v4.content.LocalBroadcastManager;import com.splxtech.powermanagor.IMqttService;import com.splxtech.powermanagor.engine.MqttService;import com.splxtech.splxapplib.activity.BaseActivity;/** * Created by li300 on 2016/10/19 0019. */public abstract class MqttBaseActivity extends BaseActivity {    //全局消息接收器    public MessageMqttReciver mReciver;    private IntentFilter mIntentFilter;    private Intent mServiceIntent;    private LocalBroadcastManager localBroadcastManager;    //调用该接口中方法来实现数据发送与主题订阅    public IMqttService iMqttService;    //标记是否已经进行了服务绑定与全局消息注册    private boolean flag;    private ServiceConnection conn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            iMqttService = IMqttService.Stub.asInterface(iBinder);        }        @Override        public void onServiceDisconnected(ComponentName componentName) {            iMqttService = null;        }    };    @Override    public void onStart()    {        flag = false;        if(mReciver!=null)        {            flag = true;            initMqtt();            localBroadcastManager.registerReceiver(mReciver,mIntentFilter);            bindService(mServiceIntent,conn,BIND_ABOVE_CLIENT);        }        super.onStart();    }    @Override    public void onDestroy()    {        if(flag==true)        {            unbindService(conn);            localBroadcastManager.unregisterReceiver(mReciver);        }        super.onDestroy();    }    public void initMqtt()    {        localBroadcastManager = LocalBroadcastManager.getInstance(this);        mServiceIntent = new Intent(this, MqttService.class);        mIntentFilter = new IntentFilter();        mIntentFilter.addAction(MqttService.MQTT_RECE_MESSAGE_ACTION);    }    public abstract class MessageMqttReciver extends BroadcastReceiver    {        @Override        public abstract void onReceive(Context context, Intent intent);    }}
1 0
原创粉丝点击