【移动开发】Android中异步加载数据(一)Handler + Thread
在我们开发当中很多时候都会遇到:从网络上获取数据的操作或大量的IO数据操作时,会耗费很长时间,为了使用户的体验效果更好,现在大多数开发者都会使用异步的方式去处理这些的操作,异步的方式有好多种,在此我汇总了以下两种:
1.Handler + Thread的方式
2.AsyncTask异步更新UI界面
这两种方式各自有各自的优缺点,这里先介绍一下第一种方式
1.Hanlder介绍:
官方文档:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
一个处理程序允许你发送和处理消息或者Runnable对象关联到的一个线程的MessageQueue。每个处理程序的实例相关联着一个单个线程和一个线程的消息队列。当你创建一个新的处理程序,它被绑定到线程/消息队列的线程时创建它——从那时起,它将提供消息和runnables给消息队列和执行他们出来的消息队列。
Handler主要方法:
常用方法 | 解释 |
Message obtainMessage(int what, Object obj) | 获取一个Message对象,包含了一个标识和value |
boolean post(Runnable r) | 将Runnable r添加到消息队列,之后会被UI线程执行 |
boolean postDelayed(Runnable r, long delayMillis) | 延迟多少毫秒之后,再将该线程加入到消息队列中 |
boolean sendEmptyMessage(int what) | 发送一个只有标识的消息 |
boolean sendMessage(Message msg) | 添加一条消息到消息队列的末尾 |
void handleMessage(Message msg) | 用于处理接收到的消息 |
简单的说Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,就会报错,当然为了提高效率,Android为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信,在非UI线程发送消息到UI线程,最终让Ui线程来进行ui的操作
2.在非UI线程中发送消息通知UI线程更新界面:
import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * Handler + Thread 实现异步加载图片 * @author ZHF * */ public class MainActivity extends Activity { private static final int MSG_SUCCESS = 0;//成功获取图片 private static final int MSG_FAILURE = 1;//获取图片失败 public static final String IMG_URL = "http://p_w_picpaths.51cto.com/p_w_picpaths/index/Images/Logo.gi"; Button btn_handler; ImageView p_w_picpathView; private DownloadImgThread downloadImgThread; private Handler mHandler = new Handler(){ public void handleMessage(Message msg) { //该方法是在UI主线程中执行 switch(msg.what) { case MSG_SUCCESS: p_w_picpathView.setImageBitmap((Bitmap)msg.obj); //显示图片 break; case MSG_FAILURE: Toast.makeText(MainActivity.this, "获取图片失败", Toast.LENGTH_LONG); break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); p_w_picpathView = (ImageView) this.findViewById(R.id.p_w_picpathView); btn_handler = (Button) this.findViewById(R.id.btn_handler); btn_handler.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //开启一个非UI线程,用于下载图片 downloadImgThread = new DownloadImgThread(); downloadImgThread.start(); } }); } /**图片下载线程**/ private class DownloadImgThread extends Thread{ HttpURLConnection conn; InputStream inputStream; Bitmap imgBitmap; @Override public void run() { try { URL url = new URL(IMG_URL); if(url != null) { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(2000); connection.setDoInput(true); connection.setRequestMethod("GET"); int code = connection.getResponseCode(); if(200 == code) { inputStream = connection.getInputStream(); imgBitmap = BitmapFactory.decodeStream(inputStream); //获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象 mHandler.obtainMessage(MSG_SUCCESS,imgBitmap).sendToTarget(); }else { //获取图片失败,向ui线程发送MSG_FAILURE mHandler.obtainMessage(MSG_FAILURE).sendToTarget(); } } } catch (Exception e) { e.printStackTrace(); } } } }
在自己的线程中获取到imgBitmap,不能直接p_w_picpathView.setImageBitmap(imgBitmap),会报错的哦! 通过Message中obj将其存储起来,传递到UI线程中再对其操作。
权限:
<uses-permission android:name="android.permission.INTERNET"/>
2.View的post(Runnable r)方法
在获取到imgBitmap之后,可以通过下面代码实现UI界面的更新哦:
//另外一种更简洁的发送消息给ui线程的方法。 mImageView.post(new Runnable() { @Override public void run() {//run()方法会在ui线程执行 mImageView.setImageBitmap(bm); } });
这是为什么那?
因为Android框架为每个应用的主线程创建了一个隐式的handler,使用post方法的原理就是给这个隐式的handler发送一个Runnable对象,然后隐式的handler会在ui线程执行Runnable对象的run()方法。
下一篇(AsyncTask):http://smallwoniu.blog.51cto.com/3911954/1252156