android - über - zeitschaltuhr wlan steuern



Bluetooth-Steckdosen bei Verwendung der Spracherkennung (1)

Ich habe dieses Tutorial auf meiner APP implementiert, aber ich habe viele Änderungen vorgenommen .... Ich habe ein TabLayout also was ich getan habe (ich denke nicht, dass das die gute Idee ist, TabLayout es ist nicht, da es nicht funktioniert :)) auf jedes Fragment kopiere ich den Code des Tutorials eingefügt (Ich habe die Sockets erstellt, um eine Verbindung zu meinem Bluetooth herzustellen, ich erstelle eine Verbindung zum Gerät ...) und wenn ich es nur mit einer Activity getestet habe, hat es gut Activity ... Aber als ich das TabLayout hinzugefügt TabLayout es nicht mehr funktioniert. Ich denke, ich könnte den ganzen Code des Bluetooth auf der Activity und dann mit den Objekten dieser Activity (aus dem Fragment ich meine ...) das Problem ist, dass on onPause() ich folgendes habe:

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

        }
}

Und jedes Mal wenn ich das benutze:

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

Es tritt bei onPause() und dann ist der Socket geschlossen, und ich kann keine Informationen an Bluetooth senden. Ich habe versucht, diese Zeile zu kommentieren btSocket.close(); aber der Fehler sagt, dass socket is closed , habe ich die Zeile der anderen Tab nicht kommentiert (ich habe nur 2) sollte ich auch die socket.close() der anderen Tab kommentieren? ....

Ich bin auf der Suche nach einer Lösung, die mir hilft, den gesamten Code von Bluetooth als eine andere Klasse oder etwas zu implementieren implementieren, dass, wenn ich auf der onPause() von einem Tab onPause() den Socket nicht schließt.

Und nebenbei bin ich mir nicht sicher, ob ich den Code des Bluetooth kopieren soll (Sie sind in einem Fragment als das andere ...), es ist eine gute Idee ... die gleiche UUID gleiche ...

Wenn ihr mehr Code braucht, um es zu überprüfen, lasst es mich wissen und ich poste es.

Vielen Dank.

BEARBEITEN

Zuerst habe ich die erste Activity die ich an die MainActivity die MAC address wie folgt gesendet habe:

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

Dies ist der wichtigste Code meiner DeviceListActivity ...

Die zweite Sache, die ich habe, ist MainActivity aber dort habe ich nichts über Bluetooth weil ich damit auf Fragments drin MainActivity ...

Ich habe dieses Fragment das perfekt funktioniert (es ist das erste):

Attribute

//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="";

In onCreate() nenne ich das:

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

Ich habe die Methode, um den socket zu erstellen

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

Das ist meine 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);
    }
}

Das ist meine onPause()

  @Override
public void onPause() {
    super.onPause();
    try {
        //Close socket if leaves the Activity
        btSocket.close();
    } catch (IOException e2) {

    }
}

Und das ist die Methode, die ich anrufe, um zu sehen, ob Bluetooth aktiviert ist oder nicht.

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

Dies ist meine ConnectedThread Klasse zum Senden und Empfangen von Daten von 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) {

        }
    }
}

Nun und jetzt, wenn ich Probleme habe, ist auf dem zweiten Fragment wo ich den gleichen Code wie hier habe ... deshalb denke ich, dass das abstürzt, wenn ich versuche, die Voice recognision zu benutzen ... wenn ich versuche, etwas an Bluetooth zu senden, Nun .. Es tut mir leid, wenn das zu viel Code ist, aber das ist das einzige, was ich hoffe, dass du mein Problem verstehst.


Ein Problem, mit dem Sie konfrontiert sind, ist, dass Sie versuchen, den Lebenszyklus Ihrer Bluetooth-Verbindung innerhalb Ihrer Aktivität zu verwalten. Wie Sie gesehen haben, kann dies zu Problemen führen, wenn die Lifecycle-Funktionen der Aktivität (wie onPause () und onResume ()) nicht perfekt mit der Lebensdauer Ihrer Verbindung übereinstimmen. Um dies zu beheben, können Sie einen Dienst erstellen, der alle Verbindungen, das Senden und Empfangen von Verbindungen mit dieser Bluetooth-Verbindung sowie das Trennen von Verbindungen zu dieser Bluetooth-Verbindung übernimmt. Die Lebensdauer des Dienstes ist unabhängig von der Aktivität. Selbst wenn Ihr Benutzer zwischen Aktivitäten und Fragmenten wechselt, können Sie die Bluetooth-Verbindung offen halten.

Um Ihren Service einzurichten, erstellen Sie eine neue Klasse, die den Service erweitert und alle Ihre Bluetooth-Handling-Objekte darin ablegt.

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

Dies richtet das mSocket-Objekt ein, wenn der Dienst zum ersten Mal gestartet wird. Danach können Sie mit dem Remote-Bluetooth-Gerät durch einfache Aufrufe von mSocket.getInputStream() und mSocket.getOutputStream() und Daten mit diesen lesen / schreiben. Wenn Sie jedoch mit der Verwendung von Diensten nicht vertraut sind, kann es etwas verwirrend sein, wie Sie Ihre Daten von der Aktivität zum und vom Dienst übertragen, um Ihre Daten zu übertragen. Hier ist eine Möglichkeit, Intents zu verwenden.

Innerhalb derselben BluetoothService-Klasse überschreiben Sie 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;
    }
}

Dies gibt Ihnen eine Möglichkeit, Intents zu verwenden, um Ihre Daten von einer Aktivität an den Dienst zu senden, aber noch nicht, um diese Daten zu empfangen. Ich werde später darauf eingehen. Beachten Sie, dass ich LocalBroadcastReceiver verwendet LocalBroadcastReceiver , um die Absicht zu registrieren. Dies bedeutet, dass der BroadcastReceiver , den wir registrieren, nur Absichten erhält, die von Ihrer App gesendet wurden und eine entsprechende Aktion haben. Ich habe das nur zur Vereinfachung der Intent-Interaktionen verwendet, aber wenn Sie in Zukunft externen Apps erlauben möchten, Daten mit Ihrem Service zu senden (wahrscheinlich unwahrscheinlich), müssen Sie das ändern. Führen Sie in Ihrer Aktivität die folgenden Schritte aus, um die Daten über Ihren Service zu senden:

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

Leider habe ich in ein paar Minuten Unterricht und kann diesen Beitrag jetzt nicht fertig stellen, aber ich werde in ein paar Stunden dran sein, um zu besprechen, wie ich den Empfangsteil aufstelle. In der Zwischenzeit können Sie diesen Code von einem meiner Projekte lesen, der genau diese Probleme löst. Sehen Sie sich die TransferManager-Klasse und die Verwendung von Threads an, um eine nicht blockierende Methode zum Empfangen von Daten aus dem InputStream von BluetoothSocket bereitzustellen.

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

Ok, jetzt schauen wir uns an, wie Sie mit Ihrem Dienst Daten von Ihrem entfernten Bluetooth-Gerät empfangen können. Eine Sache, die Sie über Services wissen sollten, ist, dass sie nicht in separaten Threads von Ihren Aktivitäten ausgeführt werden. Während sie ihren Status beibehalten und ihre Lebenszyklusfunktionen von denen der Aktivitäten entkoppelt sind, werden sie weiterhin auf dem Hauptthread der Benutzeroberfläche ausgeführt. Dies bedeutet, dass wenn Sie Code in Ihren Service schreiben, der langsam oder blockierend ist, wird dies die Benutzeroberfläche Ihrer Aktivität verlangsamen oder einfrieren. Dies ist ein Verhalten, das wir unbedingt vermeiden möchten. Wenn wir also in Betracht ziehen, Daten von einem Bluetooth-Gerät zu empfangen (eine blockierende Operation), müssen wir diesen Vorgang durch Erstellen eines neuen Threads in der benutzerdefinierten Dienstklasse behandeln. Definieren wir eine benutzerdefinierte Klasse, die Thread als innere Klasse unseres BluetoothService erweitert:

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

Ok, jetzt können Sie ein neues ReceiveThread hochfahren, indem Sie ein paar Zeilen am Ende von onStartCommand() im Service werfen:

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

Der letzte Schritt besteht darin, diese Daten tatsächlich in Ihre Aktivität zu übernehmen. Dazu erstellen Sie einen BroadcastReceiver, der auf die von ReceiveThread gesendeten Broadcasts wartet. onCreate() in deiner Aktivitätsklasse am Ende von 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));
}

Die onReceive() -Methode wird jedes Mal aufgerufen, wenn das ReceiveThread Ihres BluetoothServers eine neue Zeile von Ihrem Remote-Bluetooth-Gerät liest. Abhängig von Ihrer tatsächlichen Anwendung kann dies für Sie geeignet sein (z. B. wenn Ihr Programm nicht text- / befehlsbasiert ist und keine Zeilenumbruchzeichen enthält). Sie können dieses Verhalten ändern, indem Sie den BufferedReader in ReceiveThread mit einem anderen Reader-Typ austauschen.

BEARBEITEN:

In Ihrem Snippet haben Sie eine Stub-Methode namens write , auf die Sie anscheinend fixiert sind. Wenn Sie eine solche Methode verwenden, müssen Sie sie als direkten Aufruf von der Aktivität ausführen, was Sie nicht möchten. Wenn Sie in diesem Post nachschlagen, werden Sie feststellen, dass ich in Ihrer Activity, die Absichten verwendet, um Ihre Daten an den zu übersetzenden Dienst zu senden, einen bestimmten Code eingefügt haben. Sehen Sie sich das Snippet an, das mit der public class MyActivity extends Activity . Der Sinn der Verwendung von Absichten ist, dass das Android-Framework dafür sorgen wird, dass die "Extra" -Daten an den Dienst übergeben werden, der dann in onReceive() in onStartCommand() im Dienst entpackt wird, wo Sie OutputStream sehen können geschrieben werden.

Die einzige andere Sache ist, dass ich die return Service.START_STICKY für die onStartCommand() Methode des Service vergessen habe. Überall, wo Sie die write , die Sie in Ihrem Snippet erstellt haben, einfügen möchten, fügen Sie den Code zum Erstellen und Senden der Intent mit dem LocalBroadcastManager ein.