安卓之异步消息处理机制

这篇具有很好参考价值的文章主要介绍了安卓之异步消息处理机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

安卓之异步消息处理机制

Handler

简介

  • 为什么叫异步消息处理机制?
    • 先来看同步消息机制,它是指发送方必须等待接收方处理完消息后才能继续执行,也就是顺序执行。在 Android 中,如果在主线程中执行耗时操作,就会导致主线程阻塞,应用无法响应用户交互,这就是同步阻塞的例子。因此,需要在子线程中执行耗时操作以避免主线程阻塞。而异步消息机制是指在发送和接收消息时,发送方和接收方不需要同时执行,它们之间是异步的,也就是并行执行。因此,我们需要在子线程中执行这些耗时操作,但是在执行完后需要将结果返回到主线程中更新 UI,这时就可以通过异步消息机制来实现。
  • 不用它行不行?
    • 如果你要在主线程中执行一些耗时操作可能会导致应用程序卡顿或崩溃,你可能会想到一个办法就是在子线程中执行这些操作,然后再通过回调机制将执行结果传递到主线程中进行处理,这样也没毛病。但是使用 Handler 有以下优势:
      • ①Handler 内部已经帮我们实现了线程间通信的机制,我们只需要发送消息即可,很方便。
      • ②Handler 可以进行消息的延时发送,定时发送等操作,非常灵活。
      • ③Handler 可以通过消息队列实现消息的异步处理,避免了线程阻塞。
      • ④Handler 可以通过 Looper 机制实现消息循环,处理消息的顺序非常有序。
  • 异步消息机制的实现方式有很多,其中比较常见的包括:
    • 线程池:利用线程池中的线程来处理消息,避免了线程频繁创建和销毁的开销。
    • Handler 和 Looper:Android 中经典的异步消息处理机制,发送方通过 Handler 发送消息,接收方通过 Looper 接收并处理消息。
    • AsyncTask:Android 提供的一个异步任务框架,封装了线程池和 Handler,提供了方便的异步操作接口。
    • RxJava:一个基于观察者模式的异步编程框架,提供了强大的异步操作能力和线程调度功能。
    • Java NIO(非阻塞 I/O):提供了异步 I/O 操作,通过注册事件和回调机制实现异步通信。
    • 线程池、Handler 都是基于线程实现的,而 AsyncTask 则是基于 Handler 实现的;而 RxJava 则是基于线程池和调度器的机制来实现异步任务的处理。
  • 同步消息机制:
    • CountDownLatch:计数器,等待某些操作完成后再继续执行。使用 await() 方法等待计数器变为 0,使用 countDown() 方法减少计数器的值。当计数器为 0 时,await() 方法将返回;
    • CyclicBarrier:回环栅栏,等待一组线程全部完成后再继续执行。使用 await() 方法等待其他线程到达栅栏,当所有线程都到达栅栏时,栅栏将开放,所有线程可以继续执行;
    • synchronized:同步锁,用于保护共享资源的访问。使用 synchronized 关键字锁定对象或方法,使得同一时刻只有一个线程可以访问共享资源。

组成

  • 主要由4个部分组成:Message、Handler、MessageQueue、Looper。
    • Message:
      • 定义:它是在线程之间传递的信息。
      • 作用:它可以携带少量信息,用于在不同线程之间传递数据。
      • 常用参数:
        • what:表示一个整数常量,用于标识消息类型。
        • “arg1” 和 “arg2” :表示两个整数常量,用于传递消息的参数。
        • obj:表示一个对象。
    • Handler:
      • 它负责接收并处理系统发来的消息。
      • 常用方法:
        • 发送消息:
          • sendMessage(Message msg): 将消息发送到 MessageQueue 队列,等待处理;
          • sendMessageAtTime(Message msg, long uptimeMillis): 将消息发送到 MessageQueue 队列,并指定处理时间;
          • sendMessageDelayed(Message msg, long delayMillis): 将消息发送到 MessageQueue 队列,并延迟指定时间后处理。
        • 处理消息:
          • handleMessage(Message msg): 在 Handler 中处理消息的方法,需要子类进行实现。
        • 其他方法:
          • obtainMessage(): 获取一个空的 Message 对象;
          • obtainMessage(int what): 获取一个 what 值为指定整数的 Message 对象;
          • obtainMessage(int what, Object obj): 获取一个 what 和 obj 值的 Message 对象;
          • obtainMessage(int what, int arg1, int arg2): 获取一个 what 和 arg1、arg2 值的 Message 对象;
          • obtainMessage(int what, int arg1, int arg2, Object obj): 获取一个 what、arg1、arg2 和 obj 值的 Message 对象;
          • post(Runnable r): 将 Runnable 对象发送到 MessageQueue 队列,等待处理;
          • postDelayed(Runnable r, long delayMillis): 将 Runnable 对象发送到 MessageQueue 队列,并延迟指定时间后处理。
    • MessageQueue:
      • 它主要用于存放所有通过Handler发送的消息。
      • 这些消息会一直存在于消息队列中,等待被处理。
      • 每个线程只会有一个MessageQueue对象。
    • Looper:
      • Looper是每个线程中的MessageQueue的管家。
      • 调用Looper.loop()后,就会进入一个无限循环中,每当MessageQueue中存在一条消息是,就会将它取出,并传递到Handler的handleMessage()中。
      • 每个线程只会有一个Looper对象。

使用步骤

  • (1)创建一个Handler对象:在主线程中创建一个Handler对象,可以在Activity或者Fragment中创建。
    •  // 在主线程中创建一个Handler对象
       private Handler mHandler = new Handler();
      
  • (2)实现Handler中的消息处理方法:在创建的Handler对象中实现消息处理方法,用于处理来自其他线程的消息。
    •  // 实现消息处理方法
       private Handler mHandler = new Handler() {
       	@Override
       	public void handleMessage(Message msg) {
       		// 处理消息,更新UI等操作
       	}
       };
      
  • (3)发送消息:在子线程中,通过Handler对象发送消息Message对象。这个消息会被添加到消息队列MessageQueue中等待被处理,Looper会一直尝试从MessageQueue中取出待处理的消息,最后分发回Handler的handleMessage()方法中。
    •  // 在其他线程中发送消息
       mHandler.sendEmptyMessage(MSG_WHAT);
      
  • (4)处理消息:当Handler接收到消息时,调用消息处理方法中的代码进行处理。

内存泄露问题

  • 一般都是使用静态内部类加上弱引用来实现,刚接触时一直没去仔细思考为什么要这样写?
    • 拿Activity举例:
      • 我们在Activity中创建一个Handler,这个Handler用来处理其他线程传来的消息或者处理自己线程的消息(不想写重复代码),像这样自定义地去处理消息就得写一个类去继承Handler,在Handler中,你大概率的会调用外部Activity类的方法,此时比如你用的是成员内部类,它会隐式地持有外部类的引用,你可以很方便的调用它的方法,但是这有一个问题,在Activity销毁时,Handler还持有Activity的引用,那么该Activity实例无法立即被回收,需要等待MessageQueue中的所有Message都被处理完成之后才能回收,这样就可能会导致内存泄漏问题。
      • 其次,为了防止这种隐式地强引用,我们可以使用静态内部类,静态内部类并不会持有外部类的对象,同时也不能再直接调用外部类的成员方法,因此要想调用还得另想它法。我们可以把外部类的对象通过静态内部类的构造方法传入,再在内部创建一个Activity类型的变量引用它,这样不就可以了。但是这样直接传入再引用还是属于强引用,在Activity销毁时仍可能会发生内存泄漏,因此这里需要使用弱引用WeakReference持有它,这样即便Activity销毁时消息队列中还有消息,这个引用依然可以被回收。

实例

  • 1.创建Handler类:
    •  private static class MyHandler extends Handler {
       
       	private final WeakReference<MainActivity> mActivity;
      
           public MyHandler1(MainActivity activity) {
               mActivity = new WeakReference<>(activity);
           }
      
           @Override
           public void handleMessage(Message msg) {
               super.handleMessage(msg);
               MainActivity activity = mActivity.get();
               if (activity != null) {
                   switch (msg.what) {
                       case 1:
                           activity.test1();
                           break;
                       case 2:
                           activity.test2();
                           break;
                   }
               }
           }
       }
      
  • 2.创建Handler实例:
    •  private MyHandler mHandler = new MyHandler(this);
      
  • 3.发送消息:
    • mHandler.sendEmptyMessageAtTime(1, 0);
      

在子线程中创建Handler

Handler的使用
  • 在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。
    •  public class Activity extends android.app.Activity {
      
           private Handler mHandler = new Handler(){
               @Override
               public void handleMessage(Message msg) {
                   super.handleMessage(msg);
                   // 更新UI
               }
           };
      
           @Override
           public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
               super.onCreate(savedInstanceState, persistentState);
               setContentView(R.layout.activity_main);
               
               new Thread(new Runnable() {
                   @Override
                   public void run() {
                       // 执行耗时任务
                       ...
                       // 任务执行完后,通知Handler更新UI
                       Message message = Message.obtain();
                       message.what = 1;
                       mHandler.sendMessage(message);
                   }
               }).start();
           }
       }
      
Handler架构
  • Handler消息机制主要包括:MessageQueue、Handler、Looper这三大部分,以及Message。
    • Message:需要传递的消息,可以传递数据;
    • MessageQueue:消息队列,但是它的内部实现并不是用的队列,而是通过单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能是向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)。
    • Handler:消息辅助类,主要功能是向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
    • Looper:消息控制器,不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
      安卓之异步消息处理机制,安卓,android
    • 从图中可以看出:
      • Looper有一个MessageQueue消息队列;
      • MessageQueue有一组待处理的Message;
      • Message中记录发送和处理消息的Handler;
      • Handler中有Looper和MessageQueue。
    • MessageQueue、Handler和Looper三者之间的关系:
      • 每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。
        主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。
        每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。
        Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。
        安卓之异步消息处理机制,安卓,android

Handler的运行流程

在子线程执行完耗时操作,当Handler发送消息时,将会调用MessageQueue.enqueueMessage,向消息队列中添加消息。
当通过Looper.loop开启循环后,会不断地从消息池中读取消息,即调用MessageQueue.next,
然后调用目标Handler(即发送该消息的Handler)的dispatchMessage方法传递消息,
然后返回到Handler所在线程,目标Handler收到消息,调用handleMessage方法,接收消息,处理消息。
安卓之异步消息处理机制,安卓,android

总结

安卓之异步消息处理机制,安卓,android文章来源地址https://www.toymoban.com/news/detail-527534.html

方式一:基本方式
  •  private Handler mHandler;
     //创建子线程handler
     private void createHandler(){
         new Thread(new Runnable() {
             @Override public void run() {
                 //当前子线程创建looper
                 Looper.prepare();
                 //传入子线程Looper,也可以不传,默认也为该线程looper
                 //Looper.myLooper():返回当前线程的Looper对象
                 mHandler = new Handler(Looper.myLooper());
                 //启动looper
                 Looper.loop();
             }
         }).start();
     }
     
     //推送任务到消息队列
     private void postRun(){
         mHandler.post(new Runnable() {
             @Override public void run() {
                 //执行耗时操作
                 SystemClock.sleep(5000);
             
                 //更新UI
                 MainActivity.this.runOnUiThread(new Runnable() {
                     @Override public void run() {
                         actTextReslt.setText("完成操作");
                     }
                 });
             }
         });
     }
    
  • 注:Looper.prepare() -> new Handler() -> Looper.loop()要按此顺序执行,由于子线程默认没有Looper而handler工作需要looper对象,直接创建handler对象会报java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()。
方式二:HandlerThread方式(HandlerThread内部创建消息队列,外部通过handler通知HandlerThread执行)
  •  HandlerThread hanlerThread = new HandlerThread("子线程");
     hanlerThread.start();
     final Handler handler = new Handler(hanlerThread.getLooper()) {
         @Override
         public void handleMessage(Message msg) {
             super.handleMessage(msg);
             Log.d("----->", "线程:" + Thread.currentThread().getName());
         }
     };
     findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             handler.sendEmptyMessage(100);
         }
     });
    
  • 方式二是Android对方式一的封装,使用起来更简单。其中HandlerThread构造方法的第二个参数表示优先级(范围-20~19,越小优先级越高)。
  • handler消息队列是串行的(即处理完当前消息,才进行处理下一个消息),背后只有一个线程,这里区别于线程池,对于并行需求考虑线程池解决。
方式三:子线程中创建主线程handler
  •  new Thread(() -> {
     	Handler handler = new Handler(Application.getContext().getMainLooper());
     }).start();
    

AsyncTask

简介

  • 从名称就能简单看出它是“异步任务”。
  • 它封装了线程池和Handler 。
  • AsyncTask是Android提供的一个轻量级异步框架,可以在后台执行耗时操作,同时也可以在主线程更新UI,简化了在Android中使用线程的复杂度。
  • AsyncTask有四个泛型参数:
    • Params:执行异步任务需要传入的参数类型。
    • Progress:异步任务执行过程中,返回进度值的类型。
    • Result:异步任务执行完成后,返回结果的类型。
    • AsyncTask<Params, Progress, Result>:这里指的是AsyncTask本身,也就是说,它继承自Object类,同时也是一个泛型类,其中的三个泛型参数表示上述三个泛型参数。
  • 注:
    • AsyncTask通常被用于执行轻量级的操作,如果需要执行一些重量级的操作,例如,网络请求等,可以考虑使用其他的异步框架。
    • AsyncTask在后台线程中执行,因此不应该在doInBackground()方法中访问UI元素,如果需要更新UI,应该在onProgressUpdate()方法和onPostExecute()方法中执行。
    • AsyncTask在Android 4.0之后,使用的是单线程池,因此,如果需要同时执行多个异步任务,应该使用多线程池,或者使用其他的异步框架。

使用步骤:

  • onPreExecute():在后台任务开始之前执行,通常进行一些预处理操作,例如,显示进度条等。
  • doInBackground(params…):这个方法在异步线程(子线程)中执行,在这里执行任务。任务一旦完成可以通过return将执行结果返回,
  • onProgressUpdate(progress…):在后台任务执行doInBackground()的过程中调用publishProgress(progress…):传入进度信息,然后在这个方法中更新UI。
  • onPostExecute(result):在后台任务执行完成之后执行,通常在这里进行一些结果的处理,例如:更新UI,关闭进度条等。

实例

  • 1.定义AsyncTask:
    •   public class MyAsyncTask extends AsyncTask<Void, Void, String> {
            private WeakReference<Context> mContext;
      
            public MyAsyncTask(Context context) {
                this.mContext = new WeakReference<>(context);
            }
      
            @Override
            protected String doInBackground(Void... voids) {
                // 后台工作在这里执行,不可以更新UI
                String result = "";
                // 模拟一个耗时的操作
                try {
                    Thread.sleep(2000);
                    result = "Hello, AsyncTask!";
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return result;
            }
      
            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                // 任务执行完成后,可以更新UI
                Context context = mContext.get();
                if (context != null) {
                    Toast.makeText(context, s, Toast.LENGTH_SHORT).show();
                }
            }
        }
      
  • 2.调用:
    •  MyAsyncTask myAsyncTask = new MyAsyncTask(this);
       myAsyncTask.execute();
      

到了这里,关于安卓之异步消息处理机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Selenium处理异步加载请求获取XHR消息体的2种方法

    目录 通过Log读取XHR 简单使用示例 异步加载情况下,不涉及浏览器全局的加载,因此selenium会直接往下执行,这就导致异步结果还没返回,脚本就继续执行了。 构造chrome driver: 通过log来获取xhr: 其中,上述中“message”的消息如下: 通过requestId可以获得详细的消息体: Git

    2023年04月08日
    浏览(25)
  • 直播弹幕系统(二)- 整合RabbitMQ进行消息广播和异步处理

    上一篇文章 SpringCloud网关对WebSocket链接进行负载均衡 中把主要的架子搭建好了,这一篇文章就要开始写业务逻辑了。在分布式系统下,如何达到SpringBoot - WebSocket的使用和聊天室练习的效果。 我们页面上,通过 WebSocket 发送弹幕信息的时候,后端通过 @OnMessage 注解修饰的函数

    2023年04月08日
    浏览(27)
  • Android的消息机制--Handler

    Android的消息机制是由Handler、Message、MessageQueue,Looper四个类支撑,撑起了Android的消息通讯机制,Android是一个消息驱动系统,由这几个类来驱动消息与事件的执行 Handler: 用来发送消息和处理消息 无论使用的post ,还是send,都会执行enqueueMessage 方法,将消息加到队列中 发送的

    2024年02月21日
    浏览(44)
  • 高效处理消息:使用Spring Boot实现消息重试机制

    当涉及到消息发送和接收的可靠性,Spring Boot提供了一些机制来确保消息的可靠传递。其中包括消息确认机制和重试机制。下面是一个示例代码,演示如何在Spring Boot中实现可靠的消息发送和接收。 首先,我们需要配置RabbitMQ的连接信息和相关属性。在 application.properties 文件中

    2024年02月11日
    浏览(38)
  • ijkplayer 源码分析(2):消息分发处理机制

    上篇博客 ijkplayer 源码分析(1):初始化流程 的 4.1.1 ijkmp_create() 的部分简要说明了下 ijkplayer 的消息处理机制,本文再根据源码进行详细分析,搞清楚其消息机制及处理流程。 播放器是一个较为复杂的多线程工程,如数据读取线程、音频解码线程、视频解码线程、视频播放

    2023年04月08日
    浏览(25)
  • 【Android开发基础】多线程-Handle消息机制

    描述:一个静态的UI界面,如果需要同时加载多个资源,并能在视觉上得到动态反馈,就需要用到Handle消息机制。 知识点:Handle消息机制 难度:初级 理解 1、为什么要用Handle 根据官方规则:子线程不允许操作主线程中的组件 2、原理图 3、关键对象 (1)Message(消息类)   

    2024年02月09日
    浏览(30)
  • Android中的多线程编程与异步处理

    在移动应用开发中,用户体验是至关重要的。一个流畅、高效的应用能够吸引用户并提升用户满意度。然而,移动应用面临着处理复杂业务逻辑、响应用户输入、处理网络请求等多个任务的挑战。为了确保应用的性能和用户体验,多线程编程和异步处理成为了不可或缺的技术

    2024年02月11日
    浏览(39)
  • 【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合!

    🎉🎉 欢迎光临,终于等到你啦 🎉🎉 🏅我是 苏泽 ,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.

    2024年03月10日
    浏览(39)
  • Android源码解析--享元设计模式,handler消息传递机制(基于Android API 33 SDK分析)

    使用共享对象可有效地支持大量的细粒度的对象 核心:对象复用。 1.1 享元模式Demo 火车票购票Demo 缓存对象在一个Map中。下面我们还会分析 用法 跟进去 这就是最明显的一个享元设计模式。 Android 开发一个知识点:UI 不能够在子线程中更新。 我们跟进post函数 Handler 传递了一个

    2024年02月11日
    浏览(24)
  • Android,ios,安卓app推送消息通知,java后台向手机推送app的通知教程

    个推是商用级的移动应用消息推送云服务供应商,客户端 SDK 支持 Android 和 iOS 两大平台,开发者集成 SDK 后,可以通过个推强大的 web 端及丰富的 API 开放接口,发送推送消息、统计分析推送效果。可有效提高 App 活跃度,增加用户留存率。 如果您还没有个推 账号,可在 个推

    2024年02月04日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包