android之handle

 

异步消息处理机制Handler : http://www.cnblogs.com/lyajs/p/5347990.html

Android 中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue 和Looper。

1. Message

  Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。(字段有what arg1 arg2 obj)

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操作了。

 

 

测试 结构图:

 

package com.example.multithreadind012;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {

    private Button btn;
    private TextView tv;
    private ListView lv;
    private BaseAdapter adapter;
    private List<User> userList = new ArrayList<User>();
    // Runnable是一个接口,不是一个线程,一般线程会实现Runnable。
    private Runnable doInBackground1;
    private Runnable doInBackground2;

    // 1.跟着主线程走,可以碰UI
    // 2.能够接受子线程发送的消息(Message)
    // 子线程类本身不可以发信息
    private Handler handler;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        System.out.println("UI_MainThread ~~~~ id:" + Thread.currentThread().getId());

        // 模拟数据访问产生数据
        for (int i = 0; i < 5; i++) {
            User u = new User();
            u.setUsername("小明" + i);
            u.setSex("女" + i);
            userList.add(u);
        }

        tv = (TextView) findViewById(R.id.textView1);
        btn = (Button) findViewById(R.id.button1);
        btn.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                // 1.访问数据库或者互联网(但会卡的)
                // 2.更新界面
                Thread t1 = new Thread(doInBackground1);
                t1.start();

                Thread t2 = new Thread(doInBackground2);
                t2.start();

            }
        });
        // BaseAdapter就Android应用程序中经常用到的基础数据适配器,它的主要用途是将一组数据传到像ListView、Spinner、Gallery及GridView等UI显示组件,它是继承自接口类Adapter,
        adapter = new BaseAdapter() {

            public int getCount() {
                return userList.size();
            }

            public View getView(int position, View convertView, ViewGroup parent) {
                LayoutInflater inflater = MainActivity.this.getLayoutInflater();
                View view;
                if (convertView == null) {
                    view = inflater.inflate(R.layout.item, null);// 创建
                } else {
                    view = convertView; // 复用
                }

                TextView tv_username = (TextView) view
                        .findViewById(R.id.username);
                TextView tv_sex = (TextView) view.findViewById(R.id.sex);
                tv_username.setText(userList.get(position).getUsername());
                tv_sex.setText(userList.get(position).getSex());
                return view;
            }

            public Object getItem(int position) {
                return null;
            }

            public long getItemId(int position) {
                return 0;
            }
        };
        lv = (ListView) findViewById(R.id.listView1);
        // 为ListView绑定Adapter 
        lv.setAdapter(adapter);

        handler = new Handler() {

            // 1.消息msg来自于子线程
            // 2.消息可以多个,采用msg.what识别
            // 3.处理消息,一般就会更新UI
            // 4.此方法可以参考onPostExecute
            public void handleMessage(Message msg) {

                super.handleMessage(msg);
                // what 是用来保存消息标示的
                int msgwhat = msg.what;
                System.out.println("handler ~~~~ 已经收到消息,消息what:"+msgwhat+",id:"    + Thread.currentThread().getId());

                if (msgwhat == 1) {
                    // 更新helloworld
                    tv.setText("子线程让我更新" + msgwhat);
                }
                if (msgwhat == 2) {
                    // 更新ListView
                    adapter.notifyDataSetChanged();
                }

            }

        };

        // 子线程代码1
        doInBackground1 = new Runnable() {

            public void run() {
                System.out.println("sub_Thread ~~~~ 子线程1启动,id:"    + Thread.currentThread().getId());
                
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 1.访问数据库或者互联网,不在UI进程,所以不卡
                Message msg = new Message();
                // 对消息一个识别号,便于handler能够识别
                msg.what = 1;
                handler.sendMessage(msg);
                System.out.println("sub_Thread ~~~~ 子线程1已经发送消息给handler");
                
            }
        };

        // 子线程代码1
        doInBackground2 = new Runnable() {

            public void run() {
                
                System.out.println("sub_Thread ~~~~ 子线程2启动,id:"    + Thread.currentThread().getId());

                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                Message msg = new Message();
                // 对消息一个识别号,便于handler能够识别
                msg.what = 2;
                // handler.sendMessage(msg);
                handler.sendMessageDelayed(msg, 500);

                // 访问互联网,下载最新的,更新data,但不碰界面
                for (User user : userList) {
                    user.setSex("不男不女");
                }

                System.out.println("sub_Thread ~~~~ 子线程2已经发送消息给handler");
            }
        };

    }
}
MainActivity.java
package com.example.multithreadind012;

import java.util.List;

import android.os.AsyncTask;
import android.widget.BaseAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.multithreadind012.R;

public class MyTask extends AsyncTask {

    private BaseAdapter adapter;
    private List<User> userList;
    private MainActivity activity;

    public MyTask(MainActivity activity) {
        this.activity = activity;
    }

    // 1.所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)
    // 2.绝对不能碰UI
    protected Object doInBackground(Object... params) {

        userList = (List<User>) params[0];
        adapter = (BaseAdapter) params[1];
        for (int i = 0; i < userList.size(); i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            userList.get(i).setUsername("小红" + i);
            userList.get(i).setSex("男" + i);
            publishProgress(i);
        }


        // 返回给前端
        return "天气:22度";
    }

    // 准备
    protected void onPreExecute() {
        Toast.makeText(activity, "开始准备", Toast.LENGTH_SHORT).show();

    }

    // 做完后执行
    // onProgressUpdate()方法用于更新异步执行中,在主线程中处理异步任务的执行信息
    protected void onPostExecute(Object result) {
        String r = result.toString();
        TextView tv = (TextView) activity.findViewById(R.id.textView1);
        tv.setText("访问完成!" + r);

    }

    // 分步完成
    protected void onProgressUpdate(Object... values) {

        // 0,1,2,3,4
        int bar = Integer.parseInt(values[0].toString());
        bar = (bar + 1) * 20;
        ProgressBar progressBar = (ProgressBar) activity
                .findViewById(R.id.progressBar1);
        // 更改进度条进度
        progressBar.setProgress(bar);
        
        adapter.notifyDataSetChanged();
    }

}
MyTask.java
package com.example.multithreadind012;

public class User {
    private String username;
    private String sex;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }

}
User.java
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.multithreadind012.MainActivity" >
 
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignTop="@+id/textView1"
        android:layout_marginRight="53dp"
        android:text="Button" />

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_marginTop="84dp" >
    </ListView>

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/listView1"
        android:layout_alignRight="@+id/button1"
        android:layout_below="@+id/button1"
        android:layout_marginTop="28dp" />

</RelativeLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <TextView 
        android:id="@+id/username"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="45dp"
        />

    <TextView 
        android:id="@+id/sex"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="45dp"
        />
</LinearLayout>
item.xml

 

 

====================================================================

 

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

 

原文: http://www.cnblogs.com/devinzhang/archive/2011/12/30/2306980.html

知识点总结补充:

   很多初入Android或Java开发的新手对Thread、Looper、Handler和Message仍然比较迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由于目前市面上的书籍等资料都没有谈到这些问题,今天就这一问题做更系统性的总结。我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。

   对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。

   1. 对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处理,其中还提供了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒

   2. 当然推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供 PreTranslateMessage这些干涉内部的方法。

  3. Looper又是什么呢? ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。

  4.Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 .

  5. java.util.concurrent对象分析,对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到Android为我们已经设计好的Task机制,这里不做过多的赘述,相关原因参考下面的介绍:

  6. 在Android中还提供了一种有别于线程的处理方式,就是Task以及AsyncTask,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处理这些异步任务。

 


 

 Android Handler机制: http://blog.csdn.net/stonecao/article/details/6417364

 

  Handler对Activity finish影响。

    在开发的过程中碰到一个棘手的问题,调用Activity.finish函数Acitivity没有执行生命周期的ondestory函数,后面查找半天是因为有一个handler成员,

    因为它有一个delay消息没有处理,调用Activity.finish,Activity不会马上destory,所以记得在Ativity finish前清理一下handle中的未处理的消息,这样Activity才会顺利的destory


 

 Android异步消息处理(一): http://www.2cto.com/kf/201109/105866.html

什么是异步消息处理:

        对于普通的线程来说,执行完run()方法内的代码后线程就结束了。而异步消息处理线程是指:线程启动后会进入一个无限循环体之中,每执行一次,从线程内部的消息队列中取出一个消息,并回调相应的消息处理函数,执行完一个消息后则继续循环。如果消息队列为空,线程会暂停(一般也就是我们调用休眠方法),直到消息队列中又新的消息。

 

异步消息处理特点:

      从上面的描述可以看出,异步消息处理其实就是一种线程机制,只不过这种机制用的上的地方非常多,最后就单独提炼了“异步消息处理”这个名词。

 

异步消息处理的使用情况:

     一般情况下,如果任务具有以下两个特点,就可以使用异步消息处理机制:

     1.任务常驻内存(编程中体现就是run()方法中是无限循环),比如用于处理用户事件的任务。

     2.任务需要根据外部传递的消息做不同的操作。

 

通用的实现异步消息处理机制的方式:

      1.每个异步线程内部包含一个消息队列,用来缓存消息。

      2.线程的执行中使用while(true)进行无限循环,循环体中从消息队列取出消息,并根据消息的来源,回调相应的消息处理函数(从这里可以看出:异步消息处理,消息的具体处理并不是异步消息处理机制负责的,异步消息处理机制只是负责转发消息给处理函数)

      3.其他外部线程可以向本线程的消息队列发送消息,由于有两个或以上的线程访问消息队列,那么,消息队列内部的读写操作必须进行加锁。

 

Android中异步消息处理的实现方式:

     在线程内部有一个或多个Handler对象,外部线程通过该Handler对象的引用向本线程发送异步消息,消息通过Handler对象加入消息队列(MessageQueue)。线程内部只有一个MessageQueue对象,线程的run()方法从MessageQueue中读取消息,并回调Handler对象中的回调函数handleMessage()处理消息。

 

在编程中应该注意的问题:

       1.由于异步消息处理机制的一个重要环节是MessageQueue,所以,在发送消息之前,必须确定MessageQueue已经创建。在android中,给应用程序员提供的创建MessageQueue的接口是:Looper.prepare()。

       2.由于异步消息处理机制是一个循环线程,而循环的启动也是由程序员控制的,所以,在你要处理消息之前,应该启动循环。在android中,给应用程序员提供的启动循环的接口是:Looper.loop()。

       3.细心的朋友可能发现,在activity中使用Handler发送,处理消息时,并没有上面两步,其实是系统帮我们做了的。在创建Activity之前,系统就会为Activity创建一个异步消息处理线程。

 


 

  

Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系: http://blog.csdn.net/lmj623565791/article/details/38377229

 

 

Android Handler 异步消息处理机制的妙用 创建强大的图片加载类:  http://blog.csdn.net/lmj623565791/article/details/38476887

 

posted @ 2016-12-08 15:13  Seven-7-  阅读(226)  评论(1编辑  收藏  举报