Handler系列之创建子线程Handler

  上一篇我介绍了Handler机制的工作原理,默认情况下,ActivityThread类为我们创建的了主线程的Looper和消息队列,所以当你创建Handler之后发送消息的时候,消息的轮训和handle都是在ui线程进行的。这种情况属于子线程给主线程发消息,通知主线程更新ui...等,那么反过来,怎么才能让主线程给子线程发消息,通知子线程做一些耗时逻辑??

  之前的学习我们知道,Android的消息机制遵循三个步骤:

    1  创建当前线程的Looper  

    2  创建当前线程的Handler 

    3  调用当前线程Looper对象的loop方法

  看过之前文章的朋友会注意到,本篇我特意强调了“当前线程”。是的之前我们学习的很多都是Android未我们做好了的,譬如:创建主线程的Looper、主线程的消息队列...就连我们使用的handler也是主线程的。那么如果我想创建非主线程的Handler并且发送消息、处理消息,这一系列的操作我们应该怎么办那???不怎么办、凉拌~~~什么意思???依葫芦画瓢,依然遵循上面的三步走,直接上代码!!!!

public class ChildThreadHandlerActivity extends Activity {
    private MyThread childThread;

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

        childThread = new MyThread();
        childThread.start();

        Handler childHandler = new Handler(childThread.childLooper){//这样之后,childHandler和childLooper就关联起来了。
            public void handleMessage(Message msg) {

            };
        };
    }

    private class MyThread extends Thread{
        public Looper childLooper;

        @Override
        public void run() {
            Looper.prepare();//创建与当前线程相关的Looper
            childLooper = Looper.myLooper();//获取当前线程的Looper对象
            Looper.loop();//调用此方法,消息才会循环处理
        }
    }
}

代码如上,我们依然循序Android的三步走战略,完成了子线程Handler的创建,难道这样创建完了,就可以发消息了么?发的消息在什么线程处理?一系列的问题,怎么办?看代码!!!运行上述代码,我们发现一个问题,就是此代码一会崩溃、一会不崩溃,通过查看日志我们看到崩溃的原因是空指针。谁为空???查到是我们的Looper对象,怎么会那?我不是在子线程的run方法中初始化Looper对象了么?话是没错,但是你要知道,当你statr子线程的时候,虽然子线程的run方法得到执行,但是主线程中代码依然会向下执行,造成空指针的原因是当我们new Handler(childThread.childLooper)的时候,run方法中的Looper对象还没初始化。当然这种情况是随机的,所以造成偶现的崩溃。

  那怎么办?难道我们不能创建子线程Handler ???No!!!No!!!No!!!,你能想到的Android早就为我们实现好了,HandlerThread类就是解决这个问题的关键所在,看代码!!!

public class HandlerThreadActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        TextView textView = (TextView) findViewById(R.id.tv);
        textView.setText("HandlerThreadActivity.class");

        HandlerThread handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();

        Handler mHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
            }
        };

        Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
        mHandler.sendEmptyMessage(1);
    }
}

创建HandlerThread对象的时候,有个参数,是指定线程名字的。上面的代码不管运行多少次都不会奔溃!!!并且这种方法创建的handler的handleMessage方法运行在子线程中。所以我们可以在这里处理一些耗时的逻辑。到此我们完成了主线程给子线程发通知,在子线程做耗时逻辑的操作。

  下面我们去看看源码,看看为什么使用HandlerThread就可以避免空指针那?

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

HandlerThread类的getLooper方法如上,我们看到当我们获取当前线程Looper对象的时候,会先判断当前线程是否存活,然后还要判断Looper对象是否为空,都满足之后才会返回给我Looper对象,否则处于等待状态!!既然有等待,那就有唤醒的时候,在那里那???我们发现HandlerThread的run方法中,有如下代码:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

说明了什么那???HandlerThread类start的时候,Looper对象就初始化了,并唤醒之前等待的。所以HandlerThread很好的避免了之前空指针的产生。所以以后要想创建非主线程的Handler时,我们用HandlerThread类提供的Looper对象即可。

  至此,前三篇我们讲了Handler的使用、工作原理、创建子线程Handler。下一篇我会讲使用Handler引起的内存泄漏的解决办法。

时间: 2024-10-12 11:40:36

Handler系列之创建子线程Handler的相关文章

关于Handler的理解,子线程不能更新UI的纠正和回调的思考

开发Android这么久了,总会听到有人说:主线程不能访问网络,子线程不能更新UI.Android的主线程的确不能长时间阻塞,但是子线程为什么不能更新UI呢?今天把这些东西整理,顺便在子线程更新UI. 首先写了一个handler在子线程更新主线程UI,在子线程做了一个耗时操作:从网络下载了一个图片并利用handler发送到handleMessage()的回调中,并更新到主线程的bitmap.图片显示成功,没有问题.接下来在子线程中更新onCreate()中实例化的textview,报错: and

C# WebService中任务处理线程创建子线程后

protected void WriteLog(string message) { lock (lockObject) { var file = System.IO.File.AppendText("C:\\log.txt"); file.WriteLine(message); file.Close(); } } protected void asyctest(int threadid) { this.WriteLog(string.Format("主线程({0})的子线程(

Handler消息传递机制(四)子线程接收主线程发送的消息

package com.example.looper; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widg

Handler系列之内存泄漏

本篇简单的讲一下平常使用Handler时造成内存泄漏的问题. 什么是内存泄漏?大白话讲就是分配出去的内存,回收不回来.严重会导致内存不足OOM.下面来看一下造成内存泄漏的代码: public class MemoryLeakActivity extends Activity { private MyHandler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(save

Android中Handler的使用方法——在子线程中更新界面

本文主要介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联. Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Han

使用Handler在子线程中更新UI

Android规定只能在主线程中更新UI,如果在子线程中更新UI 的话会提示如下错误:Only the original thread that created a view hierachy can touch its view((只有原来的线程创建一个视图层次可以触摸它的视图). 只能在主线程中更新UI的原因是:android中相关的view和控件不是线程安全的,我们必须单独做处理. 有的时候需要再子线程中实现更新UI,下面介绍使用Handler实现线程通信的特点实现在子线程中更新UI. H

Android子线程更新UI主线程方法之Handler

背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. 下面说下有关Handler相关的知识. 多线程一些基础知识回顾:在介绍Handler类相关知识之前,我们先看看在Java中是如何创建多线程的方法有两种:通过继承Thread类,重写Run方法来实现通过继承接口Runnable实现多线程 具体两者的区别与实现,看看这篇文章中的介绍:http://de

转 在子线程中new Handler报错--Can't create handler inside thread that has not called Looper.prepare()

在子线程中new一个Handler为什么会报以下错误? java.lang.RuntimeException:  Can't create handler inside thread that has not called Looper.prepare()  这是因为Handler对象与其调用者在同一线程中,如果在Handler中设置了延时操作,则调用线程也会堵塞.每个Handler对象都会绑定一个Looper对象,每个Looper对象对应一个消息队列(MessageQueue).如果在创建Ha

iOS-Senior10-多线程(子线程创建)

1.多线程概述 程序:由源代码生成的可执行应用.(eg:QQ.app) 进程:一个正在运行的程序可以看做一个进程.(eg:正在运行的QQ就是一个进程),进程用用独立运行所需的全部资源. 线程:程序中独立运行的代码段.(eg:接收QQ消息的代码) 一个进程是由一或多个线程组成.进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行. 1.1单线程 每个正在运行的程序(即进程),至少包含一个线程,这个线程就叫主线程. 主线程在程序启动时被创建,用于执行main函数. 只有一个主线程的