Android Handler详解--实战篇

概述

之前写了一篇文章讲解Handler机制,但是我觉得理论总是不比实践来的深刻,所以今天我们来自己实现一个Handler。如果没看过之前的文章,建议先看一下Android Handler详解–理论篇

MyMessageQueue

首先实现MyMessageQueue,当然在这之前我们要实现MyMeessage类

public class MyMessage {

    public MyHandler target;
}

为了方便,我只写了一个Handler成员变量
接下来就是MyMessageQueue的实现

public class MyMessageQueue {

    private volatile List<MyMessage> queue;

    public MyMessageQueue() {
        queue = new ArrayList<>();
    }

    public void add(MyMessage message) {
        queue.add(message);
    }

    /**
     * 取出队头的消息并出队,队列位空的时候阻塞
     * @return
     */
    public MyMessage next() {
        while (true) {
            if (!queue.isEmpty()) {
                break;
            }
        }
        MyMessage message = null;
        synchronized (MyMessageQueue.this) {
            message = queue.get(0);
            queue.remove(0);
        }
        return message;
    }
}

代码很简单,有两个方法,入队和出队,对列为空的时候出队方法next()会被阻塞直到有新的消息进来。

MyLooper

MyLooper的实现依赖ThreadLocal,我们提供prepare()、getLooper()、loop()三个静态方法,在内部维护一个静态的ThreadLocal< MyLooper >变量,通过这个变量设置或获取当前线程的Looper实例。代码如下

public class MyLooper {

    private static ThreadLocal<MyLooper> sLooper = new ThreadLocal<>();

    public MyMessageQueue queue;

    public MyLooper() {
        queue = new MyMessageQueue();
    }

    public static void prepare() {
        if (sLooper.get() == null) {
            sLooper.set(new MyLooper());
        }
    }

    public static MyLooper getLooper() {
        return sLooper.get();
    }

    public static void loop() {
        if (sLooper.get() == null) {
            throw new RuntimeException("no looper exist");
        }
        while (true) {
            MyMessage myMessage = sLooper.get().queue.next();
            //Log.d("Debug", myMessage.toString());
            myMessage.target.handleMessage(myMessage);
            break;
        }
    }
}

在Looper内部维护一个MessageQueue变量,在loop()方法中无限循环查询queue,有消息就调用Message.target.handleMessage()。但是我们这里在取出一条消息处理之后就跳出循环了,这是为什么呢?不要着急,下面会详细解释,虽然只取一条,但是也足够展示在子线程更新UI的效果了。

MyHandler

MyHandler持有当前线程的Looper,在创建的时候根据当前线程获得,如果当前线程没有Looper则报错。在sendMessage方法中将Message的target赋值,然后入队。

public abstract class MyHandler {

    private MyLooper looper;

    private MyMessageQueue queue;

    public MyHandler() {
        looper = MyLooper.getLooper();
        if (looper == null) {
            throw new RuntimeException("current thread does not have looper");
        }
        queue = looper.queue;
    }

    public void sendMessage(MyMessage message) {
        message.target = MyHandler.this;
        queue.add(message);
    }

    public abstract void handleMessage(MyMessage message);
}

效果展示

好了,现在我们在布局文件中放一个TextView显示Hello,World

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:layout_centerInParent="true"/>
</RelativeLayout>

MainActivity代码如下

public class MainActivity extends AppCompatActivity {

    private TextView textView;

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

        textView = (TextView) findViewById(R.id.text);

        MyLooper.prepare();

        final MyHandler myHandler = new MyHandler() {
            @Override
            public void handleMessage(MyMessage message) {
                textView.setText("change");
            }
        };

        new Thread(new Runnable() {
            @Override
            public void run() {
                myHandler.sendMessage(new MyMessage());
            }
        }).start();
        MyLooper.loop();
    }
}

效果图如下
这里写图片描述

疑点解惑

虽然我们完成了子线程更新UI,但是还是有一些问题的,假设我们将Looper的loop方法设为无限循环,而不是取到一条消息就退出循环,那会怎么样?其实都不用试,想一下就知道那主线程就被阻塞了,无法响应用户操作,然后报ANR错误。这样另一个问题就出来了,那为什么主线程原本的Looper不会阻塞呢?他的源码里也是无限循环啊?接下来根据源码来说明一下这个问题。
大家都知道java程序有一个入口点public static void main(String[] args),那既然Android是用java写的,大家一直就不疑惑入口点在哪吗?其实android程序的入口点在ActivityThread这个类当中,代码如下

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

我们可以看到在代码的最后开启了loop,这里就无限循环了,假设一下,如果没有无限循环,那么main方法就结束了,那程序不就结束了么。。。。
Android是基于事件驱动的,也就是说我们的整个应用就是运行在这个最大的Looper循环中的,比如现在系统有一个Activity,在某一情况下需要调用他的onPause方法,怎么办?系统会通过本身的Handler发送一个消息,然后这个Looper获取然后处理(也就是调用Activity的onPause回调方法)。再比如现在啥都不干,那系统就阻塞着,也不会ANR,直到我们有什么动作(比如有触摸事件产生),那么Looper就继续处理。
那ANR在什么情况下会发生?ANR只会在handler处理一条消息超过一定时间才会发生,而不是阻塞就会发生(系统基于事件驱动,说明有事件才会动,没事件就阻塞)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值