首页 > 其他分享 >Android Handler使用方法

Android Handler使用方法

时间:2024-02-04 14:26:53浏览次数:27  
标签:public Handler 线程 activity msg Android 方法 Message

目录:

目录

 

1. 前言

本篇文章介绍 Android Handler 的基本使用方法,且 Demo 会以 Java & Kotlin 两种代码形式进行展示。 在 Android 实际开发中,我们经常会遇到耗时任务,比如:网络请求API接口来获取数据、数据库 CRUD 操作等等,我们需要额外创建开启工作线程来处理这些耗时任务。由于 Android 规定只能由主线程才能处理 UI 工作,所以这时候我们就需要通过 Handler 来通知主线程处理 UI 工作。

1.1 定义

Handler:子线程与主线程之间的沟通中介,用于传递处理消息。

在 Android 中,为保证处理 UI 工作的线程稳定安全,规定只有主线程才能更新处理 UI 工作。所以当子线程想处理 UI 工作时,就需要通过 Handler 来通知主线程作出相对应的 UI 处理工作。 如下图所示:

Handler是子线程与主线程的沟通中介 本篇文章,我将介绍 Handler 的三种使用方法,分别是:

 

  1. Handler.sendMessage(Message)
  2. Handler.post(Runnable)
  3. Handler.obtainMessage(what).sendToTarget();

2. Handler.sendMessage()方法

Handler.sendMessage(Msg) 方法是最为常见的一种方法。

2.1 使用步骤说明

其使用步骤分四步,如下所示:

  1. 步骤一:新建 Handler 对象,覆写 handleMessage(Message) 方法。
  2. 步骤二:新建 Message 对象,设置其携带的数据。
  3. 步骤三:在子线程中通过 Handler.sendMessage(Message) 方法发送信息。
  4. 步骤四:在 Handler 的 handleMessage(Message msg) 方法中处理消息,通知主线程作出相对应的 UI 工作。
  • 步骤一:新建 Handler 对象,覆写 handleMessage(Message) 方法
  复制代码
//创建 Handler对象,并关联主线程消息队列
mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
            ···略···
        }
    }
};
  • 步骤二:新建 Message 对象,设置其携带的数据
  复制代码
Bundle bundle = new Bundle();
bundle.putInt(CURRENT_PROCESS_KEY, i);
Message msg = new Message();
msg.setData(bundle);
msg.what = 2;
  • 步骤三:在子线程中通过 Handler.sendMessage(Message) 方法发送信息
  复制代码
mHandler.sendMessage(msg)
  • 步骤四:在 Handler 的 handleMessage(Message msg) 方法中处理消息,通知主线程作出相对应的 UI 工作
  复制代码
mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //根据信息编码及数据做出相对应的处理
        switch (msg.what) {
            case 1:
                //更新 TextView UI
                mDisplayTv.setText("CustomChildThread starting!");
                break;
            case 2:
                //获取 ProgressBar 的进度,然后显示进度值
                Bundle bundle = msg.getData();
                int process = bundle.getInt(CURRENT_PROCESS_KEY);
                mProgressBar.setProgress(process);
                break;
            default:
                break;
        }
    }
};

2.2 具体例子

根据上述的使用步骤,我们来完成一个完整的例子: 新建一个线程,模拟处理耗时任务,然后将处理进度通过 ProgressBar 显示出来。 效果如下所示:点击按钮,开启线程处理耗时操作。

 

Handler.sendMessage() Demo 具体代码见下:

 

2.2.1 Java版本Demo

Java版本的具体代码如下所示:

  复制代码
public class HandlerAddThreadActivity extends AppCompatActivity {
    public static final String CURRENT_PROCESS_KEY = "CURRENT_PROCESS";
    private TextView mDisplayTv;
    private Handler mHandler;
    private ProgressBar mProgressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_add_thread);

        TextView titleTv = findViewById(R.id.title_tv);
        titleTv.setText("Handler + Thread");
        
        mDisplayTv = findViewById(R.id.display_tv);
        mProgressBar = findViewById(R.id.test_handler_progress_bar);

        //mHandler用于处理主线程消息队列中的子线程消息
        mHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        //更新 TextView UI
                        mDisplayTv.setText("CustomChildThread starting!");
                        break;
                    case 2:
                        //获取 ProgressBar 的进度,然后显示进度值
                        Bundle bundle = msg.getData();
                        int process = bundle.getInt(CURRENT_PROCESS_KEY);
                        mProgressBar.setProgress(process);
                        break;
                    default:
                        break;
                }

            }
        };
        
        Button mClickBtn = findViewById(R.id.click_btn);
        mClickBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //开启子线程,子线程处理UI工作
                CustomChildThread customThread = new CustomChildThread();
                customThread.start();
            }
        });
    }

    /**
     * 子线程,用于处理耗时工作
     */
    public class CustomChildThread extends Thread {

        @Override
        public void run() {
            //在子线程中创建一个消息对象
            Message childThreadMessage = new Message();
            childThreadMessage.what = 1;
            //将该消息放入主线程的消息队列中
            mHandler.sendMessage(childThreadMessage);

            //模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。
            for (int i = 1; i <= 5; i++) {
                try {
                    //让当前执行的线程(即 CustomChildThread)睡眠 1s
                    Thread.sleep(1000);

                    //Message 传递参数
                    Bundle bundle = new Bundle();
                    bundle.putInt(CURRENT_PROCESS_KEY, i);
                    Message progressBarProcessMsg = new Message();
                    progressBarProcessMsg.setData(bundle);
                    progressBarProcessMsg.what = 2;
                    mHandler.sendMessage(progressBarProcessMsg);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

执行结果看上图 Handler.sendMessage() Demo

2.2.2 Kotlin版本Demo

Kotlin版本的代码如下所示:

  复制代码
class TestThreadAddHandlerActivity : AppCompatActivity() {
    companion object {
        const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_thread_add_handler)

        handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {
            //工作线程开始模拟下载任务
            val workThread: WorkThread = WorkThread(this)
            workThread.start()
        })
    }

    class WorkThread(activity: TestThreadAddHandlerActivity) : Thread() {
        private var handler: MyHandler = MyHandler(activity)

        override fun run() {
            super.run()
            for (i in 0..6) {
                sleep(1000)
                //通过 Handler 将进度参数传递给 主线程,让其更新 progressBar 进度
                val message = Message()
                message.what = 1
                val bundle = Bundle()
                bundle.putInt(PROGRESS_VALUE_KEY, i)
                message.data = bundle
                handler.sendMessage(message)
            }
        }
    }

    /**
     * 静态内部类,防止内存泄漏
     */
    class MyHandler(activity: TestThreadAddHandlerActivity) : Handler() {
        private var weakReference = WeakReference(activity)

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            //处理消息
            when (msg.what) {
                1 -> {
                    val activity = weakReference.get()
                    if (activity != null && !activity.isFinishing) {
                        //获取消息中携带的任务处理进度参数,然后设置成 ProgressBar 的进度。
                        val progressValue: Int = msg.data.get(PROGRESS_VALUE_KEY) as Int
                        activity.handlerAddThreadProgressBar.progress = progressValue
                    }
                }
            }
        }
    }
}

执行结果看上图 Handler.sendMessage() Demo

3. Handler.post()方法

除了使用 Handler.sendMessage(Message) 来发送信息,Handler 还支持 post(Runnable) 方法来传递消息,通知主线程做出相对应的 UI 工作。使用方法如下:

  复制代码
/**
 * 将可运行的 Runnable 添加到消息队列。Runnable 将在该 Handler 相关的线程上运行处理。
 * The runnable will be run on the thread to which this handler is attached.
 */
new Handler().post(new Runnable() {
    @Override
    public void run() {
        //更新处理 UI 工作
    }
});

3.1 具体例子

实现上述同样的功能 -> 点击按钮,子线程开始工作模拟处理耗时任务,通过 Handler 将进度告诉主线程,再通过 ProgressBar 将进度显示出来。 效果如下:

 

Handler.post() Demo 具体代码见下:

 

3.1.1 Java版本Demo

Java版本的具体代码如下:

  复制代码
public class HandlerPostFunctionActivity extends AppCompatActivity {
    private Handler mMainHandler;
    private ProgressBar mProgressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_add_thread);

        TextView titleTv = findViewById(R.id.title_tv);
        titleTv.setText("Handler post() function");

        mProgressBar = findViewById(R.id.test_handler_progress_bar);

        //新建静态内部类 Handler 对象
        mMainHandler = new Handler(getMainLooper());

        Button mClickBtn = findViewById(R.id.click_btn);
        mClickBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //开启子线程,子线程处理UI工作
                CustomChildThread customThread = new CustomChildThread();
                customThread.start();
            }
        });
    }

    /**
     * 子线程,用于处理耗时工作
     */
    public class CustomChildThread extends Thread {

        @Override
        public void run() {
            //模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。
            for (int i = 1; i <= 5; i++) {
                try {
                    //让当前执行的线程(即 CustomChildThread)睡眠 1s
                    Thread.sleep(1000);

                    //新创建一个 Runnable 用户处理 UI 工作
                    MyRunnable runnable = new MyRunnable(HandlerPostFunctionActivity.this, i);
                    //调用Handler post 方法。
                    mMainHandler.post(runnable);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将 Runnable 写成静态内部类,防止内存泄露
     */
    public static class MyRunnable implements Runnable {
        private int progressBarValue;
        private WeakReference<HandlerPostFunctionActivity> weakReference;

        MyRunnable(HandlerPostFunctionActivity activity, int value) {
            this.weakReference = new WeakReference<>(activity);
            this.progressBarValue = value;
        }

        @Override
        public void run() {
            HandlerPostFunctionActivity activity = weakReference.get();
            if (activity != null && !activity.isFinishing()) {
                activity.mProgressBar.setProgress(progressBarValue);
            }
        }
    }
}

执行结果看上图 Handler.post() Demo

3.1.2 Kotlin版本Demo

Kotlin版本的具体代码如下:

  复制代码
class TestHandlerPostRunnableActivity : AppCompatActivity() {
    private var mMainHandler: Handler? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_thread_add_handler)

        handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {
            //工作线程开始模拟下载任务
            val workThread: WorkThread = WorkThread(this)
            workThread.start()
        })

        //创建 Handler,关联App的 主Looper 对象
        mMainHandler = Handler(Looper.getMainLooper())
    }

    class WorkThread(private var activity: TestHandlerPostRunnableActivity) : Thread() {
        private var handler: Handler? = activity.mMainHandler

        override fun run() {
            super.run()
            for (i in 0..6) {
                sleep(1000)
                //新建 Runnable 设置进度参数传,然后通过 post(Runnable) 方法,让其更新 progressBar 进度
                val runnable: MyRunnable = MyRunnable(activity, i)
                handler?.post(runnable)
            }
        }
    }

    /**
     * 处理 UI 工作。
     * 静态内部类,防止内存泄露
     */
    class MyRunnable(activity: TestHandlerPostRunnableActivity, value: Int) : Runnable {
        private var weakReference = WeakReference(activity)
        private var progressValue = value

        override fun run() {
            val activity = weakReference.get()
            if (activity != null && !activity.isFinishing) {
                //获取任务执行进度参数,更新 progressBar 进度
                activity.handlerAddThreadProgressBar.progress = progressValue
            }
        }
    }
}

执行结果看上图 Handler.post() Demo

4. obtainMessage()方法

obtainMessage() 方法与 sendMessage() 方法很相似,通过 mHandler.obtainMessage().sendToTarget() 发送信息。该方法与 sendMessage() 的区别就是你不用额外去创建一个 Message 对象。

obtainMessage() 方法有三种,分别是:

  复制代码
//指定 what 用于区分,通过 Message.what 获得
public final Message obtainMessage(int what);

//传递obj参数,通过 Message.obj 获得
public final Message obtainMessage(int what, @Nullable Object obj)

//传递arg1 arg2参数,通过 Message.arg1 Message.arg2 获得
public final Message obtainMessage(int what, int arg1, int arg2)

4.1 具体例子

同样实现上述同样的功能 -> 点击按钮,子线程开始工作,通过 Handler 将进度告诉主线程,通过 ProgressBar 将进度显示出来。 效果如下:

 

Handler.obtainMessage() Demo 具体代码如下:

 

4.1.1 Java版本Demo

Java版本的具体代码如下:

  复制代码
public class HandlerObtainMessageActivity extends AppCompatActivity {
    private TextView mDisplayTv;
    private Handler mHandler;
    private ProgressBar mProgressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_add_thread);

        TextView titleTv = findViewById(R.id.title_tv);
        titleTv.setText("Handler + Thread");

        mDisplayTv = findViewById(R.id.display_tv);
        mProgressBar = findViewById(R.id.test_handler_progress_bar);

        //mHandler用于处理主线程消息队列中的子线程消息
        mHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        //更新 TextView UI
                        mDisplayTv.setText("Handler obtainMessage() Test!!");
                        break;
                    case 2:
                        //通过 msg.obj 获取 ProgressBar 的进度,然后显示进度值
                        int process = (int) msg.obj;
                        mProgressBar.setProgress(process);
                        break;
                    default:
                        break;
                }

            }
        };

        Button mClickBtn = findViewById(R.id.click_btn);
        mClickBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //开启子线程,子线程处理UI工作
                CustomChildThread customThread = new CustomChildThread();
                customThread.start();
            }
        });
    }

    /**
     * 子线程,用于处理耗时工作
     */
    public class CustomChildThread extends Thread {

        @Override
        public void run() {
            //发送第一条消息,代表开始执行异步任务
            mHandler.obtainMessage(1).sendToTarget();

            //模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。
            for (int i = 1; i <= 5; i++) {
                try {
                    //让当前执行的线程(即 CustomChildThread)睡眠 1s
                    Thread.sleep(1000);

                    //将执行进度参数 i 传递给主线程 progressBar
                    mHandler.obtainMessage(2, i).sendToTarget();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


        }
    }

}

执行结果看上图 Handler.obtainMessage() Demo

4.1.2 kotlin版本Demo

Kotlin版本的具体代码如下:

  复制代码
class TestHandlerObtainMessageActivity : AppCompatActivity() {
    companion object {
        const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_thread_add_handler)

        handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {
            //工作线程开始模拟下载任务
            val workThread: WorkThread = WorkThread(this)
            workThread.start()
        })
    }

    class WorkThread(activity: TestHandlerObtainMessageActivity) : Thread() {
        private var handler: MyHandler = MyHandler(activity)

        override fun run() {
            super.run()
            for (i in 0..6) {
                sleep(1000)
                //通过 Handler 将进度参数传递给 主线程,让其更新 progressBar 进度
                val bundle = Bundle()
                bundle.putInt(PROGRESS_VALUE_KEY, i)
                handler.obtainMessage(1, bundle).sendToTarget()
            }
        }
    }

    /**
     * 静态内部类,防止内存泄漏
     */
    class MyHandler(activity: TestHandlerObtainMessageActivity) : Handler() {
        private var weakReference = WeakReference(activity)

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                1 -> {
                    val activity = weakReference.get()
                    if (activity != null && !activity.isFinishing) {
                        //获取任务执行进度参数,然后通过 ProgressBar 显示出来
                        val bundle: Bundle = msg.obj as Bundle
                        val progressValue: Int = bundle.get(PROGRESS_VALUE_KEY) as Int
                        activity.handlerAddThreadProgressBar.progress = progressValue
                    }
                }
            }
        }
    }
}

执行结果看上图 Handler.obtainMessage() Demo

5. 总结:

在实际开发中,三种方法的使用都可行,具体用哪种方法,需结合你的实际情况及个人喜好。另外,在实际使用中往往将 Handler 写成静态内部类,这时需要注意防止内存泄露!(The handler class should be static or leaks might occur),具体代码见上方!

5.1 在子线程中创建Handler

思考: 在上面代码中, 我们都是在主线程中创建了 Handler 对象,那如果在子线程中创建一个 Handler 对象呢?会发生什么呢? 如下所示:我们在 CustomChildThread 线程中,新建一个 Handler 对象。

  复制代码
public class CustomChildThread extends Thread {
    @Override
    public void run() {
        Handler handler = new Handler(Activity.this);
        //会报错:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    }
}

结果: 抛出异常: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。 原因: 因为在创建 Handler对象时要关联所处线程的 Looper对象,而我们的子线程没有 Looper,所以会抛出上述异常。 解决方法: 通过调用,Looper.prepare() 方法为子线程创建一个 Looper 对象,并且调用 Looper.loop() 方法开始消息循环。如下所示:

  复制代码
class CustomChildThread extends Thread {
	@Override
    public void run() {
    	//为当前线程创建一个 Looper 对象
        Looper.prepare();
        
		//在子线程中创建一个 Handler 对象
        Handler handler = new Handler() {
            public void handleMessage(Message msg) {
                // 在这里处理传入的消息
            }
        };
        //开始消息循环
        Looper.loop();
    }
}

至此关于 Handler 的使用方法也就介绍完毕了,如果你想深入学习 Handler,了解其通信机制以及源码分析,请看博客 Android Handler深入学习(源码分析)

 

标签:public,Handler,线程,activity,msg,Android,方法,Message
From: https://www.cnblogs.com/Alex80/p/18006087

相关文章

  • 误删/lib64/libkrb5.so.3.3的恢复方法
    误删系统库的情况下,想要恢复,一般比较麻烦,常用的方法有:1.通过传输、下载来获取删除的文件,工具有:sftp,ftp,scp,wget,curl,yum等,具体使用方式就不讲了。2.如果网络不可用,即无法通过传输的方式把文件放到系统中。那么,还可以通过U盘,挂载等方式,拷贝库到系统中。3.当网络,挂载都不可......
  • 2024最新Android设备UUID/UDID使用指南
    摘要本篇博客主要介绍了Android设备的唯一标识符(UUID和UDID)的使用教程。在Android平台上获取设备ID一直是开发者面临的难题,因为缺乏稳定的API来获取设备ID。本文将介绍几种获取设备ID的方法,并分析它们的优缺点。引言UDID和UUID是Android设备的唯一标识符,用于标识不同设备或不同......
  • Android Graphics 显示系统 - 如何模拟多(物理)显示屏?
    “ 本着花小钱办大事,不花钱也办事的原则,为了避免花钱买设备,那如何更便捷地学习/测试Android多屏显示的内容呢?本文就给大家介绍一种模拟Android多个物理屏幕显示的方法。” 01—AndroidEmulator旧方式的缺憾 早前的文章中,曾经介绍了使用AndroidEmulator模拟多......
  • js 数组和对象的深拷贝的方法
    数组深拷贝的方法方法一:for循环实现vararr=[1,2,3,4,5]vararr2=copyArr(arr)functioncopyArr(arr){letres=[]for(leti=0;i<arr.length;i++){res.push(arr[i])}returnres} 方法二:slice方法原理也比较好理解,他是将原数......
  • 六大方法助您建立起完整的销售管理流程
    实施精细化销售过程管理的主要步骤包括:一、明确销售目标;二、建立客户数据库;三、制定销售策略;四、优化销售流程;五、建立绩效考核体系;六、持续改进和创新。通过这些步骤,企业可以提高销售效率,拓展市场份额,实现可持续发展。一、明确销售目标实施精细化销售过程管理的第一步是明确销......
  • 提升销售线索数量,学会这六种方法就够了!
    提高销售线索质量和数量的方法有:一、优化网站和营销内容;二、利用社交媒体和网络营销;三、提供优质的客户服务和体验;四、定期的市场调研和数据分析;五、建立合作关系和联盟;六、持续学习和创新。通过这些方法,可以帮助企业更好地了解客户需求,拓展市场份额并增加销售额。 一、优......
  • # yyds干货盘点 # 盘点一个txt文档合并的实战需求(方法二)
    大家好,我是皮皮。一、前言前几天在Python最强王者交流群【FiNε_】问了一个Pandas数据合并的问题。问题如下图所示:上一篇文章中我们已经看到了两个方法,这一篇文章我们一起来看看另外一个方法。二、实现过程这里【哎呦喂 是豆子~】给了一个指导,如下所示:并给出了如下代码:importpand......
  • 【APP逆向11】Android基础
    1.发送网络请求基于okhttp3表单格式newThread(){@Overridepublicvoidrun(){OkHttpClientclient=newOkHttpClient();//user=xwl&age=99&size18FormBodyform=newFormBody.Builder().add(&qu......
  • 简单方法使 kernel32.dll 发生重定位
    简单方法使kernel32.dll发生重定位系统启动后kernel32.dll在每个进程的加载地址都相同。。。。吗?相信很多人都知道这个,因为这个是远程线程注入的基础,类似的还有user32.dll之类的系统dll。不过这个概念是错误的,或者说它在绝大多数时候是正确的因为通常来说这些系统dll默......
  • 安卓开发1——安装Android studio
    去网上找Android的studio1的下载教程找到对应的下载链接,下载 因为我有安装完成后就把下载的软件安装的文件删除的习惯所以就不展示了具体的安装教程可以看AndroidStudio安装配置教程-Windows(详细版)-CSDN博客 安装后新建项目完成后在最右侧的第三个可以挑选自己安卓......