zoukankan      html  css  js  c++  java
  • 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);
    		}
    		
    	}
    }
    

      

  • 相关阅读:
    asp.net的Context.Cache缓存过期策略
    sql语句执行时算术运算导致溢出。
    sqlserver进行join的方式选择
    Apollo配置中心
    sqlserver的left join优化
    iis设置上传文件大小限制
    Android中的颜色值
    Network authentication method and device for implementing the same
    MongoDB GridFS
    MongoDB 正则表达式
  • 原文地址:https://www.cnblogs.com/deng-c-q/p/5227142.html
Copyright © 2011-2022 走看看