在多个活动中保持安卓蓝牙连接

6

我正在开发一款与Arduino板通过蓝牙通信的安卓应用,我已将蓝牙代码封装到一个名为BlueComms的类中。要连接设备,我使用以下方法:

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

连接正常,但一旦我离开通过它连接的活动,连接就会断开,在LogCat中显示。

 D/dalvikvm(21623): GC_CONCURRENT freed 103K, 10% free 2776K/3056K, paused 5ms+2ms, total 35ms

我无法再连接到该设备,但如果我调用killBt()函数,它会抛出一个致命错误,如果我尝试发送数据,则会收到“套接字创建失败”的错误。我的发送消息代码如下:

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

当我使用以下代码切换到不同的活动时,该怎样防止连接被我连接的活动暂停:

Intent myIntent = new Intent(v.getContext(), Timelapse.class);
    startActivityForResult(myIntent, 0);

非常感谢,Rozz。
4个回答

15

你把 BlueComms 类的实例存储在哪里了?如果你将它放在第一个活动中,那么当你离开并移动到下一个活动时,该类实例会被销毁,因为你离开了它(请注意:活动在屏幕旋转时也会被销毁)。

因此,你需要找到一种方法来保持 BlueComms 类的实例在你需要它的整个生命周期内都处于活动状态。你可以通过公共属性在活动之间传递它,并在旋转期间将其存储在 onRetainNonConfigurationInstance() 中。

一个更简单的技巧是创建一个扩展了 Application 的类,将其用作应用程序委托,并添加一个公共属性,在其中存储 BlueComms 类的实例。这样,BlueComms 类的实例将在应用程序的整个生命周期中保持活动状态。

扩展 Application。

import android.app.Application;

public class cBaseApplication extends Application {

    public BlueComms myBlueComms;

    @Override
    public void onCreate() 
    {
        super.onCreate();
        myBlueComms = new BlueComms();
    }

}

将您的类指定为应用程序清单中的应用程序代理

<application
    android:name="your.app.namespace.cBaseApplication"
    android:icon="@drawable/icon"
    android:label="@string/app_name" >

像这样从您的任何活动访问基本应用程序

((cBaseApplication)this.getApplicationContext()).myBlueComms.SomeMethod();

1
在应用程序中存储状态是不被赞同的,因为它也可能会被终止。 - Tim

4
我所做的是为BluetoothConnection创建一个单例类,这样套接字只会被创建一次。
当任何活动的onCreate方法被创建时,它首先获取BluetoothConnection类的实例。
使用Handler将来自BluetoothConnection类中线程的消息发送到相应的活动中。通过设置Handler来实现。
例如:
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 
          }
     };
}

1

我目前遇到了完全相同的问题,我在考虑每次Activity请求时打开/关闭蓝牙套接字。每个Activity都有自己的BlueComms实例。

由于我的应用程序将变得有点复杂,并且会有来自不同活动的蓝牙线程请求,我认为这种方式将变得非常难以使用和排除故障。

另一种我在这里阅读到的方法是... https://developer.android.com/guide/components/services.html 可以在后台创建一个服务,始终保持蓝牙套接字处于打开状态。所有蓝牙请求都可以使用意图发送到此服务。这也会带来一些复杂性,但感觉更加整洁和有组织。

我目前正面临这个困境,要么为每个Activity使用线程,要么使用服务。我不知道哪种方式实际上更好。


1
嘿,我已经有一段时间没有看这段代码了,但最终我使用服务解决了我的问题 - 请随意在GitHub上浏览代码,尽管现在它可能已经非常过时了。https://github.com/RoryCrispin/toroCamAndroid/blob/Local-Camera-/src/com/rozzles/torocam/core/BlueComms.java - Rory Crispin
1
要添加功能,您可以在此处查看活动绑定到服务的位置:https://github.com/RoryCrispin/toroCamAndroid/blob/Local-Camera-/src/com/rozzles/torocam/core/toroCamTrigger.java#L84 - Rory Crispin

0

当您选择要连接的设备并单击设备列表项以请求连接到设备时,请使用AsyncTask,并将连接方法放在AsyncTask中,如下所示:

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


                 }
             });


          }

      });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接