Android Handler 异步消息处理机制
2015-08-13 20:08
561 查看
引言
首先说明我是怎么接触到这个Handler对象的,当我尝试在Activity中处理网络传输的时候,报了异常。上网查了下资料,知道了再Android 4.0 版本以上是不能在主线程(UI线程 | Activity)中进行网络连接, 网络连接通常执行耗时操作,这样会造成主线程阻塞,主线程阻塞在手机上看就是卡死机了,这样会带来很不好的用户体验。好,既然不能在主线程执行,那我就创建一个线程呗,一运行问题又来了。经过查询得知,Android 系统也不允许在子线程执行UI操作,既然不能再子线程操作,那就回Activity操作吧。经过查询,获知Handler这个对象。 然后经过上网大量查询资料,下面列出我的一些理解。概念
Handler 翻译过来就是一个处理器,负责消息处理,Android 系统中UI线程是不安全的,并且不能通过子线程更新UI,所以引入了Handler这个东西。那么Handler是怎么东西?Handler负责子线程和主线程之间发送消息。Handler机制
首先我要引入4个对象:MessageQueue
Looper
Handler
Message
MessageQueue 是一个消息队列,在Activity创建的时候会自动创建一个MessageQueue 对象和一个Looper对象。然后Looper又是什么鬼?Looper 我们暂且称为轮询器,Handler 是处理消息的处理器,Message就是传说中的消息。
下面我来理一理他们之间的关系:
首先Activtity创建,自动创建MessageQueue 和 Looper
第二步是Looper 会一直循环检测MessageQueue 里面是否有消息,如果有消息,就通知给Handler处理
第三步,Handler 收到通知,然候执行
handleMessage(Message msg)方法(下面会讲到这个方法)
这是系统内部自动执行的操作,我这里暂且不研究源代码怎么做的,大家有兴趣的话,网上有很多分析的,我这里就只是宏观上展示一下Handler处理的过程。
下面通过一个图来看看他们的关系:
图可能画得有点丑,不过也能表达我的意思了,就是Looper一直在检查MessageQueue是否有消息,如果有,就把消息给Handler处理,而在我们开发中,实际要使用的仅仅是一个Handler 对象,Handler对象不是系统创建的,需要我们在Activity中创建,然后在子线程中调用, 在创建的时候需要复写一个
HandleMessage(Message msg)方法,这个方法是收到消息的时候调用。
static Handler handler = new Handler() { public void handleMessage(Message msg) { //处理代码 } };
子线程调用方法是
sendMessage(Message msg)其实有很多重载的方法,这里只列举一个。
Message msg = Message.obtain(); handler.sendMessage(msg);
通常我们需要携带数据,这时候就需要说到Message对象了,Message对象有一个obj 属性,是一个Object类型了,你可以赋值任何的数据类型。然后如果有多个消息发送个Handler,怎么区别呢? Message还有一个成员叫做 what ,你可以指定一个int的值给它,在handlerMessage中就可以通过
switch(msg.what)方法来分别处理不同的消息了。
使用Handler需要注意的地方
除了在Activity中会自动创建MessageQueue 和 Looper 之外,子线程是没有这两个对象的,所以在使用的时候要小心。
调用了Looper的
Looper.prepare()和
Looper.loop()两个才会创建MessageQueue对象并且开始检测消息队列的。具体源码我就不分析了。
最后我给大家一个完整的Handler使用实例,该类是用于下载网络图片的。
import java.io.File; import java.io.FileOutputStream; 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.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.View; import android.widget.ImageView; public class MainActivity extends Activity { static ImageView im; static Handler handler = new Handler() { public void handleMessage(Message msg) { switch(msg.what){ case 0: Bitmap bitmap = (Bitmap) msg.obj; im.setImageBitmap(bitmap); break; case 1: } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); im = (ImageView) findViewById(R.id.imageView); } /** * Android 4.0 以上不允许在UI线程执行网络等耗时操作 并且只有主线程才能操作UI,所以要用到Handler * */ public void click(View v) { final String path = "http://192.168.32.113/crm/ui/images/ban_1.gif"; //判断缓存中是否存在图像 final File file = new File(getCacheDir(),getFileName(path)); if(file.exists()){ Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); Message msg = Message.obtain(); msg = handler.obtainMessage(); // 以上两种方式获取的message对象是从message池里面获取,可以节省内存 msg.obj = bitmap; msg.what = 0; handler.sendMessage(msg); System.out.println("从缓存获取"); }else{ System.out.println("从网络中获取"); Thread t = new Thread() { public void run() { try { URL url = new URL(path); // 强转为httpconnection HttpURLConnection conn = (HttpURLConnection) url .openConnection(); // 设置请求方式 conn.setRequestMethod("GET"); // 设置连接超时 conn.setConnectTimeout(5000); // 设置读取超时 conn.setReadTimeout(5000); // 发送请求,与服务器建立连接 conn.connect(); System.out.println(conn.getResponseCode()); if (conn.getResponseCode() == 200) { //获取输入流 InputStream is = conn.getInputStream(); //缓存图片 File file = new File(getCacheDir(),getFileName(path)); FileOutputStream fos = new FileOutputStream(file); byte[] b = new byte[1024]; int len = 0 ; while((len=is.read(b))>0){ fos.write(b, 0, len); } fos.close(); Message msg = Message.obtain(); Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); // msg = handler.obtainMessage(); // 以上两种方式获取的message对象是从message池里面获取,可以节省内存 msg.obj = bitmap; msg.what = 0; }else{ Message msg = Message.obtain(); msg.what = 1; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } }; }; t.start(); } } public String getFileName(String path){ return path.substring(path.lastIndexOf("/")); } }
以上代码为了能在一行显示,有些缩进太难看,不要介意哈。
如果大家有兴趣剖析一下源代码的话,网上有很多资料。各种解析,各种牛逼哄哄的分析,我写这个适合初学者宏观上理解一下Handler是运行机制,并没有深入解析。
最后我在加一点,通常在项目中,Handler对象都是定义为静态的,这样有很大好处,方便Android其他组件和Activity线程通信,而且Google也是支持我们这样做的。
相关文章推荐
- Android URI简介
- android的camera
- Edittext请求输入法无效的解决方案
- Android编译环境配置
- android大牛博客收集贴
- Android编译环境配置
- android 的soundpool播放声音
- android用户登录客户端代码
- Android 学习之Fragment生命周期
- android/bitmap.h 详解
- android-ndk开发helloworld
- android定位和地图开发实例
- Android闹钟
- Android ScrollView不在最顶部解决办法
- Android平台免Root无侵入AOP框架Dexposed使用详解
- Android之Service启动方式
- 平安科技移动开发二队技术周报(第十五期)
- 平安科技移动开发二队技术周报(第十四期)
- 最美应用-从Android研发工程师的角度之[厨房故事]
- Android开发实现连续跳转几个界面后在最后一个界面完美跳回最初的界面