Service to receive UDP packet - android

I've created a service in order to listen if I receive message from a server (PC). But it doesn't work and I don't know why. Someone can help me ?
I call my service in my main activity , in the increate() like this :
Intent intent = new Intent(this, UDPlisten.class);
startService(intent);
I don't know how I can retrieve the data sent by the PC in my main activity. Because I would like receive a notification when the server send a message to the app.
Service :
public class UDPlisten extends Service {
static String UDP_BROADCAST = "UDPBroadcast";
DatagramSocket socket;
private void listenAndWaitAndThrowIntent(int port) throws Exception {
byte[] recvBuf = new byte[15000];
if (socket == null || socket.isClosed()){
socket = new DatagramSocket(port);
socket.setBroadcast(true);
}
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
Log.e("UDP", "Waiting for UDP broadcast");
socket.receive(packet);
String message = new String(packet.getData()).trim();
Log.e("UDP", "message: " + message);
// broadcastIntent(senderIP, message);
//socket.close();
}
private void broadcastIntent(String message) {
Intent intent = new Intent(UDPlisten.UDP_BROADCAST);
intent.putExtra("message", message);
sendBroadcast(intent);
}
Thread UDPBroadcastThread;
void startListenForUDPBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
// InetAddress broadcastIP =InetAddress.getByName("10.0.2.16");
int port = 4000;
while (shouldRestartSocketListen) {
listenAndWaitAndThrowIntent(port);
}
//if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.start();
}
private Boolean shouldRestartSocketListen=true;
void stopListen() {
shouldRestartSocketListen = false;
socket.close();
}
#Override
public void onCreate() {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
};
#Override
public void onDestroy() {
stopListen();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
Thanks in advance !

Related

QoS=1 with MqttAsyncClient subscription miss messages

I have foreground service acting as MQTT client. I'm using MqttAsyncClient mqttClient for this purpose.
I'm using QoS=1 on subscribe to topic:
mqttClient.subscribe("sensors/s1/", 1);
But in case my phone gets offline for some period of time it miss current period messages. Whole code is below.
Im my another application I'm using MqttAndroidClient mqttAndroidClient and in this case QoS=1 brings all missed messages.
mqttAndroidClient.subscribe(topic, 1, null, new IMqttActionListener() {...})
Why subscription with MqttAsyncClient with QoS=1 not retrieves all messages?
Whole code :
public class MqttGndService extends Service {
private String ip="ssl:myserver",port="8887";
private final IBinder mBinder = new LocalBinder();
private Handler mHandler;
private static final String TAG = "mqttservice";
private static boolean hasWifi = false;
private static boolean hasMmobile = false;
private ConnectivityManager mConnMan;
private volatile IMqttAsyncClient mqttClient;
private String uniqueID;
class MQTTBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
IMqttToken token;
boolean hasConnectivity = false;
boolean hasChanged = false;
NetworkInfo infos[] = mConnMan.getAllNetworkInfo();
for (int i = 0; i < infos.length; i++) {
if (infos[i].getTypeName().equalsIgnoreCase("MOBILE")) {
if ((infos[i].isConnected() != hasMmobile)) {
hasChanged = true;
hasMmobile = infos[i].isConnected();
}
Timber.tag(Utils.TIMBER_TAG).v( infos[i].getTypeName() + " is " + infos[i].isConnected());
} else if (infos[i].getTypeName().equalsIgnoreCase("WIFI")) {
if ((infos[i].isConnected() != hasWifi)) {
hasChanged = true;
hasWifi = infos[i].isConnected();
}
Timber.tag(Utils.TIMBER_TAG).v(infos[i].getTypeName() + " is " + infos[i].isConnected());
}
}
hasConnectivity = hasMmobile || hasWifi;
Timber.tag(Utils.TIMBER_TAG).v( "hasConn: " + hasConnectivity + " hasChange: " + hasChanged + " - " + (mqttClient == null || !mqttClient.isConnected()));
if (hasConnectivity && hasChanged && (mqttClient == null || !mqttClient.isConnected())) {
Timber.tag(Utils.TIMBER_TAG).v("Ready to connect");
doConnect();
Timber.tag(Utils.TIMBER_TAG).v("do connect done");
} else
{
Timber.tag(Utils.TIMBER_TAG).v("Connection not possible");
}
}
}
public class LocalBinder extends Binder {
public MqttGndService getService() {
// Return this instance of LocalService so clients can call public methods
return MqttGndService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void publish(String topic, MqttMessage message) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);// we create a 'shared" memory where we will share our preferences for the limits and the values that we get from onsensorchanged
try {
mqttClient.publish(topic, message);
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void onCreate() {
Timber.tag(Utils.TIMBER_TAG).v("Creating MQTT service");
mHandler = new Handler();//for toasts
IntentFilter intentf = new IntentFilter();
setClientID();
intentf.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(new MQTTBroadcastReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
Timber.tag(Utils.TIMBER_TAG).v( "onConfigurationChanged()");
android.os.Debug.waitForDebugger();
super.onConfigurationChanged(newConfig);
}
#Override
public void onDestroy() {
super.onDestroy();
Timber.tag(Utils.TIMBER_TAG).v("Service onDestroy");
}
private void setClientID() {
uniqueID = android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
Timber.tag(Utils.TIMBER_TAG).v("uniqueID=" + uniqueID);
}
private void doConnect() {
String broker = ip + ":" + port;
Timber.tag(Utils.TIMBER_TAG).v("mqtt_doConnect()");
IMqttToken token;
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
options.setMaxInflight(100);//handle more messages!!so as not to disconnect
options.setAutomaticReconnect(true);
options.setConnectionTimeout(1000);
options.setKeepAliveInterval(300);
options.setUserName("cc50e3e91bf4");
options.setPassword("b".toCharArray());
try {
options.setSocketFactory(SocketFactoryMQ.getSocketFactory(this,""));
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
Timber.tag(Utils.TIMBER_TAG).v("set socket factory done");
try {
mqttClient = new MqttAsyncClient(broker, uniqueID, new MemoryPersistence());
token = mqttClient.connect(options);
token.waitForCompletion(3500);
mqttClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable throwable) {
try {
mqttClient.disconnectForcibly();
mqttClient.connect();
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void messageArrived(String topic, MqttMessage msg) throws Exception {
Timber.tag(Utils.TIMBER_TAG).v("Message arrived from topic " + topic+ " msg: " + msg );
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("published");
}
});
Timber.tag(Utils.TIMBER_TAG).v("will subscribe");
mqttClient.subscribe("sensors/s1/", 1);
} catch (MqttSecurityException e) {
Timber.tag(Utils.TIMBER_TAG).v("general connect exception");
e.printStackTrace();
} catch (MqttException e) {
switch (e.getReasonCode()) {
case MqttException.REASON_CODE_BROKER_UNAVAILABLE:
mHandler.post(new ToastRunnable("WE ARE OFFLINE BROKER_UNAVAILABLE!", 1500));
break;
case MqttException.REASON_CODE_CLIENT_TIMEOUT:
mHandler.post(new ToastRunnable("WE ARE OFFLINE CLIENT_TIMEOUT!", 1500));
break;
case MqttException.REASON_CODE_CONNECTION_LOST:
mHandler.post(new ToastRunnable("WE ARE OFFLINE CONNECTION_LOST!", 1500));
break;
case MqttException.REASON_CODE_SERVER_CONNECT_ERROR:
Timber.tag(Utils.TIMBER_TAG).v( "c " + e.getMessage());
e.printStackTrace();
break;
case MqttException.REASON_CODE_FAILED_AUTHENTICATION:
Intent i = new Intent("RAISEALLARM");
i.putExtra("ALLARM", e);
Timber.tag(Utils.TIMBER_TAG).v("b " + e.getMessage());
break;
default:
Timber.tag(Utils.TIMBER_TAG).v( "a " + e.getMessage() +" "+ e.toString());
}
}
mHandler.post(new ToastRunnable("WE ARE ONLINE!", 500));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timber.tag(Utils.TIMBER_TAG).v("onStartCommand");
String input = intent.getStringExtra(INTENT_ID);
Timber.tag(Utils.TIMBER_TAG).v("onStartCommand "+ input);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag");
wakeLock.acquire();
return START_STICKY;
}
}
You are setting cleansession to true (options.setCleanSession(true)); from the docs for setCleanSession:
If set to true the client and server will not maintain state across restarts of the client, the server or the connection. This means
Message delivery to the specified QOS cannot be maintained if the client, server or connection are restarted
The server will treat a subscription as non-durable
I think that the mqtt specs state this more clearly:
If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a new one. This Session lasts as long as the Network Connection. State data associated with this Session MUST NOT be reused in any subsequent Session
So when your application looses the connection the session is discarded and new messages will not be queued up for delivery. In addition unless you resubscribe when the connection comes back up you will not receive any additional messages.
However be aware that if you set cleansession to false then any new messages received while your client is offline will be queued for delivery (subject to the configuration of the broker) and this might not be what you want to happen if the client could be offline for a long time.

How to start socket.io like foreground service to stay connected and listen for messages

I am using this source app to chat with other devices. But how to make it to start like a Service so I can to start foreground service.
Do I need MainFragment and LoginActivity rewrite in Service?
socket.io app socket.io-android-chat
I have tried something like that in class SocketService, what other I need to include in Service for App to get notification messages even if app is closed.
public class SocketService extends Service {
private Socket mSocket;
public static final String TAG = SocketService.class.getSimpleName();
private static final String NOTIFICATION_CHANNEL_ID_DEFAULT = "App running in background";
String GROUP_KEY_WORK_EMAIL = "com.android.example.WORK_EMAIL";
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw null;
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "on created", Toast.LENGTH_SHORT).show();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setGroup(GROUP_KEY_WORK_EMAIL);
Notification notification = builder.build();
NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
// Set big text style.
builder.setStyle(bigTextStyle);
startForeground(3, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "start command", Toast.LENGTH_SHORT).show();
try {
mSocket = IO.socket(Constants.CHAT_SERVER_URL);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
mSocket.on("newMessageReceived", onNewMessage);
mSocket.connect();
return START_STICKY;
}
private Emitter.Listener onNewMessage = new Emitter.Listener() {
#Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
String username;
String message;
try {
username = data.getString("username");
message = data.getString("message");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
Log.d(TAG, "call: new message ");
setNotificationMessage(message, username);
}
};
public void setNotificationMessage(CharSequence message, CharSequence title) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle(title);
builder.setContentText(message);
NotificationManagerCompat nm = NotificationManagerCompat.from(this);
nm.notify(3, builder.build());
}
}
you shouldn't use a foreground service in order to get notification messages when your app is in the background
instead you should use firebase push notification
however if you still need to use the socket connection in your foreground service
just simply create a singleton class to handle all socket connection and use it your foreground service as shown below
public class SocketManger {
private static SocketManger socketManger;
Socket socket;
Callback<Boolean> onConnect;
public void init(Callback<Boolean> onConnect){
this.onConnect = onConnect;
connectToSocket();
listenToPublicEvents();
}
private void connectToSocket(){
try{
IO.Options opts = new IO.Options();
//optional parameter for authentication
opts.query = "token=" + YOUR_TOKEN;
opts.forceNew = true;
opts.reconnection = true;
opts.reconnectionDelay = 1000;
socket = IO.socket(YOUR_URL, opts);
socket.connect();
}
catch(URISyntaxException e){
throw new RuntimeException(e);
}
}
private void listenToPublicEvents(){
socket.on(Socket.EVENT_CONNECT, args -> {
if(onConnect!=null)
onConnect.onResult(true);
} );
socket.on(Socket.EVENT_DISCONNECT, args ->{
if(onConnect!=null)
onConnect.onResult(false);
});
}
public void emit(String event, JSONObject data, Ack ack){
socket.emit(event, new JSONObject[]{data}, ack);
}
public void on(String event, Emitter.Listener em){
socket.on(event, em);
}
public static SocketManger getSocketManger() {
if(socketManger == null){
socketManger = new SocketManger();
}
return socketManger;
}
public boolean isConnected(){
return socket!=null && socket.connected();
}
public void onDestroy() {
onConnect = null;
socket.disconnect();
}
public interface Callback<T> {
void onResult(T t);
}
}
and add this code to your foreground service
SocketManager socketManger = SocketManger.getSocketManger();
#Override
public void onCreate() {
socketManger.init(this::onSocketEvent);
}
public void onSocketEvent(boolean connect){
//your code when the socket connection connect or disconnect
}
and make sure to disconnect the socket when the service is destroyed
#Override
public void onDestroy() {
socketManger.onDestroy()
super.onDestroy();
}
You can start it from a custom Application class in onCreate method if you want the service to be started immediately after the app is launched.
Or you you can start it from any Activity eg. from onCreate method in case you want to start the service from certain activity.
Or you can start from BroadcastReceiver when device is booted. In this case use BOOT_COMPLETED action:
To start your service just use this code, anywhere you want to start your service:
Intent intent = new Intent(context, SocketService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}

bluetooth as service and send/receive from it

I'm trying to recieve/send data to arduino board using bluetooth, and I can connect to board from one activity. I know that I can make my other activities connect with bluetooth using service but I don't know how to make bluetooth as service. and i don't know how to send and recieve from it.
my Paired Devices code:
public class BTConnect extends AppCompatActivity {
private static final String TAG = "BTConnect";
ListView IdLista;
public static String EXTRA_DEVICE_ADDRESS = "device_address";
private BluetoothAdapter mBtAdapter;
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_btconnect);
}
#Override
public void onResume()
{
super.onResume();
//---------------------------------
VerificarEstadoBT();
//
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.btconnect_nombre);
IdLista = (ListView)findViewById(R.id.Id2);
IdLista.setAdapter(mPairedDevicesArrayAdapter);
IdLista.setOnItemClickListener(mDeviceClickListener);
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
if (pairedDevices.size() > 0)
{
for (BluetoothDevice device: pairedDevices) {
mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
}
private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView av, View v, int arg2, long arg3) {
String info = ((TextView) v).getText().toString();
String address = info.substring(info.length() - 17);
Intent i = new Intent(BTConnect.this, device.class);
i.putExtra(EXTRA_DEVICE_ADDRESS, address);
startActivity(i);
}
};
private void VerificarEstadoBT() {
mBtAdapter= BluetoothAdapter.getDefaultAdapter();
if(mBtAdapter==null) {
Toast.makeText(getBaseContext(), "the device can't connect to BT", Toast.LENGTH_SHORT).show();
} else {
if (mBtAdapter.isEnabled()) {
Log.d(TAG, "...Bluetooth Activation...");
} else {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}}
And my first activity 'device' :
public class device extends AppCompatActivity {
Button IdEncender, IdApagar,IdDesconectar,IdReset;
ArrayList<String> addArray = new ArrayList<String>();
ListView show;
//-------------------------------------------
Handler bluetoothIn;
final int handlerState = 0;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder DataStringIN = new StringBuilder();
private ConnectedThread MyConexionBT;
// Identificador unico de servicio - SPP UUID
private static final UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// String para la direccion MAC
private static String address = null;
//-------------------------------------------
#SuppressLint("HandlerLeak")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_device);
IdEncender = (Button) findViewById(R.id.IdEncender);
IdApagar = (Button) findViewById(R.id.IdApagar);
IdDesconectar = (Button) findViewById(R.id.IdDisconectar);
show = (ListView) findViewById(R.id.LIST1) ;
IdReset=(Button)findViewById(R.id.IdReset);
bluetoothIn = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) {
String readMessage = (String) msg.obj;
DataStringIN.append(readMessage);
int endOfLineIndex = DataStringIN.indexOf("#");
if (endOfLineIndex > 0) {
String dataInPrint = DataStringIN.substring(0, endOfLineIndex);
String newline = "\r\n";
DataStringIN.delete(0, DataStringIN.length());
//--List adapter--//
addArray.add(dataInPrint);
ArrayAdapter<String> adapter =new ArrayAdapter<String>(device.this, android.R.layout.simple_list_item_1, addArray);
show.setAdapter(adapter);
}
}
};
btAdapter = BluetoothAdapter.getDefaultAdapter(); // get Bluetooth adapter
VerificarEstadoBT();
IdEncender.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
MyConexionBT.write("1");
}
});
IdApagar.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MyConexionBT.write("0");
}
});
IdDesconectar.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (btSocket!=null)
{
try {btSocket.close();}
catch (IOException e)
{ Toast.makeText(getBaseContext(), "Error", Toast.LENGTH_SHORT).show();;}
}
finish();
}
});
configurebutton();
}
private void configurebutton() {
Button startbutton = (Button)findViewById(R.id.Start);
startbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(device.this,chart.class));
}
});
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException
{
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
}
#Override
public void onResume()
{
super.onResume();
Intent intent = getIntent();
address = intent.getStringExtra(BTConnect.EXTRA_DEVICE_ADDRESS);
BluetoothDevice device = btAdapter.getRemoteDevice(address);
try
{
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
Toast.makeText(getBaseContext(), "fail", Toast.LENGTH_LONG).show();
}
try
{
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {}
}
MyConexionBT = new ConnectedThread(btSocket);
MyConexionBT.start();
}
#Override
public void onPause()
{
super.onPause();
try
{
btSocket.close();
} catch (IOException e2) {}
}
private void VerificarEstadoBT() {
if(btAdapter==null) {
Toast.makeText(getBaseContext(), "error in bluetooth connection", Toast.LENGTH_LONG).show();
} else {
if (btAdapter.isEnabled()) {
} else {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
public void savefile(String file, String text){
try {
FileOutputStream fos = openFileOutput(file, Context.MODE_APPEND);
fos.write(text.getBytes());
fos.close();
Toast.makeText(device.this, "saved!", Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(device.this,"error",Toast.LENGTH_LONG).show();
}
}
//Crea la clase que permite crear el evento de conexion
private class ConnectedThread extends Thread
{
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket)
{
InputStream tmpIn = null;
OutputStream tmpOut = null;
try
{
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run()
{
byte[] buffer = new byte[256];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
String readMessage = new String(buffer, 0, bytes);
bluetoothIn.obtainMessage(handlerState, bytes, -1, readMessage).sendToTarget();
} catch (IOException e) {
break;
}
}
}
public void write(String input)
{
try {
mmOutStream.write(input.getBytes());
}
catch (IOException e)
{
Toast.makeText(getBaseContext(), "fail to connect", Toast.LENGTH_LONG).show();
finish();
}
}
}
}
If shortly move your connection/message_exchange logic to Thread/Runnable and start it from Service. You can exchange data between Service and Activity/Activities with BroadcastReceivers or Messenger. I also recommend you to use Bluetooth lib:
implementation 'me.aflak.libraries:bluetooth:1.3.4' this one already have inside thread so it is become easy to embed in your service.
Example code of service which use this lib and Messenger:
private ArrayList<Messenger> mClients = new ArrayList<>();
final private Messenger inComingMessenger = new Messenger(mIncomingHandler);
#SuppressLint("HandlerLeak")
final Handler mIncomingHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
Log.d(TAG, "handleMessage: new client connected. Total: " + mClients.size());
break;
case Constants.MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
Log.d(TAG, "handleMessage: client disconnected. Total: " + mClients.size());
break;
case Constants.MSG_YOUR_MESSAGE_TYPE:
String val = (String) msg.obj;
// stuff to do
break;
}
}
};
/**
* Send message to connected activities
*/
public void sendMessageToClients(int msgSignal, Object obj) {
if (mClients.size() == 0)
return;
sendMessage(mClients.get(0), Message.obtain(null, msgSignal, obj));
for (int i = 1; i < mClients.size(); i++) {
if (mClients.get(i) == null)
continue;
sendMessage(mClients.get(i), Message.obtain(null, msgSignal, obj));
}
}
/**
* Send message to binded activity
*/
private void sendMessage(Messenger msgr, Message msg) {
try {
msgr.send((msg));
} catch (RemoteException e) {
Log.e(TAG, "can't send message", e);
e.printStackTrace();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.d(TAG, "onStartCommand");
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return inComingMessenger.getBinder();
}
#Override
public void onCreate() {
super.onCreate();
bleDevices = new ArrayList<>();
if(bluetooth == null) {
bluetooth = new Bluetooth(this);
bluetooth.setBluetoothCallback(new BluetoothCallback() {
#Override
public void onBluetoothTurningOn() {}
#Override
public void onBluetoothOn() {}
#Override
public void onBluetoothTurningOff() {
bluetooth = null;
}
#Override
public void onBluetoothOff() { }
#Override
public void onUserDeniedActivation() {
// when using bluetooth.showEnableDialog()
// you will also have to call bluetooth.onActivityResult()
}
});
bluetooth.setDiscoveryCallback(new DiscoveryCallback() {
#Override public void onDiscoveryStarted() {
}
#Override public void onDiscoveryFinished() {
bleDevices.clear();
}
#Override public void onDeviceFound(BluetoothDevice device) {
if(bleDevices.indexOf(device)<0) {
bleDevices.add(device);
Log.d(TAG, "Found new device while scanning: "+device.getAddress());
sendMessageToClients(Constants.MSG_BLE_DEVICE_FOUND, device);
}
}
#Override public void onDevicePaired(BluetoothDevice device) {}
#Override public void onDeviceUnpaired(BluetoothDevice device) {}
#Override public void onError(String message) {
Log.e(TAG, "DiscoveryCallback onError "+message);
}
});
bluetooth.setDeviceCallback(new DeviceCallback() {
#Override public void onDeviceConnected(BluetoothDevice device) { }
#Override public void onDeviceDisconnected(BluetoothDevice device, String message) { }
#Override public void onMessage(String message) {
// Handle your message
yourHandleFunction(message.replaceAll(" ",""));
Log.d(TAG, message);
}
#Override public void onError(String message) {
Log.e(TAG, "DeviceCallback onError "+message);
}
#Override public void onConnectError(BluetoothDevice device, String message) { }
});
bluetooth.onStart();
}
connectToSavedDevice();
}
}
private void connectToSavedDevice() {
Log.d(TAG, "connectToSavedDevice state="+getState());
if(getState() != STATE_DISCONNECTED) return;
SharedPreferences pref = getSharedPreferences(App.TAG, 0);
String address = pref.getString(Constants.PREF_AUTO_CONNECT_TO_ADDRESS, null);
if(address == null) {
Log.d(TAG, "saved address==null start scan for devices");
scanDevices();
return;
}
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
if(device!=null) {
Log.d(TAG, "device found try to connect/bound, connect to Arduino");
bluetooth.connectToAddress(address,false);
}
}
In activity implement ServiceConnection interface:
protected synchronized void unbindService() {
if (!isBound()) {
return;
}
// lock object (prevents access to service while disconnecting)
synchronized (outComingMessenger) {
sendMessageToService(Message.obtain(null, Constants.MSG_UNREGISTER_CLIENT));
unbindService(this);
outComingMessenger = null;
}
}
protected void bindService() {
bindService(mServiceIntent, this, BIND_AUTO_CREATE);
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
outComingMessenger = new Messenger(service);
final Message msg = Message.obtain(null, Constants.MSG_REGISTER_CLIENT);
msg.replyTo = inComingMessenger;
msg.obj = getClass().getSimpleName();
sendMessageToService(msg);
sendMessageToService(Constants.MSG_CHECK_IS_CONNECTION_READY);
}
#Override
public void onServiceDisconnected(ComponentName name) {
outComingMessenger = null;
bindService();
}
/**
* Send message to service
*/
protected void sendMessageToService(Message msg) {
if (!isBound()) {
return;
}
try {
msg.replyTo = inComingMessenger;
outComingMessenger.send(msg);
} catch (RemoteException e) {
}
}
/**
* Send simple message to connected service
* #param messageId
*/
protected void sendMessageToService(int messageId) {
sendMessageToService(Message.obtain(null, messageId));
}
/**
* Send simple message to connected service
* #param messageId
*/
protected void sendMessageToService(int messageId, Object obj) {
sendMessageToService(Message.obtain(null, messageId, obj));
}
/**
* Service is connected?
*/
protected final boolean isBound() {
return outComingMessenger != null;
}
#SuppressLint("HandlerLeak")
private final Handler mIncomingHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_YOUR_MESSAGE:
int res = (int) msg.obj;
break;
case Constants.MSG_CONNECTION_READY:
isReady = (boolean) msg.obj;
if(isReady) {
// show toast or what you want to do with the UI
} else {
// do something else
}
break;
case Constants.MSG_BLE_DEVICE_FOUND:
BluetoothDevice device = (BluetoothDevice)msg.obj;
SharedPreferences pref = getSharedPreferences(App.TAG, 0);
String deviceAddress = pref.getString(Constants.PREF_AUTO_CONNECT_TO_ADDRESS, null);
break;
}
}
};
final private Messenger inComingMessenger = new Messenger(mIncomingHandler);

Android -(Thread) app will hang when I run a new thread

I have 3 activities in my android Application. In the first activity, on the click of a bluetooth device from the list of paired devices, I'm starting a service to keep the bluetooth connection visible to all the actives. In the service class I'm reading data continuously from the bluetooth device and I'm binding the second activity to the service class to read the data received.
I'm not able to get the instance of the binder outside the onServiceConnected() method of service connection method. So I'm calling a user-defined thread from onServiceConnected() method. In this way I'm getting values continuously from the service class. But the app will not respond after few seconds of successful execution.
It is blocking the main thread I think. But I'm not getting where I need to modify my code. The code below is my second Activity(MainActivity). "bluetoothManager" is my service class. I need to do a similar task in third activity also.
I'm not getting whether the problem is with binding or the thread. I need to call the thread outside of the Service connection class. If I do so, I'll get a null pointer exception. So I'm calling the thread from onServiceConnected() function where the binder object is not null. I have to use the boolean mIsBound for the while loop. But now it will be always true. Please help me. I'm new to android.
bluetoothManager.class
public class bluetoothManager extends Service{
final int handlerState = 0; // used to identify handler message
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder recDataString = new StringBuilder();
public ConnectedThread mConnectedThread;
static Handler bluetoothIn;
int bp;
String sensor0,sensor1;
static Handler mHandler;
// SPP UUID service - this should work for most devices
private static final UUID BTMODULEUUID = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
IBinder mBinder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
bluetoothManager getService() {
return bluetoothManager.this;
}
}
#Override
public void onCreate() {
/// Toast.makeText(this, " MyService Created ", Toast.LENGTH_LONG).show();
// flag="created";
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
// creates secure outgoing connecetion with BT device using UUID
}
public String getBPM(){
return sensor1;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, " MyService Started", Toast.LENGTH_LONG).show();
final String address=intent.getStringExtra("address");
final int currentId = startId;
if(address!=null)
{
btAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = btAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
Toast.makeText(getBaseContext(), "Socket creation failed",
Toast.LENGTH_LONG).show();
}
// Establish the Bluetooth socket connection.
try {
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
// insert code to deal with this
}
}
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
// I send a character when resuming.beginning transmission to check
// device is connected
// If it is not an exception will be thrown in the write method and
// finish() will be called
mConnectedThread.write("x");
}
bluetoothIn = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) { // if message is what we want
String readMessage = (String) msg.obj; // msg.arg1 = bytes
// from connect
// thread
recDataString.append(readMessage); // keep appending to
// string until ~
int endOfLineIndex = recDataString.indexOf("~"); // determine
// the
// end-of-line
if (endOfLineIndex > 0) { // make sure there data before ~
String dataInPrint = recDataString.substring(0,
endOfLineIndex); // extract string
//txtString.setText("Data Received = " + dataInPrint);
/*int dataLength = */dataInPrint.length(); // get length of
// data received
/*txtStringLength.setText("String Length = "
+ String.valueOf(dataLength));*/
if (recDataString.charAt(0) == '#') // if it starts with
// # we know it is
// what we are
// looking for
{
sensor0 = recDataString.substring(1,3);
// get
sensor1=sensor0;
Log.d("bpm", sensor0);
}
recDataString.delete(0, recDataString.length()); // clear
// all
// string
// data
// strIncom =" ";
dataInPrint = " ";
}
}
}
};
// get Bluetooth
// adapter
return currentId;
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final OutputStream mmOutStream;
// creation of the connect thread
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
// Create I/O streams for connection
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[256];
int bytes;
// Keep looping to listen for received messages
while (true) {
try {
bytes = mmInStream.read(buffer); // read bytes from input
// buffer
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity via handler
bluetoothIn.obtainMessage(handlerState, bytes, -1,
readMessage).sendToTarget();
} catch (IOException e) {
break;
}
}
}
// write method
public void write(String input) {
byte[] msgBuffer = input.getBytes(); // converts entered String into
// bytes
try {
mmOutStream.write(msgBuffer); // write bytes over BT connection
// via outstream
} catch (IOException e) {
// if you cannot write, close the application
Toast.makeText(getBaseContext(), "Connection Failure",
Toast.LENGTH_LONG).show();
}
}
}
#Override
public void onRebind(Intent intent) {
Log.v("myservice", "in onRebind");
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
Log.v("myapp", "in onUnbind");
return true;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v("myservice", "in onDestroy");
}
}
MainActivity.java
public class MainActivity extends Activity {
private ServiceConnection mConnection;
TextView sensorView0;
boolean mIsBound;
bluetoothManager bm;
private Handler bpmHandler;
private ServiceConnection mConnection;
final int handlerState = 0; // used to identify handler message
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorView0 = (TextView) findViewById(R.id.bpm);
bpmHandler=new Handler(){
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) {
String s=(String)msg.obj;
sensorView0.setText("BPM="+s);
}
}
};
}
#Override
public void onResume() {
super.onResume();
mConnection= new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mIsBound = false;
bm=null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalBinder myBinder = (LocalBinder)service;
mIsBound = true;
bm=myBinder.getService();
mConnectedService=new ConnectedService(mIsBound);
mConnectedService.start();
}
};
Intent intent = new Intent(this, bluetoothManager.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private class ConnectedService extends Thread {
final boolean bound;
public ConnectedService(boolean mIsBound){
bound =mIsBound;
}
public void run() {
String s;
while (bound) {
s= bm.getBPM();
Message msg = new Message();
msg.what =handlerState ;
msg.obj=s; MainActivity.this.bpmHandler.sendMessage(msg);
}
}
};
#Override
public void onPause() {
super.onPause();
unbindService(mConnection);
mIsBound = false;
}
}
I feel the connectedservice thread code is causing the issue. Instead of continuously racing grtBPM method, why don't you post the message only when there is a change. You can use local broadcast manager to broadcast the message from service and catch that in activity and update UI accordingly. The connectedservice thread runs continuously and keep posting the message to handler which is causing load on the main thread.

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

I've got problem and I don't know how to resolve it. I wanted create service which will sent sensor changes to client (thread).
I've got thread where inside I start services, and client thread receive answers and sent them through bluetooth. The problem is I can't handle service.
public class SensorMsgService extends Service implements SensorEventListener{
public static final int MSG_SAY_HELLO = 1;
public static final int MSG_REGISTER_CLIENT = 1;
public static final int MSG_UNREGISTER_CLIENT = 2;
public static final int MSG_SET_VALUE = 3;
static final String TAG = "Sensor Msg Service";
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private SensorManager mSensorManager;
private Sensor mSensor;
private ArrayList<Float> temp;
private Looper mServiceLooper;
public class IncomingHandler extends Handler{
public IncomingHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
//mValue = msg.arg1;
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
/*mClients.get(i).send(
Message.obtain(null, MSG_SET_VALUE, 1, 0));*/
Log.d(TAG, "Message from client");
}
//catch (RemoteException e) {
catch (Exception e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
break;
default:
super.handleMessage(msg);
}
}
//Toast.makeText(getApplicationContext(), "Hello service test", Toast.LENGTH_SHORT).show();
}
final Messenger mMessenger = new Messenger(new IncomingHandler(mServiceLooper));
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "binding");
//return null;
return mMessenger.getBinder();
}
#Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
// TODO Auto-generated method stub
super.bindService(service, conn, flags);
// mServiceLooper.prepare();
temp = new ArrayList<Float>();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
return true;
}
#Override
public void unbindService(ServiceConnection conn) {
// TODO Auto-generated method stub
super.unbindService(conn);
mSensorManager.unregisterListener(this);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
temp.add(event.values[0]);
temp.add(event.values[1]);
temp.add(event.values[2]);
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
mClients.get(i).send(
Message.obtain(null, MSG_SET_VALUE, temp));
Log.d(TAG, "Message service to client sending");
}
//catch (RemoteException e) {
catch (Exception e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
}
I've initiated this service as I said from thread:
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private Context mContext;
/** Messenger for communicating with service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mIsBound;
ArrayList<Float> temp;
private Looper mServiceLooper;
public ConnectedThread(BluetoothSocket socket, Context ctxx) {
Log.d("ConnectedThread", "constructor ConnectedThread");
mContext = ctxx;
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
temp = new ArrayList<Float>();
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e("ConnectedThread", "it was trying create input and output sockets", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.i("ConnectedThread", "run mConnectedThread");
byte[] buffer = new byte[1024];
/*int i = 0;
while(true)
{
Log.i("ConnectedThread", "sending int test value");
write(i++);
}*/
doBindService();
}
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) {
Log.e("ConnectedThread", "Exception during write", e);
}
}
public void write(int out) {
try {
mmOutStream.write(out);
} catch (IOException e) {
Log.e("ConnectedThread", "Exception during write", e);
}
}
public void cancel() {
try {
//mmOutStream.write(EXIT_CMD);
mmSocket.close();
} catch (IOException e) {
Log.e("ConnectedThread", "close() of connect socket failed", e);
}
}
///------------------------------
/**
* Handler of incoming messages from service.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SensorMsgService.MSG_SET_VALUE:
temp = (ArrayList<Float>) msg.obj;
//mCallbackText.setText("Received from service: " + msg.arg1);
Log.d("Handle message from service", temp.get(0) + " " + temp.get(1) + " " + temp.get(2) + "\n");
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = new Messenger(service);
//mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
Message msg = Message.obtain(null,
SensorMsgService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
msg = Message.obtain(null, SensorMsgService.MSG_SET_VALUE, this
.hashCode(), 0);
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(mContext, "Service connected",
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
//mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(mContext, "Service disconnedcted",
Toast.LENGTH_SHORT).show();
}
};
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
// do Bind
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
mContext.bindService(new Intent(mContext, SensorMsgService.class),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
Log.d("ConnectedThread", "doBindService");
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null,
SensorMsgService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
mContext.unbindService(mConnection);
mIsBound = false;
//mCallbackText.setText("Unbinding.");
}
}
}
The problem is when I wanted run this thread:
04-04 01:36:26.853: W/dalvikvm(29341): threadid=12: thread exiting with uncaught exception (group=0x420372a0)
04-04 01:36:26.853: E/AndroidRuntime(29341): FATAL EXCEPTION: Thread-11596
04-04 01:36:26.853: E/AndroidRuntime(29341): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
04-04 01:36:26.853: E/AndroidRuntime(29341): at android.os.Handler.<init> (Handler.java:121)
04-04 01:36:26.853: E/AndroidRuntime(29341): at com.nauka.bluetooth.ConnectedThread$IncomingHandler.<init>(ConnectedThread.java:127)
04-04 01:36:26.853: E/AndroidRuntime(29341): at com.nauka.bluetooth.ConnectedThread.<init>(ConnectedThread.java:200)
Could you show me how to handle this issue? thx

Categories

Resources