Android多线程消息处理机制

(1)主线程和ANR

主线程:UI线程,界面的修改只能在主线程中,其它线程对界面进行修改会造成异常。这样就解决了多线程竞争UI资源的问题。

一旦主线程的代码阻塞,界面将无法响应,这种行为就是Application Is Not Respond(ANR),应用失去响应。

如果主线程中某个事件操作时间超过5秒没有得到响应,Android可能会弹出一个应用程序没有响应的对话框。activity可能会被杀掉。

下面用实例来说明ANR,demo如下,这个时候按钮就会失去响应

private Button btn;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn=(Button)findViewById(R.id.button1);
		btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				try {
					//主线程耗时操作
					Thread.sleep(10*1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
	}

ANR的成因和避免:1,耗时操作是不可避免的,如网络连接和网络数据的获取,对存储器的读写,大量数据的计算,任何一种操作都有可能占用时间,例如网络不稳定,磁盘在忙,cpu被占用。 2,UI线程是负责事件的监听和绘图,因此必须保证UI线程能够随时响应用户的需求,任何对UI线程的阻塞无论时间长短都会影响用户的体验或是导致anr,UI线程中的操作要尽量的短小,耗时操作(网络连接数据访问等)要在单独的线程中完成。

(2)Android的消息机制,由于android中不可避免的要进行耗时操作,主线程阻塞会影响与用户的交互,需要一种机制能够通知主线程更新界面,这就是消息机制,Looper消息机制(主线程视角看消息机制,消息队列,looper循环读取消息,进行消息的回调),Handler消息机制(编程视角看消息机制)。下图就是Looper和Handler处理消息机制的图

对消息队列的操作只能通过Handler来进行,当Handler的sendMessage方法时就会发送一个消息,主线程Looper就会发现消息队列的改变,并响应这个消息,消息被处理后会从队里移除,处理响应消息也是由Handler来处理,handleMessage方法。这个方法是运行在主线程中的。handler中常用的方法,void handleMessage()方法,消息发送后通过这个方法来处理,这个方法需要重写。boolean sendEmptyMessage(int what)方法,只发送一个what值的消息。boolean sendMesssage(Message message)发送消息到handler,在handler中处理消息,sendMessageDelayed(Message msg ,long s),removeMessages(int what)删除message/取消定时的message。

下面以一个demo来说明Handler和message的用法,demo如下

package com.example.handlerdemo;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private static final int MESSAGE_OK=1;
	private static final int MESSAGE_CANCLE=2;
	private static final int MESSAGE_ARG1=3;
	private static final int MESSAGE_ARG2=4;
	private Handler handler=new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case MESSAGE_OK:
				txt.setText("ok");
				break;
			case MESSAGE_CANCLE:
				if(msg.arg1==MESSAGE_ARG1){
					txt.setText("cancle"+"by user");
				}else{
					txt.setText("cancle"+"by system");
				}
				break;

			default:
				break;
			}
		};
	};
	private Button btn_ok;
	private Button btn_cancle;
	private TextView txt;
	private Button btn;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn_cancle=(Button)findViewById(R.id.btn_cancle);
		btn_ok=(Button)findViewById(R.id.btn_ok);
		btn=(Button)findViewById(R.id.btn);
		txt=(TextView) findViewById(R.id.txt);

		btn_ok.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				/*Message msg=Message.obtain();//获取message实例的正规方法
				msg.what=MESSAGE_OK;
				handler.sendMessage(msg);
				handler.sendEmptyMessage(MESSAGE_OK);*///sendEmptyMessage的方法
				handler.sendEmptyMessageDelayed(MESSAGE_OK, 10000);//延时发送消息,还有定时发送消息的方法
			}
		});
		btn_cancle.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				Message msg=Message.obtain();
				msg.what=MESSAGE_CANCLE;
				msg.arg1=MESSAGE_ARG1;//message的参数。
				handler.sendMessage(msg);
			}
		});
		btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				handler.removeMessages(MESSAGE_OK);//取消发送消息
			}
		});
	}
}

在使用Message和Looper的时候我们还需要注意一下几点:1,Looper和MessageQueue是在主线程创建时就会创建的,新建线程的时候是不会默认创建的,一般情况下我们是不会去直接访问者两个对象。2,Handler必须在有Looper实例的情况下才能实例化,在新建的线程中默认是不能被实例化的,否则就会出错。3,子线程默认情况下是没有实例化Looper和MessageQueue对象的。

(3)Thread和Handler,handler的sendmessage()方法可以运行在其它线程中,这样我们就何以通过Thread和Handler的结合来解决耗时操作的问题,一下我们用一个动态显示进度条控件的demo来说明效果如下图

代码如下:

package com.example.handlerandthreaddemo;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

	private Button btnStart;
	private TextView tvDownLoad;
	private ProgressBar pbDownLoad;
	private static final int MESSAGE_NUMBER=1;
	private int i=0;
	private Handler handler=new Handler(){
		public void handleMessage(android.os.Message msg) {
			if(msg.what==MESSAGE_NUMBER){
				pbDownLoad.setProgress(i);
				tvDownLoad.setText("下载进度"+i+"%");
			}
		};
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		btnStart=(Button) findViewById(R.id.btnStart);
		tvDownLoad=(TextView)findViewById(R.id.txtDownLoad);
		pbDownLoad=(ProgressBar) findViewById(R.id.pbDownLoad);

		btnStart.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				new Thread(){
					public void run() {
						while(i<100){
							try {
								sleep(500);
								i++;
								Message msg=Message.obtain();
								msg.what=MESSAGE_NUMBER;
								handler.sendMessage(msg);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					};
				}.start();
			}
		});
	}
}

(4)AsyncTask线程间的异步通信。

AsyncTask也是一种异步加载机制,使用时要注意一下几点:1,Task的实例必须在主线程中创建,2,execute()方法必须在主线程中调用,3,不要调用onpreExecute(),doinbackground(),onprogressUpdate(),onpostExecute()等方法。4,该实例化后的task只能被执行一次,重复调用会出现异常。

AsyncTask几种常用方法:onpreExecute()方法,该方法在执行实际的后台之前被主线程调用;doInBackground()方法,是执行耗时操作,执行在后台的进程中,是抽象方法,子类必须重写,在这个方法中调用publishprogress()方法来实现实时跟新的任务进行;onProgressUpdate()方法在publisprogress()方法被调用后,UI线程调用此方法。onPostExecute()方法是在后台操作完成后来跟新界面。下面以一个实例和效果图来说明

代码

package com.example.asynctask;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private Button btnStart,btn;
	private TextView tv;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn=(Button) findViewById(R.id.button2);
		btnStart=(Button)findViewById(R.id.button1);
		tv=(TextView) findViewById(R.id.textView1);
		btnStart.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				MyAsyncTask myTask=new MyAsyncTask();
				myTask.execute();
			}
		});
		btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {

			}
		});
	}
	class MyAsyncTask extends AsyncTask<Void, Void, Void>{

		@Override
		protected void onPreExecute() {
			tv.setText("之前");
			super.onPreExecute();
		}
		@Override
		protected void onProgressUpdate(Void... values) {
			super.onProgressUpdate(values);
			String s=tv.getText().toString();
			tv.setText(s+"+");
		}
		@Override
		protected Void doInBackground(Void... params) {
			try {
				for(int i=0;i<10;i++){
					Thread.sleep(1000);
					publishProgress(null);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return null;
		}
		@Override
		protected void onPostExecute(Void result) {
			tv.setText("完成");
			super.onPostExecute(result);
		}
	}
}

  AsyncTask的参数以及使用方法:一个完整的asyncTask需要经历三次传递参数。第一次传参,execute()启动时传递给doInBackground(),如需要复制哪个文件,数据上传至哪个服务器等;第二次传参,是doInBackground()在调用 publishProgress()时传递给onProgressUpdate()如:目前的下载进度,已经下载到服务器的列表;第三次传参,是doInBackground()在运行完以后传递参数给onPostExecute(),如数据获取是否成功。

AsyncTask定义了三种泛型类型参数分别是Params,Progress和Result,即AsyncTask<Params,Progress,Result>;Params是启动任务执行的参数,比如Http的请求URL;Progress后台执行的百分比;Result后台执行的任务最终返回的结果,比如String,下面以一个demo来说明

效果图如下

demo代码如下

package com.example.asynctaskparams;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private MyTask task;
	private Button btnStart,btnClose;
	private TextView txtContext;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btnStart=(Button)findViewById(R.id.btnStart);
		btnClose=(Button)findViewById(R.id.btnClose);
		txtContext=(TextView)findViewById(R.id.txtContext);

		btnStart.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				task=new MyTask();
				//Task发送第一个Task的参数
				task.execute(15);
			}
		});
		btnClose.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				task.cancel(true);
				task=null;
				txtContext.setText("Close");
			}
		});
	}
	@Override
	protected void onDestroy() {
		if(task!=null){
			task.cancel(true);
		}
		super.onDestroy();
	}
	class MyTask extends AsyncTask<Integer, Integer, Boolean>{

		@Override
		protected void onPreExecute() {
			txtContext.setText("开始");
			super.onPreExecute();
		}
		@Override
		protected void onProgressUpdate(Integer... values) {
			//Task在此方法体中使用第二个参数
			txtContext.setText(""+values[0]);
			super.onProgressUpdate(values);
		}
		@Override
		protected Boolean doInBackground(Integer... params) {
			//Task使用第一个参数
			for(int i=0;i<params[0];i++){
				//Task发送第二个参数
				publishProgress(i);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//Task发送第三个参数
			return true;
		}
		@Override
		protected void onPostExecute(Boolean result) {
			//Task使用第三个参数
			txtContext.setText(""+result);
			super.onPostExecute(result);
		}

	}
}

  

时间: 2024-10-21 03:11:59

Android多线程消息处理机制的相关文章

Android异步消息处理机制(3)asyncTask基本使用

本文翻译自android官方文档,结合自己测试,整理如下. 概述 AsyncTask抽象类,翻译过来就是异步任务,能够合理并方便的使用UI线程.该类可以实现将后台操作结果显示在UI线程中,而不需要我们自己实现子线程或者handler(当然它内部也是借助这两者实现的). 虽然AsyncTask可以提供后台运行并将结果显示在UI上,但是理想情况应该是后台操作最多只能是几秒钟,若要执行长时间的操作强烈建议使用java中的Executor,ThreadPoolExecutor,FutureTask等.

[学习总结]6、Android异步消息处理机制完全解析,带你从源码的角度彻底理解

开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了. 这种处理方式被称为异步消息处理线程,虽然我相信大家都会用,可是你知道它背后的原理是什么样的吗?今天我们就来一起

Android异步消息处理机制(4)AsyncTask源码解析

上一章我们学习了抽象类AsyncTask的基本使用(地址:http://blog.csdn.net/wangyongge85/article/details/47988569),下面我将以问答的方法分析AsyncTask源码内容,源码版本为:API22. 1. 为什么必须在UI线程实例化我们的AsyncTask,并且必须在主线程中调用execute(Params... params)? 在分析为什么在UI线程调用之前,我们先看一下实例化AsyncTask并调用execute(Params...

【转】android的消息处理机制(图+源码分析)——Looper,Handler,Message

原文地址:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html#!comments 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android的消息处理机制,我看了Loo

android 的消息处理机制

android的消息处理机制(图+源码分析)——Looper,Handler,Message android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类.下面一一介绍: 线程的魔法师 Looper Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程.所谓Looper线程就是循环工作的线程.在程序开发中(尤

Android异步消息处理机制详解及源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 最近相对来说比较闲,加上养病,所以没事干就撸些自己之前的知识点为博客,方便自己也方便别人. 1 背景 之所以选择这个知识点来分析有以下几个原因: 逛GitHub时发现关注的isuss中有人不停的在讨论Android中的Looper , Handler , Me

Android异步消息处理机制(2)源码解析

上一章讲解了Android异步消息处理机制的基本使用,下面将简单地探寻一下异步机制背后的奥妙,源码版本为:API22. 首先,声明一下本文是在我参考了一下各位大神的文章之后才慢慢熟悉的, 若有不足之处,还望各位批评指正!.菜鸟上路,,,, 郭霖博客 鸿洋博客 刘超 深入解析android5.0系统 任玉刚博客 先后顺序按照拼音排序,无关技术本身. 先简单地总结一下Looper,MessageQueue,Message和Handler四者之间的关系: Looper和MessageQueue Loo

android即时消息处理机制

在android端做即时消息的时候,遇到的坑点是怎么保证消息即时性,又不耗电.为什么这么说呢? 原因是如果要保证消息即时性,通常有两种机制pull或者push.pull定时轮询机制,比较浪费服务器资源:push服务器推送机制,需要保持长连接,客户端和服务器都要求比较高(网络环境,服务器保持连接数等),它们的详细优缺点不描述了.上面这两种机制都要求客户端长期处于活动状态,前提是cpu处于唤醒状态,而android端有休眠机制,保证手机在大部分时间里都是处于休眠,降低耗电量,延长机时间.手机休眠后,

android的消息处理机制——Looper,Handler,Message

这篇文章有一半是copy别人的,站在巨人的肩膀上,我们才能看得更高更远...... 在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语. 通信的同步(Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将会在服务端得到同步,直到服务端返回请求. 通信的异步(Asynchronous):指客户端在发送请求后,不必等待服务端的回应就可以发送下一个请求. 所谓同步调用,就是在一个函数或方法调用时,没有得到结果之前,该调用就