Android基础学习(二十一)—— Handler

一、一文读懂 Handler 机制

Handler 在整个 Android 开发体系中占据着很重要的地位,是一种标准的事件驱动模型,对开发者来说起到的作用很明确,就是为了实现线程切换或者是执行延时任务,稍微更高级一点的用法可能是为了保证多个任务在执行时的有序性。由于 Android 系统中的主线程有特殊地位,所以像 EventBus 和 Retrofit 这类并非 Android 独有的三方库,都是通过 Handler 来实现对 Android 系统的特殊平台支持。

该文章讲解了Handler的内部实现机制

二、关于Handler 的这 15 个问题,你都清楚吗?

该文章从问题出发来讲解了原理

三、几个问题的解答
1、更新UI的方式
2、子线程向主线程发消息,使主线程更新UI
3、主线程向子线程发消息
4、子线程向子线程发消息
5、Handler的实现中涉及到循环的地方?
6、Looper.loop阻塞主线程时,怎么响应用户操作和回调Activity生命周期相关的方法?
7、Handler内存泄漏?

1、更新UI的方式

共有四种方式(https://blog.csdn.net/ruancoder/article/details/52081614):

(1)Handler.sendEmptyMessage 或者 Handler.sendMessage

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what == MSG_REFRESH) {
            mTextView.setText("123");
        }
    }
};
 
private void refresh1() {
    mHandler.sendEmptyMessage(MSG_REFRESH);
}

(2)Handler.post

private void refresh2() {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mTextView.setText("123");
        }
    });
}

(3)View.post

private void refresh3() {
    mTextView.post(new Runnable() {
        @Override
        public void run() {
            mTextView.setText("123");
        }
    });
}

(4)runOnUiThread

private void refresh4() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mTextView.setText("blog.csdn.net/ruancoder");
        }
    });
}

在 Activity 的源码中:

... 
final Handler mHandler = new Handler();
... 
public final void runOnUiThread(Runnable action) {
     if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
     } else {
        action.run();
     }
}
...

2、子线程向主线程发消息,使主线程更新UI

public class MainActivity extends AppCompatActivity {
    TextView textView;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if(msg.what == 0x01){
                //刷新UI操作
                String ss  = msg.obj.toString();
                textView.setText(ss);
            }
        }
    };
    private void test(){
        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                Message msg = Message.obtain();
                msg.obj = "123";
                msg.what = 0x01;
                handler.sendMessage(msg);
            }
        };
        thread.start();
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_1);
        test();
    }
}

或者是在子线程中声明Handler,此时构造函数中需指明主线程的Looper:

ublic class MainActivity extends AppCompatActivity {
    TextView textView;
    private Handler handler;
    private void test(){
        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                handler = new Handler(Looper.getMainLooper()){
                    @Override
                    public void handleMessage(@NonNull Message msg) {
                        super.handleMessage(msg);
                        if(msg.what == 0x01){
                            //刷新UI操作
                            String ss  = msg.obj.toString();
                            textView.setText(ss);
                        }
                    }
                };
                Message msg = Message.obtain();
                msg.obj = "123";
                msg.what = 0x01;
                handler.sendMessage(msg);
            }
        };
        thread.start();
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_1);
        test();
    }
}

3、主线程向子线程发消息

public class MainActivity extends AppCompatActivity {
    Button button;
    private Handler handler;
    private void test(){
        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                Looper.prepare();
                handler = new Handler(){
                    @Override
                    public void handleMessage(@NonNull Message msg) {
                        super.handleMessage(msg);
                        Log.i("zxy","msg ::" + msg.obj.toString());
                    }
                };
                Looper.loop();
            }
        };
        thread.start();
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.btn);
        test();
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(handler != null){
                    Message msg = Message.obtain();
                    msg.obj = "123";
                    msg.what = 0x01;
                    handler.sendMessage(msg);
                }else{
                    Toast.makeText(MainActivity.this, "请再点击一次", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

4、子线程向子线程发消息

同理,建立关于子线程A的aHandler,子线程B通过aHandler发消息给子线程A

或者通过全局变量

5、Handler的实现中涉及到循环的地方?

MessageQueue.enqueueMessage:从链表头向链表尾遍历,寻找链表中第一条时间戳比 msg 大的消息,将 msg 插到该消息的前面

MessageQueue.next:从MessageQueue中读取消息,如果消息队列中没有消息或者是队头消息还没到可以处理的时间,该方法就会调用nativePollOnce()将Loop线程休眠挂起

Looper.loop :循环向MessageQueue 取出消息并执行,当其中的MessageQueue.next() 方法触发阻塞操作时,loop()也会被阻塞着

6、Looper.loop阻塞主线程时,怎么响应用户操作和回调Activity生命周期相关的方法?

在application启动时,不止一个main线程,还有其它两个Binder线程(ApplicationThread和ActivityManagerProxy)用来和系统进程进行通信操作,接收系统进程发送的通知。

当系统收到因用户操作产生的通知时,会通过Binder方式跨进程通知ApplicationThread(?之后再看看);它通过Handler机制,往ActivityThread的MessageQueue中插入消息,唤醒主线程;loop()中queue.next()拿到消息,然后dispatchMessage完成事件分发。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdi42lju-1670763529757)(C:\Users\zxy\AppData\Roaming\Typora\typora-user-images\image-20221211195500515.png)]

7、Handler内存泄漏?

在Java中,非静态内部类会持有一个外部类的隐式引用,可能会造成外部类无法被GC;

比如这里的Handler,就是非静态内部类,它会持有Activity的引用从而导致Activity无法正常释放。

而单单使用静态内部类,Handler就不能调用Activity里的非静态方法了,所以加上「弱引用」持有外部Activity。

private static class MyHandler extends Handler {
    //创建一个弱引用持有外部类的对象
    private final WeakReference<MainActivity> content;

    private MyHandler(MainActivity content) {
        this.content = new WeakReference<MainActivity>(content);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity= content.get();
        if (activity != null) {
            switch (msg.what) {
                case 0: {
                    activity.notifyUI();
                }
            }
        }
    }
}

转换成Kotlin:(Tips:Kotlin 中的内部类,默认是静态内部类,使用inner修饰才为非静态~)

private class MyHandler(content: MainActivity) : Handler() {
    //创建一个弱引用持有外部类的对象
    private val content: WeakReference<MainActivity> = WeakReference(content)

    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
        val activity = content.get()
        if (activity != null) {
            when (msg.what) {
                0 -> {
                    activity.notifyUI()
                }
            }
        }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值