Android 线程间通信之Handler

元胞蝉翼
• 阅读 4063

Android 线程间通信之Handler

一:前言
Android为了确保UI操作的线程安全,规定所有的UI操作都必须在主线程(UI线程)中执行,决定了UI线程中不能进行耗时任务,在开发过程中,需要将网络,IO等耗时任务放在工作线程中执行,工作线程中执行完成后需要在UI线程中进行刷新,因此就有了Handler进程内线程通信机制,当然Handler并不是只能用在UI线程与工作线程间的切换,Android中任何线程间通信都可以使用Handler机制。
二:使用Handler实现线程间通信
1.UI线程中使用Handler
UI线程中使用Handler非常简单,因为框架已经帮我们初始化好了Looper,只需要创建一个Handler对象即可,之后便可以直接使用这个Handler实例向UI线程发消息(子线程--->UI线程)

 private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //处理消息
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_six);


    }
}

这种方式会导致内存泄露。
我们通过Handler发送消息,在Message对象中会持有当前Handler对象的引用,在Java中非静态成员类、内部类、匿名类会持有外部对象的引用(这里在源码中有提到),而Looper是线程局部变量,其生命周期与UI线程相同,Looper持有MessageQueue的引用,MessageQueue持有Message的引用,当通过Handler发送一个延时消息未处理之前用户已经离开当前Activity,会导致Activity不能及时释放而内存泄漏。
解决思路:
1.官方推荐的一种:

 private Handler handler=new Handler(new Handler.Callback() {
       @Override
       public boolean handleMessage(@NonNull Message msg) {
        switch (msg.what){
               case 1:
               //处理子线程发过来的消息
                   Toast.makeText(SixActivity.this,(String)msg.obj,Toast.LENGTH_LONG).show();
                   Log.d("aa",(String) msg.obj);
                   break;

           }
           return false;
       }
   });

2.静态内部类

  private MyHandler myHandler=new MyHandler(this);
   private static class MyHandler extends Handler{
       private WeakReference<Context> reference;
       public MyHandler(Context context){
           reference=new WeakReference<>(context);
           
       }

       @Override
       public void handleMessage(@NonNull Message msg) {
      
           //do something
           
           if (reference.get()!=null){
               if (msg.what==1){
                   Log.d("bb",(String) msg.obj);
               }
           }
       }
   }

子线程发送消息

  new Thread(new Runnable() {
            @Override
            public void run() {
                //Message message=new Message();//可以使用new Message来创建消息,但是一般不这样使用
                 Message message=Message.obtain();//来创建消息
                message.obj="我是子线程消息";
                message.what=1;
                // 封装完数据发送给主线程
                handler.sendMessage(message);
                
                //第二种方式
                  Message message=Message.obtain();
                message.obj="我是子线程静态消息";
                message.what=1;
                myHandler.sendMessage(message);
            }
        }).start();

主线程给子线程发送消息(UI线程--->子线程)

public class SixActivity extends AppCompatActivity {
    // 在子线程中创建消息Handler
    // 子线程创建方式
    private Handler handler;
    private Button btn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_six);
        new MyOneThread().start();
       btn= findViewById(R.id.dian);
       btn.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Message message=Message.obtain();
               message.what=1;
               message.obj="我是主线程的消息发送给子线程";
               // 封装完数据发送给子线程
               handler.sendMessage(message);
           }
       });
 
    }


        class MyOneThread extends Thread{
        @Override
        public void run() {
            //在子线程中处理消息,子线程中处理消息,没有默认的Loop
            //由于只有主线程成才默认的Looper.prepare(), Looper.loop();
            //创建Looper
            Looper.prepare();//如果不添加会报错
            handler=new Handler(){
                @Override
                public void handleMessage(@NonNull Message msg) {
                    switch (msg.what){
                        case 1:
                            Log.d("aa",(String) msg.obj);
                            break;
                    }
                }
            };
            //循环读取messageQueue
            Looper.loop();//如果不添加读取不到消息


        }
    }
}

也可以使用这个方式来获取Looper

   handler=new Handler(Looper.getMainLooper()){
                @Override
                public void handleMessage(@NonNull Message msg) {
                    switch (msg.what){
                        case 1:
                            Log.d("aa",(String) msg.obj);
                            break;
                    }
                }
            };

子线程发送消息到子线程(子线程----->子线程)

 btn.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
            /*   Message message=Message.obtain();
               message.what=1;
               message.obj="我是主线程的消息发送给子线程";
               // 封装完数据发送给子线程
               handler.sendMessage(message);*/
               new Thread(new Runnable() {
                   @Override
                   public void run() {
                       Message message=Message.obtain();
                       message.obj="我是子线程发送到子线消息";
                       message.what=1;
                       handler.sendMessage(message);
                   }
               }).start();
           }
       });
       
       
        class MyOneThread extends Thread{
        @Override
        public void run() {
            //在子线程中处理消息,子线程中处理消息,没有默认的Loop
            //由于只有主线程成才默认的Looper.prepare(), Looper.loop();
            //创建Looper
//            Looper.prepare();
            handler=new Handler(Looper.getMainLooper()){
                @Override
                public void handleMessage(@NonNull Message msg) {
                    switch (msg.what){
                        case 1:
                            Log.d("aa",(String) msg.obj);
                            break;
                    }
                }
            };
            //循环读取messageQueue
//            Looper.loop();


        }
    }

使用Handler.post()直接更新ui


 private Handler handler=new Handler();
  @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_six);

       btn= findViewById(R.id.dian);

        new Thread(new Runnable() {
            @Override
            public void run() {
             /*   Message message=Message.obtain();
                message.obj="我是子线程静态消息";
                message.what=1;
                handler.sendMessage(message);*/
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d("aa","直接更新Ui");
                        btn.setText("我是更新的消息");
                    }
                });
            }
        }).start();
    }
  1. post和sendMessage本质上是没有区别的,只是实际用法中有一点差别
  2. post也没有独特的作用,post本质上还是用sendMessage实现的,post只是一中更方便的用法而已

Android 线程间通信之Handler

Android 线程间通信之Handler
面试解答:
1.Looper和Handler一定要处于一个线程吗?子线程中可以用MainLooper去创建Handler吗?
答:
(1)子线程中Handler handler = new Handler(Looper.getMainLooper());,此时两者就不在一个线程中
(2)可以的。
2.Handler的post方法发送的是同步消息吗?可以发送异步消息吗?
答:
用户层面发送的都是同步消息
不能发送异步消息
异步消息只能由系统发送。
3.Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?
答:
由Looper所在线程决定的
最终逻辑是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此有Looper所在线程决定。
4.Handler构造方法中通过Looper.myLooper();是如何获取到当前线程的Looper的?
答:
myLooper()内部使用ThreadLocal实现,因此能够获取各个线程自己的Looper

END:欲穷千里目,更上一层楼

点赞
收藏
评论区
推荐文章
九路 九路
5年前
IntentService使用以及源码分析
一概述我们知道,在Android开发中,遇到耗时的任务操作时,都是放到子线程去做,或者放到Service中去做,在Service中开一个子线程来执行耗时操作。那么,在Service里面我们需要自己管理Service的生命周期,何时开启何时关闭,还是很麻烦的,还好Android给我们提供了一个这样的类,叫做IntentService那么Intent
Stella981 Stella981
4年前
Android中AsyncTask的使用
原文https://blog.csdn.net/liuhe688/article/details/6532519在Android中实现异步任务机制有两种方式,Handler和AsyncTask。Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细
Wesley13 Wesley13
4年前
Java并发基础05. 传统线程同步通信技术
先看一个问题:有两个线程,子线程先执行10次,然后主线程执行5次,然后再切换到子线程执行10,再主线程执行5次……如此往返执行50次。看完这个问题,很明显要用到线程间的通信了,先分析一下思路:首先肯定要有两个线程,然后每个线程中肯定有个50次的循环,因为每个线程都要往返执行任务50次,主线程的任务是执行5次,子线程的任务是执行10次。线程间通信
Stella981 Stella981
4年前
Android中的常见通信机制和Linux中的通信机制
HandlerHandler是Android系统中的一种消息传递机制,起作用是应对多线程场景。将A进程的消息传递给B线程,实现异步消息处理。很多情况是将工作线程中需要更新UI的操作消息传递给UI主线程,而实现更新UI操作。因为工作线程和主线程是共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程和主线
Stella981 Stella981
4年前
AsyncTask的用法
AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程..为什么需要使用异步任务?我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避
Stella981 Stella981
4年前
Android异步操作总结
Android中经常会有一些操作比如网络请求,文件读写,数据库操作,比较耗时,我们需要将其放在非UI线程去处理,此时,我们需要处理任务前后UI的变化和交互。我们需要通过类似js中异步请求处理,这里总结我所了解到的,方便自己记忆,也方便别人的浏览。1.AsyncTasknewAysncTask().execute();AsyncTask会
Stella981 Stella981
4年前
Android 异步加载
一般会使用线程Thread、Timer或者使用AsyncTask,而这些操作都是在在后台另外开一个线程给我们找数据,具体得到的数据需要使用Handler去更新UI,AsyncTask也是一样使用到的Handler只是它将Handler封装在了onPostExecute执行操作中。使用过AsyncTask的同学都知道一个异步加载数据最少要
Stella981 Stella981
4年前
Notification使用详解之二:可更新进度的通知
上次和大家分享了关于Notification的基础应用,包括简单的通知和自定义视图的通知。今天和大家分享一下如何实现一个可更新进度的通知。我们将会模拟一个下载任务,先启动一个线程负责模拟下载工作,在这个过程中更新进度信息,然后下载线程把最新的进度信息以消息的形式,发送到UI线程的消息队列中,最后UI线程负责根据最新的进度信息来更新进度通知的UI界面。
Stella981 Stella981
4年前
Android IPC(跨进程通信)之AIDL
AndroidIPC(跨进程通信)之AIDLIPC——跨进程通信,是指两个进程之间的数据交换过程。在说IPC的同时我们要知道什么是进程,什么是线程。线程是CPU调度的最小单元,进程可以理解为一个程序或者一个应用。一个进程中可以运行多个线程,而在Android程序中有一个主线程,也叫UI线程。在And
Wesley13 Wesley13
4年前
IOS多线程
一、多线程每一个iOS应用程序中都有一个主线程用来更新UI界面、处理用户的触摸事件、解析网络下载的数据,因此不能把一些太耗时的操作(比如网络下载数据)放在主线程中执行,不然会造成主线程堵塞(出现界面卡死,防止界面假死),带来极坏的用户体验。iOS的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程异步编程是防止主线程堵塞,增加运
Stella981 Stella981
4年前
Notification使用详解之三:通过服务更新进度通知&在Activity中监听服务进度
上次我们讲到如何实现一个可更新的进度通知,实现的方式是启动一个线程模拟一个下载任务,然后根据任务进度向UI线程消息队列发送进度消息,UI线程根据进度消息更新通知的UI界面。可是在实际应用中,我们一般会将上传、下载等比较耗时的后台任务以服务的形式运行,更新进度通知也是交由后台服务来完成的。不过有的时候,除了在通知里面显示进度信息,我们也要在Activit