How to Move Bluetooth Activity into a Service

How to move Bluetooth activity into a Service

I have written a Bluetooth services which runs in the background and can communicate to any Activity in the application using Messenger http://developer.android.com/guide/components/bound-services.html.

public class PrinterService extends Service {
private BluetoothAdapter mBluetoothAdapter;
public static final String BT_DEVICE = "btdevice";
public static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming
// connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing
// connection
public static final int STATE_CONNECTED = 3; // now connected to a remote
// device
private ConnectThread mConnectThread;
private static ConnectedThread mConnectedThread;
// public mInHangler mHandler = new mInHangler(this);
private static Handler mHandler = null;
public static int mState = STATE_NONE;
public static String deviceName;
public Vector<Byte> packdata = new Vector<Byte>(2048);
public static Device device = null;

@Override
public void onCreate() {
Log.d("PrinterService", "Service started");
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
mHandler = ((MyAplication) getApplication()).getHandler();
return mBinder;
}

public class LocalBinder extends Binder {
PrinterService getService() {
return PrinterService.this;
}
}

private final IBinder mBinder = new LocalBinder();

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("PrinterService", "Onstart Command");
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
device = (Device) intent.getSerializableExtra(BT_DEVICE);
deviceName = device.getDeviceName();
String macAddress = device.getMacAddress();
if (macAddress != null && macAddress.length() > 0) {
connectToDevice(macAddress);
} else {
stopSelf();
return 0;
}
}
String stopservice = intent.getStringExtra("stopservice");
if (stopservice != null && stopservice.length() > 0) {
stop();
}
return START_STICKY;
}

private synchronized void connectToDevice(String macAddress) {
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress);
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}

// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
mConnectThread = new ConnectThread(device);
mConnectThread.start();
setState(STATE_CONNECTING);
}

private void setState(int state) {
PrinterService.mState = state;
if (mHandler != null) {
mHandler.obtainMessage(AbstractActivity.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
}

public synchronized void stop() {
setState(STATE_NONE);
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}

if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mBluetoothAdapter != null) {
mBluetoothAdapter.cancelDiscovery();
}
stopSelf();
}

@Override
public boolean stopService(Intent name) {
setState(STATE_NONE);
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}

if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
mBluetoothAdapter.cancelDiscovery();
return super.stopService(name);
}

private void connectionFailed() {
PrinterService.this.stop();
Message msg = mHandler.obtainMessage(AbstractActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(AbstractActivity.TOAST, getString(R.string.error_connect_failed));
msg.setData(bundle);
mHandler.sendMessage(msg);
}

private void connectionLost() {
PrinterService.this.stop();
Message msg = mHandler.obtainMessage(AbstractActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(AbstractActivity.TOAST, getString(R.string.error_connect_lost));
msg.setData(bundle);
mHandler.sendMessage(msg);
}

private static Object obj = new Object();

public static void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (obj) {
if (mState != STATE_CONNECTED)
return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}

private synchronized void connected(BluetoothSocket mmSocket, BluetoothDevice mmDevice) {
// 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;
}

mConnectedThread = new ConnectedThread(mmSocket);
mConnectedThread.start();

// Message msg =
// mHandler.obtainMessage(AbstractActivity.MESSAGE_DEVICE_NAME);
// Bundle bundle = new Bundle();
// bundle.putString(AbstractActivity.DEVICE_NAME, "p25");
// msg.setData(bundle);
// mHandler.sendMessage(msg);
setState(STATE_CONNECTED);

}

private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;

public ConnectThread(BluetoothDevice device) {
this.mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(UUID.fromString(SPP_UUID));
} catch (IOException e) {
e.printStackTrace();
}
mmSocket = tmp;
}

@Override
public void run() {
setName("ConnectThread");
mBluetoothAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
connectionFailed();
return;

}
synchronized (PrinterService.this) {
mConnectThread = null;
}
connected(mmSocket, mmDevice);
}

public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e("PrinterService", "close() of connect socket failed", e);
}
}
}

private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;

public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e("Printer Service", "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}

@Override
public void run() {
while (true) {
try {
if (!encodeData(mmInStream)) {
mState = STATE_NONE;
connectionLost();
break;
} else {
}
// mHandler.obtainMessage(AbstractActivity.MESSAGE_READ,
// bytes, -1, buffer).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
connectionLost();
PrinterService.this.stop();
break;
}

}
}

private byte[] btBuff;

public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);

// Share the sent message back to the UI Activity
mHandler.obtainMessage(AbstractActivity.MESSAGE_WRITE, buffer.length, -1, buffer).sendToTarget();
} catch (IOException e) {
Log.e("PrinterService", "Exception during write", e);
}
}

public void cancel() {
try {
mmSocket.close();

} catch (IOException e) {
Log.e("PrinterService", "close() of connect socket failed", e);
}
}

}

public void trace(String msg) {
Log.d("AbstractActivity", msg);
toast(msg);
}

public void toast(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}

@Override
public void onDestroy() {
stop();
Log.d("Printer Service", "Destroyed");
super.onDestroy();
}

private void sendMsg(int flag) {
Message msg = new Message();
msg.what = flag;
handler.sendMessage(msg);
}

private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {//
if (!Thread.currentThread().isInterrupted()) {
switch (msg.what) {
case 3:

break;

case 4:

break;
case 5:
break;

case -1:
break;
}
}
super.handleMessage(msg);
}

};
}

UPDATE

you need to use Handler in your MyApplication Class

Handler.Callback realCallback = null;
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (realCallback != null) {
realCallback.handleMessage(msg);
}
};
};
public Handler getHandler() {
return handler;
}
public void setCallBack(Handler.Callback callback) {
this.realCallback = callback;
}

Android bluetooth service with multiple activities

I see two options for you to achieve what you seek:

  • Manage your Bluetooth connection in the Service, and use the Android doc to write a working service. And you can bind each of your activities to your running service using bindService(). The service runs in background and won't stop when you go from one activity to the other. But for that you need to move most of the code you have into a service. Again, follow the doc.

  • Use fragments: Android Doc. And if you know nothing about fragments: Android doc. Basically, a fragment is very similar to an activity but it is recommended by Android to use fragments. You will have one activity that manages you bluetooth connection and hosts your fragments: you will replace all the activities you wanted to have in your app by fragments. So when you navigate from a fragment to the other, your activity won't be stopped and so your bluetooth connection will be safe.

I don't know exactly what you want to do, but the first option is the only right option if your bluetooth connection has to run even when the user has their phone in their pocket. The second option is better if the user doesn't need to keep a bluetooth connection when they leave the app.

Hope it's clear.

I loose data from Bluetooth moving between activities and using Services

I will try to suggest a couple of things although it's hard to know what you are trying to achieve and why you want to launch a new activity in the middle of a readout.

I'd recommend changing your readChannel() return type into SharedFlow and convert the readByteArrayStream() function into SharedFlow as well using shareIn() call. Then collect it as is without converting it into livedata. This library was built before StateFlow and SharedFlow were introduced so these new flow types might come in handy in this case.

Another approach might be to use SharedFlow again that will act as an event bus and re-emit the data collected by readByteArrayStream() see here for an example of such an EventBus implementation.

That's all I can suggest for now. I'm really not sure about your use case and I would like to check a sample project linked to the relevant issue if it's possible for better understanding.

How to pass a handler from activity to service

You have to bind to the service from activity and establish a ServiceConnection and then get the instance of service and set your handler.

Here is the activity and service class which i use for one of my media player application.....

public class MainActivity extends Activity
{
private CustomService mService = null;

private boolean mIsBound;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
startService(new Intent(this.getBaseContext(), CustomService.class));
doBindService();
}

private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
mService = ((CustomService.LocalBinder)iBinder).getInstance();
mService.setHandler(yourHandler);
}

@Override
public void onServiceDisconnected(ComponentName componentName)
{
mService = null;
}
};

private void doBindService()
{
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}

private void doUnbindService()
{
if (mIsBound)
{
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}

@Override
protected void onDestroy()
{
super.onDestroy();
doUnbindService();
}

}

CustomService Code ....

public class CustomService extends Service
{
private final IBinder mIBinder = new LocalBinder();

private Handler mHandler = null;

@Override
public void onCreate()
{
super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flag, int startId)
{
return START_STICKY;
}

@Override
public void onDestroy()
{
if(mHandler != null)
{
mHandler = null;
}
}

@Override
public IBinder onBind(Intent intent)
{
return mIBinder;
}

public class LocalBinder extends Binder
{
public CustomService getInstance()
{
return CustomService.this;
}
}

public void setHandler(Handler handler)
{
mHandler = handler;
}

}


Related Topics



Leave a reply



Submit