zoukankan      html  css  js  c++  java
  • Android实现异步处理 -- HTTP请求

    原帖:http://www.cnblogs.com/answer1991/archive/2012/04/22/2464524.html

    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对象,当我们需要新的线程的时候,去那里取。

     

     
    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服务,我们的整个应用程序也只需要一个HttpClient对象,可以生成一个线程安全的HttpClient,这个HttpClient可以为我们多个HttpGet、HttpPost提供服务。具体代码如下:

     

     
    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有更为深远的意义,但是我暂时还没领会到,所以就暂时沿用自己的这种写法了。

  • 相关阅读:
    程序猿也爱学英语(上),有图有真相
    New Year's resolution for 2016
    Got the Best Employee of the year 2015 Star Award
    AngularJs项目文件以及文件夹结构
    完成AngularJS with MVC 5, Web API 2项目
    两则新闻引发的思考
    关于招聘的最新信息
    架构师面试更新版
    2015新加坡总统府
    关于2014年的旅行
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4925197.html
Copyright © 2011-2022 走看看