MQTT send message from main thread - java

I have simple MQTT subscriber implemented in MqttHelper class that works fine and receives subscriptions. But how I should deal when I need to send message to server from main program. I have method publish that works fine from IMqttActionListener but how to send text from main program on button pressed event?
package com.kkk.mqtt.helpers;
import android.content.Context;
import android.util.Log;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import java.io.UnsupportedEncodingException;
public class MqttHelper {
public MqttAndroidClient mqttAndroidClient;
final String serverUri = "tcp://tailor.cloudmqtt.com:16424";
final String clientId = "ExampleAndroidClient";
public final String subscriptionTopic = "sensor";
final String username = "xxx";
final String password = "yyy";
public MqttHelper(Context context){
mqttAndroidClient = new MqttAndroidClient(context, serverUri, clientId);
mqttAndroidClient.setCallback(new MqttCallbackExtended() {
#Override
public void connectComplete(boolean b, String s) {
Log.w("mqtt", s);
}
#Override
public void connectionLost(Throwable throwable) {
}
#Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
Log.w("Mqtt", mqttMessage.toString());
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
});
connect();
}
public void setCallback(MqttCallbackExtended callback) {
mqttAndroidClient.setCallback(callback);
}
public void publish(String topic, String info)
{
byte[] encodedInfo = new byte[0];
try {
encodedInfo = info.getBytes("UTF-8");
MqttMessage message = new MqttMessage(encodedInfo);
mqttAndroidClient.publish(topic, message);
Log.e ("Mqtt", "publish done");
} catch (UnsupportedEncodingException | MqttException e) {
e.printStackTrace();
Log.e ("Mqtt", e.getMessage());
}catch (Exception e) {
Log.e ("Mqtt", "general exception "+e.getMessage());
}
}
private void connect(){
Log.w("Mqtt", "connect start " );
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttConnectOptions.setCleanSession(false);
mqttConnectOptions.setUserName(username);
mqttConnectOptions.setPassword(password.toCharArray());
try {
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener()
{
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.w("Mqtt", "onSuccess " );
DisconnectedBufferOptions disconnectedBufferOptions = new DisconnectedBufferOptions();
disconnectedBufferOptions.setBufferEnabled(true);
disconnectedBufferOptions.setBufferSize(100);
disconnectedBufferOptions.setPersistBuffer(false);
disconnectedBufferOptions.setDeleteOldestMessages(false);
mqttAndroidClient.setBufferOpts(disconnectedBufferOptions);
subscribeToTopic();
publish(MqttHelper.this.subscriptionTopic,"information");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.w("Mqtt", "Failed to connect to: " + serverUri + exception.toString());
}
});
} catch (MqttException ex){
ex.printStackTrace();
}
}
private void subscribeToTopic() {
try {
mqttAndroidClient.subscribe(subscriptionTopic, 0, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.w("Mqtt","Subscribed!");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.w("Mqtt", "Subscribed fail!");
}
});
} catch (MqttException ex) {
System.err.println("Exception whilst subscribing");
ex.printStackTrace();
}
}
}
Code that starts MQTT subscriber:
private void startMqtt() {
mqttHelper = new MqttHelper(getApplicationContext());
mqttHelper.setCallback(new MqttCallbackExtended()
{
#Override
public void connectComplete(boolean b, String s) {
Log.w("Mqtt", "Connect complete"+ s );
}
#Override
public void connectionLost(Throwable throwable) {
Log.w("Mqtt", "Connection lost" );
}
#Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
Log.w("Mqtt", mqttMessage.toString());
dataReceived.setText(mqttMessage.toString());
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
Log.w("Mqtt", "Delivery complete" );
}
});
Log.w("Mqtt", "will publish");
}

Paho does not run on the UI thread, but it may asynchronously call back to the UI thread.
Just let an Activity or Fragment implement the MqttCallbackExtended interface:
public class SomeActivity extends AppCompatActivity implements MqttCallbackExtended {
...
#Override
public void connectComplete(boolean reconnect, String serverURI) {
Log.d("Mqtt", "Connect complete > " + serverURI);
}
#Override
public void connectionLost(Throwable cause) {
Log.d("Mqtt", "Connection lost");
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.d("Mqtt", "Received > " + topic + " > " + message.toString());
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.d("Mqtt", "Delivery complete");
}
}
And construct the MqttHelper with SomeActivity as it's MqttCallbackExtended listener:
public MqttHelper(Context context, MqttCallbackExtended listener) {
this.mqttAndroidClient = new MqttAndroidClient(context, serverUri, clientId);
this.mqttAndroidClient.setCallback(listener);
}
For example:
this.mqttHelper = new MqttHelper(this);
this.mqttHelper.setCallback(this);
this.mqttHelper.publish("Java", "SomeActivity will handle the callbacks.");
Handling these in Application is problematic, because Application has no UI and it's Context has no Theme. But for classes extending Activity, Fragment, DialogFragment, RecyclerView.Adapter, etc. it makes senses to implement the callback interface, when wanting to interact with their UI.
For reference, MqttCallbackExtended extends MqttCallback.

Another Solution:
Create a MQTTService class that extends android.app.Service.
Android Service class works in the main thread. So if you want to use another thread, you can use MqttAsyncClient simply.
You will receive messages from the broker in another thread automatically (not main thread) in messageArrived() using callback method.
Pass Data/command from the application UI (Activity-Fragment,...) to the MQTTService by EventBus library simply.
Again use the EventBus in the messageArrived() callback method to pass the received data from the broker to the desired section of your application.
Please note that in this step if your destination is an application UI, you have to use #Subscribe(threadMode = ThreadMode.MAIN) in the destination to get data in the main thread.
Sample Code:
public class MQTTService extends Service {
private MqttAsyncClient mqttClient;
private String serverURI;
#Override
public void onCreate() {
//do your initialization here
serverURI = "tcp://yourBrokerUrlOrIP:yourBrokerPort";
EventBus.getDefault().register(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
init();
connect();
}
private void init() {
mqttClient = new MqttAsyncClient(serverURI, yourClientId, new MemoryPersistence())
mqttClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable cause) {
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
//now you will receive messages from the broker in another thread automatically (not UI Thread).
//You can do your logic here. for example pass the received data to the different sections of the application:
EventBus.getDefault().post(new YourPOJO(topic, message, ...));
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
}
});
}
private MqttConnectOptions getOptions(){
MqttConnectOptions options = new MqttConnectOptions();
options.setKeepAliveInterval(...);
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
options.setAutomaticReconnect(true);
options.setCleanSession(false);
options.setUserName(...);
options.setPassword(...);
//options.setWill(...);
//your other configurations
return options;
}
private void connect() {
try {
IMqttToken token = mqttClient.connect(getOptions(), null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
//do works after successful connection
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
}
});
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void onDestroy() {
EventBus.getDefault().unregister(this);
mqttClient.close();
mqttClient.disconnect();
}
//this method receives your command from the different application sections
//you can simply create different "MqttCommandPOJO" classes for different purposes
#Subscribe
public void receiveFromApp1(MqttCommandPOJO1 pojo1) {
//do your logic(1). For example:
//publish or subscribe something to the broker (QOS=1 is a good choice).
}
#Subscribe
public void receiveFromApp2(MqttCommandPOJO2 pojo2) {
//do your logic(2). For example:
//publish or subscribe something to the broker (QOS=1 is a good choice).
}
}
Now You can simply receive the data passed from the MQTTService in every section of your application.
For example:
public class MainActivity extends AppCompatActivity {
...
#Subscribe(threadMode = ThreadMode.MAIN)
public void receiveFromMQTTService(YourPojo pojo){
//Do your logic. For example update the UI.
}
}
Another links:
General instructions
Best wishes

Related

How to close WebSockets properly from Application class?

I am using WebSockets for my chat app in android. For convenience, I am creating the connection in Application class so that it can be used by activities and fragments with one instance. Here is the code for my Application class:
public class Main extends Application implements LifecycleObserver {
private static WeakReference<Context> context;
private WebSocket webSocket;
private final Request request;
private final OkHttpClient client;
public static final int SOCKET_CLOSE_CODE = 1000;
#Override
public void onCreate() {
super.onCreate();
context = new WeakReference<>(getApplicationContext());
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
this.client = new OkHttpClient();
this.request = new Request.Builder().url("ws://192.168.1.9:8080").build();
}
public void connect() {
webSocket = client.newWebSocket(request, new WebSocketListener() {
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
}
#Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
super.onClosing(webSocket, code, reason);
}
#Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
}
});
}
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (webSocket == null) return;
connect();
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
if (webSocket == null) return;
disconnect();
}
private void disconnect() {
webSocket.close(SOCKET_CLOSE_CODE, null);
webSocket = null;
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
if (client == null) return;
client.connectionPool().evictAll();
client.dispatcher().executorService().shutdown();
}
}
For some reason, the socket does not close in either onPause or onDestroy (tried both). I am using Ratchet in server side. What's wrong with this code? Note that I want to close the connection exactly from the Application class itself instead of closing from activities or fragments or any other components.
The answer might be WebSocketActivity extends FragmentActivity implements WebSocketListener {} because Activity would implement these lifecycle methods, therefore they can be overridden.

Implementation of onDestroy to close a Billing Client

I am trying to make example of Play Billing application described here
In Last step they have described
To clean all the resources and unregister the observer, you just need to call BillingClient.endConnection. So define a method with this call inside BillingManager and then call it from GamePlayActivity.onDestroy:
according to above information I have made function called destroy like this in BillingManagerjava class.
public void destroy() {
mBillingClient.endConnection();
}
My Full BillingManager Class is like below
public class BillingManager implements PurchasesUpdatedListener {
private final BillingClient mBillingClient;
private final Activity mActivity;
private static final String TAG = "BillingManager";
public BillingManager(Activity activity) {
mActivity = activity;
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#BillingClient.BillingResponse int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
#Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
public void startPurchaseFlow(final String skuId, final String billingType) {
// Specify a runnable to start when connection to Billing client is established
Runnable executeOnConnectedService = new Runnable() {
#Override
public void run() {
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setType(billingType)
.setSku(skuId)
.build();
mBillingClient.launchBillingFlow(mActivity, billingFlowParams);
}
};
// If Billing client was disconnected, we retry 1 time
// and if success, execute the query
startServiceConnectionIfNeeded(executeOnConnectedService);
}
#Override
public void onPurchasesUpdated(#BillingClient.BillingResponse int responseCode,
List<Purchase> purchases) {
Log.d(TAG, "onPurchasesUpdated() response: " + responseCode);
}
private static final HashMap<String, List<String>> SKUS;
static
{
SKUS = new HashMap<>();
SKUS.put(BillingClient.SkuType.INAPP, Arrays.asList("gas", "premium"));
SKUS.put(BillingClient.SkuType.SUBS, Arrays.asList("gold_monthly", "gold_yearly"));
}
public List<String> getSkus(#BillingClient.SkuType String type) {
return SKUS.get(type);
}
public void querySkuDetailsAsync(#BillingClient.SkuType final String itemType,
final List<String> skuList, final SkuDetailsResponseListener listener) {
// Specify a runnable to start when connection to Billing client is established
Runnable executeOnConnectedService = new Runnable() {
#Override
public void run() {
SkuDetailsParams skuDetailsParams = SkuDetailsParams.newBuilder()
.setSkusList(skuList).setType(itemType).build();
mBillingClient.querySkuDetailsAsync(skuDetailsParams,
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode,
List<SkuDetails> skuDetailsList) {
listener.onSkuDetailsResponse(responseCode, skuDetailsList);
}
});
}
};
// If Billing client was disconnected, we retry 1 time
// and if success, execute the query
startServiceConnectionIfNeeded(executeOnConnectedService);
}
private void startServiceConnectionIfNeeded(final Runnable executeOnSuccess) {
if (mBillingClient.isReady()) {
if (executeOnSuccess != null) {
executeOnSuccess.run();
}
} else {
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#BillingClient.BillingResponse int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
if (executeOnSuccess != null) {
executeOnSuccess.run();
}
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
#Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
}
public void destroy() {
mBillingClient.endConnection();
}
}
And My GamePlayActivity is like below
public class GamePlayActivity extends FragmentActivity implements BillingProvider {
#Override
protected void onDestroy() {
super.onDestroy();
// I want call method here
}
}
Now I want call above function in my game play activity. I have no idea how to call it.
As it mentioned in documentation
call it from GamePlayActivity.onDestroy
but you defined your own method.
Override onDestroy method of GamePlayActivity and put mBillingClient.endConnection(); into it.
#Override
protected void onDestroy() {
mBillingClient.endConnection();
}
I assume your Activity already has an instance of the BillingManager
public class GamePlayActivity extends FragmentActivity implements BillingProvider {
BillingManager bm; // assign this in onCreate
#Override
protected void onDestroy() {
super.onDestroy();
bm.destroy();
}
}

RxJava2/RxAndroid Fetching information from website issue

I'm trying to explore Rxjava 2 and get some issue with fetching information from website. Actutally Observer give me an error.
My code:
observable.subscribe(getObserver());
Observable
Observable<String> observable = Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
String data = getHeadlines(SOURCE_WEB);
return data;
}
});
Observer
protected <String> Observer<String> getObserver() {
return new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
Log.i("Observer get: ", "mamy sub");
Toast.makeText(context, "Wait...", Toast.LENGTH_SHORT).show();
}
#Override
public void onNext(String value) {
Log.i("Observer get: ",value.toString());
}
#Override
public void onError(Throwable e) {
Log.i("Observer get ", "Error");
}
#Override
public void onComplete() {
Log.i("Observer ", "complete");
}
};
}
My fetching method
public String getHeadlines(String source) throws IOException {
Document doc = (Document) Jsoup.connect(source).get();
Elements newsHeadlines = doc.select("h2");
return newsHeadlines.toString();
}
When I'm using AsyncTask everything work fine...

Override Cordova Log

I need to override the Log in Cordova Plugin because the code I'm using has some other functions i.e. Log.info which are causing a load of errors
In the code I'm using they override as such
public class GridActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize YiCameraPlatform
try {
Platform.initialize(new Logger() {
#Override
public void verbose(String message) {
Log.v("YiCameraPlatform", message);
}
#Override
public void info(String message) {
Log.i("YiCameraPlatform", message);
}
#Override
public void warning(String message) {
Log.w("YiCameraPlatform", message);
}
#Override
public void error(String message) {
Log.e("YiCameraPlatform", message);
}
});
} catch (Exception ex) {
}
I need to do something similar in the plugin initialiser
public class XiaomiYiCordovaPlugin extends CordovaPlugin {
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
Can anyone let me know how to do this?
I'm trying things such as
Log = new Logger() {
#Override
public void verbose(String message) {
Log.v("YiCameraPlatform", message);
}
#Override
public void info(String message) {
Log.i("YiCameraPlatform", message);
}
#Override
public void warning(String message) {
Log.w("YiCameraPlatform", message);
}
#Override
public void error(String message) {
Log.e("YiCameraPlatform", message);
}
};
To no avail..

Wait until socket as service gets available

I have read about Thread sleep, Handler, Runnable, AsyncTask and TimerTask, tried Thread sleep but could not figure out how can I accomplish the task.
I have implemented socket.io.client-java as service and using Volley as well as multiple activities & fragments. I need as follow:
Check if socket is connected before making Volley request.
If socket is connected then process Volley request otherwise re-check socket connectivity (a maximum of 5 times with 1 second interval).
In case of failure in 5 attempts, show a Toast message and continue the Volley request.
I 'm using a common class to perform Volley requests and others.
Socket Service
public class SocketClient extends Service {
private Socket mSocket = null;
private static SocketClient iSocket;
public SocketClient() {
super();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onCreate(){
super.onCreate();
iSocket = this;
startSocket();
}
public static synchronized SocketClient getInstance(){return iSocket;}
private void startSocket(){
try {
mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
//...
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
//...
}
}).on(...{...});
mSocket.connect();
} catch (URISyntaxException e){
e.printStackTrace();
}
}
public boolean isConnected(){ return (mSocket != null && mSocket.connected()); }
#Override
public void onDestroy(){
super.onDestroy();
mSocket.disconnect().close();
mSocket = null;
}
}
Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
Helpers.startServices(getApplicationContext(), 1);
}
#Override
protected void onResume() {
Helpers.getNotifed(); //A Volley Request
}
Helpers Class
public static void makeReq(Map<String, Object> params, String api, final Context context, final VolleyListener listener)
{
manager = new SessionManager(context);
final String url = baseURL + api;
int i = 0;
boolean ss = SocketClient.getInstance().isConnected();
Log.i(TAG, "Socket status" + ss);
while(i < 5 && !ss) {
try {
Log.i(TAG, "Socket status" + ss);
Log.i(TAG, "sleeping " + i);
Thread.sleep(1000);
ss = SocketClient.getInstance().isConnected();
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!ss)
Toast.makeText(context, "Operation interrupt!", Toast.LENGTH_LONG).show();
//Volley JSON Object Request
}
public static void startServices(Context context, int st) {
switch (st) {
case 1: //Socket
if (!isServiceRunning(context, SocketClient.class))
context.startService(new Intent(context, SocketClient.class));
break;
}
}
Issues:
Getting NullPointerException at boolean ss = SocketClient.getInstance().isConnected(); as class might not initialized yet.
ANR (Application Not Responding) because the main Thread is halted in while loop.
P.S.: I'm just a beginner in Android/Java so reply with clear code would be appreciable.

Categories