IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android中Handler知识(一) -> 正文阅读

[移动开发]Android中Handler知识(一)

Handler、Looper、Message?

篇幅较长

Handler。

首先,我们创建两个Handler对象。这边我们一个在主线程中实例,一个放在线程中实例:
public class MainActivity extends BaseActivity { 
    private Handler mHandler0; 
    private Handler mHandler1;
    @Override protected void onCreate(Bundle savedInstanceState) {                                             
        ......

        mHandler0 = new Handler(); 
        new Thread(new Runnable() { 
            @Override public void run() {                             
                mHandler1 = new Handler(); 
            } 
        }).start(); 
}

此时会发现在线程中创建的Handler对象会崩溃,错误信息是:Can't create handler inside thread that has not called Looper.prepare()

根据提示,我们没有使用Looper.prepare();我们添加此行代码调试:

new Thread(new Runnable(){ 
    @override public void run(){ 
        Looper.prepare(); 
        mHandler1 = new Handler(); 
    }
}).start();

我们发现不崩溃了。此时会有疑问,为什么会出现这样的问题呢?我们来跟跟源码,从构造函数开始:

public Handler() { this(null, false);}

构造函数中只是简单调用了this函数,我们继续跟踪this函数

public Handler(Callback callback, boolean async) { 
    ......

    mLooper = Looper.myLooper(); 
    if (mLooper == null) { 
        throw new RuntimeException( "Can't create handler inside thread that has not called         
    Looper.prepare()"); 
    }
    mQueue = mLooper.mQueue;
    mCallback = callback; 
    mAsynchronous = async; 
}

可以看出在此函数中 mLooper为空时,抛出次异常。那什么时候为空呢,继续跟踪myLooper();

public static Looper myLooper() { return sThreadLocal.get(); }

我们在这个方法中就会有疑问了,sThreadLocal是啥?作用是干嘛的。我们先看类中sThreadLocal的申明:

static final ThreadLocat<Looper> sThreadLocal = new ThreadLocal<Looper> ();

可以简单的看成是Looper的一个集,若sThreadLocal变量存在Looper对象,就可以被get()到,否则返回null。跟踪到这边我们大概可以猜到Looper.prepare()做啥了,去设置sThreadLocal;来看看源码:

private static void prepare(boolean quitAllowed) { 
    if (sThreadLocal.get() != null) { 
        throw new RuntimeException("Only one Looper may be created per thread");             
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

到这边'为什么要prepare再创建'就结束了。但我们的好奇心会问了:为什么主线程不需要我们先perpare再创建?

为什么主线程不需要我们先prepare再创建

主线程不需要prepare的原因是:提前已经给我们处理过了这个问题。我们查看ActivityThread中的main()方法:

public static void main(String[] args) { 
    ......
    Looper.prepareMainLooper(); 
    ......
    if (false) { 
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));                 
    }
    Looper.loop(); 
    throw new RuntimeException("Main thread loop unexpectedly exited");
    }

在此方法中我们能看到执行了Looper.prepareMainLooper();这个方法做什么用呢?我们继续跟踪:

public static void prepareMainLooper() { 
    prepare(false); 
    synchronized (Looper.class) { 
        if (sMainLooper != null) { 
            throw new IllegalStateException("The main Looper has already been prepared."); 
        }
    }
}

我们看到在此方法中执行了prepare()。
总结:主线程中google已经帮我们创建了Looper,而子线程中没有自动帮我们创建,所以需要我们自己调用Looper.prepare()。

发送信息

接下来我们来了解一下怎么发送消息。流程是创建一个Message->给Message对象设置值->借助Handler发送。

new Thread(new Runnable() {
     @Override public void run() {
        Message msg = Message.obtain(); 
        msg.arg1 = 1; 
        Bundle bundle = new Bundle(); 
        bundle.putString("key","frenk"); 
        msg.setData(bundle); 
        mHandler0.sendMessage(msg); 
    } 
}).start();

发送后我们再利用Handler中的handleMessage()方法这个Message拿出来使用即可。但内部具体流程是怎么样的,我们一起来分析一下源码。从发送方法开始sendMessage()。源码太长有些方法会直接介绍作用,有兴趣的可自己去看源码

入队操作

public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);}
public final boolean sendMessageDelayed(Message msg, long delayMillis){ 
    if (delayMillis < 0) { 
        delayMillis = 0; 
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 
    MessageQueue queue = mQueue; 
    if (queue == null) { 
        RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); 
        Log.w("Looper", e.getMessage(), e); 
        return false; 
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

由sendMessageAtTime()分析得出,Message对象创建一个消息队列MessageQueue它由mQueue赋值,源码中mQueue = mLooper.mQueue,而mLooper是Looper对象,由最上面介绍Handler时有介绍,每个线程只有一个Looper,因此,相应的Looper对应相应的MessageQueue。之后enqueueMessage(queue, msg, uptimeMillis)是入队操作。

private boolean enqueueMessage(MessageQueue queue, Message msg, long upt imeMillis) {         
    msg.target = this; 
    if (mAsynchronous) { 
        msg.setAsynchronous(true); 
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) { 
    ......
    synchronized (this) { 
        if (mQuitting) { 
            IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); 
            Log.w("MessageQueue", e.getMessage(), e);
            msg.recycle(); 
            return false; 
        }
        msg.markInUse(); 
        msg.when = when; 
        Message p = mMessages; 
        boolean needWake; 
        if (p == null || when == 0 || when < p.when) { 
            msg.next = p; 
        mMessages = msg; 
        needWake = mBlocked; 
    } else { 
        needWake = mBlocked && p.target == null && msg.isAsynchronous (); 
        Message prev; 
        for (;;) { 
            prev = p;
            p = p.next; 
            if (p == null || when < p.when) { break; }
            if (needWake && p.isAsynchronous()) { needWake = false; } }
            msg.next = p; // invariant: p == prev.next 
            prev.next = msg; 
        }
        if (needWake) { nativeWake(mPtr); } }
    return true;
}

观察方法内代码,队列中根据时间when来排序,这个时间就是我们一路传过来的参数,之后再根据时间顺序调用msg.next来指定下一个要处理的消息是什么,如果通过sendMessageAtFrontOfQueue()中也是通过enqueueMessage(queue, msg, 0)进行入队,不过它没有设置延时时间,此时Message会直接添加到对头。入队到这边就了解差不多了,接下来是出队操作。

出队操作

我们队列MessageQueue对象是在Looper中赋值,所以可直接在Looper类中找出队操作。来看一看Looper.Loop()方法:

public static void loop() { 
    final Looper me = myLooper(); 
    if (me == null) { 
        throw new RuntimeException("No Looper; Looper.prepare() wasn't ca lled on this     
thread."); 
    }
    final MessageQueue queue = me.mQueue; 
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    for (;;) { 
        Message msg = queue.next(); 
        if (msg == null) {return; }
        ......
        msg.target.dispatchMessage(msg);
        ......
        msg.recycleUnchecked(); 
    }
}

代码过多,只挑重要的分析,进入for死循环,之后不断从MessageQueue对象中取出消息msg,next()就是进行队列的出队方法,next()方法代码有点长,这边就不贴出来了。next的主要逻辑是:判断当前MessageQueue是否存在待处理的mMessages消息,如果有,就出队此消息,然后让下一个消息成为mMessages,否则就进入阻塞状态,一直等到有新的消息入队唤醒。回看loop方法,在执行next()方法后会执行msg.target.dispatchMessage(msg);,此时msg.target就是Handler对象,最后看一下dispatchMessage()方法:

public void dispatchMessage(Message msg) { 
    if (msg.callback != null) { 
        handleCallback(msg); 
    } else { 
        if (mCallback != null) { 
            if (mCallback.handleMessage(msg)) { 
                return;
            } 
        }
        handleMessage(msg); 
    }
}

如果mCallback 不为null则调用mCallback.handleMessage(),否则调用handleMessage(),并将消息作为参数传出去。这样我们就明白为什么要使用handleMessage()来捕获我们之前传递过去的信息。

根据上面的理解,不难写出异步消息处理机制的线程。

new Thread(new Runnable(){ 
    @override public void run(){ 
        Looper.prepare(); 
        mHandler1 = new Handler(){ 
            @Override public void handleMessage(Message msg) { 
                super.handleMessage(msg); //处理消息 
            } 
        }; 
        Looper.loop();
    }
}).start();

总结

Looper负责创建一个消息队列MessageQueue对象,然后进入死循环中不断的取出消息,这些消息都是由一个或者多个Handler进行创建处理的。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-07 10:56:29  更:2021-09-07 10:57:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:21:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码