Androidが非同期処理を実現--HTTPリクエストを例に

21029 ワード

AndroidがUIを操作する方法はスレッドセキュリティではありません.つまり、開発者が自分で生成したスレッドオブジェクトはUIを操作できません.例えば、新しいスレッドでTextViewを修正し、Toastを生成します.
 
時間のかかる業務を処理し、UIを両立させるためには、新しいスレッドを生産しなければならないが、このスレッドはUIを両立させることができず、メインスレッドに更新UIのMessageを送信し、メインスレッドのメッセージポンプでメッセージをキャプチャして処理することができる.
 
Androidも開発者のために上記のソリューションをパッケージ化し、AsynTaskを使用しています.しかし、個人的にはこれはあまり使いにくいと思います.結局、異なる任務はAsynTaskを新しく書く必要がありますが、このAsynTaskはそんなに便利ではありません.むしろRunnableインタフェースを直接実現し、自分のスレッドプールとHandlerで非同期処理を実現したほうがいい(実はAsynTaskはスレッドプールとHandlerをカプセル化しているので、興味があるのは私の前回のAsynTaskを紹介したブログを参考にしてください).
 
Android非同期処理を自分で実現する方法を紹介します.
 
まず,非同期処理には新しいスレッドが必要であり,このスレッドにはHTTPリクエストなどのブロックされるトラフィックを入れる.では、自分のスレッドオブジェクトを管理するためにスレッドプールが必要です.具体的にはjavaを用いる.util.concurrent.ThreadPoolExecutorクラス、concurrentはjdk 1.5の新しいパッケージで、開発者にスレッドとスレッドに関連するいくつかのメカニズムを提供します.私の同級生の話を聞いて、今から自分でnew Thread()に行かないでください.そうすると性能が下がります.アプリケーション全体にThreadPoolExecutorオブジェクトを1つだけ提供し、新しいスレッドが必要になったら、そこに取りに行きます.
 
package com.chenjun.utils;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


/**
 *       ,                   。
 *          、     、           ,            。
 *          Android AsynTask    。
 * @author zet
 *
 */
public class ThreadPoolUtils {
    
    private ThreadPoolUtils(){
        
    }
    
    //        
    private static int CORE_POOL_SIZE = 5;
    
    //        
    private static int MAX_POOL_SIZE = 100;
    
    //           
    private static int KEEP_ALIVE_TIME = 10000;
    
    //    。         ,           ,        。
    private static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(
            10);
    
    //    
    private static ThreadFactory threadFactory = new ThreadFactory() {
        private final AtomicInteger integer = new AtomicInteger();

        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "myThreadPool thread:" + integer.getAndIncrement());
        }
    };
    
    //   
    private static ThreadPoolExecutor threadPool;
    
    static {
        threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
                MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, workQueue,
                threadFactory);
    }
    
    
    /**
     *          ,     Runnable  
     * @param runnable
     */
    public static void execute(Runnable runnable){
        threadPool.execute(runnable);
    }

}

 
スレッドプールがあれば、独自のRunnable(またはCallable)を作成してビジネスを実現し、スレッドプールに渡してスレッドを割り当ててビジネスを完了させます.
 
ここの業務はAndroidのHTTPダウンロードを例に挙げています.
 
AndroidのHTTPサービスでは、アプリケーション全体で1つのHttpClientオブジェクトしか必要とせず、スレッドが安全なHttpClientを生成することができます.このHttpClientは、複数のHttpGet、HttpPostにサービスを提供することができます.具体的なコードは以下の通りです.ここでは「Android 3に精通している」のソースコードを直接コピーしました.
 
package com.chenjun.network.http;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;


/**
 *    ,              HttpClient  。
 *                  ,       HttpGet、HttpPost     
 * @author zet
 *
 */
public class HttpClientHelper {
    private static HttpClient httpClient;
    
    private HttpClientHelper(){
        
    }
    
    public static synchronized HttpClient getHttpClient(){
        if(null == httpClient){
            //     
            HttpParams params = new BasicHttpParams();
            
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
            HttpProtocolParams.setUseExpectContinue(params, true);
            
            
            //          
            ConnManagerParams.setTimeout(params, 1000);
            
            //      
            HttpConnectionParams.setConnectionTimeout(params, 5000);
            //  Socket  
            HttpConnectionParams.setSoTimeout(params, 10000);
            
            SchemeRegistry schReg = new SchemeRegistry();
            schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 80));
            
            ClientConnectionManager conManager = new ThreadSafeClientConnManager(params, schReg);
            
            httpClient = new DefaultHttpClient(conManager, params);
        }
        
        return httpClient;
    }
}

 
 
    
このHttpClientにはいくつかの初期化構成の属性があり、HttpGetとHttpPostが特定の属性を設定していない場合、生成されたHttpGetとHttpPostはHttpClientの初期化属性に沿って使用されます.しかし、状況に応じて、HttpGetとHttpPostにプロパティを設定することができます.これらのプロパティは、HttpClientの初期化プロパティを上書きします.これにより、私たちが得たHttpGetとHttpPostには特定のプロパティがあります.
   
以上の内容があれば、私たちは自分のActivityでRunnableとHandlerを実現することができます.Runnableでビジネスロジックを完了し、適時にMessageをHandlerに送信してUIを更新し、HandlerでMessageを処理し、UIとインタラクティブにします.インスタンスコード:
 
package com.chenjun.httpdemo;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Toast;

import com.chenjun.asynctask.DownloadImageTask;
import com.chenjun.network.http.HttpClientHelper;
import com.chenjun.utils.ThreadPoolUtils;

public class HttpDemoActivity extends Activity {
    private static final int START_DOWNLOAD_MESSAGE = 0x01;
    private static final int FINISH_DOWNLOAD_MESSAGE = 0x02;
    private static final int ERROR_DOWNLOAD_MESSAGE = 0x03;
    
    private Handler myHandler;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        myHandler = new MyHandler();
        ThreadPoolUtils.execute(new MyRunnable());
    }
    private class MyRunnable implements Runnable{
        @Override
        public void run() {
            
            HttpGet httpGet = new HttpGet("http://www.sina.com.cn");
            
            //   HttpGet         ,      HttpClient
            HttpParams params = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(params, 60000);
            httpGet.setParams(params);
            
            myHandler.sendEmptyMessage(START_DOWNLOAD_MESSAGE);
            
            try {
                
                HttpResponse httpResponse = HttpClientHelper.getHttpClient().execute(httpGet);;
                
                byte[] bytes = EntityUtils.toByteArray(httpResponse.getEntity());
                //       ,        XML  Json。              Message 。
                //
                String result = new String(bytes);
                
                Message msg = myHandler.obtainMessage();
                msg.what = FINISH_DOWNLOAD_MESSAGE;
                msg.obj = result;
                
                myHandler.sendMessage(msg);
                
            } catch (Exception ex){
                ex.printStackTrace();
                myHandler.sendEmptyMessage(ERROR_DOWNLOAD_MESSAGE);
            }
        }
    }
    
    private class MyHandler extends Handler{
        @Override
        public void dispatchMessage(Message msg) {
            switch(msg.what){
            case START_DOWNLOAD_MESSAGE:
                Toast.makeText(HttpDemoActivity.this, "    ", Toast.LENGTH_SHORT).show();
                break;
case FINISH_DOWNLOAD_MESSAGE: Toast.makeText(HttpDemoActivity.this, " ", Toast.LENGTH_SHORT).show(); // , 。 System.out.println(msg.obj); break;
case ERROR_DOWNLOAD_MESSAGE: Toast.makeText(HttpDemoActivity.this, " ", Toast.LENGTH_SHORT).show(); break;
default: System.out.println("nothing to do"); break; } } } }

 
まとめ:個人的にはAsynTaskに慣れていないので、このような書き方に傾いています.Googleが開発したAsynTaskはもっと深い意味を持っているかもしれませんが、私はまだ理解していないので、しばらく自分のこのような書き方を続けました.
また、著者は就職活動中であり、意向のある連絡[email protected].誠にあらず邪魔するな.