2014年1月22日水曜日

Simple guide to ExecutorService Android

ExecutorService is almost same as ThreadPoolExecutor, it allows you to carry out multiple background thread in sequentially.

What can ExecutorService do? Do you know that powerful AsyncTask method is only allows to be executed one time in single activity. If you execute AsyncTask more then one time in single activity, you will get the errors.

So it is where ExecutorService comes, it help you handle multiple background task which AsyncTask cannot be done.


Try it out!!

It is always easy to understand if you get something work first. 

First, prepare server script. 
<?php  
 echo "hello! I am server.";
?>

Remember the url of your server. Then, copy following android code and replace the url to your own and give permission to internet.

package com.example.apps1;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
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.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.Toast;

public class MainActivity extends Activity {

 private Button mButton;
 private ExecutorService pool;
 
 public static final String URL = "http://path_to_server/";
 
 private static final String TAG = MainActivity.class.getName();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
               
       
        mButton = new Button(this);
        mButton.setText("Press");
        
        addContentView(mButton, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
         
        pool = Executors.newSingleThreadExecutor();
        
        mButton.setOnClickListener(mClickListener);
    }
    
    private View.OnClickListener mClickListener = new View.OnClickListener(){

  @Override
  public void onClick(View v) {
   pool.execute(new NetworkService());   
  }     
    };    
    
    
    private class NetworkService implements Runnable {
     private Handler mHandler;
     public NetworkService(){
      mHandler = new Handler(Looper.getMainLooper()){
       
       @Override
       public void handleMessage(Message inputMessage){
        
        String msg = (String) inputMessage.obj;        
        Toast.makeText(getApplicationContext(), ""+msg, Toast.LENGTH_SHORT).show();        
       }       
      };
     }
     
  @Override
  public void run() {
   String recv = HttpRequest(URL);
   sendMessage(1, recv);
  }
  
  public void sendMessage(int what, String msg){
   Message message = mHandler.obtainMessage(what , msg); 
   message.sendToTarget();
  }
     
    }
    
    // Doing all Networking task, if failed to get message from server, it will return null  
 // 
 public String HttpRequest(String url) {
      
   DefaultHttpClient client = new DefaultHttpClient();
   HttpResponse response = null;
     
   try{          
    HttpGet httpGet = new HttpGet(url);     
    response = client.execute(httpGet);     
           
    return EntityUtils.toString(response.getEntity());

   }catch(ClientProtocolException e){
    Log.e(TAG,"ClientProtocolException : "+e);
   }catch(IOException e){
    Log.e(TAG,"IOException : "+e);
   }      

   return null;
  }
}

Press the button and toast will show you the message from server.




Explanation

ExecutorService can be instanced in few different way.

  1. Executors.newSingleThreadExecutor();
  2. Executors.newFixedThreadPool(10); 
  3. Executors.newScheduledThreadPool(10);

Based on how ExecutorService is instanced, ExecutorService will be working in different way. For more detail please checking Executors API. In this guide, I am using newSingleThreadExecutor, it only allows one task in the same time.

NetworkService is implemented with Runnable. NetworkService is almost same as AsyncTask, contains UI thread callback method, doInBackground method.

In this case, same method of AsnycTask and NetworkService

  • doInBackground == run
  • onPostExecute == handleMessage
private class NetworkService implements Runnable {
 private Handler mHandler;
 public NetworkService(){
  
  // Handler looper constructor to declare this handler
  // is going to link to UI Thread 
  mHandler = new Handler(Looper.getMainLooper()){
   
   // handleMessage method is callback of background work.
   // All the tasks in handleMessage will be pass to UI thread
   //
   // This method is same as AsyncTask.onPostExecute        
   @Override
   public void handleMessage(Message inputMessage){
    
    String msg = (String) inputMessage.obj;        
    Toast.makeText(getApplicationContext(), ""+msg, Toast.LENGTH_SHORT).show();        
   }       
  };
 }
 
 // Runnable.run method doing all tasks in background
 // It is same as AsyncTask.doInBackground
 @Override
 public void run() {
  String recv = HttpRequest(URL);
  
  // Get the result from HttpRequest and past it to handler.
  sendMessage(1, recv);
 }
 
 public void sendMessage(int what, String msg){
  Message message = mHandler.obtainMessage(what , msg); 
  message.sendToTarget();
 }
 
}


Every time user press the button, NetworkService will be created and added to background's pool. If there is no task queue in front the NetworkService task, it will be executed.

pool.execute(new NetworkService());   

Background task is not an easy task. There are much more work then the sample code on above such as shutdown or pause the ExecutorService. What I have explain in this article is to get you works on your apps quickly. 

End

Hope this article help you in coding. Thank you for reading.

3 件のコメント: