本文介绍了蓝牙使用语音识别时clossing插座的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现这个<一个href=\"https://wingoodharry.word$p$pss.com/2014/04/15/android-sendreceive-data-with-arduino-using-bluetooth-part-2/\"相对=nofollow>教程在我的APP ,但我做了很多的变化....我创建了一个 TabLayout 所以我做了什么(我不我不觉得是个好主意,那么它是不是,因为它并不在我复制粘贴教程code每个片断工作:))(我创建了套接字连接到我的蓝牙,我创建连接到该设备...),当我只有一个活动进行了测试效果不错......但是当我加入 TabLayout 它开始不工作。我想我可以做所有的蓝牙活动的code,然后与对象一起工作该活动的(从片段我的意思是......)的问题是,的onPause()我有这样的:

I've implemented this tutorial on my APP, but I did many changes.... I've created a TabLayout so what I did (I don't think that's the good idea, well it is not since it doesn't work :)) on each fragment I copy pasted the code of the tutorial (I created the sockets to connect to my Bluetooth, I create connection to the device...) and when I tested it only with one Activity it worked well... but when I added the TabLayout it started to don't work. I think I could do all of the code of the Bluetooth on the Activity and then work with the objects of that Activity (from the Fragment I mean...) the problem is that on onPause() I have this :

@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(),然后关闭套接字,我不能发送信息,以蓝牙我试图评论说,行 btSocket.close(); 但错误说关闭套接字,我没'T评论其他设置页的行(我只有2)我要评论也是 socket.close()其他的设置页?....

It enters on onPause() and then the socket is closed, and I can't send info to Bluetooth I tried to comment that line btSocket.close(); but the error says that socket is closed, I didn't commented the line of the other Tab (I only have 2) should I comment also the socket.close() of the other Tab?....

我正在寻找一个解决方案,可以帮助我实现/指导如何实现所有蓝牙$ C $的C 作为另一个类或东西,这如果我上输入的onPause()从一个设置页插座不紧密。

I'm looking for a solution that helps to me to implement / guide how to implement all of the code of Bluetooth as another class or something, that if I enter on the onPause() from one Tab the socket doesn't close..

顺便说一句,我不知道,复制粘贴蓝牙code (他们是在一个片段相同比其他....)这是一个好主意......同样的 UUID 都一样...

And by the way I'm not sure that copying pasting the code of the Bluetooth (They are the same in one Fragment than the other....) it's a good idea... same UUID same all...

如果你们需要更多的code检查出来,让我知道,我会后。

If you guys need more code to check it out, let me know and I'll post it.

感谢。

首先我第一个活动我送到 MainActivity MAC地址如下:

First I have the first Activity which I sent to the MainActivity the MAC address as follows :

Intent i = new Intent(DeviceListActivity.this, MainActivity.class);
i.putExtra(EXTRA_DEVICE_ADDRESS, address);
i.putExtra("name", name);
startActivity(i);

这是我的 DeviceListActivity ...

This is the most important code of my DeviceListActivity...

这是我的第二件事是 MainActivity 但我没有任何关于蓝牙什么因为我做的东西与它片段它里面...

The second thing that I have is MainActivity but there I don't have anything about Bluetooth because I do stuff with it on Fragments inside of it...

我有这样的片段这完美的作品(这是第一个):

I have this Fragment which works perfect (it's the first one) :

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

我要创建的方法插座

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) {

    }
}

这是我打电话,看看蓝牙或不启用的方法。

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 类来发送和从蓝牙

This is my ConnectedThread class to send and recieve stuff from 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) {

        }
    }
}

好了,现在,当我有问题是关于第二个片段在那里我有在同一个 code作为在这里。 ..这就是为什么我想,尝试使用时崩溃语音聚类的 ...当我尝试发送的东西蓝牙,以及..我很抱歉,如果这是太多了code,但就是这样我希望你明白我的问题的唯一的事情。

Well and now, when I'm having problems is on the second Fragment where I have THE SAME code as here... that's why I guess that crashes when trying to use Voice recognision... when I try to send something to Bluetooth, well.. I'm sorry if that's too much code but that's the only thing that I have hope you understand my problem.

推荐答案

这是你面临的一个问题是,它似乎是你想从所有的活动中管理蓝牙连接的生命周期。正如你所看到的,这可能会导致问题时,活动的生命周期函数(如的onPause()和onResume())并不完全符合您的连接的生命周期一致。为了解决这个问题,你可以创建处理所有的连接,发送和接收,并从该蓝牙连接断开的服务。该服务的生命周期是独立的活动,所以即使你的用户活动和片段之间切换,可以保持蓝牙连接打开。

One problem that you're facing is that it seems that you're trying to manage the lifecycle of your Bluetooth connection all from within your activity. As you've seen, this can cause problems when the Activity's lifecycle functions (such as onPause() and onResume()) don't perfectly align with the lifetime of your connection. To solve this, you can create a Service that handles all of your connecting, sending and receiving, and disconnecting from that Bluetooth connection. The Service's lifetime is independent from the Activity, so even if your user is switching between Activities and Fragments, you can keep the Bluetooth connection open.

要设置您的服务,使该服务扩展了新的类,并把所有的蓝牙处理的对象在里面。

To set up your Service, make a new class that extends Service and put all of your Bluetooth handling objects in it.

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() ,并利用这些读/写数据。但是,如果你不熟悉如何使用服务,它可以是一个有点混乱,至于如何从活动让您的数据和从服务转移数据。这里有一个方法使用意图去做。

This sets up the mSocket object when the Service is first launched. After that point, you'll be able to interact with the remote bluetooth device by simple calls to mSocket.getInputStream() and mSocket.getOutputStream() and reading/writing data using those. However, if you're not familiar with using Services, it can be a little confusing as to how to get your data from the Activity to and from the Service to transfer your data. Here's a way to do it using Intents.

在同一BluetoothService类,重写 onStartCommand()

Inside the same BluetoothService class, override 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;
    }
}

这会给你一个方式使用意图从一个活动到服务发送的数据,但尚未收到该数据。我会得到以后。请注意,我用 LocalBroadcastReceiver 注册意图。这意味着广播接收器,我们登记将只因为都是从广播你的应用程序中的意图和具有匹配的动作。我只是使用了简化的意图的相互作用,但在未来,如果你想允许外部应用程序使用您的服务(大概不太可能)发送数据,那么你需要改变这种状况。不管怎样,从你的活动,请执行下列操作通过服务发送的数据:

This will give you a way to use Intents to send your data from an Activity to the Service, but not yet to receive that data. I'll get to that later. Note that I've used LocalBroadcastReceiver to register the intent. This means that the BroadcastReceiver that we register will only be given intents that were both broadcast from within your app and have a matching action. I just used that to simplify the intent interactions, but in the future if you want to allow external apps to send data using your service (probably unlikely), then you'll need to change that. Anyway, from your Activity, do the following to send the data through your Service:

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

不幸的是我在几分钟类,不能马上完成这个职位,但我会在几个小时内就涵盖如何设置接收部分。在此期间,随时检查出从我的一个项目,该项目正好解决了这些问题,这code 。看TransferManager类以及它如何使用线程提供非阻塞的办法从的BluetoothSocket的InputStream的接收数据。

Unfortunately I have class in a few minutes and can't finish this post right now, but I'll be on in a few hours to cover how to set up the receiving part. In the meantime, feel free to check out this code from a project of mine that solves exactly these problems. Look at the TransferManager class and how it uses Threads to provide a non-blocking way to receive data from the InputStream of the BluetoothSocket.

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

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

好了,现在让我们来看看如何使用您的服务,从您的远程蓝牙设备接收数据。有一件事要了解服务是他们没有从你的活动单独的线程运行。虽然他们保持自己的状态,并在其生命周期功能从这些活动中去耦,它们仍然都在主UI线程上执行。这意味着,如果你把code在你的服务是缓慢或堵塞,这将分别放缓或冻结你的Activity的UI。这是行为我们绝对要避免的,所以当我们考虑从一个蓝牙设备(阻塞操作)接收数据,我们需要通过创建自定义的服务类中的一个新的线程来处理该操作。让我们来定义继承Thread作为一个内部类我们BluetoothService的自定义类:

Ok, now let's look at how you can use your Service to receive data from your remote Bluetooth device. One thing to know about Services is that they are not run on separate threads from your Activities. While they maintain their state and their lifecycle functions are decoupled from those of Activities, they are still both executed on the main UI thread. This means that if you put code in your Service that is slow or blocking, it will respectively slow down or freeze your Activity's UI. This is behavior that we definitely want to avoid, so when we consider receiving data from a Bluetooth device (a blocking operation), we need to handle that operation by creating a new Thread within the custom Service class. Let's define a custom class that extends Thread as an inner class of our 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;
                }
            }
        }
    }
}

好了,现在你可以通过抛出几行到年底旋转了一个新的ReceiveThread onStartCommand()中的服务:

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

最后一步是实际获得这些数据进入你的活动。要做到这一点,你会创建一个侦听由ReceiveThread发出的广播一个BroadcastReceiver。在你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));
}

的onReceive()方法被调用每次你BluetoothService的ReceiveThread读取您的远程蓝牙设备的新行的时间。根据您的实际应用中,这可能会或可能不适合你(例如,如果你的程序是不是基于文本/命令,并在它没有换行符)。您可以通过交换在ReceiveThread中的BufferedReader与其他类型的读者改变这一行为。

The onReceive() method gets called every time your BluetoothService's ReceiveThread reads a new line from your remote bluetooth device. Depending on your actual application, this may or may not be suitable for you (for example, if your program isn't text/command based and has no newline characters in it). You can change that behavior by swapping out the BufferedReader in the ReceiveThread with another type of Reader.

编辑:

在您的片断,你已经建立了一个存根方法,你似乎就不得不被固定。有了这样的方法需要您来执行它作为活动,这是不是你想要的直接调用。如果你在这个岗位仰望,你会看到,我已经把一些code,它是为了从您的活动,它使用意图将数据传送到服务写入调用。看看与公共类MyActivity开始延伸片段活动。使用意图的一点是,Android框架将接管到服务中,然后在的onReceive()法无包装运输额外数据护理 onStartCommand()在服务,在这里你可以看到的OutputStream被写入。

In your snippet you've built a stub method called write that you seem to be fixated on having. Having a method like this would require you to execute it as a direct call from the Activity, which isn't what you want. If you look up in this post, you'll see that I've put some code that was meant to be called from your Activity which uses intents to deliver your data to the Service to be written. Look at the snippet beginning with public class MyActivity extends Activity. The point of using intents is that the Android framework will take care of carrying the "extra" data over to the Service, which is then unpackaged in the onReceive() method in onStartCommand() in the Service, where you can see the OutputStream is being written to.

唯一的另一件事是,我没有忘记返回Service.START_STICKY 的的 onStartCommand()方法服务。无处不在,你会想要把那个您在片段制作方法,把code关于创建和发送使用LocalBroadcastManager的意图。

The only other thing is that I did forget the return Service.START_STICKY for the onStartCommand() method of the Service. Everywhere you would want to put that write method that you made in your snippet, put the code about creating and sending the Intent using the LocalBroadcastManager.

这篇关于蓝牙使用语音识别时clossing插座的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 09:37