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); }}
- android4.0以上实现Mqtt客户端
- MQTT客户端推送实现
- Python MQTT客户端实现
- Android4.0以上 代码混淆
- android4.4以上,实现状态栏颜色设置
- android4.4~6.0以上沉浸式实现
- Android4.4以上实现沉浸式状态栏
- Android4.4及以上实现透明状态栏
- STM串口转tcp实现Mqtt客户端
- MQTT客户端--基于paho实现(Java)
- Eclipse paho实现的MQTT Java客户端
- android4.0以上pad隐藏状态栏
- android4.0以上pad隐藏状态栏
- android4.0以上平板 隐藏状态栏
- android4.0以上pad隐藏状态栏
- Android4.0以上调用设置方法
- Android4.4系统以上实现沉浸式状态栏
- mqtt协议实现安卓的推送客户端实现
- JavaScript四种函数调用方式
- Thoughtworks笔试题
- AndroidStudio 和 Gradle 总结
- 局域网内,两台计算机ping不通的原因分析
- strtok()函数的用法
- android4.0以上实现Mqtt客户端
- Java网络编程之服务器与客户端通信
- Unity3D - 代码模板的修改
- 什么样的前端组件比较合适
- 宏和函数的区别
- 浅谈HQL
- 对存储器(MLC/TSD/EMMC)的一些理解
- openssl RSA基本加密解密
- HTML5&CSS3笔记:响应式设计中的 HTML5