Android中异步消息处理主要由四个部分组成,Message、handler、messageQueue和looper。

1、message

  message是线程之间传递的消息,他可以在内部携带少量的信息,用于在不同线程之间交换数据。除了使用Message中的what字段、还可以使用arg1和arg2字段携带一些整型数据,使用obj字段携带一个Object对象。

2、Handler

  Handler顾名思义是处理者意思,它主要用于发送和处理消息。发送消息一般使用handler的sendMessage()方法,而发出的消息经过一系列的辗转处理后,最终会传递到handler的handleMessage()方法中。

3、MessageQueue

  MessageQueue是消息列队的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存放于消息列队中,等待被处理,每个线程中只会有一个MessageQueue对象。

4、Looper

  Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就好进入到一个无限循环当中,当然每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

异步消息流程梳理:

  1、主线程上创建一个Handler对象,并重写handleMessage()方法。

  2、当子线程中需要UI操作,创建Message对象,并通过Handler将这条信息发送出去。

  3、消息被添加到MessageQueue的队列中等待被处理、Looper会一直尝试从MessageQueue中取出待处理消息、分发会Handler的handleMessage()方法中。

  **因为handler是在主线程中创建,所以handleMessage()方法中代码也会在主线程中运行,于是我们可以安心对UI操作了。

整个异步消息处理机制的流程示意图:

  

运用handler改写昨天的代码:

MainActivity.java

package com.example.threadasynctask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast; public class MainActivity extends Activity { private Button btn;
private TextView tv;
private ProgressBar progressBar;
private Runnable doInBackground1;
private Runnable doInBackground2;
private String str;
private int n; //1.跟着主线程走,可以碰UI
//2.能够接受子线程发送的消息(Message)
// 子线程类本身不可以发信息
private Handler handler; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("UI_MainThread","主线程id:"+Thread.currentThread().getId()); tv =(TextView)findViewById(R.id.tv_start);
btn =(Button)findViewById(R.id.btn_start);
progressBar = (ProgressBar)findViewById(R.id.progressBar1); btn.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) { //AsyncTask异步信息更新
/* MyTask mt = new MyTask(tv,progressBar);
mt.execute(1000);//里面的参数是传给doInBackground*/ Thread t1 = new Thread(doInBackground1);
t1.start(); Thread t2 = new Thread(doInBackground2);
t2.start(); } }); handler = new Handler(){ //1.消息msg来自于子线程
//2.消息可以多个,采用msg.what识别
//3.处理消息,一般就会更新UI
//4.此方法可以参考onPostExecute
@Override
public void handleMessage(Message msg) { super.handleMessage(msg);
int msgwhat = msg.what;
Log.i("handler","已经收到消息,消息what:"+msgwhat+",id:"+Thread.currentThread().getId()); if (msgwhat==1){
//土司
Toast.makeText(MainActivity.this, "开始下载", Toast.LENGTH_SHORT).show();
}
if (msgwhat==2){
//更新helloworld
tv.setText("下载完成"+str); } } }; //子线程代码1
doInBackground1 = new Runnable() { @Override
public void run() {
Log.i("sub_Thread","子线程1启动,id:"+Thread.currentThread().getId()); try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} //1.访问数据库或者互联网,不在UI进程,所以不卡
Message msg = new Message();
//对消息一个识别号,便于handler能够识别
msg.what = 1;
handler.sendMessage(msg);
Log.i("sub_Thread","子线程1已经发送消息给handler");
}
}; //子线程代码2
doInBackground2 = new Runnable() { @Override
public void run() {
Log.i("sub_Thread","子线程2启动,id:"+Thread.currentThread().getId()); try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} Message msg = new Message();
//对消息一个识别号,便于handler能够识别
msg.what = 2; //访问互联网,下载最新的,更新data,但不碰界面 handler.sendMessage(msg);
}
}; }
}

handler之内存泄露

内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄漏了。
Handler也是造成内存泄露的一个重要的源头,主要Handler属于TLS(Thread Local Storage)变量,生命周期和Activity是不一致的
,Handler引用Activity会存在内存泄露。

Handler 的生命周期与Activity 不一致

  • 当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
  • 当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。

handler 引用 Activity 阻止了GC对Acivity的回收

  • 在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
  • 如果外部类是Activity,则会引起Activity泄露 。

    当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

如何避免修?

  • 使用显形的引用,1.静态内部类。 2. 外部类
  • 使用弱引用 2. WeakReference


android之handle的更多相关文章

  1. android中Handle类的用法

    android中Handle类的用法 当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无 ...

  2. 三、android中Handle类的用法

    当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉.Ha ...

  3. android的handle

    Handler的定义:  用来接收子线程发送过来的数据,并利用该数据直接更新主线程的UI. 安卓中,一个应用启动时会开启一个主线程(UI线程),他的责任是负责管理界面中的控件.比如当你点击一个Butt ...

  4. Android中Handle详解

    上图为本人总结的Handler,网上发现一片总结很好的博客就copy过来:作为参考 Handler有何作用?如何使用? 一 .Handler作用和概念 包含线程队列和消息队列,实现异步的消息处理机制, ...

  5. Android面试题(一)

    1. 请描述一下Activity 生命周期. 答: 如下图所示.共有七个周期函数,按顺序分别是: onCreate(), onStart(), onRestart(), onResume(), onP ...

  6. android面试题

    1. 请描述一下Activity 生命周期. 答: 如下图所示.共有七个周期函数,按顺序分别是: onCreate(), onStart(), onRestart(), onResume(), onP ...

  7. Android基础面试题

    1. 请描述一下Activity 生命周期. 答: 如下图所示.共有七个周期函数,按顺序分别是: onCreate(), onStart(), onRestart(), onResume(), onP ...

  8. Android 抽屉类SlidingDrawer的使用

     比较简单,设置好SlidingDrawer控件的handle和content属性就可以了.  android:content="@+id/content"  android:ha ...

  9. Handle类的用法

    android中Handle类的用法 当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无 ...

随机推荐

  1. ZOJ 3481. Expand Tab

    题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4278 题意: 给出一些文本片段,把文本中的 Tab 字符根据配置,替换成 ...

  2. C++设计模式-Memento备忘录模式

    Memento模式作用:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态. UML图: Originator:负责创建一个备忘录Me ...

  3. Javascript中判断变量是 array还是object(是数组还是对象)

    段文字是从github上截取由本人翻译过来的. 原文地址:https://github.com/nathansmith/javascript-quiz/blob/master/ANSWERS.md 怎 ...

  4. Salesforce 动态审批

    由于Salesforce只支持根据条件动态选择审批分支,如果我们想进一步支持动态根据页面的某种条件选择审批人,Salesforce是不支持的.因此我们只能通过override salesforce审批 ...

  5. JS传递对象数组为参数给后端,后端获取

    前端JS代码: var conditons = []; var test1 = new Object(); test1.name="1"; test1.id="2&quo ...

  6. JS判断input按了回车键

    参考代码如下: <input type="textbox" id="textbox1" onkeypress="CheckInfo" ...

  7. 1045 - Access denied for user 'root'@'localhost'(using password NO)解决方案

    原因为输入了不正确的密码,连接数据库时输入正确的密码即可! 另外一个原因是安装MySQL后MySQL产生了一个随机密码,而登录的时候没有将随机密码填上,解决方案见:http://www.cnblogs ...

  8. FusionCharts中仪表盘相关属性

    上上周用FusionCharts做了几个报表,里面有个仪表盘,当时查属性查疯了,现在把相关的一些属性记下来,方便以后查找. -------------------------仪表盘重要属性解析---- ...

  9. 柯朗数(Courant number)研究

    在数值计算过程中,对于计算结果的准确性和效率有很高的要求,但是这两者之间往往互相矛盾:而使用柯朗数可用于平衡两者. 1.柯朗数的定义: C = sqrt(gh)*t/s 其中,t是时间步长,s是网格在 ...

  10. C# 设计模式之工厂模式(一)

    写在前面,PS一句:笔记专用,轻拍勿喷,看的不爽绕路前行即可. 一.举栗说明 1.剧情:某银行财务有三位员工,分别为A.B.C三人,主要任务就是去银行取钱,如下: class EmloyeeA: { ...