• Android Handler消息传递


    一、背景

    出于性能优化考虑,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://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);
        }
    }
  • 相关阅读:
    gitea (git服务器), 修改配置,更换IP地址
    使用ffmpeg裁剪和合并视频
    linux/windows/Mac平台生成随机数的不同方法
    Android源代码同步脚本(增加设置线程参数)
    小米2S Mk6.0.1 [只能做测试体验,不能使用]
    MultiROM for the XIAOMI MI2S/2C/2! (Kexec HardBoot Enabled with Kexec HardBoot Patch!)
    [PATCH] UBUNTU: SAUCE: (no-up) apparmor: Sync to apparmor3
    [转载]起动service保存android系统log( logcat服务)
    小米2S TWRP 3.0.2-0 最新中文版本Recovery
    centos 64位编译安装 glibc-2.14
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/4259520.html
Copyright © 2020-2023  润新知