关于android:Android-线程间通信之Handler

66次阅读

共计 4714 个字符,预计需要花费 12 分钟才能阅读完成。

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 只是一中更不便的用法而已


面试解答:
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: 欲穷千里目,更上一层楼

正文完
 0