Android 线程间通信 - Java 层

一个 Java 层简单的 Handler 示例如下:

public void egHandler() {
    Looper.prepare();
    Looper.loop();
    
    Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            // TODO; 处理 Event
        }
    };

    new Thread() {
        @Override
        public void run() {
            Message msg = new Message();
            msg.what = 0;
            handler.sendMessage(msg);
        }
    }.start();
} 

Java 层的线程通信主要涉及到了Handler、MessageQueue、Message、Looper。

主线程创建的时候默认会执行Looper.prepare()、Looper.loop()方法,即默认会创建Looper

Looper

源码路径frameworks/base/core/java/android/os/Looper.java

Looper.prepare

public static void prepare() {
    // 调用Looper.prepare
    prepare(true);
}

private static void prepare(boolean quitAllowed) { // 默认传入 true, 允许中断
    // 从ThreadLocal中尝试拿出Looper对象
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    
    // ThreadLocal中没有Looper对象则进行创建
    sThreadLocal.set(new Looper(quitAllowed));
} 

Looper.Looper

private Looper(boolean quitAllowed) {
    // 搞出来一个MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    // 用一个变量保存当前线程(对应上面的示例也就是主线程)
    mThread = Thread.currentThread();
} 

MessageQueue.MessageQueue

MessageQueue(boolean quitAllowed) {
    // 接收Looper传进的参数,默认允许中断
    mQuitAllowed = quitAllowed;
    // 初始化native方法,下一篇文章会说到
    mPtr = nativeInit();
} 

Looper.loop

@SuppressWarnings("AndroidFrameworkBinderIdentity")
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.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
               + " before this one completed.");
    }
	
    // 标识 Looper 开始 loop
    me.mInLoop = true;

    // 清空远程调用端的pid和uid,使用本地进程的pid和uid代替
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // Allow overriding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
        SystemProperties.getInt("log.looper."
                                + Process.myUid() + "."
                                + Thread.currentThread().getName()
                                + ".slow", 0);

    me.mSlowDeliveryDetected = false;
	
    // 无线循环,调用loopOnce
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
} 

Looper.myLooper

public static @Nullable Looper myLooper() {
    // 从当前线程中拿出 Looper 对象
    return sThreadLocal.get();
} 

Looper.loopOnce

@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,
                                final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // 从MessageQueue中取出Message,当MessageQueue中没有消息时,此处会阻塞
    
    // ......
    
    // Make sure the observer won't change while processing a transaction.
    final Observer observer = sObserver;

	// ......
    
    Object token = null;
    if (observer != null) {
        // 当注册了观察者时,调用观察者messageDispatchStarting方法
        token = observer.messageDispatchStarting();
    }
    long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    try {
        // 调用消息的dispatchMessage方法,默认为Handler.dispatchMessage方法
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            // 调用观察者 messageDispatched方法
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
    // ......
    
	// 回收消息
    msg.recycleUnchecked();

    return true;
} 

Looper.java中还有一些不常用的方法

Looper.myLooper

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

Looper.myQueue

public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
} 

Looper.isCurrentThread

public boolean isCurrentThread() {
    return Thread.currentThread() == mThread;
} 

Looper.quit

public void quit() {
    mQueue.quit(false);
} 

Looper.quitSafely

public void quitSafely() {
    mQueue.quit(true);
} 

Looper.getQueue

public @NonNull MessageQueue getQueue() {
    return mQueue;
} 

Observer

在上面Looper.loopOnce中提到了Observer,该接口定义在Looper.java中,共三个方法

public interface Observer {
    Object messageDispatchStarting();

    void messageDispatched(Object token, Message msg);

    void dispatchingThrewException(Object token, Message msg, Exception exception);
} 

可以实现该接口,通过setObserver方法注册进去

public static void setObserver(@Nullable Observer observer) {
    sObserver = observer;
} 

Handler

源码路径:frameworks/base/core/java/android/os/Handler.java

Handler.Handler

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

@Deprecated
public Handler(@Nullable Callback callback) {
    this(callback, false);
}

// 指定Looper
public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

// 指定Looper和Callback
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}

public Handler(@Nullable Callback callback, boolean async) {
    // ......

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

@UnsupportedAppUsage // 此注解表示App无法使用该构造方法
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
} 

Handler.sendMessage

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

// 定时发送消息,sendMessage代表now发送消息
public boolean sendMessageAtTime(@NonNull 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;
    }
    
    // 调用MessageQueue方法,将Message加入到MessageQueue中
    return enqueueMessage(queue, msg, uptimeMillis);
} 

还有另外一种发送消息的方法,且消息中带有Callback

Handler.post

public final boolean post(@NonNull Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
}

// token 即为message携带的数据
public final boolean postAtTime(
    @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
} 

Handler.getPosrMessage

private static Message getPostMessage(Runnable r) {
    // 从消息池中取出一个空白消息
    Message m = Message.obtain();
    // 给message设置callback
    m.callback = r;
    return m;
}

@UnsupportedAppUsage
private static Message getPostMessage(Runnable r, Object token) {
    Message m = Message.obtain();
    // 给message设置obj数据
    m.obj = token;
    m.callback = r;
    return m;
} 

Handler.obtainMessage

// 从消息池中取出一个Message使用
public final Message obtainMessage() {
    return Message.obtain(this);
} 

Handler.dispatchMessage

// Looper.loopOnce中调用过来的,处理消息
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        // message有callback,直接走callback
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // handler有callback,走callback,callback内返回true则直接return,不会执行handlerMessage方法
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
} 

Handler.handleMessage

// 由Handler的子类实现,用来处理消息,不实现则不会做任何事,空函数
public void handleMessage(@NonNull Message msg) {
} 

MessageQueue

源码路径:frameworks/base/core/java/android/os/MessageQueue.java

MessageQueue.enqueueMessage

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                // inUse 抛出异常
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                // 表示正在执行结束流程
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                // 回收掉此message,加入message池
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            // mMessages 为Message链表,MessageQueue的主要数据结构
            Message p = mMessages;
            // 唤醒标识
            boolean needWake;
            
            // 三种条件:
            // 1. MessageQueue中无Message
            // 2. 消息分发的时间为now
            // 3. 新消息的发送时间小于链表头部消息的时间
            if (p == null || when == 0 || when < p.when) {
                // 放在链表头
                msg.next = p;
                mMessages = msg;
                
                // 需要唤醒MessageQueue.next()
                needWake = mBlocked;
            } else {
                // 链表头不为空,则需要将Message插入到链表中
                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;
            }

            // 需要唤醒则调用native方法唤醒MessageQueue.next
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    } 

enqueueMessage中,新message的位置有两种可能:

  • 链表头为空、当前message的发送时间为0、message的发送时间小于链表头的message发送时间,满足任意一项则将message插入到链表头部
  • 循环遍历链表,找到链表节点中message的时间大于新message时间的位置 ,将新message插入该位置,找不到则插入到链表尾部

MessageQueue.next

@UnsupportedAppUsage
Message next() {
    // 此处保存的是native层NativeMessageQueue指针
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; 
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
		
        // 此处调用native方法,会阻塞,第一次调用会一直阻塞(pendingIdleHandlerCount = -1代表一直阻塞)
        nativePollOnce(ptr, nextPollTimeoutMillis);
		
        // 阻塞超时、MessageQueue中有新消息、发生ERROR 时调用以下方法
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // MessageQueue中链表头结点的发送时间大于now,需要等待
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    // 取出MessageQueue链表的头节点,给Looper.loopOnce使用
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // 如果MessageQueue中无Message或者链表头Message还没都发送的时间,从mIdleHandlers中获取到idleHandler个数
            // mIdleHandlers通过addIdleHandler添加
            if (pendingIdleHandlerCount < 0
                && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 从mPendingIdleHandlers取出IdelHandler执行
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                // queueIdle返回false,则将当前IdelHandler从mIdleHandlers中彻底移除掉
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // 将pendingIdleHandlerCount置空
        pendingIdleHandlerCount = 0;

        // 在执行IdelHander的时候,可能MessageQueue中可能已经有消息到来或者原有消息分发时间已到,将等待时间变为0
        nextPollTimeoutMillis = 0;
    }
} 

当MessageQueue中有消息时,会将消息返回Looper.loopOnce,如没有消息或分发时间未到,则会执行IdelHandler中方法(IdelHandler需要Api调用者通过addIdleHander手动add),不会浪费等待的时间。

MessageQueue.addIdleHandler

public void addIdleHandler(@NonNull IdleHandler handler) {
    if (handler == null) {
        throw new NullPointerException("Can't add a null IdleHandler");
    }
    synchronized (this) {
        mIdleHandlers.add(handler);
    }
} 

MessageQueue.removeMessages

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        // Message链表头
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
} 

该函数的作用是从MessageQueue中清除条件满足msg.target = h && msg.what = what && (msg.obj == null || msg.obj = object)的message。

清除的方式也很特别,分为两段进行

  • 找到和给定条件相等的message
    • 找到则清除掉该msg,并让链表头和指向该节点的next
    • 找不到则退出循环
  • 从上一个循环的位置开始,接着遍历链表
    • 找到和给定条件相等的msg,清除掉msg
    • 找不到接着遍历,直到 p == null

MessageQueue.quit

void quit(boolean safe) {
    if (!mQuitAllowed) {
        // mQuitAllowed是构造方法传进来的,为true代表可以终止,为false在此方法内会抛出异常
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            // 正在 quit,直接 return
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
} 

quit方法主要作用是停掉MessageQueue,释放掉MessageQueue中的节点,而释放掉哪些节点是通过参数safe进行控制的。

当safe为true时,调用removeAllFutureMessagesLocked方法,该方法内部会判断链表头节点msg的when

  • 当msg.when > now时,证明链表中的所有节点都没有进行消息分发,可以全部释放
  • 当msg.when < now时,需要遍历链表,找到msg.when > now的节点,从该节点开始进行释放,该节点之前的节点不可以释放,可能该节点之前的节点已经被分发出去了

当safe为false时,调用removeAllMessagesLocked方法,暴力释放掉MessageQueue的所有节点

MessageQueue.removeAllMessagesLocked

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
} 

从链表中移除掉所有message,并且将链表头mMessages置为 null

MessageQueue.removeAllFutureMessagesLocked

private void removeAllFutureMessagesLocked() {
    // 获取当前时间
    final long now = SystemClock.uptimeMillis();
    // 拿到链表头
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            // 链表头的msg发送时间大于当前时间,那链表中所有节点msg的发送时间也一定大于now,具体可以看enqueueMessage方法的实现
            // 暴力移除全部节点
            removeAllMessagesLocked();
        } else {
            Message n;
            // 遍历链表,找到msg.when > now的节点
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            // 断开节点
            p.next = null;
            
            // 回收掉链表剩余节点
            do {
                p = n;
                n = p.next;
                // 回收节点
                p.recycleUnchecked();
            } while (n != null);
        }
    }
} 

Message

源码路径:frameworks/base/core/java/android/os/Message.java

Filed

字段含义
what可以看做消息的类型,以此字段标识消息
arg1参数1
arg2参数2
obj消息携带的数据
when消息送给looper的时间
flag标识当前Message的状态,目前有FLAG_IN_USE、FLAG_ASYNCHRONOUS、FLAGS_TO_CLEAR_ON_COPY_FROM三种状态
nextMessagePool中链表节点链接指针

调用栈

-------------------------------------------- 向 MessageQueue 发送消息 -----------------------------------------------------
frameworks/base/core/java/android/os/Handler.java
    | ---> public final boolean sendMessage(@NonNull Message msg)
        | ---> return sendMessageDelayed(msg, 0);
            | ---> return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
                | ---> return enqueueMessage(queue, msg, uptimeMillis);
                    | ---> msg.target = this; // 将target指向当前 handler
                    | ---> return queue.enqueueMessage(msg, uptimeMillis);

frameworks/base/core/java/android/os/MessageQueue.java
                        | ---> msg.markInUse();
                        | ---> msg.when = when;
                        | ---> Message p = mMessages; // 拿到链表头
                        | ---> if (p == null || when == 0 || when < p.when) // 插在链表头部
                            | ---> msg.next = p;
                            | ---> mMessages = msg;
                            | ---> needWake = mBlocked;
                        | ---> else
                            | ---> for (;;) // 遍历链表,在链表中找到msg合适的插入位置
                            | ---> msg.next = p;
                            | ---> prev.next = msg; // 插入
                        | ---> nativeWake(mPtr); // if (needWake) 唤醒等待message的next()方法

------------------------------------------ Looper 循环从 MessageQueue 中拿到消息并处理 --------------------------------------
frameworks/base/core/java/android/os/Looper.java
    | ---> public static void loop()
        | ---> final Looper me = myLooper();
        | ---> for (;;)
            | ---> if (!loopOnce(me, ident, thresholdOverride)) // return;
                | ---> Message msg = me.mQueue.next(); // 从MessageQueue中拿到message,此处可能会阻塞

frameworks/base/core/java/android/os/MessageQueue.java
                    | ---> for (;;)
                        | ---> nativePollOnce(ptr, nextPollTimeoutMillis); // 可能会阻塞,需要nativeWake唤醒
                        | ---> Message msg = mMessages;
                        | ---> if (msg != null && msg.target == null)
                            | ---> while (msg != null && !msg.isAsynchronous());
                                | ---> prevMsg = msg;
                                | ---> msg = msg.next;
                        | ---> if (msg != null)
                            | ---> if (now < msg.when)
                                | ---> nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); // 计算需要等待的时间
                            | ---> else
                                | ---> prevMsg.next = msg.next; // if (prevMsg != null)
                                | ---> mMessages = msg.next; // else
                                | ---> msg.next = null;
                                | ---> msg.markInUse();
                                | ---> return msg; // 返回链表头节点

frameworks/base/core/java/android/os/Looper.java
                | ---> msg.target.dispatchMessage(msg); // target 指向自实现的 handler

frameworks/base/core/java/android/os/Handler.java
                    | ---> if (msg.callback != null)
                        | ---> handleCallback(msg);
                            | ---> message.callback.run();
                    | ---> else
                        | ---> if (mCallback != null)
                            | ---> if (mCallback.handleMessage(msg))
                                | ---> return // 上面的mCallback.handleMessage方法如返回true,则不会执行Handler.handleMessage方法
                        | ---> handleMessage(msg);

frameworks/base/core/java/android/os/Looper.java
                | ---> msg.recycleUnchecked(); // 回收掉message,将该msg加入到message poll 

总结

要想成为架构师,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取【保证100%免费】↓↓↓
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中线程通信可以通过多种方式实现。其中一种常见的方式是使用Handler和Looper机制。通过在主线程中创建Handler对象,可以将消息发送到主线程的消息队列中,然后主线程的Looper会不断地从消息队列中取出消息并处理。在子线程中,可以通过Handler的post方法将消息发送到主线程。这样就实现了主线程和子线程之间的通信。\[2\] 另外,如果需要在子线程之间进行通信,可以使用HandlerThread类。HandlerThread是一个带有Looper的线程,可以在其中创建Handler对象,并通过Handler发送消息到该线程的消息队列中。这样就可以实现子线程之间的通信。\[3\] 总结起来,Android中线程通信可以通过Handler和Looper机制实现主线程和子线程之间的通信,也可以使用HandlerThread实现子线程之间的通信。这些机制可以帮助开发者更好地处理多线程编程中的同步问题。\[1\] #### 引用[.reference_title] - *1* [Android线程间通信原理以及多线程](https://blog.csdn.net/a734474820/article/details/125561136)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【Android线程间通信——Handler消息机制](https://blog.csdn.net/qq_40265247/article/details/123898141)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [android线程间通信的几种方法_Android进程间和线程间通信方式](https://blog.csdn.net/Goals1989/article/details/127966389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值