zoukankan      html  css  js  c++  java
  • android之handle

      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





  • 相关阅读:
    02-vue过滤器和键盘修饰符
    01-vue指令
    webpack
    笔记:随机生成数、字符串
    笔记:long、longlong、int、float、NSString相互转换
    关于NSStringFromClass的一点见解
    iOS中UITextField常用设置和方法
    iOS 倒计时的一种实现
    iOS UITextField的代理<UITextFieldDelegate>的几点笔记
    iOS 单例模式简单实例
  • 原文地址:https://www.cnblogs.com/lgk1002/p/6137714.html
Copyright © 2011-2022 走看看