使用语音识别时的蓝牙clossing套接字

时间:2016-03-11 16:13:10

标签: android sockets android-fragments bluetooth

我在我的APP上实现了这个tutorial,但我做了很多改变......我创建了一个TabLayout所以我做了什么(我不认为这是个好主意,好吧它不是因为它不起作用:))每个片段我复制粘贴教程的代码(我创建了套接字连接到我的Bluetooth,我创建了与设备的连接...)当我只用一个Activity进行测试时效果很好......但是当我添加TabLayout时,它开始无效。我想我可以在Bluetooth上执行Activity的所有代码,然后使用Activity的对象(来自Fragment我的意思......)问题是在onPause()我有这个:

@Override
public void onPause() {
    super.onPause();
    Toast.makeText(getActivity(), "onPause", Toast.LENGTH_SHORT).show();
        try {
            btSocket.close();
        } catch (IOException e2) {

        }
}

每次我使用它:

private void startVoiceRecognitionActivity(){
    Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
    intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
    intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.VoiceControllerText));
    startActivityForResult(intent, REQUEST_CODE);
}

它进入onPause(),然后套接字关闭,我无法向Bluetooth发送信息我试图评论该行btSocket.close();,但错误显示{{1}我没有评论其他socket is closed的行(我只有2)我是否应该评论另一个Tab的{​​{1}}?....

我正在寻找一个解决方案,帮助我实现/指导如何将socket.close()的所有代码实现为另一个类或其他类,如果我从Tab输入Bluetooth套接字onPause()没有关闭..

顺便说一下,我不确定复制粘贴Tab的代码(它们在一个Bluetooth中是相同的而不是另一个......),这是一个好主意。同样Fragment全部......

如果你们需要更多代码来查看它,请告诉我,我会发布它。

感谢。

修改

首先,我发送了UUID Activity MainActivity,如下所示:

MAC address

这是我DeviceListActivity ...

中最重要的代码

我所拥有的第二件事是Intent i = new Intent(DeviceListActivity.this, MainActivity.class); i.putExtra(EXTRA_DEVICE_ADDRESS, address); i.putExtra("name", name); startActivity(i); ,但我没有关于MainActivity的任何内容,因为我在Bluetooth里面对它做了什么......

我有Fragments这完美(这是第一个):

Atributes

Fragment
//Sending info Handler bluetoothIn; private ConnectedThread mConnectedThread; final int handlerState = 0; //used to identify handler message private BluetoothAdapter btAdapter = null; private BluetoothSocket btSocket = null; // SPP UUID service - this should work for most devices private static final UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // String for MAC address private static String address=""; 中的

我称之为:

onCreate()

我有创建btAdapter = BluetoothAdapter.getDefaultAdapter();// get Bluetooth adapter if (btAdapter == null) { Toast.makeText(getActivity(), getString(R.string.BtNotSupported), Toast.LENGTH_SHORT).show(); } checkBTState();

的方法
socket

这是我的private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException { return device.createRfcommSocketToServiceRecord(BTMODULEUUID); }

onResume()

这是我的@Override public void onResume() { super.onResume(); //Get MAC del intent Intent intent = getActivity().getIntent(); address = intent.getStringExtra(DeviceListActivity.EXTRA_DEVICE_ADDRESS); //Creates a device with the MAC from DeviceListActivity if(btAdapter!=null) { BluetoothDevice device = btAdapter.getRemoteDevice(address); try { btSocket = createBluetoothSocket(device); } catch (IOException e) { ShowSnack(getString(R.string.SocketCreationFailed), Color.RED); } //Trying to connect try { btSocket.connect(); } catch (IOException e) { try { btSocket.close(); } catch (IOException e2) { } } mConnectedThread = new ConnectedThread(btSocket); mConnectedThread.start(); } else{ ShowSnack(getString(R.string.toast_bt_unavailable), Color.RED); } }

onPause()

这是我打电话来查看是否启用 @Override public void onPause() { super.onPause(); try { //Close socket if leaves the Activity btSocket.close(); } catch (IOException e2) { } } 的方法。

Bluetooth

这是我的private void checkBTState() { if (btAdapter == null) { ShowSnack(getString(R.string.toast_bt_unavailable), Color.RED); } else { if (btAdapter.isEnabled()) { } else { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, 1); } } } 课程,用于发送和接收来自ConnectedThread的内容。

Bluetooth

嗯,现在,当我遇到问题时,第二个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); //read bytes from input buffer String readMessage = new String(buffer, 0, bytes); bluetoothIn.obtainMessage(handlerState, bytes, -1, readMessage).sendToTarget(); } catch (IOException e) { break; } } } //Send stuff to Bluetooth public void write(char input) { try { mmOutStream.write(input); } catch (IOException e) { } } } 我在这里有相同代码......这就是为什么我想在尝试使用时崩溃的原因Fragment ...当我尝试向Voice recognision发送内容时,嗯..如果代码太多,我很抱歉,但这是我唯一希望您理解我的问题。

1 个答案:

答案 0 :(得分:2)

您遇到的一个问题是,您似乎正在尝试从活动中管理蓝牙连接的生命周期。正如您所见,当Activity的生命周期功能(例如onPause()和onResume())与您的连接的生命周期完全一致时,这可能会导致问题。要解决此问题,您可以创建一个服务来处理您与该蓝牙连接的所有连接,发送和接收以及断开连接。服务的生命周期独立于活动,因此即使您的用户在活动和片段之间切换,您也可以保持蓝牙连接处于打开状态。

要设置您的服务,请创建一个扩展服务的新类,并将所有蓝牙处理对象放入其中。

public class BluetoothService extends Service {
    public static final String BLUETOOTH_SERIAL_UUID = "00001101-0000-1000-8000-00805F9B34FB";
    private BluetoothSocket mSocket;
    private String mAddress = "bluetooth_mac_address_here";

    public void onCreate() {
        //Set up Bluetooth socket.
        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
        if(btAdapter.isEnabled()) {
            BluetoothDevice btDevice = btAdapter.getRemoteDevice(mAddress);
            mSocket = btDevice.createRfcommSocketToServiceRecord(BLUETOOTH_SERIAL_UUID);
            btAdapter.cancelDiscovery();
            mSocket.connect();
        }
    }
}

这在首次启动服务时设置mSocket对象。在此之后,您可以通过简单调用mSocket.getInputStream()mSocket.getOutputStream()并使用这些来读取/写入数据,从而与远程蓝牙设备进行交互。但是,如果您不熟悉使用服务,那么如何从活动中将数据传入和传出服务以传输数据可能会有些混乱。以下是使用Intents的方法。

在同一个BluetoothService类中,覆盖onStartCommand()

public class BluetoothService extends Service {
...
public static final String ACTION_SEND_DATA = "send_data";
public static final String ACTION_RECEIVED_DATA = "received_data";
public static final String EXTRA_BLUETOOTH_DATA = "bluetooth_data";

public int onStartCommand(Intent intent, int flags, int startId) {
    //Register a BroadcastReceiver to handle "send" requests.
    LocalBroadcastManager.getInstance(this).registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //Parse your data to send from the intent.
            if(intent.getAction().equals(ACTION_SEND_DATA)) {
                byte[] data = intent.getByteArrayExtra(EXTRA_BLUETOOTH_DATA);
                //Send the data over the Bluetooth Socket.
                try {
                    mSocket.getOutputStream().write(data);
                } catch(IOException ioe) {
                    //This might happen if you try to write to a closed connection.
                    ioe.printStackTrace();
                }
            }
        }
        return Service.START_STICKY;
    }
}

这将为您提供一种方法,使用Intent将数据从Activity发送到服务,但尚未接收该数据。我稍后会谈到的。请注意,我已使用LocalBroadcastReceiver注册了意图。这意味着我们注册的BroadcastReceiver只会被赋予从您的应用中广播并具有匹配操作的意图。我只是用它来简化意图交互,但将来如果你想允许外部应用程序使用你的服务发送数据(可能不太可能),那么你需要改变它。无论如何,从您的活动中,执行以下操作以通过您的服务发送数据:

public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        ...
        String myString = "This is some data I want to send!";
        //Create an intent with action saying to send data
        //with the byte[] of data you want to send as an extra.
        Intent sendIntent = new Intent(BluetoothService.ACTION_SEND_DATA);
        sendIntent.putExtra(BluetoothService.EXTRA_BLUETOOTH_DATA, myString.getBytes());
        //Sends the intent to any BroadcastReceivers that have registered receivers for its action.
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
}

不幸的是,我在几分钟内上课,现在无法完成这篇文章,但我将在几个小时内开始讲述如何设置接收部分。在此期间,请随意查看我的一个项目this code,该项目可以解决这些问题。查看TransferManager类以及它如何使用Threads提供一种非阻塞方式来从BluetoothSocket的InputStream接收数据。

=============================================== ===========================

好的,现在让我们来看看如何使用您的服务从远程蓝牙设备接收数据。关于服务的一件事是,它们不会在您的活动的单独线程上运行。虽然它们保持状态并且它们的生命周期功能与活动的功能分离,但它们仍然在主UI线程上执行。这意味着如果您将服务中的代码放慢或阻塞,它将分别减慢或冻结您的活动用户界面。这是我们绝对想要避免的行为,因此当我们考虑从蓝牙设备接收数据(阻塞操作)时,我们需要通过在自定义Service类中创建新线程来处理该操作。让我们定义一个自定义类,它将Thread扩展为我们的BluetoothService的内部类:

public class BluetoothService extends Service {
    ...
    public void onCreate() {...}
    public int onStartCommand(...) {...}

    public static class ReceiveThread extends Thread {
        private boolean isRunning;
        private InputStream mBluetoothInputStream;

        public ReceiveThread(InputStream bluetoothInputStream) {
            mBluetoothInputStream = bluetoothInputStream;
            isRunning = true;
        }

        @Override
        public void run() {
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(mBluetoothInputStream));
            String line;
            while(isRunning) {
                try {
                    //This is the line that blocks until a newline is read in.
                    line = bufferedReader.readLine();
                } catch(IOException ioe) {
                    //This happens if the InputStream is closed.
                    ioe.printStackTrace();
                    //Stop the thread from looping.
                    isRunning = false;
                }

                //Make sure our line in isn't null or blank.
                if(line == null || line.equals("") {
                    continue; //Start again at top of while loop.
                }

                //Notify your Activity about the new data.
                Intent receivedIntent = new Intent(BluetoothService.this, MyActivity.class);
                receivedIntent.setAction(ACTION_RECEIVED_DATA);
                receivedIntent.putExtra(EXTRA_BLUETOOTH_DATA);
                LocalBroadcastManager.getInstance(BluetoothService.this).sendBroadcast(receivedIntent);

                try {
                    //This is an arbitrary sleep time just to prevent
                    //this from looping without any restriction.
                    Thread.sleep(20);
                } catch(InterruptedException e) {
                    //This happens if the Thread is interrupted for any reason.
                    e.printStackTrace();
                    isRunning = false;
                }
            }
        }
    }
}

好的,现在你可以通过在服务的onStartCommand()末端抛几行来启动一个新的ReceiveThread:

ReceiveThread receiver = new ReceiveThread(mSocket.getInputStream());
receiver.start();

最后一步是将数据实际存入您的Activity。为此,您将创建一个BroadcastReceiver,用于侦听ReceiveThread发出的广播。在您的Activity类中,将其放在onCreate()

的末尾
public void onCreate() {
    ...
    LocalBroadcastManager.getInstance(this).registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //Get your data out of the intent.
            byte[] data = intent.getByteArrayExtra(BluetoothService.EXTRA_BLUETOOTH_DATA);
        }
    }, new IntentFilter(BluetoothService.ACTION_RECEIVED_DATA));
}

每次BluetoothService的ReceiveThread从远程蓝牙设备读取新行时,都会调用onReceive()方法。根据您的实际应用程序,这可能适用于您,也可能不适合您(例如,如果您的程序不是基于文本/命令且其中没有换行符)。您可以通过使用其他类型的Reader交换ReceiveThread中的BufferedReader来更改该行为。

修改

在您的代码段中,您构建了一个名为write的存根方法,您似乎已经注意到它了。拥有这样的方法需要你从Activity中直接调用它来执行它,这不是你想要的。如果你查看这篇文章,你会发现我已经从你的Activity中调用了一些代码,这些代码使用Intent将数据传递给要写入的服务。查看以public class MyActivity extends Activity开头的代码段。使用意图的关键是Android框架将负责携带"额外"数据到服务,然后在服务的onReceive()中的onStartCommand()方法中解压缩,您可以在其中看到正在写入的OutputStream。

唯一的另一件事是我忘记了return Service.START_STICKY服务的onStartCommand()方法。在任何地方,您都希望将您在代码段中创建的write方法放入,使用LocalBroadcastManager放置有关创建和发送Intent的代码。