When I close my application, the service that had been started is supposed to continue running in the background. For some reason, the application crashes and throws a NullPointerException upon closing it.
I am using a MQTTConstants class to keep all the Constants in one place, and within that class I have a Hashset declared, which is then modified throughout the life of the application.
Is it possible that by closing the app, this information is being cleared? Although the service is never disconnected.
The service would continue to work off of this TOPIC_SET as it continues to run in the background.
public static HashSet<String> TOPIC_SET = new HashSet<String>();
STACK TRACE
02-20 14:14:30.620: E/AndroidRuntime(14753): FATAL EXCEPTION:
MQTTservice 02-20 14:14:30.620: E/AndroidRuntime(14753): Process:
com.l.ltestmqtt, PID: 14753 02-20 14:14:30.620:
E/AndroidRuntime(14753): java.lang.NullPointerException 02-20
14:14:30.620: E/AndroidRuntime(14753): at
com.l.ltestmqtt.MQTTService.handleStartAction(MQTTService.java:315)
02-20 14:14:30.620: E/AndroidRuntime(14753): at
com.l.ltestmqtt.MQTTService.handleStart(MQTTService.java:231)
02-20 14:14:30.620: E/AndroidRuntime(14753): at
com.l.ltestmqtt.MQTTService$2.run(MQTTService.java:196)
02-20 14:14:30.620: E/AndroidRuntime(14753): at
java.lang.Thread.run(Thread.java:841)
Here are the methods that are named within the Stack Trace
handleStart
synchronized void handleStart(Intent intent, int startId) {
// before we start - check for a couple of reasons why we should stop
Log.e("SERVICE", "----------HANDLESTART()-----------");
if (mqttClient == null) {
// we were unable to define the MQTT client connection, so we stop
// immediately - there is nothing that we can do
stopSelf();
return;
}
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
if (cm.getBackgroundDataSetting() == false) // respect the user's request not to use data!
{
// user has disabled background data
connectionStatus = MQTTConnectionStatus.NOTCONNECTED_DATADISABLED;
// update the app to show that the connection has been disabled
broadcastServiceStatus("Not connected - background data disabled");
// we have a listener running that will notify us when this
// preference changes, and will call handleStart again when it
// is - letting us pick up where we leave off now
return;
}
if (!handleStartAction(intent)) {
// the Activity UI has started the MQTT service - this may be starting
// the Service new for the first time, or after the Service has been
// running for some time (multiple calls to startService don't start
// multiple Services, but it does call this method multiple times)
// if we have been running already, we re-send any stored data
rebroadcastStatus();
rebroadcastReceivedMessages();
}
// if the Service was already running and we're already connected - we
// don't need to do anything
if (isAlreadyConnected() == false) {
// set the status to show we're trying to connect
connectionStatus = MQTTConnectionStatus.CONNECTING;
// we are creating a background service that will run forever until
// the user explicity stops it. so - in case they start needing
// to save battery life - we should ensure that they don't forget
// we're running, by leaving an ongoing notification in the status
// bar while we are running
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification =
new Notification(R.drawable.ic_launcher, "MQTT", System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.flags |= Notification.FLAG_NO_CLEAR;
Intent notificationIntent = new Intent(this, MQTTNotifier.class);
PendingIntent contentIntent =
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(this, "MQTT", "MQTT Service is running", contentIntent);
nm.notify(MQTTConstants.MQTT_NOTIFICATION_ONGOING, notification);
// before we attempt to connect - we check if the phone has a
// working data connection
if (isOnline()) {
// we think we have an Internet connection, so try to connect
// to the message broker
if (connectToBroker()) {
// we subscribe to a topic - registering to receive push
// notifications with a particular key
// in a 'real' app, you might want to subscribe to multiple
// topics - I'm just subscribing to one as an example
// note that this topicName could include a wildcard, so
// even just with one subscription, we could receive
// messages for multiple topics
//subscribe to initial TOPIC_SET topics, ie device_id_topic, all_topic
subscribeToAllTopics();
//subscribeToTopic(topicName);
}
} else {
// we can't do anything now because we don't have a working
// data connection
connectionStatus = MQTTConnectionStatus.NOTCONNECTED_WAITINGFORINTERNET;
// inform the app that we are not connected
broadcastServiceStatus("Waiting for network connection");
}
}
// changes to the phone's network - such as bouncing between WiFi
// and mobile data networks - can break the MQTT connection
// the MQTT connectionLost can be a bit slow to notice, so we use
// Android's inbuilt notification system to be informed of0
// network changes - so we can reconnect immediately, without
// having to wait for the MQTT timeout
if (netConnReceiver == null) {
netConnReceiver = new NetworkConnectionIntentReceiver();
registerReceiver(netConnReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
// creates the intents that are used to wake up the phone when it is
// time to ping the server
if (pingSender == null) {
pingSender = new PingSender();
registerReceiver(pingSender, new IntentFilter(MQTTConstants.MQTT_PING_ACTION));
}
}
handleStartAction
private boolean handleStartAction(Intent intent) {
String action = intent.getAction();
if (action == null) {
return false;
}
if (action.equalsIgnoreCase(MQTTConstants.MQTT_SUBSCRIBE_TOPIC_INTENT)) {
handleSubscribeTopicIntent(intent);
}
if (action.equalsIgnoreCase(MQTTConstants.MQTT_PUBLISH_MSG_INTENT)) {
handlePublishMessageIntent(intent);
}
if (action.equalsIgnoreCase(MQTTConstants.MQTT_UNSUBSCRIBE_TOPIC_INTENT)) {
handleUnsubscribeTopicIntent(intent);
}
return true;
}
UPDATES: The problem exists within the handleStart() method. If I comment this if (!handleStartAction(intent)) { the issue no longer occurs.
FOR ρяσѕρєя K
The service is started inside MQTTNotifier Activity using this
MQTTServiceDelegate.startService(this);
which references this method inside the MQTTServiceDelegateClass
public static void startService(Context context) {
Intent svc = new Intent(context, MQTTService.class);
context.startService(svc);
}
I have solved the issue, I will mark this as the answer unless someone is able to provide a better solution.
I ran a quick test to see if the intent was == null, and if it was I just logged it, otherwise I processed the code.
private boolean handleStartAction(Intent intent) {
if (intent == null) {
Log.e("NULL INTENT", "***************NULL INTENT**************");
} else {
String action = intent.getAction();
if (action == null) {
return false;
}
if (action.equalsIgnoreCase(MQTTConstants.MQTT_SUBSCRIBE_TOPIC_INTENT)) {
handleSubscribeTopicIntent(intent);
}
if (action.equalsIgnoreCase(MQTTConstants.MQTT_PUBLISH_MSG_INTENT)) {
handlePublishMessageIntent(intent);
}
if (action.equalsIgnoreCase(MQTTConstants.MQTT_UNSUBSCRIBE_TOPIC_INTENT)) {
handleUnsubscribeTopicIntent(intent);
}
}
return true;
}
Related
My code was giving an error
Overwriting this annotation fixed the error. Is this a healthy solution?
The class giving the error is related to bluetooth.
"Add permissions check" It gives an error when I do the suggestion.
Error:
Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or explicitly handle a potential SecurityException
Thanks
I used #SuppressLint("MissingPermission") but I couldn't trust
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
// Cancel the thread that completed the connection
if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
// Cancel the accept thread because we only want to connect to one device
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(BluetoothState.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(BluetoothState.DEVICE_NAME, device.getName());
bundle.putString(BluetoothState.DEVICE_ADDRESS, device.getAddress());
msg.setData(bundle);
mHandler.sendMessage(msg);
setState(BluetoothState.STATE_CONNECTED);
}
AFAIK, in order for MissingPermission error not to appear, you would have to check for the permission within the function.
Now, if you are absolutely certain, that the permission is, and will be, getting checked before the call of this function, it is OK to use #SuppressLint("MissingPermission"). What I would additionally do, is put a comment as to why you suppressed the error. Something in the line of: #SuppressLint("MissingPermission") // permission must be checked before the call of the function!.
I can successfully write to an RN4020 module from my Android device. An LED lights up on the module to indicate successful receipt of transmission and I can see the ASCII character transmitted to RN4020 on an Arduino terminal.
The problem is: when I try to send characters in quick succession (for example, each time when a button on my Android app is pressed very quickly) then after 5 to 10 successful transmissions:
RN4020 stops receiving: the LED to indicate Rx never turns on and I can't see transmitted characters on the terminal.
Most times Android device doesn't recognize that the write failed but sometimes I get the following error:
D/BluetoothGatt: writeCharacteristic: mDeviceBusy = true, and return false
My code to write to BLE (everytime a button is clicked I call sendDataToRN4020):
public boolean sendDataToRN4020(char instruction){
//check mBluetoothGatt is available
if (mBluetoothGatt == null) {
Log.e(TAG, "lost connection");
return false;
}
BluetoothGattService Service = mBluetoothGatt.getService(UUID.fromString("<address uuid>"));
if (Service == null) {
Log.e(TAG, "service not found!");
return false;
}
BluetoothGattCharacteristic charac = Service.getCharacteristic(UUID.fromString("<service uuid>"));
if (charac == null) {
Log.e(TAG, "char not found!");
return false;
}
byte[] value = new byte[1];
value[0] = (byte) (instruction);
charac.setValue(value);
boolean status = mBluetoothGatt.writeCharacteristic(charac);
return status;
}
This is my callback for writeCharacteristic:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "Successful transmission");
}
}
This may not be an Answer to your problem but I have not enough reputation to comment so I made an answer, I worked in an application using BLE to transfer data, I ran into a problem similar in which the app stops transmission even when the BLE device it's displaying that there's connection, this was because I had a scanner for compatible BLE devices, sometimes the app connects to a device multiple times because the app is constantly scanning for signals, don't know if you are doing something similar but I would recommend the following, before making a connection make sure that if there's a previous one, disconnect:
public static void connect(final String address, final String devicename, Context context) {
mContext = context;
if (mBluetoothAdapter != null && address != null) {
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device != null) {
forceDisconnect();
Log.i(TAG,"starting connection");
mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
}
}
This is the method to disconnect:
public static void forceDisconnect(){
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect(); // from any connected device
if (mBluetoothGatt != null) mBluetoothGatt.close();
mBluetoothGatt = null;
}
I'm not sure if this is going to be of any help, what I can tell you is that after I did this adjustments to my code I was able to send data from the BLE device without issues, perhaps it's worth knowing how is your connection code to the BLE device, anyways hope it helps.
The problem was eventually resolved. The code in the question runs perfectly on a recently released Android device. The reason the failure was happening was that the device being used for testing at the time of posting this question was at least 3 years old. Newer devices have more robust support for BLE.
I need to run a service if a server port is open. I am doing this by using below method.
public Future<Boolean> ifPortIsOpenThenStartIridiumService(final Context context, final String device_mac, final String device_imei, final String input_mobile) {
return Executors.newFixedThreadPool(20).submit(new Callable<Boolean>() {
#Override
public Boolean call() {
try {
String SERVER_IP = "IP Address";
int SERVER_PORT = Server_port;
int DURATION = 1000;
Socket socket = new Socket();
socket.connect(new InetSocketAddress(SERVER_IP, SERVER_PORT), DURATION);
socket.close();
Log.d(TAG, "Port is Open");
runIridiumService(context, device_mac, device_imei, input_mobile);
return true;
} catch (Exception ex) {
Log.d(TAG, "Port is not Open");
CustomToast.showToast(context, "No Internet Access.", "If in flight, please switch to \"Aeroplane Mode\" and connect to the airline's Wi-Fi network.", 1);
return false;
}
}
});
}
Above code is working but when I run this method the application is getting hanged and black screen is shown for 5-6 seconds.
I found below message on Logcat.
W/ActivityManager: Launch timeout has expired, giving up wake lock!
After that service is started and application is working well. How can I get rid of this problem?
Thanks in advance.
After some study, as far as I understood,
Android application is hanged and showing black screen for 5-6 seconds Because,
Future -
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation.
So, it waits until operation is finished. You can get more info from here.
newFixedThreadPool -
At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available.
Get more info from here.
The possible solution of your problem is to use ScheduledExecutorService.
Before that you can check Future is completed or not using
if (YOUR_FUTURE.isDone()){
result = (String) YOUR_FUTURE.get();
}
to avoid unwanted or extra loop.
I have a problem trying to connect to a peripheral. Sometimes the callback onConnectionStateChange(...) is not called after BluetoothDevice#connectGatt(...). What I'm trying to achieve is fast and short connections triggered by user action.
This situation occurs about 1 every 10 times without specific prior action. It lasts about 20 to 30 seconds or until the application is killed and reopened. The normal sequence of steps I follow is:
Scan devices to find the peripheral.
Call BluetoothDevice#connectGatt(...). If it takes longer than 1 second to connect, it means that the connection is "stuck" and therefore it won't connect, so BluetoothDevice#connectGatt(...) is called again. This is done with a limit of 5 attempts.
onConnectionStateChange(...) is called with newState CONNECTED and begins the services discovery.
The rest of the operations are performed without problems.
After disconnection BluetoothGatt#close() is called.
The problem occurs at point 3. Sometimes onConnectionStateChange(...)is not called. I have noticed that most of the times the problem starts with a specific behavior. After calling BluetoothDevice#connectGatt(...), onConnectionStateChange(...) is called with newState CONNECTED, but almost immediately afterwards (~40 milliseconds) is called again with newStatus DISCONNECTED. Due to the short time of the status change, I can deduce that the device does not even tried to make the connection and changed the state to DISCONNECTED.
The problem ends when:
20-30 seconds have passed. During this time onConnectionStateChange(...) is never called. When the problem ends, onConnectionStateChange(...) is called the number of times that the app tried to connect. For example, if BluetoothDevice#connectGatt(...) is called 15 times, onConnectionStateChange(...) is called 15 times with newState equal to DISCONNECTED. This is curious because never in any of those connection attempts the status changed to CONNECTED.
The app is killed and started again.
This error occurs in SDK18 and SDK 21.
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
String deviceName = device.getName();
if (deviceName == null) return;
Log.d("BLUETOOTH CONNECTION", "Device found: " + device.getName());
if (mMode == SCAN_MODE) {
mListener.deviceFound(device, rssi, scanRecord);
}
else {
mDevices.put(device.hashCode(), device);
stopScan();
// Samsung devices with SDK 18 or 19 requires that connectGatt is called in main thread.
mHandler.post(new Runnable() {
#Override
public void run() {
Log.d("BLUETOOTH CONNECTION", "Executing first device.connectGatt()");
BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
retryIfNecessary(device, gatt);
mTryingToConnect = true;
}
});
}
}
private void retryIfNecessary(final BluetoothDevice device, final BluetoothGatt gatt) {
if (isRetryLimitReached()) {
Log.d("BLUETOOTH CONNECTION", "Try count limit reached");
finishConnection(gatt);
mRetryCount = 0;
mListener.error(TIMEOUT);
return;
}
mRetryCount++;
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d("BLUETOOTH CONNECTION", "Check if it is frozen.");
if (isWorking()) {
Log.d("BLUETOOTH CONNECTION", "Frozen, create new connection.");
BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
retryIfNecessary(device, gatt);
}
}
}, RETRY_INTERVAL_MS);
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
Log.d("BLUETOOTH CONNECTION", "On connection state changed. Device: "+ gatt.getDevice().getAddress());
if (!mConnected && BluetoothGatt.STATE_CONNECTED == newState) {
Log.d("BLUETOOTH CONNECTION", "Connected");
mTryingToConnect = false;
mTryingToDiscoverServices = true;
mConnected = true;
gatt.discoverServices();
}
else if(BluetoothGatt.STATE_DISCONNECTED == newState) {
Log.d("BLUETOOTH CONNECTION", "Disconnected and closing gatt.");
mConnected = false;
gatt.close();
if (!mConnectionFinished && mRetryCount == 0) {
finishConnection(gatt);
}
}
}
I think that the peripheral is not relevant, because the iOS app can always connect without this problem.
Any ideas? Thanks in advance.
Edit!
This answer say that:
Direct connection has interval of 60ms and window of 30ms so
connections complete much faster. Additionally there can only be one
direct connection request pending at a time and it times out after 30
seconds. onConnectionStateChange() gets called with state=2,
status=133 to indicate this timeout.
So in this 30 seconds interval there is a pending connection request and times out at the second 30. It's unlikely but, is there anything I can do to make this time shorter? Or maybe there is an explanation for the connection failure that I am not seeing. Thanks.
EDIT 02/03/2016
A new information that may help. When the problem starts (when onConnectionStateChange(...) is called with newState=DISCONNECTED after ~40ms of being called with newState=CONNECTED), the status is 62 = 0x03E. Looking here that status code means GATT_CONN_FAIL_ESTABLISH. When I detect this status I'm closing the gatt connection, but the problem persists. I also tried disconnecting and closing. Ideas? Thanks.
If someone is having a similar issue, the problem was finally solved by changing the BLE chip used by the peripheral (arduino). Before that change, a workaround I found was turning off and on the BLE after each connection. The solution was not perfect, but improved the connection rate a lot.
Android Bluetooth needs to be recycled occasionally, have you tried restarting the BLE on the device when you encounter this timeount?
Here's a snippet I've used to restart the BLE when strange things start happening.
static Handler mHandler = new Handler();
public static void restartBle() {
final BluetoothManager mgr = (BluetoothManager) ApplicationBase.getAppContext().getSystemService(Context.BLUETOOTH_SERVICE);
final BluetoothAdapter adp = mgr.getAdapter();
if (null != adp) {
if (adp.isEnabled()) {
adp.disable();
// TODO: display some kind of UI about restarting BLE
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (!adp.isEnabled()) {
adp.enable();
} else {
mHandler.postDelayed(this, 2500);
}
}
}, 2500);
}
}
}
I am not sure if you're still looking for an answer for this question. Personally, I would not advise making "fast and short connections triggered by user action" for low energy devices. Instead you could set the autoConnect option to "true" in your connectGatt method.
device.connectGatt(mContext, true, mGattCallback); [instead of false]
Hope it helps!
I am using GCM. Its work perfect but after unregister i still receive notifications.
This is my registration:
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(context);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
GCMRegistrar.checkManifest(context);
registerReceiver(mHandleMessageReceiver, new IntentFilter(
DISPLAY_MESSAGE_ACTION));
// Get GCM registration id
final String regId = GCMRegistrar.getRegistrationId(context);
// Check if regid already presents
if (regId.equals("")) {
// Registration is not present, register now with GCM
GCMRegistrar.register(context, SENDER_ID);
} else {
// Device is already registered on GCM
if (GCMRegistrar.isRegisteredOnServer(context)) {
// Skips registration.
Toast.makeText(context, "Already registered with GCM", Toast.LENGTH_LONG).show();
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// Register on our server
// On server creates a new user
ServerUtilities.register(context, user, pass, regId);
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}`
And from different activity i am trying to unregister from GCM:
GCMRegistrar.unregister(getApplicationContext());
GCMRegistrar.onDestroy(getApplicationContext());
And after that i still receive notifications :(
First, GCMRegistrar is deprecated.
Second, unregister() indicates that this device should never again receive messages. Frequently registering and unregistering is not expected app behavior. If you want to stop receiving messages, tell your app server to stop sending them.