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

时间: 2024-10-13 00:26:15

Android实现异步处理 -- HTTP请求的相关文章

Android图片异步加载之Android-Universal-Image-Loader

将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就顺便整理记录下来,作为这一个多月来博客的重新开火做饭吧.从今天起我会陆续恢复博客的更新,也希望大家继续支持. 今天要介绍的是Github上一个使用非常广泛的图片异步加载库Android-Universal-Image-Loader,该项目的功能十分强大,可以说是我见过的目前功能最全.性能最优的图片异

浅谈android中异步加载之&quot;取消异步加载&quot;二

首先,我得解释一下为什么我的标题取消异步加载打引号,这是因为可能最后实现效果并不是你自己想象中的那样.大家看取消异步加载,这不是很简单吗?AsyncTask中不是有一个cancel方法吗?直接调用该方法不就行了吗?但是事实上是这样的吗?如果真是这样,我相信我就没有以写这个作为一篇博客的必要了.为什么会有这样的想法呢?实际上源于我上一篇中Demo中的一个BUG,然后解决该BUG,需要去取消异步任务,是怎么样,我们不妨来看看. 首先,还是来一起回顾一下上篇博客中加载进度条Demo吧. AsyncTa

Android Handler 异步消息处理机制的妙用 创建强大的图片加载类

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号:55032675 上一篇博客介绍了Android异步消息处理机制,如果你还不了解,可以看:Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 .那篇博客的最后,提出可以把异步消息处理机制不仅仅是在MainActivity中更新UI,可以用到别的地方,

Android图片异步加载之Android-Universal-Image-Loader(转)

今天要介绍的是Github上一个使用非常广泛的图片异步加载库Android-Universal-Image-Loader,该项目的功能十分强大,可以说是我见过的目前功能最全.性能最优的图片异步加载解决方案.做Android的同学都知道,Android加载大量图片时,由于系统分配给图片加载的内存大小有限,所以,如果加载图片量非常大的话容易报OOM异常,关于这个异常已经有不少解决方案了,我就不赘述.下面就简要介绍下这个开源项目的主要功能和使用: 一.功能概要 多线程图片加载: 灵活更改ImageLo

自己动手写android图片异步加载库(二)

在<自己动手写android图片异步加载库>系列的第一篇文章中,主要是学习了使用ReferenceQueue来实现一个内存缓存.在这篇文章中主要是介绍在下载很多图片是怎么控制线程和队列.在这版代码里,加入信号量和队列,可以控制下载任务的顺序.可以控制暂停和结束. 代码A:ImageLoader.java /** * 图片加载工具类 * * @author qingtian * @blog http://blog.csdn.net/bingoSpunky */ @SuppressLint(&qu

Android:异步处理之AsyncTask的应用(二)

前言 在上一篇文章中<Android:异步处理之Handler+Thread的应用(一)>,我们知道Android的UI主线程主要负责处理用户的按键事件.用户的触屏事件以及屏幕绘图事件等:既然UI老人家都这么忙了,我们这些开发者肯定不能不识趣的去添乱阻塞UI线程什么的,否则UI界面万一停止响应了呢——这不是招骂的节奏么?!所以我们知道用Handler+Thread的方法,在子线程中处理耗时的任务,任务完成后通过Handler通知UI主线程更新UI界面,皆大欢喜有木有. 可是这样,还是有某些人觉

android listview 异步加载图片并防止错位

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

Android图片异步加载之Android-Universal-Image-Loader类库的使用

Android开发中我们会经常遇到图片过多或操作不当造成Out of Memory异常,有时虽然是解决了这个问题但却会影响程序的运行效率,例如:当用户在快速滑动滚动条的过程中,我们程序在仍在艰难的加载服务器端的图片,这样给用户造成了极不好的体验.其实网络上关于图片的异步加载和缓存的讲解很多,但是其实,写一个这方面的程序还是比较麻烦的,要考虑多线程,缓存,内存溢出等很多方面,针对这一广大开发者都会遇到的问题,一些牛人们已经帮我们解决了这一问题,今天我为大家介绍一款很流行的开源类库,可以很很好的解决

android listview 异步加载图片并防止错位+双缓存

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite