Android Thread vs AsyncTask vs IntentService called from BLE onCharacteristicChanged()

為{幸葍}努か 提交于 2019-12-07 23:31:36

问题


I have an Android app from which I receive BLE data (every 62ms via notifications). The app can save data via a BufferedWriter to a file. Upon each onCharacteristicChanged() callback, I call either an AsyncTask, Thread or an IntentService to do a file write if the user enabled file save.

The AsyncTask seems to work fine. But the docs say execute must be invoked on the UI thread, and I'm calling it from the BLE callback. Is that a problem? And how should I fix it?

Using Thread causes this error: GKI_exception out of buffers https://code.google.com/p/android/issues/detail?id=65455 (except my code is not scanning but receiving notifications) and if the file save is long, I need to power cycle the Nexus 7 (the app and BLE become totally unresponsive). Why does the Thread not work and how can I fix it?

The IntentService never goes to the onHandleIntent(). What are the issues here?

Here is some code:

...
_context = this.getApplicationContext();
...
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
...
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
...
int mode = 1;
if (mode==0) // Asynctask
    new doFileWriteTask().execute(strBuild.toString());
else if (mode==1)     // Thread
{
    final String str = strBuild.toString();
    new Thread(new Runnable() {
        public void run() {
           try {
               _writer.write(str);
           } catch (Exception e) {
               e.printStackTrace();
           }
        }
    }).start();
}
else if (mode==2)   // intentService
{
    Intent mServiceIntent = new Intent(_context, writeFileService.class);
    mServiceIntent.putExtra("foo", strBuild.toString());
    startService(mServiceIntent);
}
}
...
};

private class doFileWriteTask extends AsyncTask<String, Void, Void> {
@Override
protected Void doInBackground(String... strings) {
    try {
        _writer.write(strings[0]);              
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

private class writeFileService extends IntentService {
    public writeFileService() {
        super("writeFileService");
    }

    @Override
    protected void onHandleIntent(Intent workIntent) {
        String dataString = workIntent.getStringExtra("foo"); 
        try {
            _writer.write(dataString);
        } catch (Exception e) {
           e.printStackTrace();
        }           
    }
}
...

回答1:


But the docs say execute must be invoked on the UI thread, and I'm calling it from the BLE callback. Is that a problem? And how should I fix it?

The framework triggers the AsyncTask callback methods on the same thread it was called from (presumed to be the main thread). It doesn't really affect the background work, but you could see problems if you started trying to use onPostExecute() and the like. AsyncTask probably isn't the best choice to be called from a thread that you don't have control over.

Why does the Thread not work and how can I fix it?

I can't say exactly why you are still seeing errors, through spawning a series of private unsynchronized threads will probably lead to other headaches. If you want to use a single worker thread, a better choice would be to use a single HandlerThread that you can post to from your event callbacks using a Handler, something like:

…
_workerThread = new HandlerThread("Worker");
_workerThread.start();
_handler = new Handler(_workerThread.getLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            String str = (String) msg.obj;
            _writer.write(str);

            return true;
        }
});
…

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    …
    Message msg = Message.obtain(_handler, 0, strBuild.toString());
    _handler.sendMessage(msg);
    …
}

That solution is quite a bit more code, but given the frequency of writes this is probably the most efficient choice.

The IntentService never goes to the onHandleIntent(). What are the issues here?

You should pretty much never implement a top level Android component (activity, service, content provider, receiver) as an inner class, because they have to be declared in your manifest as well (and the XML syntax for inner classes is ugly). If your service does not have a matching entry in the manifest, then you will never see it start. You might want to have a look at the docs on using services.

At a minimum, a Service written as an inner class must be public static to work. Otherwise the framework cannot see it and cannot instantiate it using a default constructor (non-static inner classes mess with the constructor). Unless you are calling startService() inside of a try/catch right now, I'm surprised it isn't crashing when you attempt this.

IntentService is probably the simplest of your three choices because it is the most decoupled and the framework will handle queueing up work and tearing down the threads when all the incoming work is done.



来源:https://stackoverflow.com/questions/28654576/android-thread-vs-asynctask-vs-intentservice-called-from-ble-oncharacteristiccha

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!