Android蓝牙连接工具

时间:2016-07-18 13:31:03

标签: java android multithreading bluetooth

我的应用程序可以连接到蓝牙设备并读取数据,但是当蓝牙脱机时,我的应用程序将冻结屏幕,直到找到设备或超时。以下是主要功能:

的Manifest.xml

    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".MyGatewayService"
            android:enabled="true"
            android:exported="true"></service>
    </application>

</manifest>

BluetoothManager.java

public class BluetoothManager {

        private static final String TAG = BluetoothManager.class.getName();
        private static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

        public static BluetoothSocket connect(BluetoothDevice dev) throws IOException{

            BluetoothSocket sock = null;
            BluetoothSocket socketFallback = null;
            Log.d(TAG,"Start Bluetooth Connection...");
            try
            {
                sock = dev.createRfcommSocketToServiceRecord(myUUID);
                Log.d(TAG, "Probably gonna wait here...");
                sock.connect();
            }catch (Exception e1){
                Log.e(TAG, "There was an error while establishing Bluetooth connection, Failing back...", e1);

            }
            return sock;
        }

    }

MyGateWayService.java

public class MyGatewayService extends AbstractGatewayService{
    private static final String TAG = MyGatewayService.class.getName();
    private BluetoothDevice dev = null;
    private BluetoothSocket sock = null;

@Override
protected void onHandleIntent(Intent intent) {
      //what should I put in here?
}

    @Override
    public void startService() throws IOException {
        final String remoteDevice = getSharedPreferences(Constants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).getString(Constants.SHARED_PREFERENCES_BLUETOOTH_SELECTION_ADDRESS_KEY, "");

        if (remoteDevice == null||"".equals(remoteDevice)){
            Toast.makeText(getApplicationContext(),"No Bluetooth device selected...",Toast.LENGTH_SHORT).show();
            Log.e(TAG,"No Bluetooth device selected...");
            stopService();
            throw new IOException();
        }else{
            final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
            dev = btAdapter.getRemoteDevice(remoteDevice);
            Log.d(TAG,"Stop bluetooth discovery...");
            btAdapter.cancelDiscovery();
            Log.d(TAG,"Start Service..");
            try{
                startServiceConnection();
            }catch (Exception e){
                Log.e(TAG, "There was an error while establishing connection..." + e.getMessage());



           stopService();
            throw new IOException();
        }
    }
}

    private void startServiceConnection() throws IOException, InterruptedException {
        Log.d(TAG, "Start the connection");
        isRunning = true;

        try{
            sock = com.ibm.us.wuxiaosh.androidbluetoothdemo.BluetoothManager.connect(dev);
        }catch (Exception e2){
            Log.e(TAG, "There was an error while connecting... stop...");
            stopService();
            throw new IOException();
        }


    }

    @Override
    protected void executeQueue(){
        Log.d(TAG,"Executing...");
        while(!Thread.currentThread().isInterrupted()){
            //Log.d(TAG,"Executing ....................");
        }
    }
    @Override
    public void stopService() {
        isRunning = false;

        if (sock!=null){
            try{
                sock.close();
            }catch (IOException e){
                Log.e(TAG, e.getMessage());
            }
            stopSelf();
        }
    }
}

Mainfunction:

@Override
        public void onServiceConnected(ComponentName className, IBinder binder) {
            Log.d(TAG, className.toString() + " service is bound");
            isServiceBound = true;
            service = ((AbstractGatewayService.AbstractGatewayServiceBinder) binder).getService();
            service.setContext(MainActivity.this);
            Log.d(TAG, "Starting live data");

            try{
                service.startService();

                Log.d(TAG , "Connected");


            } catch (IOException ioe){
                Log.e(TAG, "Failure Starting Live Data");
                doUnbindService();
            }

        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        @Override
        public void onServiceDisconnected(ComponentName className) {
            Log.d(TAG, " service is unbound");
            isServiceBound = false;
        }
    };




    private void doBindService(){
        if(!isServiceBound){
            Log.d(TAG, "Binding OBD Service..");

            Log.e(TAG,"start intent 1");
            Intent serviceIntent = new Intent(this, MyGatewayService.class);
            Log.e(TAG,"intent finished");
            bindService(serviceIntent,serviceConn, Context.BIND_AUTO_CREATE);
            Log.e(TAG,"bindService");
        }
    }

    private void doUnbindService(){
        if(isServiceBound){
            if (service.isRunning()){
                service.stopService();

                Log.d(TAG,"Ready");
            }
            Log.e(TAG, "Unbinding OBD Service...");
            unbindService(serviceConn);
            isServiceBound = false;
            Log.e(TAG, "Disconnected");
        }
    }

更新: 另一个文件:AbstractGateWayService&amp;将其更改为扩展intentService

public abstract class AbstractGatewayService extends IntentService{
    private static final String TAG = AbstractGatewayService.class.getName();
    private final IBinder binder = new AbstractGatewayServiceBinder();
    protected Context ctx;
    protected boolean isRunning = false;

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            Log.e(TAG, "Thread Run...");
                long futureTime = System.currentTimeMillis()+10000;
                while(System.currentTimeMillis()<futureTime) {
                    executeQueue();
                }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "Creating Service...");
        t.start();
        Log.d(TAG, "Service Creating...");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"Destroying service");
        t.interrupt();
        Log.d(TAG,"Service Destroyed");
    }

    class AbstractGatewayServiceBinder extends Binder {
        public AbstractGatewayService getService(){
            return AbstractGatewayService.this;
        }
    }
    public boolean isRunning() {
        return isRunning;
    }

    public void setContext(Context c) {
        ctx = c;
    }

    abstract protected void executeQueue();

    abstract public void startService() throws IOException;

    abstract public void stopService();
}

当我按下连接按钮时,应用程序将初始化服务,但它也会冻结主线程上的动画,即使我明确地将它放在一个单独的线程中。基于logcat,我认为sock.connect()文件中的BluetoothManager.java功能正在等待回调。我该如何实施以确保一切都在后台运行?

我还将所有代码放在here

欢迎任何帮助!感谢

1 个答案:

答案 0 :(得分:2)

您的问题是您在startService()方法中调用Socket连接逻辑(正如您正确扣除的那样,是阻塞调用)。 Android中的Services,虽然在逻辑上与应用程序的主流分开,但默认情况下在同一Thread上运行。通过在服务中调用阻塞调用,可以阻止UI线程。

您有多种解决方案可供选择:

  • 使用IntentService代替ServiceIntentService有自己的处理程序,并在单独的线程上运行。
  • 将逻辑保留在当前点,但将其包装在AsyncTask中。 重要:您需要包装逻辑本身,而不是Service - 启动逻辑。
  • 完全放弃Service部分,只需将逻辑放入AsyncTask

由您自己决定,IntentService解决方案似乎最适合更复杂的应用,而创建AsyncTask将是最快捷,最直接的方法: - )