[Android 笔记九]Handler机制概念理解+多线程通信Demo

本文详细解析了Android中Handler的工作原理,包括Handler、Looper、MessageQueue的运作机制,以及如何在不同线程间发送和处理消息,防止内存泄漏,更新UI的方法。

https://blog.youkuaiyun.com/carson_ho/article/details/80305411

1. 基本简介:

1.在新启动的线程中发送消息

2.在主线程中获取,并处理消息

--那在新启动的线程何时发送消息?主线程何时去获取并处理消息?

2. 主要概念:

  • Handler:快递员(属于某个快递公司的职员)

      --把Message发给Looper管理的MessageQueue,并负责处理Looper对象分给它的消息。

  • Message:包裹(可以放置很多东西的箱子)
  • MessageQueue:快递分拣中心(分拣快递的传送带) 

      --由Looper负责管理,采用先进先出的方式。

  • Looper:快递公司(具有处理包裹去向的管理中心)

       --每个线程只有一个Looper,负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。

3. 大致理解:

  • 某时,你想刷新主界面的TextView,无奈你不在主线程,此时你就会包装好Message,然后声明一个Handler,让Handler将你的Message送往主线程(Looper),Handler将你的Message送到Looper后,还需要排队等待,等轮到你的时候,主线程Looper就会告诉Handler,这个Message可以处理了,你负责分发一下,于是,Handler将该Message分发到相应的回调或者handleMessage( ) 方法中,于是,你就在该方法中更新了UI。

         示意图

4. 主要作用:

  • 使用Handler的原因:将工作线程需操作UI的消息 传递 到主线程,使得主线程可根据工作线程的需求 更新UI,从而避免线程操作不安全的问题
  • 具体描述如下

示意图

 

5. 大致总结

(1)主线程可以直接创建Handler对象,主线程默认自动初始化Looper。

(2)子线程中需要先调用Looper.prepare()才能创建Handler对象,否则运行抛出”Can’t create handler inside thread that has not called Looper.prepare()”异常信息。

子线程向主线程传值:

public class MainActivity extends AppCompatActivity {
    private Handler mHandler;//将mHandler指定轮询的Looper

    @SuppressLint("HandlerLeak")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Handler mHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 0:
                        //do something,refresh UI;
                        Log.d("当前子线程是----->",Thread.currentThread()+""+msg.obj);
                        break;
                    default:
                        break;
                }
            }

        };
        new Thread() {
            public void run() {
                Message message = new Message();
                message.obj = "子线程发送的消息Hi~Hi";
                mHandler.sendMessage(message);
            };
        }.start();
    }
}

主线程向子线程发送消息:需要在子线程里初始化Looper,并在主线程里创建的Handler引用子线程的Looper(Handler中引用的是哪个线程的Looper,就在哪个线程里处理消息,下面看代码:

HandlerThread继承于Thread,所以它本质就是个Thread,只不过在内部直接实现了Looper的实现,不需要手动去调用Looper.prepare()Looper.loop()这些方法。有了自己的looper,可以在自己的线程中分发和处理消息。start后可以获得其Looper对象,并且使用这个Looper对象实例Handler,之后Handler就可以运行在其他线程中了

import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private MyThread thread;

    //创建子线程
    class MyThread extends Thread{
        private Looper looper;//取出该子线程的Looper
        public void run() {

            Looper.prepare();//创建该子线程的Looper
            looper = Looper.myLooper();//取出该子线程的Looper
            Looper.loop();//只要调用了该方法才能不断循环取出消息
        }
    }

    private Handler mHandler;//将mHandler指定轮询的Looper

    @SuppressLint("HandlerLeak")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText("Handler实验");
        setContentView(tv);

        //实例化一个特殊的线程HandlerThread,必须给其指定一个名字
        HandlerThread thread = new HandlerThread("handler thread");
        thread.start();//千万不要忘记开启这个线程
        //将mHandler与thread相关联
        mHandler = new Handler(thread.getLooper()){
            public void handleMessage(android.os.Message msg) {
                Log.d("当前子线程是----->", Thread.currentThread()+"");
            };
        };
        mHandler.sendEmptyMessage(1);//发送消息
    }
}

子线程向子线程发消息:   

private Handler mHandler;//将mHandler指定轮询的Looper

@SuppressLint("HandlerLeak")
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Thread1 thread1= new Thread1();
    Thread2 thread2 = new Thread2();
    new Thread(thread1).start();
    if(thread1.getHandler() == null) {
        try {
            Thread.sleep(1000);
            mHandler = thread1.getHandler();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    new Thread(thread2).start();
}
private Handler handler;
//子线程一
class Thread1 implements Runnable{
    private Handler mHandler;
    //run运行后才不为null在main里判断
    public Handler getHandler(){
        return mHandler;
    }

    @SuppressLint("HandlerLeak")
    @Override
    public void run() {
        Looper.prepare();
        mHandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case 1:
                        Log.d("线程A","线程B发过来消息了--"+msg.obj);
                        break;
                }
            }
        };
        Looper.loop();
    }
}
//子线程二
class Thread2 implements Runnable{

    @Override
    public void run() {
        Message mess= Message.obtain();
        mess.what=1;
        mess.obj= "线程B"+System.currentTimeMillis();
        mHandler.sendMessage(mess);
    }
}

子线程使用Handler的步骤:https://blog.youkuaiyun.com/qq_41673194/article/details/80022875

(3)每个线程中最多只能有一个Looper对象,否则抛出异常;

         一个Looper只能对应了一个MessageQueue。

         一个MessageQueue可以对应多个Handler。

(4)可以通过Looper.myLooper()获取当前线程的Looper实例,通过Looper.getMainLooper()获取主(UI)线程的Looper实例。

(5)Handler的post方法将一个 Runnable 投递到 Handler 内部的 Looper中去处理,

           post(Runnable)
           postAtTime(Runnable, long)
           postDelayed(Runnable, long)

(6)调用Handler的send方法时,它会调用 MessageQueue 的 enqueueMessage 方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,调用消息中的Runnable或者Handler的 handleMessage方法.
           sendEmptyMessage(int)
           sendMessage(Message)
           sendMessageAtTime(Message, long)
           sendMessageDelayed(Message, long)

8. 加入弱引用,防止内存泄漏(另一篇具体讲)

来源:https://blog.youkuaiyun.com/l707941510/article/details/80641444

Handler mHandler = new UpdateUIHandler(new WeakReference<>(this));

public static class UpdateUIHandler extends Handler {
    private WeakReference<MainActivity> mainActivityWeakReference;

    public UpdateUIHandler(WeakReference<MainActivity> mainActivityWeakReference) {
        this.mainActivityWeakReference = mainActivityWeakReference;
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.what == Parameters.UPDATE_UI_HANDLER_MESSAGE) {
            removeMessages(msg.what);
            mainActivityWeakReference.get().mFragmentList.clear();
            mainActivityWeakReference.get().mApps.clear();
            mainActivityWeakReference.get().initData();
        }
    }
}

9.更新UI的方式

https://blog.youkuaiyun.com/Sean_css/article/details/79767180?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

  1. 使用 Handler 的 post() 方法更新 UI
  2. 使用 Handler 的 sendMessage() 方法更新 UI
  3. 使用 runOnUiThread() 方法更新 UI
  4. 使用 View 的 post() 方法更新 UI
  5. 子线程中创建 Handler 发送消息,在子线程中处理,然后发送给主线程(mHandler) 去更新 UI
  6. 在子线程中更新 UI
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值