深远探究Android音讯机制之Handler
分类:彩世界彩票注册平台官网

音录像/高清大图片/人工智能/直播/抖音等等这一年与顾客最严密,与大家生存最相关的本领一直都在探求最终的才干诞毕生台,从前是windows系统,而现行反革命则是运动系统了,移动系统中又是以Android占比绝超越八分之四为前提,所以Android NDK工夫已然是大家必备本领了。

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}

/**
 * Constructor associates this handler with the {@link Looper} for the
 * current thread and takes a callback interface in which you can handle
 * messages.
 * <p>
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 *
 * @param callback The callback interface in which to handle messages, or null.
 */
public Handler(Callback callback) {
    this(callback, false);
}

4 * is created by the Android environment, so you should never need

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //在message中放一个标记
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //在这里把消息放到队列里面去
    return queue.enqueueMessage(msg, uptimeMillis);
}

3 //...

// sThreadLocal.get() will return null unless you've called prepare().
//sThreadLocal在Looper中作为全局变量,用于保存每个线程中的数据,可以看做是容器
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

13 Looper.loop();

个别看一下sMainLooper是什么样,myLooper()又是怎么样?
Looper.java:

5 * to call this function yourself. See also: {@link #prepare()}

   public static void main(String[] args) {
        ......

        //在android应用程序的入口其实在ActivityThread的main方法
        //在这里,主线程会创建一个Looper对象。
        Looper.prepareMainLooper();

        ......
        ......
        ......        
        //执行消息循环
        Looper.loop();

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

9 synchronized (Looper.class) {

事先写了一篇关于Message的稿子,感兴趣的对象能够去看一下【Android消息机制之Message深入分析(面试)】,这一遍大家来聊一下音信机制中用得最多的Handler,也是面试中问得最多的之一,在此边作者先抛多少个难点出来:

3 * application's main looper. The main looper for your application

1.Handler、Looper、Thread有怎样关系?
2.怎么在子线程成立handler会抛卓殊 "Can't create handler inside thread that has not called Looper.prepare()"?
3.怎样运用handler来管理message?
4.为什么无法在子线程更新UI?

能够见到在 ActivityThread 里 调用了 Looper.prepareMainLooper() 方法创制了 主线程的 Looper ,并且调用了 loop() 方法,所以大家就足以直接采取 Handler 了。

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    //这里先判断callback是否为空
    // callback就是我们使用handler.post(Runnable r)的入参runnable
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //如果hanlder的入参callback不为空,优先处理
        if (mCallback != null) {
            //如果回调返回true.则拦截了handler.handleMessage的方法
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //这就是为什么我们使用hanlder的时候,需要重写handleMessage的方法
        handleMessage(msg);
    }
}

15}

再看回之前的代码:
looper.java:

Android应用是由Java语言进行付出的,SDK也是由Java语言编写,所以大家要上学java语言。别的,虽说kotlin语言得到了Android官方的热推,但是kotlin也是编写翻译成了java语言再运转的。对于Android来讲,只要SDK未有用kotlin重写,那么Java语言是都亟需上学的。并且Android apk的后台服务器程序大约率是java语言创设,所以读书java也是一种必然。

**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    //在主线程中,其默认初始化一个Looper对象,因此我们在主线程的操作中是不需要自己去调prepare()。
    prepare(false);
    synchronized (Looper.class) {
        //这里先进行判断,在主线程是否已经存在Looper了,
        // 避免我们手动去调用prepareMainLooper(),因为这个是给程序入口初始化的时候系统会自动调用的
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        //设置全局变量,主线程的looper
        sMainLooper = myLooper();
    }
}

每一个Handler 的线程都有一个 Looper ,主线程当然也不例外,不过大家并未有计划过主线程的 Looper 而得以一向动用,那是为啥?

Handler.java:

9 if (sMainThreadHandler == null) {

代码十分长,大家挑关键的代码来看,在代码中自个儿已写上批注,在main函数中,Looper调用了prepareMainLooer(),大家再踏入Looper看看。

接下去小编做了一部分关于高阶安卓的就学资料及思路,希望能够帮衬到大家学习升高工夫。

据此,假若大家在子线程中创设Handler的时候,大家能够这么:

图片 1

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>");

    //在android应用程序的入口其实在ActivityThread的main方法
    //在这里,主线程会创建一个Looper对象。
    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");
}

图片 2

Handler.java:

图片 3

最终一行代码中,大家来看了非常熟习的handleMessage,那不正是大家平日handler.handlerMessage的法子吗?
Demo:

群内还应该有多数免费的有关高阶安卓学习质地,满含高档UI、品质优化、架构师课程、 NDK、混合式开垦:ReactNative Weex等多少个Android才干知识的架构摄像材质,还也许有专业生涯规划及面试指点。

我们来看关键的代码:
Thread t=Thread.currentThread();

6 ActivityThread thread = new ActivityThread();

但只顾在此以前大家所看见的,假诺大家mCallback.handlerMessage(msg)重回为true的话,那样就不交付handler.handleMessage管理了。

急需那个质感的大家关怀 私信回复“安卓资料”无偿获取!

ActivityThread.java:

14 }

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    //这里先判断callback是否为空
    // callback就是我们使用handler.post(Runnable r)的入参runnable
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //如果hanlder的入参callback不为空,优先处理
        if (mCallback != null) {
            //如果回调返回true.则拦截了handler.handleMessage的方法
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //这就是为什么我们使用hanlder的时候,需要重写handleMessage的方法
        handleMessage(msg);
    }
}

12 //...

msg.target其实正是我们的handler,无论是handler通过post可能sendEmptyMessage,最后都会调用到调到那几个enqueueMessage(),在此会将handler赋值到msg.target中.

留心:Looper.loop() 是个死循环,后边的代码平常情况不会实行。

注:
英特网也可以有这个稿子关于Android音信机制的小说,大神们都写得很透顶,但为啥我还要去写这么的稿子,一方便想透过读源码来加强个人的读书技艺,另一方面也想将团结学到的享用给大家,哪怕多年过后看回本身的博客,也是别有一番滋味。从14年上马出来做android,看了成都百货上千篇章,学到了大多东西,作者很钦佩他们。特别感激郭霖、李纪钢、徐宜生等等,因为她俩的无私分享让洋德国人在办事上解决了无数难题,由此作者也愿意笔者能产生那样的人。

6 */

private static void prepare(boolean quitAllowed) {
    //先判断当前线程是否已经存在Looper了,如果存在,不允许设置新的Looper对象,一个线程只允许存在一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //在当前线程中,创建新的Looper对象,并绑定当前线程
    sThreadLocal.set(new Looper(quitAllowed));
}

图片 4

//保存一个主线程的looper
private static Looper sMainLooper;  // guarded by Looper.class

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static Looper myLooper() {
    //使用当前线程的looper
    return sThreadLocal.get();
}

Android前沿才具

小编们回去handler.dispatchMessage(Message)中,假设不是因此post那么callback就为空,我们看见了五个mCallback变量,大家看看那个Callback的概念:

13 sMainLooper = myLooper();

3.Handler得以透过通过post只怕sendMessage进行发送音讯,因为其最终会调用sendMessageDelayed,大家得以通过runnable形式大概重写handleMessage进行音讯的管理,当然借使经过handler.sendMessage(msg)的章程的话,我们可以实现Callback接口到达音讯的管理。

4 Looper.prepareMainLooper();

Looper中,sThreadLocal作为贰个全局变量,sThreadLocal其实是保存Looper的七个器皿,大家继续往ThreadLocal的get、set进行剖析。

移步框架结构师

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    //获取当前线程保存的对象--通过get函数来获取Looper对象
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T) e.value;
    }
    return setInitialValue();
}

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *              this thread-local.
 */
public void set(T value) {
    //把当前的looper保存到当前线程中
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

14

Handler.java:

10 if (sMainLooper != null) {

The callback interface in which to handle messages, or null.
笔者们能够透过完毕这一个接口,并视作一个参数字传送进去Handler来达到拍卖那个音讯的效果与利益。

架构师不是自然的,在Android里面最常用的框架结构无外乎 MVC,MVP,MVVM,可是那些思量一经和模块化,档次化,组件化混和在一道,那就不是一件那么粗略的事了,大家须求四个确实身经百战的架构师本事解说透顶个中蕴藏的深理。

在dispatchMessage函数中,意思便是散发这几个音讯,在代码中先决断msg.callback是还是不是为空,msg.callback是什么?在上一篇小说已详细介绍过了,其实正是handler.post中的runnable对象,通俗的来讲便是handler借使有post操作的,就管理post的操作,我们在探视handlerCallback这些函数。
Handler.java:

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

总结:

5

作者们带着难点去看源码:

10 sMainThreadHandler = thread.getHandler();

小心那么些函数的笺注,大致敬思是:在主线程创建贰个looper,是其一主线程的主looper,当以此app在最早化的时候就能够自行创制,由此那些函数不是给您们调用的,是给系统本人在程序创设的时候调用的。

于今去过多商城面试,除了您全部基本的可以写二个高质量app的工夫后,平常都会在协调的app里面加一些存世的对峙较 666 的技艺,这么些技艺大家称为前沿技艺。他们日常富含热进级,热修复,App Instant,强制更新,组件化路由框架结构Arouter,EnclavexJava,IOC架构方法,Hook工夫等等,当然,这一个本事你无法只会用,你需求通晓她的原理,一时候,你还必要知道什么对那么些架构实行改正。

Handler:消息的管理者,工厂中流水线的工友。
Message:系统传递的音讯,工厂中流水生产线上的制品。
MessageQueue:音讯队列,工厂中流水生产线上的传递带。
Looper:发动机,工厂中使流水生产线的传递带移动的发动机。

8 prepare;

大家继续看回来我们的Looper.loop()
Looper.java:

7public static void prepareMainLooper() {

在这里处,大家看出了sThreadLocal,大家先看看那么些sThreadLocal在Looper是干什么用的。

NDK 模块开采

Looper.java:

11 }

new Thread(new Runnable() {
    @Override
    public void run() {
        //在当前子线程创建一个Looper对象
        Looper.prepare();
        Handler handler=new Handler();
        //让消息队列动起来
        Looper.loop();
    }
}).start();

专一:平日大家感到 ActivityThread 正是主线程。事实上它而不是八个线程,而是主线程操作的官员,所以啊,笔者以为把 ActivityThread 认为就是主线程未有什么能够指责,此外主线程也可以说成 UI 线程。

/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 * <p>
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
public Handler() {
    this(null, false);
}

/**
 *......
 */
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: "  
                    klass.getCanonicalName());
        }
    }
//获取当前线程的looper
     mLooper = Looper.myLooper();
//如果当前线程没有绑定looper则抛异常
    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;
}

Java语言进级与Android相关本事基础

深远探究Android音讯机制之Handler。我们三回九转往下看,有个prepare(boolean)函数,大家去探问那个到底是用来干什么的。
Looper.java:

7 thread.attach;

private static void prepare(boolean quitAllowed) {
    //先判断当前线程是否已经存在Looper了,如果存在,不允许设置新的Looper对象,一个线程只允许存在一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //在当前线程中,创建新的Looper对象,并绑定当前线程
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper.prepareMainLooper(); 代码如下: 1/**

//关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()
msg.target.dispatchMessage(msg);

在 ActivityThread.main() 方法中有如下代码: 1//android.app.ActivityThread

private static void handleCallback(Message message) {
    message.callback.run();
}

16}

/**
 * 模拟开始
 */
private void doSth() {
    //开启个线程,处理复杂的业务业务
    new Thread(new Runnable() {
        @Override
        public void run() {
            //模拟很复杂的业务,需要1000ms进行操作的业务
            ......
            handler.post(new Runnable() {
                @Override
                public void run() {
                    //在这里可以更新ui
                    mTv.setText("在这个点我更新了:"   System.currentTimeMillis());
                }
            });
        }
    }).start();
}

2public static void main(String[] args) {

1.为何在主线程中开创Handler没有要求大家调用Looper.prepare().因为在前后相继的进口中系统会调用Looper.prepareMainLooper()来创设,何况让其主线程的Looper运维起来。假使大家在子线程成立handler,须要手动创造looper而且运营。

深远探究Android音讯机制之Handler。8

Looper.prepare(boolean)的意义就是创办二个Looper对象,并与当前线程绑定在一块。在代码中,首先推断当前线程是或不是已经存在looper,假若海市蜃楼则开立异的looper并且绑定到前段时间的线程上。

12 }

像这种类型就能够学有所成在子线程创建handler。

2 * Initialize the current thread as a looper, marking it as an

Initialize the current thread as a looper, marking it as an* application's main looper. The main looper for your application* is created by the Android environment, so you should never need* to call this function yourself.

深远探究Android音讯机制之Handler。11 throw new IllegalStateException("The main Looper has already been prepared.");

在最棒循环各个信息的时候,除了调用handler.dispatchMessage,最终还大概会调用msg.recycleUnchecked()实行回收这几个音讯,至于message怎么回收我们就不商量了,详细情形的望族可以去探视本身上一篇文章。

既然Looper中的loop()调用了msg.target.dispatchMessage,大家就看看Handler的dispatchMessage是如何开展拍卖那一个msg的。

前天我们回眸看自个儿事先说的那2个难点:为何子线程创制handler会抛非凡?
我们先看看Handler的首要的构造器:

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //处理消息
    }
};
/**
 * 调用此函数用于启动消息队列循环起来,作用相当于工厂流水线中的传送带的开关,
 * 只有把开关打开,传送带才跑起来
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    .....
    .....
    //循环通过消息队列来获取消息
    for (; ; ) {
        ......
        //最后回收这个message
        msg.recycleUnchecked();
    }
}

ActivityThread.java:

大家先从程序入口来扩充剖释,Android应用程序的入口在ActivityThread的main函数中,我们先从main函数举办深入分析:

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    //在主线程中,其默认初始化一个Looper对象,因此我们在主线程的操作中是不需要自己去调prepare()。
    prepare(false);
    synchronized (Looper.class) {
        //这里先进行判断,在主线程是否已经存在Looper了,
        // 避免我们手动去调用prepareMainLooper(),因为这个是给程序入口初始化的时候系统会自动调用的
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        //设置全局变量,主线程的looper
        sMainLooper = myLooper();
    }
}

sMainLooper在Looper做为多个全局变量,保存主线程绑定的looper,myLooper()则是取稳当前线程绑定的Looper。在prepareMainLooper()中,在主线程中成立二个新的Looper,並且绑定主线程中,同不平日间把那么些主线程的looper赋值给sMainLooer这么些全局变量。

世家先对一下的对象,脑补一下厂子的现象:

2.每叁个线程只可以存在一个Looper, Looper有三个全局变量sThreadLocal用来保存每一个线程的looper,通过get、set举办存取looper。

很简短,就一行代码,我们看来了非凡熟悉的run方法,那些不就是咱们选择post的时候传进去的Runnbale对象的run方法吗?

看构造器可得,若是在子线程创造handler,必需在子线程中先创建一个Looper对象,不然hanlder在初叶化的时候获得不了当前线程的looper,会抛出格外"Can't create handler inside thread that has not called Looper.prepare()"。

4.为啥不可能在子线程更新UI?其实更可信赖的来讲应该是UI只好在创造UI的线程中展开创新,也等于主线程,纵然子线程创立UI,其得以在子线程进行更新。

也便是说,大家的Looper对象分别保存在相呼应的线程中。大家看回来大家的prepare(boolean)函数:
looper.java:

这一段代码比较长,大家挑有汉语注释的来看,先判定当前的线程是不是留存looper,如若存在得到保存在Looper的音信队列messagequeue,然后Infiniti循环这些消息队列来得到message,注意大家留到了一段代码:

/**
 * 调用此函数用于启动消息队列循环起来,作用相当于工厂流水线中的传送带的开关,
 * 只有把开关打开,传送带才跑起来
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    //先进行判断当前线程是否有绑定looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取这个looper的消息队列
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    //循环通过消息队列来获取消息
    for (; ; ) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to "   msg.target   " "  
                    msg.callback   ": "   msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            //关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to "   msg.target   " "   msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                      Long.toHexString(ident)   " to 0x"
                      Long.toHexString(newIdent)   " while dispatching to "
                      msg.target.getClass().getName()   " "
                      msg.callback   " what="   msg.what);
        }
        //最后回收这个message
        msg.recycleUnchecked();
    }
}

ThreadLocal.java:

Looper.java:

Looper.java:

在利用程序ActivityThread.main入口中,系统除去调用Looper.prepareMainLooper,何况在终极还调用了Looper.loop(),那一个函数有何?我们脑补一下,工厂里的流程上,除了有传送带外,如若你不让它动起来,那传送带也没怎么意义,那么Looper.loop的机能就是让这一个传送拉动起来,相当于我们的让我们的音信队列动起来。

本文由彩世界注册首页发布于彩世界彩票注册平台官网,转载请注明出处:深远探究Android音讯机制之Handler

上一篇:网络软件包括网络协议软件、通信软件和网络操 下一篇:没有了
猜你喜欢
热门排行
精彩图文