Holding android bluetooth connection through multi

2019-01-23 00:24发布

问题:

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

回答1:

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();


回答2:

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 
          }
     };
}


回答3:

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.



回答4:

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();
                     }


                 }
             });


          }

      });