日志

android之handle

 来源    2016-12-06    0  

  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类的用法
日志android中Handle类的用法     当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activ ...
三、android中Handle类的用法
日志当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉.Ha ...
Android:自定义视图中的Handle Back按钮
问答我通过扩展RelativeLayout为我的应用创建了一个自定义视图.它通过拖动ImageView打开为对话框.它有EditTexts和Buttons.我在我的视图中覆盖了onKeyDown,因为我需 ...
sqlite3_open_v2(“/ data/data/com.android.packagename/databases/dump.sqlite”,&handle,1,NULL)失败
问答我正在使用sqlite数据库-.应用程序工作在2.3及更高版本-当我试图在2.2中运行-我得到一个像-的错误 E/Database(2476):sqlite3_open_v2("/data/ ...
Android Handle电话
问答我有录音,打电话的时候我需要停止录音,我该怎么办呢?::您必须使用PhoneStateListener: TelephonyManager tm = (TelephonyManager)getSyst ...
Android -- 从源码解析Handle+Looper+MessageQueue机制
日志1,今天和大家一起从底层看看Handle的工作机制是什么样的,那么在引入之前我们先来了解Handle是用来干什么的 handler通俗一点讲就是用来在各个线程之间发送数据的处理对象.在任何线程中,只要 ...
android 子线程使用handle修改主线线程内容
日志1.子线程使用handle修改主线线程内容简单案例 1).activity_handle.xml <?xml version="1.0" encoding="utf ...
Android笔记(三十四) Android中线程之间的通信(六)Handle中的post()方法详解
日志         我们之前都是使用sendMessage()方法来发送消息,使用handleMessage来处理消息的,今天我们来看另外一种方法,先看代码: package cn.lixyz.hand ...
2
Android笔记(三十三) Android中线程之间的通信(五)Thread、Handle、Looper和MessageQueue
日志ThreadLocal          往下看之前,需要了解一下Java的ThreadLocal类,可参考博文:          解密ThreadLocal Looper.Handler和Mess ...
Android:使用 DownloadManager 进行版本更新,出现 No Activity found to handle Intent 及解决办法
日志项目中,进行版本更新的时候,用的是自己写的下载方案,最近看到了使用系统服务 DownloadManager 进行版本更新,自己也试试. 在下载完成以后,安装更新的时候,出现了一个 crash,抓取的 ...
Android开发之bug-No Activity found to handle Intent
日志android.content.ActivityNotFoundException: No Activity found to handle Intent 做Android开发中,使用隐式intent ...
Android handle 多线程练习
日志Android handle <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&quo ...
Android中对Handle机制的理解
日志一.重要參考资料  [參考资料]     眼下来看,以下的几个网址中的内容质量比較不错.基本不须要再读别的网址了.  1.android消息机制一     http://xtfncel.javaeye ...
Android Handle,Looper,Message消息机制
日志尊重原创,转载请标明出处   http://blog.csdn.net/abcdef314159 我们知道在Android中更新UI都是在主线程中,而操作一些耗时的任务则须要在子线程中.假设存在多个线 ...
android – 如何只将一个USB设备连接到Docker容器
问答我一直在使用Docker来运行Android测试.我使用多个容器和Android设备并行运行测试,但是在将USB设备重定向到Docker容器时我遇到了问题. 我正在安装设备,如: docker run ...
android – MVP演示者内部的上下文相关问题
问答我是Android MVP模式的新手并且在我的项目上工作我在演示者中有一些与Android Context相关的基本问题.虽然有很多与此相关的答案,但我没有得到一个可以解决我的问题的完美答案. 我有以 ...
3
android – google plus共享网址无法呈现
问答我正在使用PlusShare.Builder分享google plus的URL,但它没有渲染. 它的渲染成功,当我只是复制粘贴URL在谷歌加,但在Android方面它不工作. 这是我的URL Plz引 ...
android – 如何在libgdx中制作敌人仪表探测器?
问答在我自上而下的游戏中,我有一个敌人探测器来探测敌人是否在附近.我的问题是我如何创建动态我的仪表栏的过渡?我是这个框架的新手.谢谢和进步 当玩家检测到附近的敌人时,仪表栏将像下面的图像屏幕一样动画 没有 ...
Android Studio是否压缩classes.dex文件?
问答看起来输出文件夹中apk文件的classes.dex与已安装的应用程序不同. 我正在使用classes.dex文件来解决一些安全问题,所以通常我解压缩最终的apk文件并从classes.dex文件中获 ...
android – 如何以编程方式设置工具栏collapseIcon颜色
问答我想在显示搜索时更改工具栏中后退按钮的颜色(带圆圈的白色箭头). 我设法改变了所有其他元素的颜色,我坚持使用后箭头颜色. 我可以从xml设置collapseIcon(后退箭头drawable): &l ...