如何在Android Studio中通过蓝牙发送/接收消息

3
我正在尝试创建一个应用程序,允许从一个Android手机发送字符串到另一个Android手机。以下是此代码。但是,它无法正常工作,因为在pairDevice()部分的try catch代码块中一直出现异常。有谁知道我可能为什么会遇到这种情况吗?
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.ParcelUuid;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Scanner;
import java.util.Set;

public class MainActivity extends AppCompatActivity {

    InputStream inStream;
    OutputStream outputStream;
    private static final int REQUEST_ENABLE_BT = 1;

    public void pairDevice() {
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter != null && !bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new 
            Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}

        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            Object[] devices = pairedDevices.toArray();
            BluetoothDevice device = (BluetoothDevice) devices[0];
            ParcelUuid[] uuid = device.getUuids();
            try {
                BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(uuid[0].getUuid());
                socket.connect();
                Toast.makeText(this, "Socket connected", Toast.LENGTH_LONG).show();
                outputStream = socket.getOutputStream();
                inStream = socket.getInputStream();
            } catch (IOException e) {
                Toast.makeText(this, "Exception found", Toast.LENGTH_LONG).show();
            }

        }
    }


public void SendMessage(View v) {
    EditText outMessage = (EditText) findViewById(R.id.editText);
    try {
        if (outputStream != null)
            outputStream.write(outMessage.toString().getBytes());
            TextView displayMessage = (TextView) findViewById(R.id.textView);
            Scanner s = new Scanner(inStream).useDelimiter("\\A");
            displayMessage.setText(s.hasNext() ? s.next() : "");
    } catch (IOException e) {/*Do nothing*/}
    Toast.makeText(this,"No output stream", Toast.LENGTH_LONG).show();
}





@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    pairDevice();
}

}

3个回答

9
我对您的应用做了一些更改: 首先,我将负责创建蓝牙连接的代码转移到了ConnectThread中。
其次,我添加了AcceptThread来监听传入的连接,以及ConnectedThread用于维护BTConnection、发送数据以及通过输入/输出流接收传入数据。 最后,我创建了两个按钮来启动ConnectThread和AcceptThread。
请注意:确保两个设备都已配对,您要连接的设备位于列表顶部(或从两个设备中删除所有已配对的设备,并只配对您要连接的设备)。此外,在启动ConnectThread之前,必须启动AcceptThread。 MAINACTIVITY.JAVA
public class MainActivity extends AppCompatActivity {

    private static final UUID MY_UUID_INSECURE =
            UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");

    private static final int REQUEST_ENABLE_BT = 1;
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    private BluetoothDevice mmDevice;
    private UUID deviceUUID;
    ConnectedThread mConnectedThread;
    private Handler handler;

    String TAG = "MainActivity";
    EditText send_data;
    TextView view_data;
    StringBuilder messages;






    public void pairDevice(View v) {

        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        Log.e("MAinActivity", "" + pairedDevices.size() );
        if (pairedDevices.size() > 0) {
            Object[] devices = pairedDevices.toArray();
            BluetoothDevice device = (BluetoothDevice) devices[0];
            //ParcelUuid[] uuid = device.getUuids();
            Log.e("MAinActivity", "" + device );
            //Log.e("MAinActivity", "" + uuid)

            ConnectThread connect = new ConnectThread(device,MY_UUID_INSECURE);
            connect.start();

        }
    }

    private class ConnectThread extends Thread {
        private BluetoothSocket mmSocket;

        public ConnectThread(BluetoothDevice device, UUID uuid) {
            Log.d(TAG, "ConnectThread: started.");
            mmDevice = device;
            deviceUUID = uuid;
        }

        public void run(){
            BluetoothSocket tmp = null;
            Log.i(TAG, "RUN mConnectThread ");

            // Get a BluetoothSocket for a connection with the
            // given BluetoothDevice
            try {
                Log.d(TAG, "ConnectThread: Trying to create InsecureRfcommSocket using UUID: "
                        +MY_UUID_INSECURE );
                tmp = mmDevice.createRfcommSocketToServiceRecord(MY_UUID_INSECURE);
            } catch (IOException e) {
                Log.e(TAG, "ConnectThread: Could not create InsecureRfcommSocket " + e.getMessage());
            }

            mmSocket = tmp;

            // Make a connection to the BluetoothSocket

            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                mmSocket.connect();

            } catch (IOException e) {
                // Close the socket
                try {
                    mmSocket.close();
                    Log.d(TAG, "run: Closed Socket.");
                } catch (IOException e1) {
                    Log.e(TAG, "mConnectThread: run: Unable to close connection in socket " + e1.getMessage());
                }
                Log.d(TAG, "run: ConnectThread: Could not connect to UUID: " + MY_UUID_INSECURE );
            }

            //will talk about this in the 3rd video
            connected(mmSocket);
        }
        public void cancel() {
            try {
                Log.d(TAG, "cancel: Closing Client Socket.");
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "cancel: close() of mmSocket in Connectthread failed. " + e.getMessage());
            }
        }
    }

    private void connected(BluetoothSocket mmSocket) {
        Log.d(TAG, "connected: Starting.");

        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(mmSocket);
        mConnectedThread.start();
    }

    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "ConnectedThread: Starting.");

            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;



            try {
                tmpIn = mmSocket.getInputStream();
                tmpOut = mmSocket.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run(){
            byte[] buffer = new byte[1024];  // buffer store for the stream

            int bytes; // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs
            while (true) {
                // Read from the InputStream
                try {
                    bytes = mmInStream.read(buffer);
                    final String incomingMessage = new String(buffer, 0, bytes);
                    Log.d(TAG, "InputStream: " + incomingMessage);

                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            view_data.setText(incomingMessage);
                        }
                    });


                } catch (IOException e) {
                    Log.e(TAG, "write: Error reading Input Stream. " + e.getMessage() );
                    break;
                }
            }
        }


        public void write(byte[] bytes) {
            String text = new String(bytes, Charset.defaultCharset());
            Log.d(TAG, "write: Writing to outputstream: " + text);
            try {
                mmOutStream.write(bytes);
            } catch (IOException e) {
                Log.e(TAG, "write: Error writing to output stream. " + e.getMessage() );
            }
        }

        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }


    public void SendMessage(View v) {
        byte[] bytes = send_data.getText().toString().getBytes(Charset.defaultCharset());
        mConnectedThread.write(bytes);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        send_data =(EditText) findViewById(R.id.editText);
        view_data = (TextView) findViewById(R.id.textView);

        if (bluetoothAdapter != null && !bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new
                    Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }


    }

    public void Start_Server(View view) {

        AcceptThread accept = new AcceptThread();
        accept.start();

    }

    private class AcceptThread extends Thread {

        // The local server socket
        private final BluetoothServerSocket mmServerSocket;

        public AcceptThread(){
            BluetoothServerSocket tmp = null ;

            // Create a new listening server socket
            try{
                tmp = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("appname", MY_UUID_INSECURE);

                Log.d(TAG, "AcceptThread: Setting up Server using: " + MY_UUID_INSECURE);
            }catch (IOException e){
                Log.e(TAG, "AcceptThread: IOException: " + e.getMessage() );
            }

            mmServerSocket = tmp;
        }

        public void run(){
            Log.d(TAG, "run: AcceptThread Running.");

            BluetoothSocket socket = null;

            try{
                // This is a blocking call and will only return on a
                // successful connection or an exception
                Log.d(TAG, "run: RFCOM server socket start.....");

                socket = mmServerSocket.accept();

                Log.d(TAG, "run: RFCOM server socket accepted connection.");

            }catch (IOException e){
                Log.e(TAG, "AcceptThread: IOException: " + e.getMessage() );
            }

            //talk about this is in the 3rd
            if(socket != null){
                connected(socket);
            }

            Log.i(TAG, "END mAcceptThread ");
        }

        public void cancel() {
            Log.d(TAG, "cancel: Canceling AcceptThread.");
            try {
                mmServerSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "cancel: Close of AcceptThread ServerSocket failed. " + e.getMessage() );
            }
        }

    }

ACTIVITY_MAIN.XML

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.hpi5.bluethoothshot.MainActivity"
    tools:layout_editor_absoluteY="81dp"
    tools:layout_editor_absoluteX="0dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/textView"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintRight_creator="1"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="58dp"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintRight_creator="1"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="153dp"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:onClick="SendMessage"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintRight_creator="1"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="22dp"
        app:layout_constraintTop_toBottomOf="@+id/editText"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Server"
        android:onClick="Start_Server"
        android:layout_marginEnd="53dp"
        tools:layout_constraintRight_creator="1"
        tools:layout_constraintBottom_creator="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginBottom="84dp" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ConnectionREq"
        android:onClick="pairDevice"
        android:layout_marginStart="34dp"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintBottom_creator="1"
        app:layout_constraintBottom_toBottomOf="@+id/button2"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@+id/button2"
        android:layout_marginLeft="30dp"
        app:layout_constraintVertical_bias="0.0" />

</android.support.constraint.ConstraintLayout>

非常感谢!应用程序完美运行,我一直在网上搜寻教程,你的答案是迄今为止唯一有效的。还有一个问题,这个方法适用于任何蓝牙设备吗(例如苹果到苹果或安卓到苹果),还是只能从安卓到安卓? - Joshua Payapulli
在iOS(iPhone、iPad、iPod等设备的操作系统)下本地运行Android应用程序是不可能的 - ray an
我收到了这个错误:尝试在空对象引用上调用虚拟方法'void com.example.projectnetwerken.MainActivity$ConnectedThread.write(byte[])' - Daniëlle
谢谢@ray an,这是我找到的第一个示例,展示了最小运行程序中的完整过程 - Google的指南缺少很多信息。 - Michael Fehr

2

异常描述肯定会有帮助,但我99%确定系统会阻止您在UI线程中执行套接字连接代码 - 因此,您需要创建一个新的线程,将套接字创建和套接字连接代码移动到该线程中,并最终创建一个回调函数,指示监听器连接是否已执行或失败。

请注意,Android会阻止大多数尝试在UI线程内进行网络相关的耗时操作,因为它会使UI变得非常缓慢 ;-)


2

两个主要问题-

1) connect() 是一个阻塞调用,你应该总是在与主活动(UI)线程分离的线程中执行此连接过程。你正在主线程上执行这个操作。

注意:在调用 connect() 之前,您应该始终调用 cancelDiscovery() 来确保设备没有正在执行设备发现。如果发现正在进行,则连接尝试会显著减慢,并且更有可能失败。

2) 如果你在第二个设备上使用相同的代码(以便你可以发送或接收数据),那么我看不到任何对 accept() 的调用。 accept() 监听连接请求。 同样,accept() 调用是一个阻塞调用,不应该在主活动 UI 线程中执行,以便您的应用程序仍然可以响应其他用户交互。

简化的服务器组件线程,用于接受传入连接:

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() { 
        // Use a temporary object that is later assigned to mmServerSocket 
        // because mmServerSocket is final. 
        BluetoothServerSocket tmp = null;
        try { 
            // MY_UUID is the app's UUID string, also used by the client code. 
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "Socket's listen() method failed", e);
        } 
        mmServerSocket = tmp;
    } 

    public void run() { 
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned. 
        while (true) { 
            try { 
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                Log.e(TAG, "Socket's accept() method failed", e);
                break; 
            } 

            if (socket != null) {
                // A connection was accepted. Perform work associated with 
                // the connection in a separate thread. 
                manageMyConnectedSocket(socket);
                mmServerSocket.close();
                break; 
            } 
        } 
    } 

    // Closes the connect socket and causes the thread to finish. 
    public void cancel() { 
        try { 
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Could not close the connect socket", e);
        } 
    } 
} 

Android文档 - 蓝牙


感谢您详细的回复。我已经将您的建议实现到我的程序中。然而,每当我点击发送按钮时,应用程序就会崩溃。通过多次运行应用程序,我知道这是由于以下两行代码引起的问题:Scanner s = new Scanner(inStream).useDelimiter("\A"); displayMessage.setText(s.hasNext() ? s.next() : ""); 您能否提供一些代码,从输出流中获取文本并将其设置为TextView上的文本,或者解释一下为什么会出现这种情况? - Joshua Payapulli

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