Best option to implement Networking Class - android

I am starting a project that will access a Xmpp server in the background.
It will maintain the connection alive and reconnect when required + do other Xmpp things.
I want to implement a class to do the work.
The class will have to interact with other Services (Location...), and BroadcastReceivers (CONNECTIVITY_CHANGE ....).
Basically, Activities and Broadcast receivers will ask the Xmpp Class to start an action like: CONNECT, DISCONNECT, RECONNECT, JOIN CHAT, SEND MESSAGE etc.
First approach is to implement it as a Service but a service runs in the main thread so the implementation is wrong.
Secondly, I wanted to make it as an IntentService because the onHandleIntent is run async and then I am out of the main thread.
But the onHandleIntent is run only once to perform an async task. So,
if I want an Activity to perform another "action", I can only send a broadcast event and I will fall in the main thread problem again.
Also, IntentService is not really aimed to be 'live' all the time.
In the Google documentation, they say you need to run AsyncTask for each network access ... is this the only way to make a network access ... this is pretty sad.
I had a look on the implementation in the GTalkSMS and they seemed to have the same problem. Actually they use a Service with a ServiceHandler management like this:
// some stuff for the async service implementation - borrowed heavily from
// the standard IntentService, but that class doesn't offer fine enough
// control for "foreground" services.
private static volatile Looper sServiceLooper;
private static volatile ServiceHandler sServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
onHandleIntent((Intent) msg.obj, msg.arg1);
}
}

Well, it seems the only way to do so is to create a service that has its own thread.
Vogella website describes a way to setup the Service in the AndroidManifest:
"4. Services in separate processes"
<service
android:name="WordService"
android:process=":my_process"
android:icon="#drawable/icon"
android:label="#string/service_name"
>
</service>
The alternative is to do a Service Handler manually as I described in my initial post like this:
public class XmppService extends Service {
public final static String ACTION_CONNECT = "action.CONNECT";
public final static String ACTION_DISCONNECT = "action.DISCONNECT";
// some stuff for the async service implementation - borrowed heavily from
// the standard IntentService, but that class doesn't offer fine enough
// control for "foreground" services.
private static volatile Looper sServiceLooper;
private static volatile ServiceHandler sServiceHandler;
private long mHandlerThreadId;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(android.os.Message msg) {
onHandleIntent((Intent) msg.obj, msg.arg1);
}
}
/**
* The IntentService(-like) implementation manages taking the intents passed
* to startService and delivering them to this function which runs in its
* own thread
*
* #param intent
* #param id
*/
void onHandleIntent(final Intent intent, int id) {
// ensure XMPP manager is setup (but not yet connected)
if (Thread.currentThread().getId() != mHandlerThreadId) {
throw new IllegalThreadStateException();
}
String action = intent.getAction();
if(action.equals(XmppService.ACTION_CONNECT)){
// Do Connect
}
else if(action.equals(XmppService.ACTION_DISCONNECT)){
// Do Disconnect
}
}
#Override
public void onCreate() {
super.onCreate();
// Start a new thread for the service
HandlerThread thread = new HandlerThread(SERVICE_THREAD_NAME);
thread.start();
mHandlerThreadId = thread.getId();
sServiceLooper = thread.getLooper();
sServiceHandler = new ServiceHandler(sServiceLooper);
}
}

Related

Is there a way for the client application to kill a running request without destroying the service and losing its associated context?

I use a bound service to run cpu-intensive tasks in the background as recommended to avoid ANR. The client application sends messages to the bound service that handles them in the handleMessage() method of its Handler.
As some requests can take very long to answer, I want to offer the user the ability to "abort/abandon" a running request. However, I can not destroy the service because I need to keep its context for future requests. Sending an "abort" request from the application will be queued and handled by the service after it will have completed its current task, which is obviously too late and not implementing the requested functionality.
Is there a way for the client application to kill a running request without killing the service and losing its associated context?
* EDIT *
Thanks to Mario's recommendation and this link, here is the solution I have implemented:
ExecutorService executor = Executors.newFixedThreadPool(1);
private volatile static Future<?> BackgroundSolveFuture = null;
public class IncomingHandler extends Handler {
#Override
public void handleMessage(Message message) {
msg = message;
msgArg1 = message.arg1;
msgWhat = message.what;
if (message.replyTo != null) {
mClient = new Messenger(message.replyTo.getBinder());
}
switch (msgWhat) {
case MSG_ABORT:
BackgroundSolveFuture = null;
break;
case MSG_SOLVE:case MSG_ANALYZE:case MSG_SUGGEST:case MSG_STEP:
BackgroundSolveFuture = executor.submit(new Runnable() {
public void run() {
solve();
}
});
break;
...
Inside solve() I regularly check whether BackgroundSolveFuture has been set to null:
if (BackgroundSolveFuture == null) {
undoAction(true);
return;
}
yes.
your idea with sending abort message is ok.
you just need create new thread for each task.
For example you have two types of messages DOIT and ABORT, when you get message DOIT you create and run new thread with task, you can even keep reference to this thread.
This allows finish quickly a handleMessage() method.
Then new message come: ABORT, you have reference to thread and you can interrupt the thread/task.

Prevent multiple instances of Service

I'm running an Android Service which is run every 3 seconds.
Many times, the working within the Service class is unable to complete in 3 seconds, but a new instance is launched anyways.
I want to avoid multiple instances. How can I do this?
My code is as follows:
public void task() {
handler.postDelayed(new Runnable() {
#Override
public void run() {
startConnection();
handler.postDelayed(this, 3000);
}
}, 3000);
}
I can think of a couple of way to deal with this:
Is there a way I can check if an instance is already running? If yes, then I can skip the loop.
Is there any inbuilt functionality to prevent running multiple instances?
Another option is to set & reset a flag & check flag status.
May be there are better ways to do this?
What is the best approach?
Thanks
Use an IntentService. From the page:
All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
An IntentService a little like this will "expire" after SERVICE_EXPIRE_TIME_MILLIS has passed, and skip to the next request. (Thought it might be useful for your particular example).
public class MyIntentService extends IntentService {
private static final String ACTION_FOO = "bubblebearapps.co.uk.scratchpad.action.FOO";
// TODO: Rename parameters
private static final String EXTRA_PAREM_TTL = "bubblebearapps.co.uk.scratchpad.extra.TTL";
private static final long SERVICE_EXPIRE_TIME_MILLIS = 3000;
/**
* Starts this service to perform action Foo with the given parameters. If
* the service is already performing a task this action will be queued.
*
* #see IntentService
*/
// TODO: Customize helper method
public static void startActionFoo(Context context) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_FOO);
intent.putExtra(EXTRA_PAREM_TTL, System.currentTimeMillis() + SERVICE_EXPIRE_TIME_MILLIS);
context.startService(intent);
}
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
final long param1 = intent.getLongExtra(EXTRA_PAREM_TTL, 0);
handleActionFoo(param1);
}
}
}
private void handleActionFoo(long expiryTime) {
if(System.currentTimeMillis() > expiryTime){
//skip work and return
}
}
}

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

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();
}
}
}
...
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.

How handler classes work in Android

I am new to android and was reading the demo applications on official android website. And I came across a method of Handler class named as postDelayed(Runnable r, long milliseconds).
Can anybody please explain what this method does ?
You can see the documentation.
But to understand the docs, you should first understand several concepts: Message, Message Queue, Handler and Looper, and their relationship.
The following illustrates how Looper works, it shows that the looper is a thread local object and its relationship with MessageQueue:
class Looper{
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
}
Several remarks:
Looper is a thread local object such that every thread has one looper. Every looper is associated with a message queue. The looper continously get messagese("tasks", "commands" or whatever you like to call them) from the queue, and dispatch the message to its target, which is a handler to handle that messag(e.g. by calling back a Runnable contained in the message). When there are no messages left in the queue, the thread blocks until there are new messages. To stop a Looper, you have to call quit() on it (which probably does not stop the loop immediately, but rather sets a private flag that is checked periodically from the loop, signaling the it to stop).
Android framework provides the Handler class to simplify things. When you create a Handler instance, it is (by default) bound to the Looper already attached to the current thread. (The Handler knows what Looper to attach to because we called prepare() earlier, which stored a reference to the Looper in a ThreadLocal.)
With a Handler, you can just call post() to "put a message into the thread's message queue" (so to speak). The Handler will take care of all the IdleHandler callback stuff and make sure your posted Runnable is executed. (It might also check if the time is right already, if you posted with a delay.)
The following code shows the typical ways we use them.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Handler is widely used in Android services. Android support inter application communication. Typically when we implement a service, which doesn't need to handle multithreading, we implements a Handler that receives a callback for each call from a client. Then create a Messenger object (reference to the Handler), which is a Binder object and return this object to clients when they bind this service. So the client can use this Messenger to send messages (into the thread-local queue, send to handler through Looper) to this service, and get them handled in the Handler. Code sample is attached:
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
postDelayed (Runnable r, long delayMillis)
Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses. The runnable will be run on the thread to which this handler is attached.
Runnable Represents a command that can be executed.
delayMillis represents the time after which it should be executed.
Basically, it delays the execution of command(some code maybe) for particular period of time (delayMillis), so as to execute the command after specified time.
public class ApiHandler {
public static final String BASE_URL = "http://xxx.yyy/xx/";
private static final long HTTP_TIMEOUT = TimeUnit.SECONDS.toMillis(120);
private static Webservices apiService;
public static Webservices getApiService() {
if (apiService == null) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(HTTP_TIMEOUT, TimeUnit.MILLISECONDS);
okHttpClient.setWriteTimeout(HTTP_TIMEOUT, TimeUnit.MILLISECONDS);
okHttpClient.setReadTimeout(HTTP_TIMEOUT, TimeUnit.MILLISECONDS);
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(BASE_URL)
.setClient(new OkClient(okHttpClient))
.setConverter(new GsonConverter(new Gson()))
.build();
apiService = restAdapter.create(Webservices.class);
/*RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setConverter(new StringConverter())
.setEndpoint(BASE_URL)
.setClient(new OkClient(new OkHttpClient()))
.setLogLevel(RestAdapter.LogLevel.NONE);
RestAdapter adapter = builder.build();
apiService = adapter.create(Webservices.class);*/
return apiService;
} else {
return apiService;
}
}
}

How to make Handler accessible around the app

There are a few threads running in a service.
The threads need to post messages to UI / Activity
How would I pass over the Handler reference to the threads ? so that they can post their state changes to Activity ?
Or Better yet is there a way to globally expose handler ref like this ?
Handler getUIHandler();
Thank you in advance ;)
Create a Handler object in your UI thread. You can just create it at instantiation time if you like. Any thread launched from your activity can post messages or runnables to that handler. Threads launched from other activities, services, or whatnot will not work because there's no guarantee that your Activity is even running. (Actually, it might be a fun experiment to see if it works when the Activity is running, but you could never base a real app on this technique.)
In fact, you don't even need to create a Handler. Every View object contains its own Handler, so you can simply post your runnables to a view.
Or you could just call runOnUiThread()
From my notes on Handlers:
Usage patterns:
Pattern 1, handler plus runnables:
// Main thread
private Handler handler = new Handler()
...
// Some other thread
handler.post(new Runnable() {
public void run() {
Log.d(TAG, "this is being run in the main thread");
}
});
Pattern 2, handler plus messages:
// Main thread
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
Log.d(TAG, "dealing with message: " + msg.what);
}
};
...
// Some other thread
Message msg = handler.obtainMessage(what);
handler.sendMessage(msg);
Pattern 3, call runOnUiThread():
// Some other thread
runOnUiThread(new Runnable() { // Only available in Activity
public void run() {
// perform action in ui thread
}
});
Pattern 4, use the built-in handler of a View:
// Some other thread
myView.post(new Runnable() {
public void run() {
// perform action in ui thread, presumably involving this view
}
});
I've answered a similar question on how to report back to activity an error in the service.
Check Best practice for error handling in an Android Service, that will give you the aproach as well as a code example that you can use.
Regards.
OK, maybe we should get back to the base issue. Are you trying to make UI updates in your activity from the service? I see two approaches to this.
First, your service could send special Intents back up to the activity. Declare the activity with a launch mode of "singleTask" and implement onNewIntent() to receive intents from the service. Then, pack any relevant information into the intent and send it to the activity to be handled.
The better way, but somewhat more complicated, would be to bind the service from the activity, and then they can easily communicate with each other over the binder. If the service and activity are both part of the same application, and both running in the same process, this becomes much simpler.
Again, from my notes:
Declare an inner class named e.g. "LocalBinder" which extends Binder and contains a method named e.g. getService() which returns the instance of the service:
public class MyService extends Service
{
public class LocalBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
private final IBinder binder = new LocalBinder();
public IBinder onBind(Intent intent) {
return binder;
}
}
Your activity contains code that looks like:
// Subclass of ServiceConnection used to manage connect/disconnect
class MyConnection extends ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder svc) {
myService = ((MyService.LocalBinder)svc).getService();
// we are now connected
}
public void onServiceDisconnected(ComponentName name) {
// we are now disconnected
myService = null;
}
}
private MyService myService;
private MyConnection connection = new MyConnection();
/**
* Bind to the service
*/
void doBind() {
bindService(new Intent(MyClient.this, MyService.class),
connection, Context.BIND_AUTO_CREATE);
}
/**
* Unbind from the service
*/
void doUnbind() {
if (connection != null) {
unbindService(connection);
}
}

Categories

Resources