New to Android and developing an app that will deliver Bluetooth data to several different activities. I have a ConnectActivity that performs discovery and presents the devices to the user and when the user selects a device, as suggested by someone in this discussion, starts a BluetoothService.
BluetoothService extends Service and starts a background thread which will eventually be used to block on an InputStream, and broadcast data to activities when it is available (i.e. after those activities poll via startService()).
Here is the Activity. Defines a receiver:
public class ConnectActivity extends AppCompatActivity {
...
// My test receiver just to see if things are working.
private BroadcastReceiver mDataReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(D)
Log.d(TAG, "Broadcast received.");
}
};
Starts the service onCreate():
protected void onCreate(Bundle savedInstanceState) {
...
IntentFilter dataFilter = new IntentFilter(BluetoothService.INCOMING_DATA);
registerReceiver(mDataReceiver, dataFilter);
// Start the service with an intent identifying the context ("this" is an Activity,
// which inherits from Context) and the recipient.
Intent i = new Intent(this, BluetoothService.class);
i.putExtra(BluetoothService.COMMAND, "MY_COMMAND"); // Add the command as payload to the intent.
if(D)
Log.d(TAG, "Starting service.");
startService(i);
}
And, finally, connect on a button press:
private void connect(){
if(D)
Log.d(TAG, "Connecting to device " + (mBluetoothDevices.get(mSelectedPos).toString()) + " at position " + mSelectedPos);
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle("Connecting");
dialog.setMessage("Please wait...");
dialog.show();
if(D)
Log.d(TAG, "Attempting to connect.");
mBluetoothService.connect(mBluetoothDevices.get(mSelectedPos), dialog);
Here is my service:
public class BluetoothService extends Service {
...
public void connect (BluetoothDevice device, ProgressDialog dialog){
....
// Start the thread to connect to the device
mConnectThread = new ConnectThread(device, dialog, this);
mConnectThread.start();
}
The ConnectThread connects successfully and starts a ConnectedThread which is intended to send data back to the acitvity as it comes in. I am including all of it since this is where my issue is:
public class ConnectedThread extends Thread {
private BluetoothSocket mmSocket;
private final String mmMYNAME = "ConnectedThread";
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private final Context mmContext;
public ConnectedThread(BluetoothSocket socket, Context context) {
mmSocket = socket;
mmContext = context;
InputStream tryIn = null;
OutputStream tryOut = null;
// Get the BluetoothSocket input and output streams
try {
tryIn = mmSocket.getInputStream();
tryOut = mmSocket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, mmMYNAME + " could not create streams.", e);
}
mmInStream = tryIn;
mmOutStream = tryOut;
mConnectedThreadEnabled = true;
}
#Override
public void run()
{
Thread.currentThread().setName(mmMYNAME);
Intent i = new Intent(INCOMING_DATA);
while(mConnectedThreadEnabled) {
SystemClock.sleep(1000);
if(D)
Log.d(TAG, "ConnectedThread is running.");
LocalBroadcastManager.getInstance(mmContext).sendBroadcast(i); //KABOOM
}
if(D)
Log.d(TAG, "Stopping ConnectedThread.");
}
public void cancel() {
if(D)
Log.d(TAG,"Canceling ConnectedThread.");
try {
if(D)
Log.d(TAG,"Shutting down existing socket.");
mConnectedThreadEnabled = false;
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
My problem is that on the line:
LocalBroadcastManager.getInstance(mmContext).sendBroadcast(i); //KABOOM
I am receiving:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:107)
at android.support.v4.content.LocalBroadcastManager.getInstance(LocalBroadcastManager.java:102)
at com.company.me.app.BluetoothService$ConnectedThread.run(BluetoothService.java:255)
I am not clear on what the issue is, given that debug indicates that mmContext and i both seem to be properly created.
which performs a mBluetoothService = new BluetoothService();
Never create instances of components yourself. Get rid of this line. Anything that has been using mBluetoothService needs to be rewritten to:
be in the service itself, or
use service binding (e.g., bindService(), with the activity then calling methods on the supplied Binder), or
use some other pattern, to avoid the activity trying to have a direct reference to the service, let alone creating an instance of the service itself
Related
I am using a AIDL system service which interacts with serial port of HW component.
My goal is on 1 method to bind to AIDL service and then connect to the HW on same method (this is a constraint).
For test, from Activity, if I bind on onCreate and use serialOpen on button event, it works (I can connect to the HW).
2021-12-09 01:15:54.322 11904-11904/com.example.hwtestapp D/RFID: Binding to serial service..
2021-12-09 01:15:54.559 11904-11904/com.example.hwtestapp D/RFID: Service connected.
If I remove the service binding from onCreate and use only serialOpen (which take care of binding), it fails! First click with binding error, the second time, I can connect (because it has been binded on first click). I do not explain the reason of that.
First click, it waits SERVICE_BINDING_TIMEOUT and raise an error. As soon it exits from the method, it connects to the service.
I tried to increase the SERVICE_BINDING_TIMEOUT behaviour is the same, it just take more delay.
2021-12-09 01:11:38.513 10927-10927/com.example.hwtestapp D/example: Binding to serial service..
2021-12-09 01:11:39.027 10927-10927/com.example.hwtestapp E/example: Error binding to serial service
2021-12-09 01:11:39.039 10927-10927/com.example.hwtestapp I/Choreographer: Skipped 32 frames! The application may be doing too much work on its main thread.
2021-12-09 01:11:39.063 10927-10927/com.example.hwtestapp D/example: Service connected.
I tried to bind on thread, asyncTask, no effect. Bind looks to wait the end of method.
it looks like to complete the binding, operating system needs to have the hand.
Any idea is welcome.
THanks!
public void serialOpen(Context context) throws IOException {
if (context == null) throw new InvalidParameterException("Context cannot be Null");
this.context = context;
// bind if not already done
if(!bound) bindToSerialService(context, true);
// wait for binding completion
long t = SystemClock.elapsedRealtime();
while(!bound && (SystemClock.elapsedRealtime() - t < SERVICE_BINDING_TIMEOUT)){
SystemClock.sleep(10);
}
if(!bound) throw new IOException("Error binding to serial service");
String[] ports;
try {
// get port names when bind has completed
ports = serial.getPortNames();
if (ports.length == 0) throw new IOException("No serial port detected");
for (String p : ports) if(DEBUG) Log.d(TAG, "Port list contains: " + p);
port = serial.open(ports[0]);
if(DEBUG) Log.d(TAG, "Open returned " + port);
if (port == null) throw new IOException("Error opening serial port");
boolean ok = port.configure(baudRate, DATA_BITS, PARITY, STOP_BITS, FLOW_CONTROL);
if(DEBUG) Log.d(TAG, "Configuration returned " + ok);
//...
isConnected = true;
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void bindToSerialService(Context context, boolean state) {
if (context == null) throw new InvalidParameterException("Context cannot be Null");
this.context = context;
if(state) {
// bind to service
if(DEBUG) Log.d(TAG, "Binding to serial service..");
Intent intent = new Intent().setComponent(new ComponentName("com.example.serial", "com.example.serial.SerialService"));
context.bindService(intent, serialConnection, Context.BIND_AUTO_CREATE);
} else {
context.unbindService(serialConnection);
bound = false;
if(DEBUG) Log.d(TAG, "Unbound to serial service.");
}
}
Refer to Android how do I wait until a service is actually connected?
Android 10 has introduced a new bindService method signature when binding to a service to provide an Executor (which can be created from the Executors). It helps to fix my issue
/**
* Same as {#link #bindService(Intent, ServiceConnection, int)} with executor to control
* ServiceConnection callbacks.
* #param executor Callbacks on ServiceConnection will be called on executor. Must use same
* instance for the same instance of ServiceConnection.
*/
public boolean bindService(#RequiresPermission #NonNull Intent service,
#BindServiceFlags int flags, #NonNull #CallbackExecutor Executor executor,
#NonNull ServiceConnection conn) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
This allows to bind to the service in a thread and wait until it is connected.
private final AtomicBoolean connected = new AtomicBoolean()
private final Object lock = new Object();
...
/**
* Monitors the connection to the serial service.
*/
private final ServiceConnection serialConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
synchronized (lock) {
serial = ISerial.Stub.asInterface(iBinder);
if (DEBUG) Log.d(TAG, "Service connected.");
bound.set(true);
lock.notifyAll();
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
serial = null;
if(DEBUG) Log.d(TAG, "Service disconnected.");
bound.set(false);
}
};
/**
* binds to serial system service
* #param context context
*/
public void bindToSerialService(Context context) throws IOException {
if (context == null) throw new InvalidParameterException("Context cannot be Null");
this.context = context;
// bind to service
if (DEBUG) Log.d(TAG, "Binding to serial service..");
ExecutorService executorService = Executors.newSingleThreadExecutor();
Intent intent = new Intent().setComponent(new ComponentName("com.example.serial", "com.example.serial.SerialService"));
context.bindService(intent, Context.BIND_AUTO_CREATE, executorService, serialConnection);
// wait the connection completion to the serial service
synchronized (lock) {
try {
if (DEBUG) Log.d(TAG, "Waiting the binding..");
lock.wait(SERVICE_BINDING_TIMEOUT);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
if (!bound.get()) {
context.unbindService(serialConnection);
throw new IOException("Error binding to serial service");
}
}
}
/**
* unbinds to serial system service
* #param context context
*/
public void unbindFromSerialService() {
if(bound.get()) {
context.unbindService(serialConnection);
bound.set(false);
if(DEBUG) Log.d(TAG, "Unbound to serial service.");
} else {
if(DEBUG) Log.d(TAG, "Not bound or already unbound to serial service.");
}
}
public void serialOpen(Context context) throws IOException {
if (context == null) throw new InvalidParameterException("Context cannot be Null");
this.context = context;
// bind to serial service if not already done
if(!bound.get()) bindToSerialService(context);
try {
// get port names when bind has completed
String[] ports = serial.getPortNames();
if (ports.length == 0) throw new IOException("No serial port detected");
for (String p : ports) if(DEBUG) Log.d(TAG, "Port list contains: " + p);
port = serial.open(ports[0]);
if(DEBUG) Log.d(TAG, "Open returned " + port);
if (port == null) throw new IOException("Error opening serial port");
boolean ok = port.configure(baudRate, DATA_BITS, PARITY, STOP_BITS, FLOW_CONTROL);
if(DEBUG) Log.d(TAG, "Configuration returned " + ok);
//...
isConnected = true;
} catch (RemoteException e) {
e.printStackTrace();
}
}
I am using Bluetooth API of android. I am here creating client-server connection using BluetoothServerSocket & BluetoothSocket but my program stuck at the certain point.
// Create a BroadcastReceiver for ACTION_FOUND
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery find a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// get the BluetoothDevice object from the Intent
BluetoothDevice mBluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i("MainActivity", "Device Name: " + mBluetoothDevice.getName() + " Address: " + mBluetoothDevice.getAddress());
new AcceptThread().start();
}
}
};
private class AcceptThread extends Thread {
private BluetoothServerSocket mBluetoothServerSocket ;
public AcceptThread() {
try {
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("BT_SERVER", UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c666"));
} catch (IOException e) {
Log.e("MainActivity", e.getMessage());
}
}
#Override
public void run() {
BluetoothSocket mBluetoothSocket;
// Keep listening until exception occurs or a socket is returned
while(true) {
try {
mBluetoothSocket = mBluetoothServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if(mBluetoothSocket != null) {
// transfer the data here
Toast.makeText(MainActivity.this, "Socket is created", Toast.LENGTH_LONG).show();;
try {
// close the connection to stop to listen any connection now
mBluetoothSocket.close();
} catch(IOException e) { }
}
}
}
}
Here my program stuck
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("BT_SERVER", UUID.fromString("a60f35f0-b93a-11de-8a39-08002009c666"));
I could not catch why it getting stuck at this point, Any idea to you for this ?
From your question it is unclear whether your application is a client or server or both. For writing bluetooth client-server applications, android phone at any instance plays a single role of server or a client. If your phone is server, then you need to listen for connections from other bluetooth devices using method listenUsingRfcommWithServiceRecord(). Then use accept() to complete the connection.
In case android phone acts as client, it will initiate a bluetooth connection to other devices. For such scenario, your broadcast receiver is needed. We need to scan for available bluetooth devices with startDiscovery() method. Your broadcast receiver's onReceive() is called when a new bluetooth device is found. To connect to this found device, call createRfcommSocketToServiceRecord() with desired UUID.
Hope this helps.
This may be obvious but did you instantiate your BluetoothAdapter? Accept Thread uses the adapter without intializing it.
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
While listening, set the discovery name to a specific value then used listenUsingRfcommWithServiceRecord method in broadcast receiver.
private class AcceptTask extends AsyncTask<UUID,Void,BluetoothSocket> {
#Override
protected BluetoothSocket doInBackground(UUID... params) {
String name = mBtAdapter.getName();
try {
//While listening, set the discovery name to a specific value
mBtAdapter.setName(SEARCH_NAME);
BluetoothServerSocket socket = mBtAdapter.listenUsingRfcommWithServiceRecord("BluetoothRecipe", params[0]);
BluetoothSocket connected = socket.accept();
//Reset the BT adapter name
mBtAdapter.setName(name);
return connected;
} catch (IOException e) {
e.printStackTrace();
mBtAdapter.setName(name);
return null;
}
}
#Override
protected void onPostExecute(BluetoothSocket socket) {
if(socket == null) {
return;
}
mBtSocket = socket;
ConnectedTask task = new ConnectedTask();
task.execute(mBtSocket);
}
}
// End
I've been having this problem for a while and haven't been able to figure it out.
I have a android application that puts all paired devices in a listview. When you click one of the list items, it will initiate a request to connect to that bluetooth device.
I can get the list of devices with their addresses no problem.
The problem is that once I try to connect I get an IOException on socket.connect();
The error message is as follows:
"connect read failed, socket might closed or timeout, read ret: -1"
Here is my code. ANY suggestions would be appreciated. I'm pretty stuck on this.
fyi: the "onEvent" methods is a library that simplifies callbacks...that part works.
When the user clicks on a list items this method is called "public void onEvent(EventMessage.DeviceSelected event)"
public class EcoDashActivity extends BaseActivity {
public static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 100;
private ArrayList<BluetoothDevice> mDevicesList;
private BluetoothDeviceDialog mDialog;
private ProgressDialog progressBar;
private int progressBarStatus = 0;
private Handler progressBarHandler = new Handler();
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
mDevicesList = new ArrayList<BluetoothDevice>();
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
setupBluetooth();
}
private void setupBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
Toast.makeText(this, "Device does not support Bluetooth", Toast.LENGTH_SHORT).show();
}
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
searchForPairedDevices();
mDialog = new BluetoothDeviceDialog(this, mDevicesList);
mDialog.show(getFragmentManager(), "");
}
}
private void searchForPairedDevices() {
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
// Add the name and address to an array adapter to show in a ListView
mDevices.add(device.getName() + "\n" + device.getAddress());
mDevicesList.add(device);
}
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a ListView
mDevicesList.add(device);
}
}
};
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
Toast.makeText(this, "BT turned on!", Toast.LENGTH_SHORT).show();
searchForPairedDevices();
mDialog = new BluetoothDeviceDialog(this, mDevicesList);
mDialog.show(getFragmentManager(), "");
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void onEvent(EventMessage.DeviceSelected event) {
mDialog.dismiss();
BluetoothDevice device = event.getDevice();
ConnectThread connectThread = new ConnectThread(device);
connectThread.start();
}
public class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
setName("ConnectThread");
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
Log.d("kent", "trying to connect to device");
mmSocket.connect();
Log.d("kent", "Connected!");
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
Log.d("kent", "failed to connect");
mmSocket.close();
} catch (IOException closeException) { }
return;
}
Log.d("kent", "Connected!");
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
Here is my logcat. Pretty short.
07-22 10:37:05.129: DEBUG/kent(17512): trying to connect to device
07-22 10:37:05.129: WARN/BluetoothAdapter(17512): getBluetoothService() called with no BluetoothManagerCallback
07-22 10:37:05.129: DEBUG/BluetoothSocket(17512): connect(), SocketState: INIT, mPfd: {ParcelFileDescriptor: FileDescriptor[98]}
07-22 10:37:40.757: DEBUG/dalvikvm(17512): GC_CONCURRENT freed 6157K, 9% free 62793K/68972K, paused 7ms+7ms, total 72ms
07-22 10:38:06.975: DEBUG/kent(17512): failed to connect
07-22 10:38:06.975: DEBUG/kent(17512): read failed, socket might closed or timeout, read ret: -1
That last line is in the "Catch" section of a try/catch...I'm just logging the error message.
Please note, there is about a 20 second gap between "trying to connect to device" and "failed to connect"
The jelly bean bluetooth stack is markedly different from the other versions.
This might help: http://wiresareobsolete.com/wordpress/2010/11/android-bluetooth-rfcomm/
In gist:
The UUID is a value that must point to a published service on your embedded device, it is not just randomly generated. The RFCOMM SPP connection you want to access has a specific UUID that it publishes to identify that service, and when you create a socket it must match the same UUID.
If you are targeting 4.0.3 device and above , use fetchUuidsWithSdp() and getUuids() to find all the published services and their associated UUID values. For backward compatibility read the article
I got the same error message after connecting the socket a second time. I simply checked if the socket is already connected.
if(!mmSocket.isConnected())
mmSocket.connect();
I was testing on Android 4.4.2 (Moto G).
I am building an Android app that communicates with an Arduino board via bluetooth, I have the bluetooth code in a class of it's own called BlueComms. To connect to the device I use the following methord:
public boolean connectDevice() {
CheckBt();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
Log.d(TAG, "Connecting to ... " + device);
mBluetoothAdapter.cancelDiscovery();
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
btSocket.connect();
outStream = btSocket.getOutputStream();
Log.d(TAG, "Connection made.");
return true;
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
Log.d(TAG, "Unable to end the connection");
return false;
}
Log.d(TAG, "Socket creation failed");
}
return false;
}
private void CheckBt() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!mBluetoothAdapter.isEnabled()) {
System.out.println("Bt dsbld");
}
if (mBluetoothAdapter == null) {
System.out.println("Bt null");
}
}
This connects fine but as soon as I leave the activity I connected through it drops the connection, showing this through LogCat,
D/dalvikvm(21623): GC_CONCURRENT freed 103K, 10% free 2776K/3056K, paused 5ms+2ms, total 35ms
I can no longer connect to the device, but if I call killBt() it throws a fatal error and if I try to send data I get a 'Socket creation failed' error. My send message code is as follows:
public void sendData(String data, int recvAct) {
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
Log.d(TAG, "Bug BEFORE Sending stuff", e);
}
String message = data;
byte[] msgBuffer = message.getBytes();
try {
outStream.write(msgBuffer);
} catch (IOException e) {
Log.d(TAG, "Bug while sending stuff", e);
}
}
How should I go about preventing the connection from being paused by the activity I connect with when I switch a different activity, I am switching activities with this code:
Intent myIntent = new Intent(v.getContext(), Timelapse.class);
startActivityForResult(myIntent, 0);
Many Thanks,
Rozz
Where did you store the instance of your BlueComms class? If you put it in the first activity then the class instance would have been killed when that activity was destroyed as you left it and moved to the next activity (NB activities also get destroyed on screen rotation)
So you need to find a way to keep the instance of BlueComms class alive for as long as you need it. You could pass it between activities via public properties and store it in onRetainNonConfigurationInstance() during rotations.
An easier trick is to create a class that extends Application use it as the application delegate for your app and add public property to it to store the instance of BlueComms class within it. That way the instance of BlueComms class would be alive for the lifetime of you app.
Extend Application
import android.app.Application;
public class cBaseApplication extends Application {
public BlueComms myBlueComms;
#Override
public void onCreate()
{
super.onCreate();
myBlueComms = new BlueComms();
}
}
Make your class the application delegate in the app manifest
<application
android:name="your.app.namespace.cBaseApplication"
android:icon="#drawable/icon"
android:label="#string/app_name" >
Access the base app from any of your Activities like this
((cBaseApplication)this.getApplicationContext()).myBlueComms.SomeMethod();
What I have done is, Created a singleton class for BluetoothConnection.
So socket creation happens only for one time.
When onCreate method of any activity is created, it first fetch instance of BluetoothConnection class.
Handler is used to send messages from thread in BluetoothConnection class to the corresponding activity by settings Handler.
Like:
Class MyBTConnection{
private static MyBTConnection connectionObj;
private Handler mHandler;
public MyBTConnection() { //constructor }
public static MyBTConnection getInstance() {
if(connectionObj == null) {
connectionObj = new MyBTConnection();
}
return connectionObj;
}
}
public void setHandler(Handler handler) {
mHandler = handler;
}
..... Code for Bluetooth Connection ....
to send message :
mHandler.obtainMessage(what).sendToTarget();
}
// in first activity
class MainActivity extends Activity {
private MyBTConnection connectionObj;
public onCreate(....) {
/*
* Since this is first call for getInstance. A new object
* of MyBTConnection will be created and a connection to
* remote bluetooth device will be established.
*/
connectionObj = MyBTConnection.getInstance();
connectionObj.setHandler(mHandler);
}
private Handler mHandler = new Handler(){
public void onReceive(...) {
/// handle received messages here
}
};
}
// in second activity
class SecondActivity extends Activity {
private MyBTConnection connectionObj;
public onCreate(....) {
/*
* Since this is second call for getInstance.
* Object for MyBTConnection was already created in previous
* activity. So getInstance will return that previously
* created object and in that object, connection to remote
* bluetooth device is already established so you can
* continue your work here.
*/
connectionObj = MyBTConnection.getInstance();
connectionObj.setHandler(mHandler);
}
private Handler mHandler = new Handler(){
public void onReceive(...) {
/// handle received messages here
}
};
}
I'm currently having exactly the same issue and I was thinking of opening/closing the Bluetooth socket each time an Activity asks for it. Each Activity has it's own BlueComms instance.
Because my application will became a bit complex and there will be Bluetooth threaded requests from different activities, I'm thinking that this way will become very difficult to use and troubleshoot.
Another way I came across by reading here...
https://developer.android.com/guide/components/services.html
A Service can be created on the background having a Bluetooth socket always on. All Bluetooth requests can be made using Intent towards this service. This also creates some fair amount of complexity but feels a lot more tidy and organized.
I'm currently having this dilemma, either to use a thread for each activity or use a service. I don't know which way is actually better.
When you are Selecting A device to connect and when you are click on the device list item for requesting a connection to the device use AsyncTask
and put the connect method inside the AsyncTask like this :-
AsyncTask.execute(new Runnable() {
#Override
public void run() {
try {
bluetoothSocket = Globals.bluetoothDevice.createRfcommSocketToServiceRecord(Globals.DEFAULT_SPP_UUID);
bluetoothSocket.connect();
// After successful connect you can open InputStream
} catch (IOException e) {
e.printStackTrace();
}
**Here is the full code for the same problem that i have cracked :-**
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
lablelexconnected.setText("Connecting ...");
bdDevice = arrayListBluetoothDevices.get(position);
//bdClass = arrayListBluetoothDevices.get(position)
// Toast.makeText(getApplicationContext()," " + bdDevice.getAddress(),Toast.LENGTH_SHORT).show();
Log.i("Log", "The dvice : " + bdDevice.toString());
bdDevice = bluetoothAdapter.getRemoteDevice(bdDevice.getAddress());
Globals.bluetoothDevice = bluetoothAdapter.getRemoteDevice(bdDevice.getAddress());
System.out.println("Device in GPS Settings : " + bdDevice);
// startService(new Intent(getApplicationContext(),MyService.class));
/* Intent i = new Intent(GpsSettings.this, MyService.class);
startService(i);*/
// finish();
// connectDevice();
AsyncTask.execute(new Runnable() {
#Override
public void run() {
try {
bluetoothSocket = Globals.bluetoothDevice.createRfcommSocketToServiceRecord(Globals.DEFAULT_SPP_UUID);
bluetoothSocket.connect();
// After successful connect you can open InputStream
InputStream in = null;
in = bluetoothSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
br = new BufferedReader(isr);
while (found == 0) {
String nmeaMessage = br.readLine();
Log.d("NMEA", nmeaMessage);
// parse NMEA messages
sentence = nmeaMessage;
System.out.println("Sentence : " + sentence);
if (sentence.startsWith("$GPRMC")) {
String[] strValues = sentence.split(",");
System.out.println("StrValues : " + strValues[3] + " " + strValues[5] + " " + strValues[8]);
if (strValues[3].equals("") && strValues[5].equals("") && strValues[8].equals("")) {
Toast.makeText(getApplicationContext(), "Location Not Found !!! ", Toast.LENGTH_SHORT).show();
} else {
latitude = Double.parseDouble(strValues[3]);
if (strValues[4].charAt(0) == 'S') {
latitude = -latitude;
}
longitude = Double.parseDouble(strValues[5]);
if (strValues[6].charAt(0) == 'W') {
longitude = -longitude;
}
course = Double.parseDouble(strValues[8]);
// Toast.makeText(getApplicationContext(), "latitude=" + latitude + " ; longitude=" + longitude + " ; course = " + course, Toast.LENGTH_SHORT).show();
System.out.println("latitude=" + latitude + " ; longitude=" + longitude + " ; course = " + course);
// found = 1;
NMEAToDecimalConverter(latitude, longitude);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
I'm very new with programming for android. I have two classes : main and btmanager. When i try to test my app on phone, all I get is a information that procees was killed. What am I doing wrong ?
Code implementation :
Main class :
public class MainActivity extends Activity {
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
Btmanager manager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (bluetooth == null)
{
Toast.makeText(this, "Bluetooth is not enabled on this device", Toast.LENGTH_LONG).show();
System.exit(0);
}
}
#Override
public void onStart()
{
super.onStart();
if (!bluetooth.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 2);
}
manager.run();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void closeApp (View view)
{
System.exit(0);
}
}
Btmanager class :
public class Btmanager extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public static final UUID myUUID = UUID.fromString("0x1101");
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
public Btmanager(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(myUUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
bluetooth.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
2 problems I see in your code:
You don't instantiate the Btmanager object, it is still null when you call run. (Will cause a NullPointerException - your app will crash).
You call the run method instead of the start method of the Btmanager. If you want the code in the run method to run in a new thread, you have to call start. Calling run will cause it to run in the same thread. This blocks your UI thread which may cause your app to crash, if it blocks for too long.
For test purporses, I don't use BTmanager class - in onStart() method I add new thread with connection implentation, but still without any results - app crash.
public void onStart()
{
super.onStart();
if (!bluetooth.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 2);
}
new Thread(new Runnable()
{
public void run()
{
try {
//Create a Socket connection: need the server's UUID number of registered
socket = device.createRfcommSocketToServiceRecord(myUUID);
socket.connect();
Log.d("EF-BTBee", "Connectted");
}
catch (IOException e)
{
Log.e("EF-BTBee", "Error : ", e);
}
}
}).start();
}