配对(连接)到新设备或配对在应用程序中不起作用

Pairing (Connecting) to a new device or paired does not work in app

我正在创建一个 android 应用程序来接收传感器数据。我能够发现 phone 上的配对设备以及附近可用的蓝牙设备。问题是当我从配对列表或发现的设备列表中单击其中一个设备时,我无法连接到该设备。

代码:

package com.example.Pillwoah;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.util.Set;
import java.util.UUID;

public class MainActivity4 extends AppCompatActivity {

    TextView phoneName, deviceList;

    private BluetoothAdapter BA;
    private Set<BluetoothDevice> pairedDevices;
    private ArrayAdapter<String> BAarray, BANewArray;
    private ListView DeviceList, NewDevice;

    private BluetoothSocket BTSocket = null;
    private BluetoothDevice device = null;

    private static final UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private final static int REQUEST_ENABLE_BT = 1;
    private final static int REQUEST_COARSE_LOCATION = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);

        Button mEnable = (Button) findViewById(R.id.enable);
        Button mOff = (Button) findViewById(R.id.off);
        Button mPaired = (Button) findViewById(R.id.paired);
        Button mDiscover = (Button) findViewById(R.id.discover);

        deviceList = findViewById(R.id.deviceTitle);
        deviceList.setText(null);
        phoneName = findViewById(R.id.name);
        phoneName.setText(getLocalBtName());

        BA = BluetoothAdapter.getDefaultAdapter();

        BAarray = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
        BANewArray = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);

        DeviceList = (ListView)findViewById(R.id.deviceListView);
        DeviceList.setAdapter(BAarray);
        DeviceList.setOnItemClickListener(DeviceClickListener);

        NewDevice = (ListView)findViewById(R.id.newDeviceView);
        NewDevice.setAdapter(BANewArray);
        NewDevice.setOnItemClickListener(DeviceClickListener);

        if (BA == null) {
            Toast.makeText(this, "Bluetooth not supported", Toast.LENGTH_SHORT).show();
            finish();
        } else {
            mEnable.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bluetoothOn(v);
                }
            });

            mOff.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bluetoothOff(v);
                }
            });

            mPaired.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkLocationPermission();
                    listPairedDevices(v);
                }
            });

            mDiscover.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkLocationPermission();
                    discover(v);
                }
            });
        }
    };

    protected void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_COARSE_LOCATION);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_COARSE_LOCATION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //discover(view); // --->
                } else {
                    //TODO re-request
                }
                break;
            }
        }
    }

    public String getLocalBtName(){
        if(BA == null){
            BA = BluetoothAdapter.getDefaultAdapter();
        }
        String name = BA.getName();
        if(name == null){
            name = BA.getAddress();
        }

        return name;
    }

    private void bluetoothOn(View view){
        if(!BA.isEnabled()){
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
            Toast.makeText(getApplicationContext(), "Bluetooth turned on", Toast.LENGTH_SHORT).show();
        }
        else{
            Toast.makeText(getApplicationContext(), "Bluetooth is already on", Toast.LENGTH_SHORT).show();
        }
    }

    private void bluetoothOff(View view){
        BA.disable();
        BAarray.clear();
        Toast.makeText(getApplicationContext(), "Bluetooth turned off", Toast.LENGTH_SHORT).show();
    }

    private void discover(View view) {
        deviceList.setText("Available Devices");
        if (BA.isDiscovering()) {
            BA.cancelDiscovery();
            deviceList.setText(null);
            BANewArray.clear();
            Toast.makeText(getApplicationContext(), "Discovery stopped", Toast.LENGTH_SHORT).show();
        }
        else {
            if (BA.isEnabled()) {
                Intent discoverIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 50);
                startActivity(discoverIntent);
                BANewArray.clear();
                BAarray.clear();
                BA.startDiscovery();
                Toast.makeText(getApplicationContext(), "Discovery started", Toast.LENGTH_SHORT).show();
                registerReceiver(BReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
            }
            else {
                Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private final BroadcastReceiver BReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if(BluetoothDevice.ACTION_FOUND.equals(action)){
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                BANewArray.add(device.getName() + "\n" + device.getAddress());
                BANewArray.notifyDataSetChanged();
            }
        }
    };

    private void listPairedDevices(View view){
        deviceList.setText("Previously Connected Devices");
        pairedDevices = BA.getBondedDevices();
        if(BA.isEnabled()){
            BANewArray.clear();
            BAarray.clear();
            for(BluetoothDevice device : pairedDevices)
                BAarray.add(device.getName() + "\n" + device.getAddress());

            Toast.makeText(getApplicationContext(), "Show paired devices", Toast.LENGTH_SHORT).show();
        }
        else{
            Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
        }
    }

    private AdapterView.OnItemClickListener DeviceClickListener = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {

            if(!BA.isEnabled()) {
                Toast.makeText(getBaseContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
                return;
            }

            String info = ((TextView) v).getText().toString();
            final String address = info.substring(info.length() - 17);
            final String name = info.substring(0,info.length() - 17);

            new Thread() {
                public void run() {
                    device = BA.getRemoteDevice(address);

                    try {
                        BTSocket = device.createRfcommSocketToServiceRecord(BTMODULEUUID);
                    } catch (IOException e) {
                        Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
                    }
                    // Establish the Bluetooth socket connection.
                    try {
                        BTSocket.connect();
                    } catch (IOException e) {
                        try {
                            BTSocket.close();
                        } catch (IOException e2) {
                            //insert code to deal with this
                            Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
                        }
                    }
                }

                public void cancel() {
                    try {
                        BTSocket.close();
                    } catch (IOException e3) {
                        Toast.makeText(getBaseContext(), "Could not close the client socket", Toast.LENGTH_SHORT).show();

                    }
                }
            }.start();
        }
    };
}

以下是与通过蓝牙连接到另一台设备相关的代码片段:

private AdapterView.OnItemClickListener DeviceClickListener = new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {

        if(!BA.isEnabled()) {
            Toast.makeText(getBaseContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
            return;
        }

        String info = ((TextView) v).getText().toString();
        final String address = info.substring(info.length() - 17);
        final String name = info.substring(0,info.length() - 17);

        new Thread() {
            public void run() {
                device = BA.getRemoteDevice(address);

                try {
                    BTSocket = device.createRfcommSocketToServiceRecord(BTMODULEUUID);

                    // this is a test message
                    Toast.makeText(getBaseContext(), "test", Toast.LENGTH_SHORT).show();
                } catch (IOException e) {
                    Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
                }
                // Establish the Bluetooth socket connection.
                try {
                    BTSocket.connect();
                } catch (IOException e) {
                    try {
                        BTSocket.close();
                    } catch (IOException e2) {
                        //insert code to deal with this
                        Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
                    }
                }
            }

            public void cancel() {
                try {
                    BTSocket.close();
                } catch (IOException e3) {
                    Toast.makeText(getBaseContext(), "Could not close the client socket", Toast.LENGTH_SHORT).show();

                }
            }
        }.start();
    }
};

这一行是我声明的UUID

private static final UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

我不确定哪里出了问题。谁能给我一些建议吗?

这是应用程序的堆栈跟踪:

2021-02-07 01:08:52.055 14096-14096/? I/xample.Pillwoa: Late-enabling -Xcheck:jni
2021-02-07 01:08:52.108 14096-14096/? E/xample.Pillwoa: Unknown bits set in runtime_flags: 0x8000
2021-02-07 01:08:52.118 14096-14096/? W/xample.Pillwoa: Core platform API violation: Ljava/lang/reflect/Field;->accessFlags:I from Landroid/os/Build; using reflection
2021-02-07 01:08:52.680 14096-14096/com.example.Pillwoah W/xample.Pillwoa: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (greylist, reflection, allowed)
2021-02-07 01:08:52.682 14096-14096/com.example.Pillwoah W/xample.Pillwoa: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (greylist, reflection, allowed)
2021-02-07 01:09:11.787 14096-14135/com.example.Pillwoah I/Adreno-EGL: <qeglDrvAPI_eglInitialize:379>: EGL 1.4 QUALCOMM build: Nondeterministic_AU_msm8974_LA.BF.1.1.3_RB1__release_AU (I741a3d36ca)
    OpenGL ES Shader Compiler Version: E031.29.00.00
    Build Date: 04/04/16 Mon
    Local Branch: mybranch19053788
    Remote Branch: quic/LA.BF.1.1.3_rb1.12
    Local Patches: NONE
    Reconstruct Branch: NOTHING
2021-02-07 01:09:11.803 14096-14135/com.example.Pillwoah W/Adreno-EGL: <qeglDrvAPI_eglGetConfigAttrib:607>: EGL_BAD_ATTRIBUTE
2021-02-07 01:09:11.816 14096-14135/com.example.Pillwoah W/Adreno-EGL: <qeglDrvAPI_eglGetConfigAttrib:607>: EGL_BAD_ATTRIBUTE
2021-02-07 01:09:11.883 14096-14135/com.example.Pillwoah W/Gralloc3: mapper 3.x is not supported
2021-02-07 01:09:13.306 14096-14096/com.example.Pillwoah W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@7040693
2021-02-07 01:09:13.543 14096-14135/com.example.Pillwoah W/Adreno-EGL: <qeglDrvAPI_eglGetConfigAttrib:607>: EGL_BAD_ATTRIBUTE
2021-02-07 01:09:16.076 14096-14096/com.example.Pillwoah W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@6df2233
2021-02-07 01:09:16.353 14096-14135/com.example.Pillwoah W/Adreno-EGL: <qeglDrvAPI_eglGetConfigAttrib:607>: EGL_BAD_ATTRIBUTE
2021-02-07 01:09:17.785 14096-14135/com.example.Pillwoah W/Adreno-EGL: <qeglDrvAPI_eglGetConfigAttrib:607>: EGL_BAD_ATTRIBUTE
2021-02-07 01:09:20.683 14096-14135/com.example.Pillwoah W/Adreno-EGL: <qeglDrvAPI_eglGetConfigAttrib:607>: EGL_BAD_ATTRIBUTE
2021-02-07 01:09:21.770 14096-14322/com.example.Pillwoah E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.example.Pillwoah, PID: 14096
    java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
        at android.widget.Toast$TN.<init>(Toast.java:410)
        at android.widget.Toast.<init>(Toast.java:124)
        at android.widget.Toast.makeText(Toast.java:289)
        at android.widget.Toast.makeText(Toast.java:279)
        at com.example.Pillwoah.MainActivity4.run(MainActivity4.java:233)
2021-02-07 01:09:21.938 14096-14322/com.example.Pillwoah I/Process: Sending signal. PID: 14096 SIG: 9

这是代码的更新部分:

private AdapterView.OnItemClickListener DeviceClickListener = new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {

        if(!BA.isEnabled()) {
            Toast.makeText(getBaseContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
            return;
        }

        String info = ((TextView) v).getText().toString();
        final String address = info.substring(info.length() - 17);
        final String name = info.substring(0,info.length() - 17);

        new Thread() {
            public void run() {
                device = BA.getRemoteDevice(address);

                try {
                    BTSocket = device.createRfcommSocketToServiceRecord(BTMODULEUUID);
                    getMainLooper().prepare();
                    Toast.makeText(getBaseContext(), "test", Toast.LENGTH_SHORT).show();
                    getMainLooper().loop();
                } catch (IOException e) {
                    getMainLooper().prepare();
                    Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
                    getMainLooper().loop();
                }
                // Establish the Bluetooth socket connection.
                try {
                    BTSocket.connect();
                    getMainLooper().prepare();
                    Toast.makeText(getBaseContext(), "test 2", Toast.LENGTH_SHORT).show();
                    getMainLooper().loop();
                } catch (IOException e) {
                    try {
                        BTSocket.close();
                        getMainLooper().prepare();
                        Toast.makeText(getBaseContext(), "test 3", Toast.LENGTH_SHORT).show();
                        getMainLooper().loop();
                    } catch (IOException e2) {
                        //insert code to deal with this
                        getMainLooper().prepare();
                        Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
                        getMainLooper().loop();
                    }
                }
            }

            public void cancel() {
                try {
                    BTSocket.close();
                } catch (IOException e3) {
                    getMainLooper().prepare();
                    Toast.makeText(getBaseContext(), "Could not close the client socket", Toast.LENGTH_SHORT).show();
                    getMainLooper().loop();
                }
            }
        }.start();
    }
};

程序在主线程创建handler后创建了一个looper对象,子线程没有。因此,在使用Toast之前需要先执行Looper.prepare(),然后再执行Looper.loop();使用 Toast 后。

 {
Looper.prepare();
Toast.makeText(getBaseContext(), "test", Toast.LENGTH_SHORT).show();
Looper.loop();
}