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

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

  1. ArcGIS Runtime for Android 使用异步GP服务绘制等值线

    关于基于Android上ArcGIS Server GP服务的调用,已经有前辈给出了很好的例子: http://blog.csdn.net/esrichinacd/article/details/92 ...

  2. Android 图片异步加载的体会,SoftReference已经不再适用

      在网络上搜索Android图片异步加载的相关文章,目前大部分提到的解决方案,都是采用Map<String, SoftReference<Drawable>>  这样软引用的 ...

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

    将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就 ...

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

    今天要介绍的是Github上一个使用非常广泛的图片异步加载库Android-Universal-Image-Loader,该项目的功能十分强大,可以说是我见过的目前功能最全.性能最优的图片异步加载解决 ...

  5. (转)ArcGIS Runtime for Android 使用异步GP服务绘制等值线

    关于基于Android上ArcGIS Server GP服务的调用,已经有前辈给出了很好的例子: http://blog.csdn.net/esrichinacd/article/details/92 ...

  6. Android高效异步图片加载框架

    概述 Android高效异步图片加载框架:一个高效的异步加载显示的图片加载框架,同时具备图片压缩,缓存机制等特性. 详细 代码下载:http://www.demodashi.com/demo/1214 ...

  7. android 网络异步加载数据进度条

    ProgressDialog progressDialog = null; public static final int MESSAGETYPE = 0; private void execute( ...

  8. 使用HttpClient来异步发送POST请求并解析GZIP回应

    .NET 4.5(C#): 使用HttpClient来异步发送POST请求并解析GZIP回应 在新的C# 5.0和.NET 4.5环境下,微软为C#加入了async/await,同时还加入新的Syst ...

  9. Android探索之HttpURLConnection网络请求

    前言: 最近一直想着学习一下比较好的开源网络框架okhttp,想着学习之前还是先总结一下Android原生提供的网络请求.之前一直在使用HttpClient,但是android 6.0(api 23) ...

随机推荐

  1. 犯罪团伙利用POS机刷信用卡积分转卖 年获利千万

      今年1月20日,广东省公安厅展示去年缴获的盗刷专用POS机. 今年1月20日,广东省公安厅展示了一批缴获的盗刷信用卡工具. 他们是一群靠信用卡谋生的年轻人,平均年龄不超过30岁. 他们将各银行信用 ...

  2. java--折半查找

    /* 折半查找 */ class TwoSearch { //折半查找可以提高效率,但必须得保证是有序的数组 public static int halfSearch(int[] arr,int ke ...

  3. Java面向对象的编程

    类的多态性: Java语言中含有方法重载与成员覆盖两种形式的多态:(区别于c++) 方法重载:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同. 成员覆盖:子类与父类允许具有 ...

  4. SVN使用技巧

    安装 下载SVN服务端:VisualSVN Server https://www.visualsvn.com/downloads/ 安装,下一步...(更改地址,Location是安装目录,Repos ...

  5. spring的常用配置

    bean.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http ...

  6. discuz bbs注册,登录流程整理!想打通bbs又不想读一遍代码可以参考一下

    bbs 用户注册流程 第一步: /source/class/class_member.php: on_register注册入口 L602 左右 if(!$activation) {//不为空,说明用户 ...

  7. Java中 hashCode()方法详解

    先来看下Object源码里hashcode方法: /**     * Returns a hash code value for the object. This method is      * s ...

  8. 再次复习数据结构:c语言链表的简单操作

    最近呢,又要面临多次的数据结构与算法方面的试题了,而我呢,大概也重新温习c语言的基本要点快一个月了,主要是针对指针这货的角度在研究c语言,感觉又学到了不少. 现在c指针感觉知道点了,也就匆忙开展数据结 ...

  9. android获取系统wifi状态等

    WIFI 获取WIFI状态 WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); ...

  10. 告别IE给我们的web开发带来的困扰(使用chrome frame v8引擎)

    茶爸爸个人微信:benyzhous,公众号:cha-baba欢迎骚扰 由于客户所有机器必须使用IE6浏览器,导致我们在开发项目过程中遇到非常多的样式与性能问题,在偶然的一次使用360软件管家搜索chr ...