2024年最全Android Handler 从使用到进阶全解析,Kotlin科学计算

总结

现在新技术层出不穷,如果每次出新的技术,我们都深入的研究的话,很容易分散精力。新的技术可能很久之后我们才会在工作中用得上,当学的新技术无法学以致用,很容易被我们遗忘,到最后真的需要使用的时候,又要从头来过(虽然上手会更快)。

我觉得身为技术人,针对新技术应该是持拥抱态度的,入了这一行你就应该知道这是一个活到老学到老的行业,所以面对新技术,不要抵触,拥抱变化就好了。

Flutter 明显是一种全新的技术,而对于这个新技术在发布之初,花一个月的时间学习它,成本确实过高。但是周末花一天时间体验一下它的开发流程,了解一下它的优缺点、能干什么或者不能干什么。这个时间,并不是我们不能接受的。

如果有时间,其实通读一遍 Flutter 的文档,是最全面的一次对 Flutter 的了解过程。但是如果我们只有 8 小时的时间,我希望能关注一些最值得关注的点。

(跨平台开发(Flutter)、java基础与原理,自定义view、NDK、架构设计、性能优化、完整商业项目开发等)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • Handler创建有两种,一个是在构造方法传CallBack,一个是重写类的 handleMessage() 方法。

private static final int MESSAGA_TEST_1 = 1;

/**

  • 主线程有初始化好Looper,所以在主线程处理消息

*/

private Handler mHandler = new Handler(new Handler.Callback() {

@Override

public boolean handleMessage(@NonNull Message msg) {

switch (msg.what) {

case MESSAGA_TEST_1:

if (tvTest != null) {

//传递消息obj

tvTest.setText((String) msg.obj);

}

break;

}

return false;

}

});

private Handler mHandler2 = new Handler() {

@Override

public void handleMessage(@NonNull Message msg) {

switch (msg.what) {

case MESSAGA_TEST_1:

if (tvTest != null) {

//传递消息obj

tvTest.setText((String) msg.obj);

}

break;

}

}

};

private void onClick() {

//当点击事件执行,就会在子线程发送消息更新textview

new Thread(new Runnable() {

@Override

public void run() {

clickEndMessage();

}

}).start();

}

/**

  • 可以在子线程发送

*/

private void clickEndMessage() {

//obtain享元模式

Message message = Message.obtain(mHandler);

//what相当于标记

message.what = MESSAGA_TEST_1;

//obj传数据

message.obj = new String(“baozi”);

//普通发送message

mHandler.sendMessage(message);

.

.

.

//发送标记,不带其他内容,内部会封装成只有标记的Message对象

mHandler.sendEmptyMessage();

//尾部带有Delayed,发送延迟消息,单位毫秒

mHandler.sendMessageDelayed(message, 1000);

//尾部带有AtTime,发送消息的时间跟Delayed差别就是Delayed是执行的当前时间+传进去的时间,AtTime就传进去的绝对时间

mHandler.sendMessageAtTime();

//在队列头插入消息

mHandler.sendMessageAtFrontOfQueue(message);

}

@Override

protected void onDestroy() {

if(mHandler!=null){

//关闭activity时,移除消息

mHandler.removeMessages(MESSAGA_TEST_1);

mHandler = null;

}

super.onDestroy();

}

复制代码

2.3 view.post()

  • 比如view,post()、postDelayed() 方法,可以延迟五秒后更新UI,实际就是使用Handler。

tvTest.postDelayed(new Runnable() {

@Override

public void run() {

tvTest.setText(“5s”);

}

},5*1000);

/**

  • View 源码

*/

public boolean postDelayed(Runnable action, long delayMillis) {

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

return attachInfo.mHandler.postDelayed(action, delayMillis);

}

// Postpone the runnable until we know on which thread it needs to run.

// Assume that the runnable will be successfully placed after attach.

getRunQueue().postDelayed(action, delayMillis);

return true;

}

复制代码

2.4 runOnUiThread

  • 经常用的 runOnUiThread() 方法也是用Handler。

runOnUiThread(new Runnable() {

@Override

public void run() {

//更新UI

}

});

/**

  • Activity 源码

*/

public final void runOnUiThread(Runnable action) {

if (Thread.currentThread() != mUiThread) {

mHandler.post(action);

} else {

action.run();

}

}

复制代码

3.子线程中使用


3.1 子线程直接创建Handler错误

  • 子线程不能直接创建Handler,会报异常,因为Looper还没创建,而主线程默认就初始化好Looper

  • 应该先Looper.

private Handler handler2;

/**

  • 子线程

*/

private void thread() {

new Thread(new Runnable() {

@Override

public void run() {

handler2 = new Handler(new Handler.Callback() {

@Override

public boolean handleMessage(@NonNull Message msg) {

return false;

}

});

}

}).start();

}

复制代码

  • 提示的错误。

子线程创建Handler

3.2 主线程默认初始化Looper

  • ActivityThread 类就能主线程找到Looper初始化,Looper.prepareMainLooper();

public static void main(String[] args) {

.

.

Looper.prepareMainLooper();

.

.

ActivityThread thread = new ActivityThread();

.

.

}

复制代码

3.3 Handler构造方法查看

  • 构造方法中可以看到 mLooper = Looper.myLooper(); 获取的 mLooper 为null,就报上面的那个异常了。

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

}

.

.

}

复制代码

3.4 子线程正确的创建

  • 先执行,Looper.prepare(); 初始化Looper,然后调用loop()方法,再创建Handler。

  • 每个线程**Looper.prepare();**只能调用一次,否则会报错。

private Handler handler2;

/**

  • 子线程

*/

private void thread() {

new Thread(new Runnable() {

@Override

public void run() {

Looper.prepare();

Looper looper = Looper.myLooper();

looper.loop();

handler2 = new Handler(looper, new Handler.Callback() {

@Override

public boolean handleMessage(@NonNull Message msg) {

return false;

}

});

}

}).start();

}

复制代码

4.Message


  • Message就是一个存放消息的类,是一个链表结构。

4.1 基本参数

public final class Message implements Parcelable {

//用于handler标记处理的

public int what;

//可以传递的int参数1

public int arg1;

//可以传递的int参数2

public int arg2;

//可以传递的obj参数

public Object obj;

//执行时间

public long when;

//传递的bundle

Bundle data;

//Message绑定的Handler

Handler target;

//Handler.post()时传的callback

Runnable callback;

//链表结构

Message next;

复制代码

4.2 享元模式obtain()

  • obtain() 可以重用Message,减少开销提高性能。

/**

  • Return a new Message instance from the global pool. Allows us to

  • avoid allocating new objects in many cases.

*/

public static Message obtain() {

synchronized (sPoolSync) {

if (sPool != null) {

Message m = sPool;

sPool = m.next;

m.next = null;

m.flags = 0; // clear in-use flag

sPoolSize–;

return m;

}

}

return new Message();

}

复制代码

4.3 回收recycle()

  • 如果发送的延迟消息,或者消息在执行,就会报错,一般我们不用调用recycle方法。

/**

  • Return a Message instance to the global pool.

  • You MUST NOT touch the Message after calling this function because it has

  • effectively been freed. It is an error to recycle a message that is currently

  • enqueued or that is in the process of being delivered to a Handler.

*/

public void recycle() {

if (isInUse()) {

if (gCheckRecycle) {

throw new IllegalStateException("This message cannot be recycled because it "

  • “is still in use.”);

}

return;

}

recycleUnchecked();

}

复制代码

  • Message用完并不是内存回收,只是把里面的内容清空,等下次复用。

  • 这个链表也不是无限的,最多就50个节点

private static final int MAX_POOL_SIZE = 50;

@UnsupportedAppUsage

void recycleUnchecked() {

// Mark the message as in use while it remains in the recycled object pool.

// Clear out all other details.

flags = FLAG_IN_USE;

what = 0;

arg1 = 0;

arg2 = 0;

obj = null;

replyTo = null;

sendingUid = UID_NONE;

workSourceUid = UID_NONE;

when = 0;

target = null;

callback = null;

data = null;

synchronized (sPoolSync) {

//最多50个

if (sPoolSize < MAX_POOL_SIZE) {

next = sPool;

sPool = this;

sPoolSize++;

}

}

}

复制代码

5.MessageQueue


  • 这是一个阻塞队列。

  • 队列是在不停的for循环的,是一个死循环,那不就一直占用着cpu?所以就有了native的方法,处理休眠唤醒。

5.1 MessageQueue每个线程只有一个

  • 消息队列每个线程只有一个,跟Looper绑定在一起,在分析Looper时会一起分析。

5.2 消息入队

  • 前面我们知道Handler所有消息入队最后都是调用 enqueueMessage(Message msg, long when)

  • 判断插入消息的位置,还判断是否唤醒操作。

boolean enqueueMessage(Message msg, long when) {

//handler为空就报异常

if (msg.target == null) {

throw new IllegalArgumentException(“Message must have a target.”);

}

//加锁

synchronized (this) {

//消息正在使用会报错

if (msg.isInUse()) {

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

msg.recycle();

return false;

}

//标记正在使用

msg.markInUse();

msg.when = when;

//链表头

Message p = mMessages;

boolean needWake;

if (p == null || when == 0 || when < p.when) {

//如果队列为空,或者消息延迟时间为0,或者延迟时间小于mMessage的,就插入在头部

// New head, wake up the event queue if blocked.

msg.next = p;

mMessages = msg;

//唤醒队列

needWake = mBlocked;

} else {

// Inserted within the middle of the queue. Usually we don’t have to wake

// up the event queue unless there is a barrier at the head of the queue

// and the message is the earliest asynchronous message in the queue.

//插入队列中间。通常,除非队列的开头有障碍并且消息是队列中最早的异步消息,否则我们不必唤醒事件队列。

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;

}

//是否唤醒

// We can assume mPtr != 0 because mQuitting is false.

if (needWake) {

nativeWake(mPtr);

}

}

return true;

}

复制代码

5.3 消息出队

  • 消息出队其实应该放在 Looper.loop() 里面分析更合适,这里先写,后面结合 loop() 一起看会更好。

  • 前面 return null; 看注释的意思是,如果looper已经退出和释放,就返回null。

  • 这里无限循环,就是一定要取到消息,有消息,阻塞时间为消息的执行时间减去当前时间,如果没消息就阻塞, nativePollOnce(ptr, nextPollTimeoutMillis)

  • 这 next() 方法只有一个地方返回msg,关注这里就行了。

@UnsupportedAppUsage

Message next() {

// Return here if the message loop has already quit and been disposed.

// This can happen if the application tries to restart a looper after quit

// which is not supported.

final long ptr = mPtr;

if (ptr == 0) {

return null;

}

int pendingIdleHandlerCount = -1; // -1 only during first iteration

int nextPollTimeoutMillis = 0;

for (;😉 {

if (nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

//没消息取就阻塞

nativePollOnce(ptr, nextPollTimeoutMillis);

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) {

// Next message is not ready. Set a timeout to wake up when it is ready.

//阻塞时间为消息的执行时间减去当前时间

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;

}

msg.next = null;

if (DEBUG) Log.v(TAG, "Returning message: " + msg);

msg.markInUse();

//只有这里返回msg

return msg;

}

} else {

//没有更多消息,设置为-1,阻塞

// No more messages.

nextPollTimeoutMillis = -1;

}

.

.

}

}

复制代码

5.4 退出

  • 主线程是不能退出。

  • 传入的 safe 处理,分别是移除消息未执行的消息,和移除全部消息。

void quit(boolean safe) {

//主线程是不能退出的

if (!mQuitAllowed) {

throw new IllegalStateException(“Main thread not allowed to quit.”);

}

synchronized (this) {

if (mQuitting) {

return;

}

mQuitting = true;

//移除消息

if (safe) {

removeAllFutureMessagesLocked();

} else {

removeAllMessagesLocked();

}

// We can assume mPtr != 0 because mQuitting was previously false.

nativeWake(mPtr);

}

}

复制代码

/**

  • 移除全部消息

*/

private void removeAllMessagesLocked() {

Message p = mMessages;

while (p != null) {

Message n = p.next;

p.recycleUnchecked();

p = n;

}

mMessages = null;

}

/**

  • 移除未执行的消息,正在运行的等待完成再回收

*/

private void removeAllFutureMessagesLocked() {

final long now = SystemClock.uptimeMillis();

Message p = mMessages;

if (p != null) {

if (p.when > now) {

//如果执行时间还未到,即未执行的消息,移除回收

removeAllMessagesLocked();

最后

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

阿里P7Android高级教程

下面资料部分截图,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

附送高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、近期面试跳槽、自身职业规划迷茫的朋友们。

Android核心高级技术PDF资料,BAT大厂面试真题解析;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

复制代码

/**

  • 移除全部消息

*/

private void removeAllMessagesLocked() {

Message p = mMessages;

while (p != null) {

Message n = p.next;

p.recycleUnchecked();

p = n;

}

mMessages = null;

}

/**

  • 移除未执行的消息,正在运行的等待完成再回收

*/

private void removeAllFutureMessagesLocked() {

final long now = SystemClock.uptimeMillis();

Message p = mMessages;

if (p != null) {

if (p.when > now) {

//如果执行时间还未到,即未执行的消息,移除回收

removeAllMessagesLocked();

最后

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

阿里P7Android高级教程

下面资料部分截图,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

[外链图片转存中…(img-Pv0rpAS1-1715840344124)]

附送高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、近期面试跳槽、自身职业规划迷茫的朋友们。

Android核心高级技术PDF资料,BAT大厂面试真题解析;
[外链图片转存中…(img-zKG4ZT7l-1715840344125)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值