Sunday, September 16, 2012

Using Services in Android Part 3 - Two way communication with Rotation Support



S. Gökhan Topçu
gtopcu@gmail.com


In Part 1 and Part 2 of this blog series, I demonstrated how you can use started and bound services in your apps. In this blog post, I will provide an example app which uses a bound service to download some data. What makes this sample important is that it retains the service instance when the screen is rotated, and provides an example of how you can communicate back to your client activity using callback methods defined in the custom Binder implementation.

As you will recall, a bound service gets destroyed when all activities unbind. So the trick to keep a service alive is two rules:

1. Bind the service using the application context instead of the client (i.e. Activity) using getApplicationContext().bindService() instead of using Activity.bindService()
2. Keep an instance of your ServiceConnection and pass it to your new activity when the configuration changes (i.e. the screen is rotated).


MainActivity.java
package com.gtware.android;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.gtware.android.DownloaderBinder.DownloaderListener;

public class MainActivity extends Activity implements DownloaderListener {
 
 private Button button;
 
 private boolean unBindService = true;
 private static DownloaderBinder binder;
 private ServiceConnection connection;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button)findViewById(R.id.button);
    }
    
    @SuppressWarnings("deprecation")
 @Override
    protected void onStart() {
     super.onStart();
     connection = (ServiceConnection)getLastNonConfigurationInstance();
     if(connection == null) {
         connection = new ServiceConnection() {
       @Override
       public void onServiceDisconnected(ComponentName name) {
        binder = null;
       }
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
        binder = (DownloaderBinder) service;
        binder.attachListener(MainActivity.this);
       }
      };
      getApplicationContext().bindService(new Intent(getApplicationContext(), DownloaderService.class), connection, Service.BIND_AUTO_CREATE);
        }
        else {
         binder.attachListener(this);
         button.setEnabled(!binder.isDownloadInProgress());
        }
    }
    
    @Override
    protected void onSaveInstanceState(Bundle outState) {
     unBindService = false;
     super.onSaveInstanceState(outState);
    }
    
    @Override
    @Deprecated
    public Object onRetainNonConfigurationInstance() {
     return connection;
    }
    
    @Override
    protected void onDestroy() {
     super.onDestroy();
     //Is the activity just being rotated or destroyed for good?
     //We check if onRetainNonConfigurationInstance is called to understand
     Log.i(MainActivity.class.getName(), "Unbind: " + unBindService);
     binder.detachListener();
     if(unBindService) {
      binder = null;
      unbindService(connection);
     }
    }

    public void download(View view) {
  if(binder != null) {
   button.setEnabled(false);
   binder.download("http://download.thinkbroadband.com/5MB.zip");
  }
 }
    
 @Override
 public void downloadComplete(int result) {
  button.setEnabled(true);
  if(result == Activity.RESULT_OK) {
   Toast.makeText(MainActivity.this, "Success", Toast.LENGTH_LONG).show();
  }
  else if(result == Activity.RESULT_CANCELED) {
   Toast.makeText(MainActivity.this, "Canceled", Toast.LENGTH_LONG).show();
  }
  else {
   Toast.makeText(MainActivity.this, "Unknown status", Toast.LENGTH_LONG).show();
  }
 }

 
}


DownloaderService.java
package com.gtware.android;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class DownloaderService extends Service {
 
 private DownloaderBinder binder;
 
 @Override
 public void onCreate() {
  super.onCreate();
  binder = new DownloaderBinder();
  binder.onCreate();
  Log.i(DownloaderService.class.getName(), "Service created");
 }
 
 @Override
 public void onDestroy() {
  super.onDestroy();
  binder.onDestroy();
  Log.i(DownloaderService.class.getName(), "Service destroyed");
 }
 
 @Override
 public IBinder onBind(Intent intent) {
  return binder;
 }
 

}


DownloadBinder.java
package com.gtware.android;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;

public class DownloaderBinder extends Binder {
 
 private HttpClient client;
 private DownloaderListener listener;
 private boolean downloadInProgress = false;
 
 private MyHandler handler;
 private class MyHandler extends Handler {
  private MyHandler(Looper looper) {
   super(looper);
  }
  public void handleMessage(Message msg) {
   doDownload((String) msg.obj);
  }
 };
 
 public interface DownloaderListener {
  public void downloadComplete(int result);
 }
 
 public void onCreate() {
  client = new DefaultHttpClient();
  HandlerThread thread = new HandlerThread("DownloaderThread", Process.THREAD_PRIORITY_BACKGROUND);
  thread.start();
  handler = new MyHandler(thread.getLooper());
 }
 
 public void onDestroy() {
  listener = null;
  client.getConnectionManager().shutdown();
 }
 
 public void attachListener(DownloaderListener listener) {
  this.listener = listener;
 }

 public void detachListener() {
  listener = null;
 }
 
 public void download(String url) {
  Message msg = handler.obtainMessage(0, url);
  handler.sendMessage(msg);
 }
 
 private void doDownload(String url) {
  downloadInProgress = true;
  Log.i(this.getClass().getName(), "Start downloading url: " + url);
  HttpGet get = new HttpGet(url);
  int result = Activity.RESULT_CANCELED;
  try {
   ResponseHandler<byte[]> responseHandler = new ResponseHandler<byte[]>() {
    @Override
    public byte[] handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
     if(response.getStatusLine().getStatusCode() == 200 && response.getEntity() != null) {
      return EntityUtils.toByteArray(response.getEntity());
     }
     return null;
    }
   };
   byte[] responseBody = client.execute(get, responseHandler);
   if(responseBody != null) {
    
    File output = new File(Environment.getExternalStorageDirectory(), 
          Uri.parse(url).getLastPathSegment());
    if(output.exists()) {
     output.delete();
    }
    
    FileOutputStream fos = new FileOutputStream(output);
    fos.write(responseBody);
    fos.close();
    
    result = Activity.RESULT_OK;
   }
  }
  catch(Exception e) {
   e.printStackTrace();
  }

  Log.i(this.getClass().getName(), "Finished, notifiying listener: " + (listener != null));
  
  final int i = result;
  if(listener != null) {
   ((Activity)listener).runOnUiThread(new Runnable() {
    @Override
    public void run() {
     listener.downloadComplete(i);
    }
   });
   
  }
  downloadInProgress = false;
 }

 public boolean isDownloadInProgress() {
  return downloadInProgress;
 }
 
 

}


Do not forget to define the service in your manifest, as well as providing internet and external memory access permissions to your manifest. When you run the example, you will see that the service doesn't get killed when the activity is, if the screen is rotated. But it will be, if you exit the app since onSaveInstanceState() doesn't get called then. Also, do not forget to unbind the listener (activity) since you will have a huge memory leak if you keep reference to the old activity. This is a nice example of how you can retain your services through your app lifecycle and communicate back to your service clients.

Another way to communicate back to your clients would be defining a Messenger in your activity and passing it to the service with the intent used to start the service. Since Messenger is a subclass of Parcelable, you can use the method intent.putExtra("Messenger", new Messenger(myHandlerInstance)); to pass it with the intent to your service.


Further Reading:
Using Services in Android Part 1 - Started Services
Using Services in Android Part 2 - Bound Services
Services
Bound Services
AIDL
Service
IBinder
Binder
ServiceConnection
Messenger
IntentService
AsyncTask
Handler
HandlerThread
Looper


Wednesday, September 12, 2012

Using Services in Android Part 2 - Bound Services



S. Gökhan Topçu

gtopcu@gmail.com


In my previous blog, I explained how you can use started services to handle one-off requests initiated by the same or different applications. In this blog, we will look at what bound services are, and where and how we can use them.

Bound services are services which override onBind() method and return an android.os.IBinder instance to its client, which the client can later use to interact with the service. Bound services can be called from the same applications, or from different applications using IPC (inter-process communication).

IPC is an OS level communication technology which works similar to RPC (remote procedure call) in Java which lets applications to execute methods residing in different JVMs (Java Virtual Machines). IPC also lets apps make native calls to components running in different application processes by marshalling/unmarshalling the variables and objects clients/services exchange while invoking each other's methods. With a bound service, you can provide clients outside your application to use the services you may provide. For instance, if you design a service which downloads and returns a list of available proxy servers and expose it by using specific intent filters (and use android:exported="true flag in your manifest), then other applications can use your service by binding to it.

A bound service has three methods that we ignored in our started service examples: onBind(), onUnbind() and onRebind(). The boolean parameter returned from the unUnbind() method specifies if the onRebind() method should be called when a client binds again after it has unbounded from the service.

Only activities, services, and content providers can bind to a service - you cannot bind to a service from a broadcast receiver. Multiple clients can bind to the same service. If the service instance does not exist when a client binds, then onCreate() gets called and an instance is created, onBind() is called and instance of the IBinder class is returned to the client. If another client binds to the service, onBind() method is not called again and the same IBinder is returned to all the other clients afterwards. And when all bound components unbind, then the service gets destroyed and onDestroy() is called.

It's important to note that a bound service can also be started - you can override onStartCommand() and throw a RuntimeException if you really want to stop any clients to start your service or just remind yourself that you shouldn't be starting this service. And if a bound service started as well, it will continue its existence even if all bound components unbind, until stopSelf() or stopService() methods are called to stop the service.

Components (activities, services or content providers) can bind to a service using bindService() method, and unbind using unbindService(). These methods take an instance of the ServiceConnection class which you override and pass them, and has two methods: onServiceConnected() and onServiceDisconnected(). onServiceConnected() gets called by the OS when your component binds the service and returns the IBinder object that is returned by the onBind() method in the service. onServiceDisconnected() is called when the service gets disconnected unexpectedly (i.e. when killed by OS to gain more resources). Be sure not to do any heavy processing here, since although these methods will be called by the OS, they will be executed on your main application thread (UI thread).

The bindService() method also takes a start mode flag as a parameter. You can use Context.BIND_AUTO_CREATE here so that the service will be created for you if no instance exists when a client decides to bound to it.

There are three ways to define a bound service depending on how the IBinder returned from the onBind() method is constructed:
1. Extending the Binder class
2. Using a Messenger
3. Using AIDL (Android Interface Definition Language)


1. Extending the Binder class

If your service doesn't need to serve other components in separate applications or processes, you can use an instance of the Binder class which is a subclass of IBinder and return it from onBind(). You can define the methods you'd like your service to provide in your Binder implementation as public methods, and these methods can call any other public methods in your service or even call other components to do their job. You can also return an instance of your service with a method in your Binder implementation, so that the clients can invoke the public methods in your service directly.

This scenario is useful if you want to offshore some background work to your service and your service will always be kept within your application. The reason that this way of defining an IBinder won't work across applications/processes is because IPC is not used when using a Binder implementation. Binding components will need to cast the IBinder class returned from onServiceStarted() method in the  ServiceConnection instance defined in the client to your own Binder implementation so that they will be able to access the methods defined.

Below is an example of a bound service which returns an IBinder using a custom Binder implementation. Notice how the binder class allows the binding component to access methods it defines, as well as the public methods in the service it resides in by returning an instance of it:


MyBoundService.java
/** @author S. Gokhan TOPCU
 */
package com.gtware.android.blogs.services;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;

public class MyBoundService extends Service {
 
    private IBinder mBinder;      // interface for clients that bind
    private boolean mAllowRebind; // indicates whether onRebind should be used   

    /*
     * This works only if the client and service are in the same application and process
     * The reason the service and client must be in the same application is so the client 
     * can cast the returned object and properly call its APIs. The service and client 
     * must also be in the same process, because this technique does not perform any 
     * marshalling across processes.
     * 
     * You can implement methods which will call methods in the service or the bound activity,
     * or just return the service itself within a binder.
     * 
     */
    public class MyBinder extends Binder {
     public void showBinderMessage() {
      Toast.makeText(MyBoundService.this, "Service Binder Message", Toast.LENGTH_SHORT).show(); 
     }
     public MyBoundService getService() {
      return MyBoundService.this;
     }
    }
    
    public void showServiceMessage() {
     Toast.makeText(MyBoundService.this, "Service Message", Toast.LENGTH_SHORT).show(); 
    }
    
    @Override
    public void onCreate() {
     mAllowRebind = true;
     mBinder = new MyBinder();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        throw new RuntimeException("You should not start this service");
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
     Toast.makeText(this, "Service Binding", Toast.LENGTH_SHORT).show(); 
        return mBinder;
    }
    
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
     Toast.makeText(this, "Service Unbinding", Toast.LENGTH_SHORT).show(); 
        return mAllowRebind;
    }
    
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    
    @Override
    public void onDestroy() {
     Toast.makeText(this, "Service Done", Toast.LENGTH_SHORT).show(); 
    }
    
}


MyBoundActivity.java
package com.gtware.android.blogs.services;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Toast;

import com.gtware.android.blogs.services.MyBoundService.MyBinder;

public class MyBoundActivity extends Activity {

 private MyBoundService myBoundService;
 
 @Override
    protected void onStop() {
     super.onStop();
     if(myBoundService != null) {
      unbindMyBoundService(null);
     }
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.bound);
    }
    
    
    public void bindMyBoundService(View view) {
     Intent i = new Intent(this, MyBoundService.class);
     bindService(i, mMyBoundServiceConnection, Context.BIND_AUTO_CREATE);
    }
    
    public void unbindMyBoundService(View view) {
     myBoundService = null;
     unbindService(mMyBoundServiceConnection);
    }
    
    public ServiceConnection mMyBoundServiceConnection = new ServiceConnection() {
     public void onServiceDisconnected(ComponentName name) {
   Toast.makeText(MyBoundActivity.this, "Service disconnected", Toast.LENGTH_LONG).show();
  }
  
  public void onServiceConnected(ComponentName name, IBinder binder) {
   MyBinder myBinder = (MyBinder)binder;
   myBinder.showBinderMessage();
   myBoundService = myBinder.getService();
   myBoundService.showServiceMessage();
   Toast.makeText(MyBoundActivity.this, "Service connected", Toast.LENGTH_LONG).show();
  }
 };
 
    
}


bound.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="wrap_content"
     android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="vertical">
        
        <Button android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="bindMyBoundService"
            android:text="Bind Service"/>
        
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="unbindMyBoundService"
            android:text="Unbind Service"/>
    </LinearLayout>

</RelativeLayout>

We cast the IBinder returned from the ServiceConnection.onServiceConnected() method to our Binder implementation MyBinder defined in the service. Then we use it to call a method to show a Toast, get the associated service using getService() which is an instance of our service just created, and this time call a method directly defined in the service to show another Toast. Do not forget, this implementation only works if your service and calling component are in the same application and process.

Binding components should unbind from a service when they're done interacting with it or when the application is stopped. Notice that we're also calling unbindService() from the onStop() method in the activity just in case you forget to click the Unbind Service button before you navigate away from the application


2. Using a Messenger 

Using a  Messenger allows you to make inter-process calls without using AIDL, so you can communicate with your services from different applications and their processes than your service is defined in. When using a Messenger, you communicate with your service (and the service may communicate back to you as well) using messages instead of invoking methods on the IBinder or the hosting service as you do when using a Binder implementation.

A Messenger class is a wrapper around a Handler implementation. Both the service and the client defines a Messenger instance variable. The service constructs the Messenger using a Handler implementation, whereas the service clients construct it using the IBinder instance returned by the service. The service provides the IBinder object from the Messenger instance, so the IBinder instance is created automatically without the need to generate it yourself using AIDL. This IBinder is then returned by the service with the onBind() method.

Same services logic and lifecycle rules apply to a service using a Messenger - destroyed when all clients unbind, can also be started, and a ServiceConnection is used again to monitor the connection between the client and the service. Below is a service example which uses this technique, and an activity which binds and communicates with the service using a Messenger:


 MyMessengerService.java
/** @author S. Gokhan TOPCU
 */
package com.gtware.android.blogs.services;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.widget.Toast;

/*
 * If you need your service to communicate with remote processes, then you can use a 
 * Messenger to provide the interface for your service. This technique allows you to 
 * perform interprocess communication (IPC) without the need to use AIDL.
 * 
 * In this way, there are no "methods" for the client to call on the service. Instead, 
 * the client delivers "messages" (Message objects) that the service receives in its Handler.
 * 
 */
public class MyMessengerService extends Service {
 
    private boolean mAllowRebind; // indicates whether onRebind should be used   
    private Messenger mMessenger;
    public static final int SHOW_TOAST = 0;

    private class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW_TOAST:
                 Toast.makeText(MyMessengerService.this, "Service Handler Message", Toast.LENGTH_SHORT).show(); 
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    public void onCreate() {
     mAllowRebind = true;
     mMessenger = new Messenger(new MyHandler());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        throw new RuntimeException("You should not start this service");
    }

    /**
     * Return the IBinder generated by the Messenger which clients will send to use messages to this service
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "Service Binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
    
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
     Toast.makeText(this, "Service Unbinding", Toast.LENGTH_SHORT).show(); 
        return mAllowRebind;
    }
    
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    
    @Override
    public void onDestroy() {
     Toast.makeText(this, "Service Done", Toast.LENGTH_SHORT).show(); 
    }
    
}



MyMessengerActivity.java
package com.gtware.android.blogs.services;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Toast;

public class MyMessengerActivity extends Activity {

 private Messenger myMessenger;
 
 @Override
    protected void onStop() {
     super.onStop();
     if(myMessenger != null) {
      unbindMyMessengerService(null);
     }
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.messenger);
    }
    
    public void bindMyMessengerService(View view) {
     Intent i = new Intent(this, MyMessengerService.class);
     bindService(i, mMyMessengerServiceConnection, Context.BIND_AUTO_CREATE);
    }
    
    public void unbindMyMessengerService(View view) {
     myMessenger = null;
     unbindService(mMyMessengerServiceConnection);
    }
    
    public ServiceConnection mMyMessengerServiceConnection = new ServiceConnection() {
     public void onServiceDisconnected(ComponentName name) {
   Toast.makeText(MyMessengerActivity.this, "Service disconnected", Toast.LENGTH_LONG).show();
  }
  
  public void onServiceConnected(ComponentName name, IBinder binder) {
   myMessenger = new Messenger(binder);
   Toast.makeText(MyMessengerActivity.this, "Service connected", Toast.LENGTH_LONG).show();
   Message message = Message.obtain(null, MyMessengerService.SHOW_TOAST, 0, 0);
   try {
    myMessenger.send(message);
   } catch (RemoteException e) {
    e.printStackTrace();
   }
  }
  
 };
 
    
}


Just as with all services, do not forget to declare the service in your manifest file. This example uses the messenger.xml layout which is the same as the previous bound.xml file except for the method names specified in onClick attributes, matching the ones declared in the activity. When you run this app and click on the Bind Service button, service gets created, activity binds to it, activity sends a message to it and a Toast is displayed. When the activity unbinds, the service's unBind() method gets called and the activity unbinds, and since there're no other clients, the service gets destroyed. Also try clicking on the bind button twice - since the activity is already bound to the service after the first click, nothing happens and the connection is maintained. You can bind to any service using the Messenger pattern and send/receive messages even it's defined outside your application and does not run in the same application process. Android handles IPC automatically and does all the marshalling behind the scenes to make the connection.

Also notice that we're catching the RemoteException when sending a message to the Messenger. This is to handle a DeadObjectException, which is a subclass of RemoteException, which is thrown when the connection is broken when you're trying to send a message over this broken connection.

One hiccup for using the Messenger patterns is that you cannot use multi-threading - the same Handler will be executed to process all the incoming messages. So this pattern works in a queued fashion just like the MyQueuedService as shown in Part 1 of this blog.


Using AIDL (Android Interface Definition Language)

AIDL is the interface which two components agree to communicate with each other with. This involves sharing objects in memory, for which class primitives/objects will need to be marshalled in a binary format to be stored. AIDL takes care of marshalling/unmarshalling these and invoking the remote method for you, so that you don't have to deal with binary processing yourself. You only need to use AIDL if you need multi-threading and your service and your client run on different processes. If they are in the same process, just use the Binder pattern and call its methods from separate threads. An .aidl file contains the signature of the methods you want to expose to the service's clients and is coded using the regular Java syntax, and you need to implement a generated interface in your service and provide all the methods you defined in the .aidl file. Have a look at the official documentation if you're sure that you have to use this pattern.


When to Bind/Unbind to a Service

If your activity needs to interact with the service even when it's running in the background, you should bind to it at onCreate() and onbind at onDestroy(). But this will increase your apps processing costs and it will be more susceptible to being killed by the OS when more resources are required, since the service will also keep running in the background. Background activities are chosen to be killed before any foreground ones by the OS, and consuming more resources in a background app means more priority for getting killed among other background apps.

If you only need to use the service when the user is interacting with your activity, then use the onStart() and onStop() methods to bind/onbind. This is the general best practice.

Do not bind/onbind to a service at onPause()/onResume() methods unless you really have to, because there will be many unnecessary bind/unbind operations since these methods gets called frequently and you usually will want to keep your service at hand until you're sure the user is leaving the app. Also, these methods need to execute fast for swift transitions, and handling resource connections in these methods is not a right practice for any resource anyway (such as closing DBs, writing preferences/files, destroying network connections etc). Also, if your app has two different activities which binds to the same service, you double the service creation/binding/unbinding/destruction process since first app will be unbound and the service destroyed before the second can bind to it.


When to use Services, and which one to use

If you have a single client that needs to do some background work, just create a simple thread, or use an AsyncTask/Handler to do the job if you need to update the UI while doing it. If multiple clients need to use this processing logic, provide a static method in a utility class or define the AsyncTask/Handler in their own class files so all clients can access and utilize them.

If you need to execute a simple task in the background within your application from a single client or multiple clients even when the user isn't interacting with your app, and you do not need multi-threading, use an IntentService.

If you also need multi-threading for the preceding scenario, extend the Service class directly and create new threads in the onStartCommand() button to execute each request that's received by the service every time a startService() call is made.

If you have multiple clients in the same application to do some background work which maintains resources such as network connections that can be shared to improve the process, then use a Bound Service and bind to the same service from all clients. This way, the service will be alive as long as at least one client is alive and maintain its resources. For network communications, this will also decrease your IO rate along with battery and resources consumption.

If you have client/clients that reside in different application processes than your service, use the Messenger pattern. Keep in mind that Messenger always executes its logic in the Handler's process. If you need the service to communicate back to the client, then create another Messenger in your client and pass it to the service so that it can use it to send messages back.

If you also need multi-threading for the preceding scenario, then using AIDL is your last resort. This pattern is usually required for providing OS-wide services to all applications, such as for providing download management.


In Part 3, you can find an example of how you can communicate back to the service client (i.e. activity) from the service and how you can keep a bound service alive across activities (i.e. when the screen is rotated).


Further Reading
Using Services in Android Part 1 - Started Services
Using Services in Android Part 3 - Two way communication with Rotation Support
Services
Bound Services
Service
IBinder
ServiceConnection
Binder
Messenger
AIDL
IntentService
AsyncTask
Handler



Monday, September 10, 2012

Using Services in Android Part 1 - Started Services



S. Gökhan Topçu

gtopcu@gmail.com


You can use services in Android to handle inter-Activity tasks or to do some background work. A service can run independently of the activity or activities which started or are bound to it and has its own lifecycle. They can also be used for inter-process communication, and depending on your design, may also support handling multiple requests simultaneously. Services do not provide a user interface and are usually used to process long-running operations, such as network transfers, IO operations, playing music, etc. You create a service by extending the android.app.Service abstract class, and you need to define it in your manifest just as you would define any other activity, content provider, or broadcast receiver:


<service android:name=".MyService"
            android:exported="false"/>


The android:exported="false" attribute is used to keep the service private to your application. Though only the android:name attribute is required, you can also include an intent-filter element as well if you will be exporting your service to other applications and you want your service to respond to any service intents with the specified filters.

There are two types of services:
1. Started services
2. Bound services

This blog will demonstrate the first one: Started Services, which are services that do not allow binding. I will be demonstrating how to use bound services in my next blog. Note that a bound service can also be started, but a started service does not have to allow binding (if you return null from onBind() method). You will understand the mechanics as you move along.


Started Services


A started service is usually used to handle a single task and stops itself when done and do not return a result to its starter. A service is started by an activity using the startService() method. Since a service's lifecyle is independent of the starter/binder activity or activities, a started service does not get destroyed even if the activity that started it is destroyed. So a service needs to be stopped manually when the task is complete by using the method stopService() from the starter activity, or by calling method stopSelf() from within the service.

You should also be aware that services are created and run using the default application process, so if you do not create additional threads, or use AsyncTask / Handler classes, then anything you do in your service will be executed on the main application thread (UI thread). And if your task takes some time to execute (which is probably why you created a service in the first place), then you might get an ANR (Application Not Responding) error, which is typically after 5 minutes and your app be killed by the OS.

When a service is started using the startService() method from an activity, first the service's onCreate() method is called. And then, onStartCommand() will be called by the system with the intent used when calling startService(), as well as the flags and a unique id for this start request which you can use later to stop the service. Last, onDestroy() will be called when the service is stopped. Below is a simple service which will stop itself after waiting for 5 seconds, and an activity which starts it:

MyService.java:
package com.gtware.android.blogs.services;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class MyService extends Service {

 private int latestStartId;

 @Override
 public void onCreate() {
  Toast.makeText(this, "Service Created", Toast.LENGTH_SHORT).show();
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  latestStartId = startId;
  new Thread(new Runnable() {
   public void run() {
    try {
     Thread.sleep(5000);
     stopSelfResult(latestStartId);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }).start();
  return START_STICKY;
 }

 @Override
 public IBinder onBind(Intent arg0) {
  return null;
 }

 @Override
 public void onDestroy() {
  Toast.makeText(this, "Service Done", Toast.LENGTH_SHORT).show();
 }

}

MyActivity.java:
package com.gtware.android.blogs.services;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main1);
    }

    public void startService(View view) {
     Intent i = new Intent(this, MyService.class);
     startService(i);
    }
}

Layout main1.xml contains a simple button which will call the startService() method in the activity. We stop the service using method stopSelfResult(latestStartId) - the new version of stopSelf(int) - which will only stop the service if no other startService() was requested by a caller and thus resulted a call to onStartCommand() (meaning the service was not started by any other callers before it was finished processing the request). If you run this example, you will see the starting/stopping Toast notifications the service outputs during its lifecycle. You can enhance this example to do something useful in your app. A service will only be created (and onCreate() method will be called) when no started instance of the service exists, and since we are not providing binding with this service, we return null from onBind() method. Notice that unlike activities, you do not need to call super() from any of the service's methods.

Just like activities, services can be killed by the Android OS if the system needs more memory. When a service is killed, it's automatically restarted when there're enough resources again. You indicate how your service should to be started when killed with the flag returned from the onStartCommand() method, which in this case is START_STICKY. This flag tells the OS to not to re-send the last intent, and will send a null one if there are no other pending intents so you should first check your intent before executing any tasks.


IntentService


If you need simple tasks queuing and do not require parallel processing of requests, then IntentService is your man. You start by extending the android.app.IntentService class and only need to override onHandleIntent() method instead of overriding onStartCommand(). The default implementation will create a worker thread in onStartCommand() method which queues the start requests and delivers them to the onHandleIntent() method for you to process the requests. Make sure you also call super() from any other methods that you override except onHandleIntent() and onBind() so that the service can do its magic . It even stops the service when all pending requests are done so you never need to call stopSelf() or stopService(), and provides a default implementation of onBind() which returns null so no activity can bind to your service. You also need to provide a default constructor which will send a name for your service to the super constructor. Below is the IntentService version of our previous example:

MyIntentService.java
package com.gtware.android.blogs.services;

import android.app.IntentService;
import android.content.Intent;
import android.widget.Toast;

public class MyIntentService extends IntentService {

 public MyIntentService() {
  super("MyIntentService");
 }
 
 @Override
 public void onCreate() {
  super.onCreate();
  Toast.makeText(this, "Service Created", Toast.LENGTH_SHORT).show(); 
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  //Call super first to have a worker thread created for you
  //and intents delivered to onHandleIntent() in a queued fashion
  return super.onStartCommand(intent, flags, startId);
 }
 
 @Override
 protected void onHandleIntent(Intent intent) {
  try {
   Thread.sleep(5000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
 
 @Override
 public void onDestroy() {
  super.onDestroy();
  Toast.makeText(this, "Service Done", Toast.LENGTH_SHORT).show(); 
 }

}

If you want proof that the IntentService does actually queue the requests in a separate, just call startService() twice from your starter activity. You will see that the UI does not get blocked since IntentService creates a separate worker thread and the service is destroyed after at least 10 seconds instead of just 5.


Service Priority and Handling Multiple Requests


You can define your own service instead of using the IntentService if you need to service multiple requests simultaneously or if you want to define a custom priority for your service. For executing the service tasks separately from the UI thread with a custom priority, you need to use a Handler implementation with a Looper you derive from a HandlerThread constructed using the priority you define.

If the user will immediately notice that your service is killed (such as for a music player service), you can also define your service as a foreground service by calling startForeground() method in your service with a custom Notification with the Notification.FLAG_ONGOING_EVENT flag, which will indicate the user that the started service is still running in the background and the notification will be kept in the notifications panel as long as your service is alive. You can stop the service from running in the foreground by calling stopForeground() afterwards.

The example below does not process requests simultaneously, but is actually an imitation of the IntentService class, queuing the requests using a Handler. You can instead define new threads in onStartCommand() method if you want multi-threading:

MyQueuedService.java
package com.gtware.android.blogs.services;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.widget.Toast;

public class MyQueuedService extends Service {

 private MyHandler myHandler;

 private final class MyHandler extends Handler {
  public MyHandler(Looper looper) {
   super(looper);
  }
  
  @Override
  public void handleMessage(Message msg) {
   try {
    Thread.sleep(5000);
    //use the unique startId so you don't stop the 
    //service while processing other requests
    stopSelfResult(msg.arg1);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }

 @Override
 public void onCreate() {
  Toast.makeText(this, "Service Created", Toast.LENGTH_SHORT).show();
  //Create a new HandlerThread with a specified priority
  HandlerThread thread = new HandlerThread("MyHandlerThread", Process.THREAD_PRIORITY_DEFAULT);
  //Start the handler thread so that our Handler queue will start processing messages
  thread.start();
  //Run the handler using the new HandlerThread
  myHandler = new MyHandler(thread.getLooper());
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  Message msg = myHandler.obtainMessage();
  msg.arg1 = startId;
  myHandler.sendMessage(msg);
  return START_STICKY;
 }

 @Override
 public IBinder onBind(Intent arg0) {
  return null;
 }

 @Override
 public void onDestroy() {
  Toast.makeText(this, "Service Done", Toast.LENGTH_SHORT).show();
 }

}

As you can see, stopSelfResult(msg.arg1) method is used here again with the int argument indicating which service instance to stop, so that we don't stop the service while executing other requests since stopSelf() stops the service immediately. Our queue service queues all requests using a Handler class, which executes its handleMessage() method using the looper we pass to it in its constructor. You can again start this service multiple times to see for yourself that indeed our work queue handles each intent one by one, and the service is kept alive until the last intent is processed.

In my next blog, I will be demonstrating how to use bound services.


Further Reading:
Using Services in Android Part 2 - Bound Services
Services
Service
IntentService
AsyncTask
Handler
HandlerThread
Looper
Notification


Sunday, September 2, 2012

TCP/IP & HTTP tracing Android Apps



S. Gökhan Topçu

gtopcu@gmail.com


If you have an Android app that uses Android's networking APIs, being able to trace the underlying TCP/IP requests your application makes is usually the best way to debug and solve problems, especially in the case of HTTP requests. Since HTTP requests are composed of a header with name/value pairs and a body of varying MIME types, being able to see what exactly is sent/received from your application to a remote server will make it much easier to understand the underlying communication which is usually well hidden by the APIs you use to make the calls.

In this blog I will be demonstrating how you can trace TCP/HTTP requests made by applications running in Android emulators. Tracing real devices is a bit trickier, since your device will have its own network adapter to reach the host, whereas the emulator goes through your PC to access the internet. Below is a simple activity which uses Apache HttpClient APIs to make HTTP GET requests to google.com.tr. Remember, you will need to add:

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

permission to your manifest to be able to access the internet from Android apps, otherwise you will run into java.net.UnknownHostException error.


MainActivity.java
package com.example.tcpip.http.tracing.android.apps;

import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
 
 //public static final String URL = "http://www.google.com.tr";
 public static final String URL = "http://10.0.2.2:666";
 
 private HttpClient client;
 private HttpGet get;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        client = new DefaultHttpClient();
        get = new HttpGet(URL);
        setContentView(R.layout.activity_main);
    }

    public void connect(View button) {
     try {
   HttpResponse response = client.execute(get);
   Toast.makeText(this, "Response Code: " + response.getStatusLine().getStatusCode(), Toast.LENGTH_LONG).show();
   Log.d(MainActivity.class.getName(), "Response Code: " + response.getStatusLine().getStatusCode());
   
  } catch (ClientProtocolException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
    }
}

And the activity_main.xml is composed of a single button to initiate the request:

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Send HTTP request"
        android:onClick="connect"/>

</RelativeLayout>

In order to trace the HTTP GET request, the first step is to change the hostname part of the URL we're calling to the IP address 10.0.2.2, which is the Linux-based IP the emulator calls for app network requests. This maps to the old friend "localhost" which you would use instead if you were debugging a regular Java application. And for the port, it's better to use something different than the standard HTTP port 80 since our PC might already be using it to serve web pages/data.

Now that we have pointed our app within the emulator to our PC, we need to setup a "listener" on our PC to pick up the request, send it to remote server, get the response, and provide the response back to our waiting app. There are many tools/applications which provides TCP/IP and HTTP tracing, but since we're already using Eclipse to develop our Android apps, I will be using the standard TCP/IP monitor that comes packaged with Eclipse here.

In order to use it, the first step is to configure it to listen to the port we have specified in our app URL, as well as the target host. You can get to the TCP/IP monitor's preferences from Eclipse -> Window -> Preferences -> Run/Debug -> TCP/IP Monitor. Check "Show the TCP/IP monitor view when there is activity" checkbox as well as "Start monitor automatically" so that the monitor will show up automatically when we make a request, and add a new monitor as with the attributes as shown below:

Figure 1: Setting TCP/IP Monitor Preferences

Now you can launch your activity, and click on the "Send HTTP request" button. You should see a Toast with response code 200 which will mean that you have successfully fetched Google data and you have setup your URL as well as the TCP/IP monitoring correctly:

Figure 2: Getting the HTTP response successfully

After you click the button, Eclipse should open the TCP/IP Monitor view for you automatically. If it does not, you can always launch it from Eclipse -> Window -> Show View -> Debug -> TCP/IP Monitor:

Figure 3: Opening the TCP/IP Monitor View from Eclipse

The monitor must now be showing the trace for your last request, which will be an HTML output since we actually made the request to a webpage using the 80 port:

Figure 4: HTTP trace in TCP/IP monitor

If we were making a HTTP request with a body (and maybe using HTTP attachments as well), the trace output would show that too in the request section.

Other Methods

Eclipse TCP/IP monitor is pretty useful and straightforward to use, but surely there are many other ways to capture your requests in Android Emulator. As I have previously mentioned, you can use any other tracing tool located and setup on your PC. Just set it with the correct local listening port and target host/port configuration, and replace the host/port in your Android application with the IP 10.0.2.2 and the local port you've configured in your tracing app on your PC.

Apache TCPMon
Another tool I frequently use which is more advanced than the Eclipse TCP/IP Monitor is the Apache TCPMon. You can set it up using the same philosophy as the Eclipse TCP/IP Monitor. Just check out the tutorial on their webpage which details how you can set it up.

Setting a Proxy for the Emulator
You can also set a global proxy for the emulator at launch so that all the TCP/IP requests made by apps running in the emulator will go through a HTTP/HTTPS proxy. To do that, launch the emulator using the http-proxy parameter:

emulator -avd <AVD_NAME> -http-proxy <PROXY_HOST>:<PROXY:PORT>

Your HTTP proxy must be setup and running on your PC to be able to launch the emulator with a proxy. Again, any HTTP proxy/tracing tool is suitable for using this method, and you don't need to change the URLs in your Android code to catch the requests since the emulator will always route the requests to the proxy.


Further Reading:
Eclipse TCP/IP Monitor Configuration
Apache TCPMon
Apache HttpClient
Android Emulator


Friday, July 20, 2012

Using Twitter's Rest API in Android


S. Gökhan Topçu

gtopcu@gmail.com

In a previous post, I presented sample code to post tweets to twitter using the twitter4j library. In this blog, I will be using Twitter's Rest API directly to read user's timeline and post tweets to it. Another method which is the easiest to use is the Twitter Web Intents, which are pop-up browser dialogs you can use by embedding into a WebView.

Most GET based operations using the Rest API are much easier than the POST ones, since we don't have to deal with OAuth authorizations. You can retrieve data from Twitter anonymously, but when you want to post tweets, change settings, profile parameters etc. you will need to have a valid authorization. OAuth authorization in Twitter works much like that of Facebook - user grants access to an app to do things on their behalf, and an app uses the consumer key and access token provided, from which Twitter identifies the user and the app when a call is made, respectively.

Reading user's timeline

Reading a user's timeline is pretty straightforward - in this case, the URL we will be issuing a GET request includes the user's Twitter username, and no authorization is required. The Rest API's URL for the GET statuses/user_timeline request is:


Just change the username to a valid twitter username to get the example working. And don't forget to add internet permission to your manifest to be able to run these examples:

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


GetActivity.java
package com.gtware.android.twitter.restapi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class GetActivity extends ListActivity {

 private static final String getURL = "http://twitter.com/statuses/user_timeline/username.json";
 private TextView label;
 private ListView list;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.get_main);
        label = (TextView)findViewById(R.id.label);
        list = getListView();
        
        try {
      HttpClient client = new DefaultHttpClient();
      HttpGet get = new HttpGet(getURL);
      HttpResponse response = client.execute(get);
      parseGetResponse(response);
     }
     catch(Exception e) {
      Log.e(GetActivity.class.getName(), e.getMessage());
      label.setText("Error: " + e.getMessage());
     }
    }
    
    private void parseGetResponse(HttpResponse response) throws IllegalStateException, IOException, JSONException, ParseException {
     StringBuilder sb = getResponseBody(response);
     if(response.getStatusLine().getStatusCode() == 200) {
      JSONArray jsArray = new JSONArray(sb.toString());
      if(jsArray.length() == 0) {
       label.setText("No results");
       return;
      }
      label.setText(jsArray.length() + " result(s):");
      ArrayList<String> tweets = new ArrayList<String>();
      for(int i = 0; i < jsArray.length(); i++) {
       JSONObject jsObject = jsArray.getJSONObject(i);
       tweets.add(jsObject.getString("created_at") + ": " + jsObject.getString("text"));
      }
      list.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, tweets));
     }
     //Not OK
     else {
      label.setText("Response Code: " + response.getStatusLine().getStatusCode() + "\nResponse: " + sb.toString());
      Log.e(GetActivity.class.getName(), "Response Code: " + response.getStatusLine().getStatusCode() + "\nResponse: " + sb.toString());
     }
    }
    
    private static StringBuilder getResponseBody(HttpResponse response) throws IllegalStateException, IOException {
     InputStream is = response.getEntity().getContent();
  BufferedReader br = new BufferedReader(new InputStreamReader(is));
  StringBuilder sb = new StringBuilder();
  String line = null;
  while((line = br.readLine()) != null) {
   sb.append(line);
  }
  br.close();
  return sb;
    }
    
}

We're using Apache Http Client for our examples to issue HTTP GET/POST requests to Twitter and org.json.* package to convert result strings to JSON objects. Since both are included with the standard Android SDK, we don't need to import any third party libraries to our project. This code retrieves the most recent 20 tweets from the user @gt_ware and parses the results into a ListView. You can check out the get_timeline documentation to see all the fields returned by the Rest API.

The layout file is pretty straightforward, a TextView as a top label, a separator, and the ListView:

get_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"/>
    
    <View 
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:padding="10dp"
        android:background="#0041c5"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"/>
    
    <ListView android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

Output:
Figure 1: GET user_timeline using Twitter's REST API

Posting Tweets

Before you can post tweets, you need to:

1. Register a new app at Twitter Apps
2. Go to the OAuth tool section of the new app and note down the consumer/access tokens and secrets. You can also generate request-specific tokens from the OAuth tool box located on the middle-right section of the screen included at the request documentations. Using the toolbox, you can retrieve the tokens and secrets from POST statuses/update documentation for this blog specific to your app.

Once you get your tokens, you can start building the Authorization HTTP header element which Twitter will be using to authenticate the requests. For POST statuses/update request (the "tweet" request), HTTP body only needs to include a status=tweet_content, where the tweet_content is the percent encoded Tweet. At the end, your HTTP POST will need to look something like this:

POST /1/statuses/update.json?include_entities=true HTTP/1.1
Accept: */*
Connection: close
User-Agent: OAuth gem v0.4.4
Content-Type: application/x-www-form-urlencoded
Authorization: 
        OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog", 
              oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", 
              oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", 
              oauth_signature_method="HMAC-SHA1", 
              oauth_timestamp="1318622958", 
              oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", 
              oauth_version="1.0"
Content-Length: 76
Host: api.twitter.com

status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21
The Rest API URL for the POST statuses/update request is in the form:
http://api.twitter.com/1/statuses/update.json

Generation of the Authorization HTTP header is not an easy task and you need to go through:

1. Authorizing a request
2. Creating a signature
3. Percent encoding parameters

documentation to understand how the process works, but the code I'm providing below, although non-optimized for the sake of simple viewing, is a working example and handles most of the confusing HmacSHA1, percent and base64 encodings along with string construction required to build the header. It lets a single predefined user tweet from the activity, and returns the tweet's ID if the post is successful. You need to change the consumer and access tokens & secrets (the String variables consumerKey, consumerSecret, accessToken, accessTokenSecret in the code) with the ones you retrieved earlier for your own twitter app to get the example working. Also you might want to optimize the code and externalize the strings too.

PostActivity.java
package com.gtware.android.twitter.restapi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class PostActivity extends Activity {
 
 public static final String ENCODING = "UTF-8";
 private static final String postURL = "http://api.twitter.com/1/statuses/update.json";
 
 //Keys & Secrets
 private static final String consumerKey = "r3dmV3mkbacY4CKdtAaNQG";
 private static final String consumerSecret = "gomuK2bcO7KPfUyk1PHtx91AAIEQCumvrYxLUwCiHlg";
 private static final String accessToken = "700867350-eFsXm3YteXZ2QRZY9Ydq2RDMkk1ZdFcnSMnb8Izr";
 private static final String accessTokenSecret = "e9bj6RDFGFBL7kOILJBgfXEEIMZ6uRwqtTggxpnLM";
 
 //OAuth Header Keys
 private static final String oauth_consumer_key = "oauth_consumer_key";
 private static final String oauth_nonce = "oauth_nonce";
 private static final String oauth_signature = "oauth_signature";
 private static final String oauth_signature_method = "oauth_signature_method";
 private static final String oauth_timestamp = "oauth_timestamp";
 private static final String oauth_token = "oauth_token";
 private static final String oauth_version = "oauth_version";
 private static final String status = "status";
 
 private EditText tweetEditor;
 private Button postButton;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.post_main);
        postButton = (Button)findViewById(R.id.postButton);
        tweetEditor = (EditText)findViewById(R.id.tweetEditor);
        tweetEditor.addTextChangedListener(new TextWatcher() {
   public void onTextChanged(CharSequence s, int start, int before, int count) {
    if(tweetEditor.getText().length() == 0) {
     postButton.setEnabled(false);
    }
    else {
     postButton.setEnabled(true);
    }
   }
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) { }
   
   public void afterTextChanged(Editable s) { }
  });
    }
    
    private void alertUser(String str) {
     new AlertDialog.Builder(this)
          .setMessage(str)
          .setNeutralButton("OK", null)
          .show();
    }
    
    public void post(View button) {
     HttpClient client = new DefaultHttpClient();
     HttpPost post = new HttpPost(postURL);
     try {
      String message = tweetEditor.getText().toString();
      addAuthorizationHeader(message, post);
      post.setEntity(new StringEntity(percentEncode(status) + "=" + percentEncode(message)));
      HttpResponse response = client.execute(post);
      parsePostResponse(response);
     }
     catch(Exception e) {
      Log.e(PostActivity.class.getName(), e.getMessage());
      alertUser("Error: " + e.getMessage());
     }
    }
    
    private void addAuthorizationHeader(String tweet, HttpRequestBase request) throws Exception {
     request.setHeader("Content-Type", "application/x-www-form-urlencoded");
  
     String nonce = base64Encode((UUID.randomUUID().toString().replaceAll("-", "").getBytes()));
  String signatureMethod = "HMAC-SHA1";
  String timeStamp = String.valueOf(new Date().getTime() / 1000);
  String version = "1.0";
  
  //Generating the Parameter String:
  //Add request parameters and status message alphabetically (DO NOT ADD SIGNATURE)
  //Encode keys and values while adding
  //Insert = character between each key and its value
  //Add an ampersand (&) to the end if there are more parameters
  String parameterString = 
     percentEncode(oauth_consumer_key) + "=" + percentEncode(consumerKey) + "&" +
     percentEncode(oauth_nonce) + "=" + percentEncode(nonce) + "&" +
     percentEncode(oauth_signature_method) + "=" + percentEncode(signatureMethod) + "&" +
     percentEncode(oauth_timestamp) + "=" + percentEncode(timeStamp) + "&" +
     percentEncode(oauth_token) + "=" + percentEncode(accessToken) + "&" +
     percentEncode(oauth_version) + "=" + percentEncode(version) + "&" +
     percentEncode(status) + "=" + percentEncode(tweet);
  
  //Generate the SignatureBaseString
  String signatureBaseString = "POST&" + percentEncode(postURL) + "&" + percentEncode(parameterString);
  
  //Generate the SigningKey
  String signingKey = percentEncode(consumerSecret) + "&" + percentEncode(accessTokenSecret);
  
  //Generate HMAC-MD5 signature
        String signature = generateHmacSHA1(signingKey, signatureBaseString);
        
  //Build the HTTP Header
        String oauthHeader =
    "OAuth " + 
    percentEncode(oauth_consumer_key) + "=\"" + percentEncode(consumerKey) + "\", " +  
    percentEncode(oauth_nonce) + "=\"" + percentEncode(nonce) + "\", " + 
    percentEncode(oauth_signature) + "=\"" + percentEncode(signature) + "\", " + 
       percentEncode(oauth_signature_method) + "=\"" + percentEncode(signatureMethod) + "\", " + 
       percentEncode(oauth_timestamp) + "=\"" + percentEncode(timeStamp) + "\", " + 
       percentEncode(oauth_token) + "=\"" + percentEncode(accessToken) + "\", " + 
       percentEncode(oauth_version) + "=\"" + percentEncode(version) + "\"";
        
        request.addHeader("Authorization", oauthHeader);
    }
    
    private String generateHmacSHA1(String key, String value) throws Exception {
     SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(ENCODING), "HmacSHA1");
     Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(keySpec);
        byte[] result = mac.doFinal(value.getBytes(ENCODING));
        return base64Encode(result);
    }
    
    private void parsePostResponse(HttpResponse response) throws IllegalStateException, IOException, JSONException {
     StringBuilder sb = getResponseBody(response);
     if(response.getStatusLine().getStatusCode() == 200) {
      alertUser("Tweet Successful!\nID: " + new JSONObject(sb.toString()).get("id"));
     }
     //Not OK
     else {
      Log.e(PostActivity.class.getName(), "Response Code: " + response.getStatusLine().getStatusCode() + "\nResponse: " + sb.toString());
      alertUser("Error Code: " + response.getStatusLine().getStatusCode() + "\n" + new JSONObject(sb.toString()).getString("error"));
     }
    }
    
    private static StringBuilder getResponseBody(HttpResponse response) throws IllegalStateException, IOException {
     InputStream is = response.getEntity().getContent();
  BufferedReader br = new BufferedReader(new InputStreamReader(is));
  StringBuilder sb = new StringBuilder();
  String line = null;
  while((line = br.readLine()) != null) {
   sb.append(line);
  }
  br.close();
  return sb;
    }
    
    private String percentEncode(String s) throws UnsupportedEncodingException {
        //This could be done faster with more hand-crafted code.
        return URLEncoder.encode(s, ENCODING)
          // OAuth encodes some characters differently:
                .replace("+", "%20").replace("*", "%2A")
                .replace("%7E", "~");
    }

    private static String base64Encode(byte[] array) {
     return Base64.encodeToString(array, Base64.NO_WRAP);
    }
    
}

A simple layout file is all we need to gather the tweet input:
post_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal" >

    <EditText
        android:id="@+id/tweetEditor"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="65"
        android:inputType="textMultiLine"
        android:maxLength="140"
        android:padding="10dp"
        android:textAppearance="@android:style/TextAppearance.Small" />

    <Button
        android:id="@+id/postButton"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="35"
        android:drawablePadding="5dp"
        android:drawableRight="@drawable/twitter_logo"
        android:enabled="false"
        android:onClick="post"
        android:text="Tweet"
        android:textAppearance="@android:style/TextAppearance.Small" />
</LinearLayout>

And this is how it looks:


Figure 2: Using the Twitter Rest API for POST statuses/update

Simple Twitter Client

Now that we know how to do GET/POST requests using the Rest API, we can combine the codes from the previous projects and build ourselves a simple twitter client with a little bit of extra tweaking. The first example retrieved the user's own tweets (by using user_timeline method) but since we now want to see tweets from others as well (just like twitter.com), we will be using GET statuses/home_timeline method instead, which does require authorization. This example executes POST statuses/update requests on the UI thread, but uses an AsyncTask for GET statuses/home_timeline executions since it might take a fair bit of time to retrieve the last 20 tweets and parse them. Also, a Timer updates the tweets every 10 seconds when the screen is active.

SimpleTwitterClient.java
package com.gtware.android.twitter.restapi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;

public class SimpleTwitterClient extends ListActivity {
 
 public static final String ENCODING = "UTF-8";
 private static final String postURL = "http://api.twitter.com/1/statuses/update.json";
 private static final String getHomeTimelineURL  = "http://api.twitter.com/1/statuses/home_timeline.json";
 
 //Consumer & Access Keys & Secrets
 private static final String consumerKey = "r3dMV3mLbacY4CKdtAaNQ";
 private static final String consumerSecret = "FomuK2bcO7KPfUyk1PHtx91AAIEQCumvrYxLUwCiHlg";
 private static final String accessToken = "700867251-eFsXm3YteXZ2QRZY9Ydq2RDMkk1ZdFcnSMnb8Izr";
 private static final String accessTokenSecret = "e9Cj6RDFGMBL7kOILJBgfXEEIMZ6uRwqtTggxpnLM";
 
 //OAuth Header Keys
 private static final String oauth_consumer_key = "oauth_consumer_key";
 private static final String oauth_nonce = "oauth_nonce";
 private static final String oauth_signature = "oauth_signature";
 private static final String oauth_signature_method = "oauth_signature_method";
 private static final String oauth_timestamp = "oauth_timestamp";
 private static final String oauth_token = "oauth_token";
 private static final String oauth_version = "oauth_version";
 private static final String status = "status";
 
 private EditText tweetEditor;
 private Button postButton;
 private TextView label;
 private ListView list;
 private ArrayAdapter<Tweet> adapter;
 private Timer timer;
 
 private class Tweet {
  String name;
  String screenName;
  String text;
  String date;
  private Tweet(String name, String screenName, String text, String date) {
   this.name = name;
   this.screenName = screenName;
   this.text = text;
   this.date = date;
  }
  @Override
  public String toString() {
   return name + " - @" + screenName + "\n" + text + "\n" + date;
  }
 }
 private ArrayList<Tweet> tweets = new ArrayList<Tweet>();
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.simple_twitter_client);

        postButton = (Button)findViewById(R.id.postButton);
        tweetEditor = (EditText)findViewById(R.id.tweetEditor);
        tweetEditor.addTextChangedListener(new TextWatcher() {
   public void onTextChanged(CharSequence s, int start, int before, int count) {
    if(tweetEditor.getText().length() == 0) {
     postButton.setEnabled(false);
    }
    else {
     postButton.setEnabled(true);
    }
   }
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) { }
   
   public void afterTextChanged(Editable s) { }
  });
        
        label = (TextView)findViewById(R.id.label);
        list = getListView();
        adapter = new ArrayAdapter<Tweet>(this, android.R.layout.simple_list_item_1, tweets);
        list.setAdapter(adapter);
    }
    
    @Override
    protected void onResume() {
     super.onResume();
     timer = new Timer();
        timer.schedule(new TimerTask() {
   @Override
   public void run() {
    runOnUiThread(new Runnable() {
     public void run() {
      updateTweets(null);
     }
    });
   }
  }, 0, 10000); 
    }
    
    @Override
    protected void onPause() {
     super.onPause();
     if(timer != null) {
      timer.cancel();
     }
    }
    
    public void updateTweets(View button) {
     getHomeTimeline();
    }
    
    private void alertUser(String str) {
     new AlertDialog.Builder(this)
          .setMessage(str)
          .setNeutralButton("OK", null)
          .show();
    }
    
    public void post(View button) {
     HttpClient client = new DefaultHttpClient();
     HttpPost post = new HttpPost(postURL);
     try {
      String message = tweetEditor.getText().toString();
      addAuthorizationHeader(message, "POST", postURL, post);
      post.setEntity(new StringEntity(percentEncode(status) + "=" + percentEncode(message)));
      HttpResponse response = client.execute(post);
      parsePostResponse(response);
     }
     catch(Exception e) {
      Log.e(SimpleTwitterClient.class.getName(), e.getMessage());
      alertUser("Error: " + e.getMessage());
     }
    }
    
    private void getHomeTimeline() {
     class TwitterTask extends AsyncTask<Void, Void, HttpResponse> {
   @Override
   protected HttpResponse doInBackground(Void... params) {
    try {
     HttpClient client = new DefaultHttpClient();
     HttpGet get = new HttpGet(getHomeTimelineURL);
     addAuthorizationHeader(null, "GET", getHomeTimelineURL, get);
     return client.execute(get);
    }
    catch(final Exception e) {
     Log.e(GetActivity.class.getName(), e.getMessage());
     label.post(new Runnable() {
      public void run() {
       label.setText("Error: " + e.getMessage());
      }
     });
        return null;
    }
   }
   @Override
   protected void onPostExecute(HttpResponse response) {
    super.onPostExecute(response);
    if(response != null) {
     try {
      parseGetHomeTimelineResponse(response);
      adapter.notifyDataSetChanged();
     }
     catch(Exception e) {
      Log.e(GetActivity.class.getName(), e.getMessage());
         label.setText("Error: " + e.getMessage());
     }
    }
   }
     }
     new TwitterTask().execute();
    }
    
    private void addAuthorizationHeader(String tweet, String method, String url, HttpRequestBase request) throws Exception {
     if(!method.equals("GET") && !method.equals("POST")) {
      throw new IllegalArgumentException();
     }
     request.setHeader("Content-Type", "application/x-www-form-urlencoded");
  
     String nonce = base64Encode((UUID.randomUUID().toString().replaceAll("-", "").getBytes()));
  String signatureMethod = "HMAC-SHA1";
  String timeStamp = String.valueOf(new Date().getTime() / 1000);
  String version = "1.0";
  
  //Generating the Parameter String:
  //Add request parameters and status message alphabetically (DO NOT ADD SIGNATURE)
  //Encode keys and values while adding
  //Insert = character between each key and its value
  //Add an ampersand (&) to the end if there are more parameters
  String parameterString = 
     percentEncode(oauth_consumer_key) + "=" + percentEncode(consumerKey) + "&" +
     percentEncode(oauth_nonce) + "=" + percentEncode(nonce) + "&" +
     percentEncode(oauth_signature_method) + "=" + percentEncode(signatureMethod) + "&" +
     percentEncode(oauth_timestamp) + "=" + percentEncode(timeStamp) + "&" +
     percentEncode(oauth_token) + "=" + percentEncode(accessToken) + "&" +
     percentEncode(oauth_version) + "=" + percentEncode(version);
  
  if(tweet != null) {
   parameterString += "&" + percentEncode(status) + "=" + percentEncode(tweet);;
  }
     
  
  //Generate the SignatureBaseString
  String signatureBaseString = method + "&" + percentEncode(url) + "&" + percentEncode(parameterString);

  //Generate the SigningKey
  String signingKey = percentEncode(consumerSecret) + "&" + percentEncode(accessTokenSecret);
  
  //Generate HMAC-MD5 signature
        String signature = generateHmacSHA1(signingKey, signatureBaseString);
        
  //Build the HTTP Header
        String oauthHeader =
    "OAuth " + 
    percentEncode(oauth_consumer_key) + "=\"" + percentEncode(consumerKey) + "\", " +  
    percentEncode(oauth_nonce) + "=\"" + percentEncode(nonce) + "\", " + 
    percentEncode(oauth_signature) + "=\"" + percentEncode(signature) + "\", " + 
       percentEncode(oauth_signature_method) + "=\"" + percentEncode(signatureMethod) + "\", " + 
       percentEncode(oauth_timestamp) + "=\"" + percentEncode(timeStamp) + "\", " + 
       percentEncode(oauth_token) + "=\"" + percentEncode(accessToken) + "\", " + 
       percentEncode(oauth_version) + "=\"" + percentEncode(version) + "\"";
        
        request.addHeader("Authorization", oauthHeader);
    }
    
    private String generateHmacSHA1(String key, String value) throws Exception {
     SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(ENCODING), "HmacSHA1");
     Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(keySpec);
        byte[] result = mac.doFinal(value.getBytes(ENCODING));
        return base64Encode(result);
    }
    
    private void parsePostResponse(HttpResponse response) throws IllegalStateException, IOException, JSONException {
     StringBuilder sb = getResponseBody(response);
     if(response.getStatusLine().getStatusCode() == 200) {
      getHomeTimeline();
      //alertUser("Tweet Successful!\nID: " + new JSONObject(sb.toString()).get("id"));
     }
     //Not OK
     else {
      Log.e(SimpleTwitterClient.class.getName(), "Response Code: " + response.getStatusLine().getStatusCode() + "\nResponse: " + sb.toString());
      alertUser("Error Code: " + response.getStatusLine().getStatusCode() + "\n" + new JSONObject(sb.toString()).getString("error"));
     }
    }
    
    private void parseGetHomeTimelineResponse(HttpResponse response) throws IllegalStateException, IOException, JSONException {
     StringBuilder sb = getResponseBody(response);
     if(response.getStatusLine().getStatusCode() == 200) {
      tweets.clear();
      JSONArray jsTweets = new JSONArray(sb.toString());
      if(jsTweets.length() == 0) {
       label.setText("No tweets");
      }
      else {
       label.setText("Last " + jsTweets.length() + " tweet(s):");
       for(int i = 0; i < jsTweets.length(); i++) {
        JSONObject jsTweet = jsTweets.getJSONObject(i);
        JSONObject jsUser = jsTweet.getJSONObject("user");
        Tweet tweet = new Tweet(jsUser.getString("name"), jsUser.getString("screen_name"), jsTweet.getString("text"), jsTweet.getString("created_at"));
        tweets.add(tweet);
       }
      }
     }
     //Not OK
     else {
      Log.e(SimpleTwitterClient.class.getName(), "Response Code: " + response.getStatusLine().getStatusCode() + "\nResponse: " + sb.toString());
      alertUser("Error Code: " + response.getStatusLine().getStatusCode() + "\n" + new JSONObject(sb.toString()).getString("error"));
     }
    }
    
    private static StringBuilder getResponseBody(HttpResponse response) throws IllegalStateException, IOException {
     InputStream is = response.getEntity().getContent();
  BufferedReader br = new BufferedReader(new InputStreamReader(is));
  StringBuilder sb = new StringBuilder();
  String line = null;
  while((line = br.readLine()) != null) {
   sb.append(line);
  }
  br.close();
  return sb;
    }
    
    private String percentEncode(String s) throws UnsupportedEncodingException {
        //This could be done faster with more hand-crafted code.
        return URLEncoder.encode(s, ENCODING)
          // OAuth encodes some characters differently:
                .replace("+", "%20").replace("*", "%2A")
                .replace("%7E", "~");
    }

    private static String base64Encode(byte[] array) {
     return Base64.encodeToString(array, Base64.NO_WRAP);
    }
    
}

simple_twitter_client.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/tweetEditor"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="65"
            android:inputType="textMultiLine"
            android:maxLength="140"
            android:padding="10dp"
            android:textAppearance="@android:style/TextAppearance.Small" />

        <Button
            android:id="@+id/postButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="35"
            android:drawablePadding="5dp"
            android:drawableRight="@drawable/twitter_logo"
            android:enabled="false"
            android:onClick="post"
            android:text="Tweet"
            android:textAppearance="@android:style/TextAppearance.Small" />
    </LinearLayout>

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:divider="#0041c5"
        android:dividerHeight="2dp"/>

</LinearLayout>

Output:
Figure 3: A simple Twitter client using the Rest API

Reading tweets can also be done using the Streaming API, but that would be another blog. There's definitely a lot of work to do to make this client usable in real life such as getting the links to work and better tweet parsing, but this example provides a good knowledge and code base to use the Rest API in your applications.


Further Reading:
Twitter Rest API
Twitter Rest API GET statuses/user_timeline
Twitter Rest API POST statuses/update
Twitter Rest API GET statuses/home_timeline
Authorizing a request
Creating a signature
Percent encoding parameters
Twitter Web Intents
Working with Timelines
Tweets
Twitter Apps
The Streaming APIs
twitter4j
Tweeting from Android Apps using twitter4j
Apache HTTP Client