Android Handler消息传递

摘要:
1、 背景为了优化性能,Android UI操作不是线程安全的,这意味着如果多个线程同时操作UI组件,可能会导致线程安全问题。为了解决这个问题,Android开发了一个简单的原则:只允许UI线程(即主线程)修改活动中的UI组件。当程序首次启动时,Android将同时启动主线程。主线程主要负责处理与UI相关的事件,例如用户按键事件、用户触摸屏事件、屏幕绘制事件和链接

一、背景

出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题。为了解决这个问题,Android制定了一条简单的原则:只允许UI线程(亦即主线程)修改Activity中的UI组件。

当一个程序第一次启动时,Android会同时启动一条主线程,主线程主要负责处理与UI相关的事件,如用户的按键事件、用户接触屏幕的事件、屏幕绘图事件,并把相关的事件分发到相应的组件进行处理,所以主线程通常又叫做UI线程。

二、使用Handler的两种常见原因

1、只能在主UI中修改UI。但实际上,有部分UI需要在子线程中控制其修改逻辑,因此子线程需要通过handler通知主线程修改UI。这在游戏开发中尤其常见,比如需要让新启动的线程周期性的改变UI。

2、为避免ANR,应该在子线程中执行耗时较长的操作,而此操作完成后,有可能需要通知主线程修改UI。

三、基本原理及步骤

1、Handler的作用主要有2个:

(1)发送消息。

(2)获取、处理消息。

2、基本原理:为了让主线程能及时处理子线程发送的消息,显然只能通过回调的方法来实现----开发者只要重写Handler类中的方法,当新启动 的线程发送消息时,消息会发送至与之关联的MessageQueue,而Handler会不断的从MessageQuere中获取并处理消息-----这 将导致Handler类中处理消息的方法被回调。

3、在线程中使用Handler的基本步骤如下:

在被调用线程中完成以下内容:

(1)调用 Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。

(2)有了Looper之后,创建Handler子类的实例,重写HandlerMessage()方法,该方法负责处理来自其它线程的消息。

(3)调用Looper的loop()方法启动Looper。

注:若被调用线程是主线程类,由于系统自动为主线程创建了Looper的实例,因此第一、三步骤可省略,而只需要做第2步即可。

在调用线程中完成:

(1)创建nessage,并填充内容。

(2)使用被调用类创建的Handler实例,调用sendMessage(Message msg)方法。

四、实例

1、主线程接收数据,并将之发送至子线程中完成一些耗时操作

package com.ljh.handlerdemo1;

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

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private final String UPPER_NUMBER = "upper";
    private CalThread calThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        calThread = new CalThread();
        Thread t = new Thread(calThread);
        t.start();
    }

    public void cal(View v) {
        EditText et_digit = (EditText) findViewById(R.id.et_digit);
        Message msg = new Message();
        msg.what = 0x1233;
        Bundle bundle = new Bundle();
        bundle.putInt(UPPER_NUMBER,
                Integer.parseInt(et_digit.getText().toString()));
        msg.setData(bundle);
        calThread.handler.sendMessage(msg);
    }

    class CalThread implements Runnable {

        public Handler handler;

        @Override
        public void run() {
            //1、调用 Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue
            Looper.prepare();
            handler = new Handler() {
                // 2、有了Looper之后,创建Handler子类的实例,重写HandlerMessage()方法,该方法负责处理来自其它线程的消息。
                @Override
                public void handleMessage(Message msg)
                {
                    if(msg.what == 0x1233)
                    {
                        int upper = msg.getData().getInt(UPPER_NUMBER);
                        List<Integer> nums = new ArrayList<Integer>();
                        // 计算从2开始、到upper的所有质数
                        outer:
                        for (int i = 2 ; i <= upper ; i++)
                        {
                            // 用i处于从2开始、到i的平方根的所有数
                            for (int j = 2 ; j <= Math.sqrt(i) ; j++)
                            {
                                // 如果可以整除,表明这个数不是质数
                                if(i != 2 && i % j == 0)
                                {
                                    continue outer;
                                }
                            }
                            nums.add(i);
                        }
                        // 使用Toast显示统计出来的所有质数
                        Toast.makeText(MainActivity.this , nums.toString()
                            , Toast.LENGTH_LONG).show();
                    }
            }
            
        
            
        };
        //调用Looper的loop()方法启动Looper。
        Looper.loop();
    }
}
}

xml:

<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=".MainActivity" >

    
    
    <EditText
        android:id="@+id/et_digit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="@string/limit" />
    
    <Button 
        android:id="@+id/bt_prime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/cal"
        android:onClick="cal"
        android:layout_below="@id/et_digit" />

</RelativeLayout>

参考归档代码HandlerDemo1

2、在主线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可。

package org.crazyit.event;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;

/**
 * Description:
 * <br/>site: <a href="http://t.zoukankan.com/http://www.crazyit.org">crazyit.org</a>
 * <br/>Copyright (C), 2001-2014, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */
public class HandlerTest extends Activity
{
    // 定义周期性显示的图片的ID
    int[] imageIds = new int[]
    {
        R.drawable.java,
        R.drawable.ee,
        R.drawable.ajax,
        R.drawable.xml,
        R.drawable.classic
    };
    int currentImageId = 0;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final ImageView show = (ImageView) findViewById(R.id.show);
        final Handler myHandler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                // 如果该消息是本程序所发送的
                if (msg.what == 0x1233)
                {
                    // 动态地修改所显示的图片
                    show.setImageResource(imageIds[currentImageId++
                        % imageIds.length]);
                }
            }
        };
        // 定义一个计时器,让该计时器周期性地执行指定任务
        new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                // 发送空消息
                myHandler.sendEmptyMessage(0x1233);
            }
        }, 0, 1200);
    }
}

免责声明:文章转载自《Android Handler消息传递》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇EIGRP 超强总结[置顶] export命令-linux下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux平台下贪吃蛇游戏的运行

1.参考资料说明:        这是一个在Linux系统下实现的简单的贪吃蛇游戏,同学找帮忙,我就直接在Red Hat中调试了一下,参考的是百度文库中“maosuhan”仁兄的文章,结合自己的一些实践遇到的问题,整理后,将解决方案和大家分享一下。 2.开发环境:        linux+gcc+netbeans 3.思路介绍:       多线程处理。...

内存管理[3]堆

VirtualAlloc 分配的内存是以 4K 为最小单位、连续的内存地址(但映射到真实的内存时它不一定是连续的), 前面说了, 它不适合分配小内存(譬如只有几个字节的变量); 局部的变量在 "栈" 中有程序自动管理, 那么那些全局的小变量怎么办呢? 这就要用到 "堆".这样看来, VirtualAlloc 分配的内存既不是 "栈" 也不是 "堆"; Vi...

IP address could not be resolved: Name or service not known

[root@test ~]# /usr/local/mysql/bin/mysqld2018-08-05T07:00:33.647509Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the...

Android Handler 避免内存泄漏的用法总结

Android开发经常会用到handler,但是我们发现每次使用Handler都会出现:This Handler class should be static or leaks might occur(null)这样的提示。Android lint就是为了提示我们,这样使用Handler会容易造成内存泄漏。但是你会发现其实改成static并没有什么用。因为...

Debug技巧

多线程调试 有些时候为了观察多个线程间变量的不同状态,以及锁的获取等,就会想到在代码里加个断点debug一下。  在IDE里断点停下来的时候,可以切换到另外的线程中,跑其他的代码,不会互相影响。这里是有个开关的,在Eclipse里默认开启, 在IntelliJ IDEA里默认是没有开启的。也就是说如果你在IDEA里代码里加断点,虽然一个线程的断了下来,但其...

Handler 机制(一)—— Handler的实现流程

   由于Android采用的是单线程模式,开发者无法在子线程中更新 UI,所以系统给我提供了 Handler 这个类来实现 UI 更新问题。本贴主要说明 Handler 的工作流程。 1. Handler 的作用 在Android为了保障线程安全,规定只能由主线程来更新UI信息。而在实际开发中,会经常遇到多个子线程都去操作UI信息的情况,那么就会导致U...