2016-07-18 5 views
1

Мое приложение может подключаться к устройству blutooth и считывать данные, но когда Bluetooth отключен, мое приложение заморозит экран, пока не найдет устройство или тайм-аут. Вот основные функции:Приложение для подключения Bluetooth для Android

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

UPDATE: другой файл: AbstractGateWayService & изменил это расширяет 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() funtion в файле BluetoothManager.java ожидает обратного вызова. Как я должен реализовать, чтобы убедиться, что все работает в фоновом режиме?

Я также положить весь код в here

Любая помощь Добро пожаловать! Спасибо

ответ

2

Ваша проблема заключается в том, что вы вызываете логику соединения сокета (которая, как вы правильно вычитаете, является блокирующим вызовом) в методе startService(). Services в Android, логически отделенный от основного потока приложения, запускается на том же Thread по умолчанию. Вызывая блокирующий вызов в службе, вы блокируете поток пользовательского интерфейса.

У вас есть несколько решений на выбор:

  • Использование IntentService вместо Service. IntentService имеет свой собственный Обработчик и работает на отдельной теме.
  • Оставьте логику в текущей точке, но оберните ее в AsyncTask. ВАЖНО: вам нужно обернуть сама логику, а не Service -направляющая логика.
  • Отбросьте Service часть вообще и просто поставить логику в AsyncTask

Это до вас, что вы выбрали, IntentService решение кажется лучшим для более сложного приложения, при создании AsyncTask будет самым быстрым и наиболее прямолинейный подход :-)

+0

Привет @Kelevandos Спасибо за ваш ответ, я думаю, что буду использовать IntentService, я также обновил свое сообщение, чтобы включить мои изменения и добавил абстрактный класс сервиса, так как я уже реализовал startService(), должен ли я переместить его в onHandleIntent? Я хочу знать, что мне следует включить в метод onHandleIntent. Спасибо – Deidara

+0

Вы можете попробовать переместить всю логику и проверить, не сработает ли она.В ваших примерах много кода, поэтому я не могу сказать, будет ли это без более широкого контекста :-) Кроме того, что касается установки IntentService, вы можете создать и использовать обработчик с Looper вместо пользовательского Thread. – Kelevandos

+1

Спасибо. Я использовал AsyncTask для решения проблемы. – Deidara