(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);
} }
}

  

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

  1. Android异步消息处理机制(多线程)

    当我们需要执行一些耗时操作,比如说发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如果不将这类操作放在子线程里去执行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用. ...

  2. 《Android进阶》之第三篇 深入理解android的消息处理机制

    Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 android的消息处理机制(图+源码分析)——Looper,Handler,Message an ...

  3. 转 Android的消息处理机制(图+源码分析)——Looper,Handler,Message

    作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种 ...

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

    原文地址:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html#!comments 作为一个大三的预备程序员,我学习 ...

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

    android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android ...

  6. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

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

    在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语. 通信的同步(Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将 ...

  8. Android多线程通信机制

    掌握Android的多线程通信机制,我们首先应该掌握Android中进程与线程是什么. 1. 进程 在Android中,一个应用程序就是一个独立的进程(应用运行在一个独立的环境中,可以避免其他应用程序 ...

  9. Android异步消息处理机制

    安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...

随机推荐

  1. PyQt(Python+Qt)学习随笔:窗口部件大小策略sizePolicy与SizeConstraint布局大小约束的关系

    在<PyQt(Python+Qt)学习随笔:Qt Designer中部件的三个属性sizeHint缺省尺寸.minimumSizeHint建议最小尺寸和minimumSize最小尺寸>. ...

  2. 深入理解C#中的异步(一)——APM模式EAP模式

    深入理解C#中的异步(一)--APM模式EAP模式 目录 深入理解C#中的异步(一)--APM模式EAP模式 1 使用异步编程的原因 2 异步编程模式 2.1 APM模式 2.1.1 APM模式示例代 ...

  3. Mac上使用Docker Desktop安装Kubernetes

    下载镜像需要特殊手段,你懂的,如果没有特殊手段,建议放弃这种方式. 1 启用Kubernetes 启用过程,会联网下载kubenetes相关的核心组件镜像,如下是我下载好的: 下载好了,kubenet ...

  4. 第五篇 Scrum 冲刺博客

    一.站立式会议 1. 会议照片 2. 工作汇报 团队成员名称 昨日(26日)完成的工作 今天(27日)计划完成的工作 工作中遇到的困难 陈锐基 - 完成发布页面的布局- 完成发布动态的功能 - 优化当 ...

  5. Acwing 734. 能量石

    贪心(微扰) + dp 这道题还是比较难的,前置知识: 贪心的微扰(邻项交换)证法,例题:国王游戏,耍杂技的牛 01背包 算法1:暴力\(O(T * n! * n)\) 可以\(dfs\)全排列枚举所 ...

  6. modelViewSet 获取未序列化的数据

    1 views/  perform_create 方法的使用  获取未序列化的数据 def perform_create(self, serializer): user_obj=serializer. ...

  7. 多个HDFS集群的fs.defaultFS配置一样,造成应用一直连接同一个集群的问题分析

    背景 应用需要对两个集群中的同一目录下的HDFS文件个数和文件总大小进行比对,在测试环境中发现,即使两边HDFS目录下的数据不一样,应用日志显示两边始终比对一致,分下下来发现,应用连的一直是同一个集群 ...

  8. CentOS 7 mini版本安装后必须要做的几件事

    Linux家族非常的庞大,诸如Debian.Ubuntu.RedHat.CentOS.ArchLinux.Gentoo,甚至还有国内比较有名的Deepin,都是Linux家族闪耀的明星.根据实际需求涉 ...

  9. Numpy的学习2-基础运算1

    import numpy as np a=np.array([10,20,30,40]) # array([10, 20, 30, 40]) b=np.arange(4) # array([0, 1, ...

  10. 在linux下搭建NFS服务器实现文件共享

    目录 一.关于NFS 二.搭建一台NFS服务器共享特定资源 三.调优 一.关于NFS 1.NFS是Network File System的简写,即网络文件系统.网络文件系统是FreeBSD支持的文件系 ...