【Android】多线程编程、异步消息处理机制以及new Handler()被标记为过时的解决办法,解决Handler内存泄漏问题和AsyncTask的基本用法

这篇具有很好参考价值的文章主要介绍了【Android】多线程编程、异步消息处理机制以及new Handler()被标记为过时的解决办法,解决Handler内存泄漏问题和AsyncTask的基本用法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、Android多线程编程

1、异步消息处理机制

1.1 弱引用

WeakReference(弱引用)是一种在Java中用于管理对象的引用的特殊引用类型。它的作用是在垃圾回收过程中,允许对象在没有强引用指向它时被回收(当一个对象只有弱引用指向它,而没有强引用指向它时,垃圾回收器可能会在下一次垃圾回收时回收该对象,即使系统内存并不紧张。),从而避免潜在的内存泄漏问题。

有些情况下,**我们可能希望对象在没有强引用指向它时能够被垃圾回收,以避免潜在的内存泄漏问题。**例如,考虑以下情况:

  1. 缓存:我们可能使用缓存来存储某些对象,但当这些对象不再被使用时,我们希望它们能够被垃圾回收,释放内存。
  2. 图片加载:在Android开发中,我们经常加载大量的图片,为了避免内存溢出,我们可能希望在图片不再显示时能够及时释放相关的资源。

WeakReference是一种特殊的引用类型,它允许对象在没有强引用指向它时被垃圾回收。

创建弱引用的方法:

Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj);

当我们不再需要弱引用所引用的对象时,可以将弱引用设置为null,使得对象变得不再可达,从而在下一次垃圾回收时被回收。

obj = null;

此时,如果没有其他强引用指向obj,它就会成为弱引用所引用的对象,因为没有强引用指向它,垃圾回收器可能会在适当的时候回收这个对象。

常用方法:

  • weakRef.get()

弱引用对象是通过 WeakReference 类来创建的,该类提供了一个 get() 方法,可以获取弱引用所引用的对象。

如果该对象还没有被垃圾回收器回收,那么 get() 方法会返回该对象的引用;但是如果对象已经被垃圾回收器回收,或者在调用 get() 方法的过程中被回收,那么 get() 方法会返回 null

1.2 强引用

当我们在Java中创建对象,并使用变量来引用这些对象时,通常使用的是强引用。强引用使得对象在有至少一个强引用指向它时保持存活状态,即使系统内存紧张,垃圾回收器也不会回收这些对象。

当我们创建一个对象并将其赋值给一个变量时,这个变量就是一个强引用。

只要强引用存在,垃圾回收器就不会回收被引用的对象,即使系统内存紧张也不会回收。只有当没有任何强引用指向一个对象时,垃圾回收器才会在适当的时候回收该对象,释放内存。

Object obj = new Object(); // 这里obj是一个强引用

即使代码中没有使用 obj,只要 obj 这个变量在作用域内且没有被显式释放(比如设置为 null 或跳出作用域),该对象依然不会被垃圾回收。只有当没有任何强引用指向这个对象时,垃圾回收器才有可能回收它。

1.3 使用Handler构建实例

在Android 10以前我们通常使用这样的方法构建Handler

Handler handler = new Handler(){
    public void handlerMessage(Messafe msg){
        
    }
}

但在已经被标记为过时,这个代码编译时会得到一个警告:“ 警告: [deprecation] Handler中的Handler()已过时” 。因为上面的代码存在内存泄露风险,那么怎么泄露内存,原理是什么呢?

当Activity被销毁时,如果其中使用的Handler被持有,并且在外部引用它的对象的生命周期之外保持活动状态,这就可能导致内存泄漏。在这种情况下,Handler所在的外部类可能无法被回收,因为它与Handler存在着相互引用的关系。

为避免内存泄漏,可以采取以下措施:

  • 使用静态内部类或独立类来实现Handler,以避免与外部类的强引用关系。
  • 尽量避免在Handler中持有外部类(Activity等)的引用,或者在不需要使用Handler时及时取消Message和Runnable的发送。

为了防止内存泄露,官方要求将handler声明为静态类,可内存泄露问题解决了,却引来了另一个很棘手的问题,静态类是无法访问外部类成员的,

怎么解决呢?这个问题很简单将外部类对象通过构造函数传递进来就可以了。

如果消息没处理完呢activity被销毁了,那么我们要怎么办?

此时我们就需要使用到弱引用的**.get()**方法,感知activity是否还存在。

public class MainActivity extends AppCompatActivity {

    public static final int UPDATE_TEXT = 1;
    static ActivityMainBinding binding;
    private Handler handler = new MyHandler(Looper.myLooper(),this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        //触发按钮修改文本文字
        binding.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();
            }
        });
    }

    static class MyHandler extends Handler{
        WeakReference<Activity> mainActivityWeakReference;

        //构造函数传入外部的this
        public MyHandler(@NonNull Looper looper, Activity mainActivity) {
            super(looper);
            //构建一个弱引用对象
            mainActivityWeakReference = new WeakReference<Activity>(mainActivity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            //判断活动是否还存在,如果不存在则结束
            Activity activity = mainActivityWeakReference.get();
            if (activity == null){
                return;
            }

            switch (msg.what){
                case UPDATE_TEXT:
                    binding.Text.setText("Nice to meet too");
            }
        }
    }
}

2、解析异步消息处理机制

Android中的异步消息处理机制主要由四个部分组成:Message、Handler、MessageQueue、Looper

2.1 Message

在线程之间传递消息,用于不同线程只见交换数据,例如上段代码的what用法。

2.2 Handler

Handler也就是处理者,主要用于发送消息和处理消息。发送消息一般使用HandlersetMessage()方法,发送的消息最终会传递到Handler的HanleMessage()方法中,我们通常需要重写HanlerMessage方法。

2.3 MessageQueue

消息队列,通常存放通过Handler发送的所以消息,每个线程中只有一个MessageQueue对象。

2.4 Looper

Looper是每个线程中MessageQueue的管家,调用Looper的loop()方法就可以进入一个无限的循环,每当在MessageQueue中发现一条消息,就会将消息取出发送给Handler的HandleMessage()方法中,同样每个线程中也只有一个Looper对象。

Looper.myLooper()是一个静态方法,用于获取当前线程的Looper对象。

2.5 异步消息处理流程

首先需要在主线程当中创建一个静态继承Handler的内部类,并重写handleMessage()方法。然后创建一个该内部类的对象MyHandler

然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过MyHandler将这条消息发送出去。

之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回内部类的handleMessage()方法中。

由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI操作了。

asynctask内存泄漏,Android,android,java,内存泄漏,Handler,AsyncTask,多线程

3、使用AsyncTask(谨用)

借助AsyncTask即使不了解异步消息处理机制也可以十分简单的从子线程切换到主线程。

AsyncTask是一个抽象方法我们需要用一个子类继承它,并且使用时需要指定三个泛型参数:

  • **Params:**在执行AsyncTask时需要传入的参数
  • **Progress:**后台任务执行时,如果需要在界面显示当前进度,则使用这里的泛型类型作为进度单位。
  • **Result:**当任务结束后的返回值。

例如:

class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
}
  • 第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传人参数给后台任务。
  • 第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单
    位。
  • 第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

接下来需要重写几个方法:

3.1 onPreExecute

这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

3.2 doInBackground()

**这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。**任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的。

比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。

3.3 OnProgressUpdate(Progress…)

当在后台任务中调用了publishProgress方法后,onProgressUpdate方法就会很快被调用。

该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

3.4 onPostExecute(Result)

当后台任务结束时并且通过retrun语句返回,这个方法就会被调用。返回的数据会作为参数传递到这个方法,可以根据数据进行UI操作。

3.5 实现自定义AsyncTask

asynctask内存泄漏,Android,android,java,内存泄漏,Handler,AsyncTask,多线程

首先使用AlertDialog和ProgressBar实现一个悬浮的加载框。

class MyAskncTask extends AsyncTask<Void,Integer,Boolean> {

    private AlertDialog alertDialog;
    int downloadPercent = 0;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setView(R.layout.progressbar);
                alertDialog = builder.create();
                alertDialog.setMessage("Downloaded: "+0+"%");
                alertDialog.show();
            }
        });
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        try {
            while (true){
                Thread.currentThread().join(100);
                downloadPercent+= 1;
                //传递下载进度
                publishProgress(downloadPercent);
                if(downloadPercent>100){
                    break;
                }
            }
        }catch (Exception e){
            return false;
        }
        return true;
    }


    @Override
    protected void onPostExecute(Boolean aBoolean) {
        alertDialog.dismiss();
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        alertDialog.setMessage("Downloaded"+values[0]+"%");
    }
}

实现后台运行加载,当再次点击按钮时,进入后台加载的页面:文章来源地址https://www.toymoban.com/news/detail-756984.html

binding.button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        runOnUiThread(new Runnable() {
            final MyAskncTask myAskncTask = new MyAskncTask(); // 在这里赋值
            @Override
            public void run() {
                //判断是否正在运行
                if ( myAskncTask.getStatus() == AsyncTask.Status.RUNNING) {
                    if (alertDialog != null) {
                        //如果正在运行重新显示弹窗
                        alertDialog.show();
                    }
                } else {
                    myAskncTask.execute();
                }
            }
        });
    }
});
  • PENDING:任务已创建,但尚未执行。
  • RUNNING:任务正在运行,它的 doInBackground() 方法正在执行。
  • FINISHED:任务已完成执行。一旦任务完成运行,它就不能再次执行。

到了这里,关于【Android】多线程编程、异步消息处理机制以及new Handler()被标记为过时的解决办法,解决Handler内存泄漏问题和AsyncTask的基本用法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程

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

    2024年03月15日
    浏览(53)
  • Android之handler消息处理机制详解

    handler是什么? ​ Handler是一个在消息处理机制中负责发送和处理消息的类,是消息处理的关键。 Handler:负责 发送消息 和处理消息 Looper:内置一个死循环,可以不断的 取出消息 并通知handler处理消息,是handler起作用的基础 Message:消息实体类 MessageQueue:消息队列,负责存储消

    2024年02月04日
    浏览(50)
  • 单线程、同步、异步、预解析、作用域、隐式全局变量、对象创建、new

    cpu 资源分配的最小单位 一个进程可以有多个线程 cpu 调度的最小单位 线程建立在进程的建立基础上的一次程序的运行单位 线程分为:单线程 多线程 单线程:js是单线程 (同一个时间只能完成一个任务) 多线程:百度是多线程 同步任务是指在主线程上排队的任务,只有当前

    2024年01月22日
    浏览(48)
  • 解密JavaScript的异步机制:打破单线程限制,提升性能与用户体验

     🎬 江城开朗的豌豆 :个人主页  🔥 个人专栏  :《 VUE 》 《 javaScript 》  📝  个人网站  :《 江城开朗的豌豆🫛 》  ⛺️ 生活的理想,就是为了理想的生活 !   目录 一、JavaScript的异步编步机制 二、事件循环(Event Loop)和任务队列(Task Queue) 三、宏任务和微任务

    2024年02月08日
    浏览(41)
  • 在C中使用Socket实现多线程异步TCP消息发送

    在本篇文章中,我们会探讨如何在C语言中使用socket来实现多线程,异步发送TCP消息的系统。虽然C标准库并没有原生支持异步和多线程编程,但是我们可以结合使用POSIX线程(pthread)库和socket来达到目的。 TCP (Transmission Control Protocol) 是一种面向连接的、可靠的、基于字节流的

    2024年02月11日
    浏览(42)
  • C# 中的多线程和异步编程

    最近在看代码的过程中,发现有很多地方涉及到多线程、异步编程,这是比较重要且常用的知识点,而本人在这方面还理解尚浅,因此开始全面学习C#中的多线程和异步编程,文中部分内容摘抄自一位前辈的网站:网址链接,为了更便于理解和学习,本人还在个别地方做了一

    2023年04月08日
    浏览(50)
  • Web APIs JavaScript执行机制与异步编程模型

    探索JavaScript的执行机制,包括同步与异步任务的处理方式,以及常见的事件循环机制,助您更深入理解JavaScript编程模型。

    2024年02月21日
    浏览(40)
  • Spring Boot 如何实现异步消息处理

    在现代应用程序中,异步消息处理是一项至关重要的任务。它可以提高应用程序的性能、可伸缩性和可靠性,同时也可以提供更好的用户体验。Spring Boot提供了多种方式来实现异步消息处理,包括使用Spring AMQP、Spring Kafka和Spring JMS等。本文将介绍如何使用Spring Boot实现异步消息

    2024年02月07日
    浏览(47)
  • 模拟实现.net中的Task机制:探索异步编程的奥秘

    .net中使用Task可以方便地编写异步程序,为了更好地理解Task及其调度机制,接下来模拟Task的实现,目的是搞清楚: Task是什么 Task是如何被调度的 从最基本的Task用法开始 这个命令的作用是将action作为一项任务提交给调度器,调度器会安排空闲线程来处理。 我们使用Job来模拟

    2024年02月06日
    浏览(38)
  • 【业务功能篇92】微服务-springcloud-多线程-异步处理-异步编排-CompletableFutrue

    一个商品详情页 展示SKU的基本信息 0.5s 展示SKU的图片信息 0.6s 展示SKU的销售信息 1s spu的销售属性 1s 展示规格参数 1.5s spu详情信息 1s   Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用 isDone 方法检查计算是否完成,或者使用 get 阻塞住调用线程,直到计算

    2024年02月10日
    浏览(58)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包