【Android】代码延迟执行 以及 Handler原理及应用

昨天在项目中遇到一个错误,Can’t create handler inside thread that has not called Looper.prepare(),意思是不能在没有调用Looper.prepare()的线程里创建handler,我原本是打算延时0.5秒后发送一个网络请求,首先想到了handler,结果出现这么一个错误,解决方案很简单,就是在线程里调用Looper.prepare(),然后调用Looper.loop()就可以了,但我最后选择不用handler.postDelay()方法延时,在线程里直接调用Thread.sleep(500)就可以了。

private void sendMessageToClient(final StringBuilder s){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sendToClient.sendDataToClient(s,clientSocketAddress);//网络请求必须在子线程
            }
        }).start();

    }

但是由这个错误联想到了之前准备面试时复习到的Handler原理,重新去复习了一下,了解了Looper.prepare()和Looper.loop()的作用以及为什么要调用这两个函数后才能创建Handler。

之前的个人总结

Handler是用来实现线程间的通信,耗时操作都需要在子线程里执行,当子线程的耗时操作完成后需要通知主线程更新UI,就需要用到Handler。
Handler用法为在主线程里声明一个handler对象,重写handlermessage方法,该方法用于接收子线程发送的信息并处理,子线程通过sendmessage方法发送一个message对象,sendmessage方法会调用enqueuemessage方法,将消息放入消息队列messagequeue,在主线程有一个looper对象,looper对象通过looper.loop方法开启了一个死循环,不断地从messagequeue里取出消息,并通过handler发到主线程,handlemessage方法就得到了执行。
如果要实现两个子线程之间的通信,需要通过looper.prepare来创建一个messagequeue,然后通过looper.loop函数开启死循环,将messagequeue里的消息取出来并处理。
使用handler容易导致内存泄漏,因为handler一般是以内部类的形式定义的,内部类会持有对外部类的引用,如果还有子线程未关闭,那子线程就会持有hander对象,这样的话Activity也会被handler持有而无法进行回收释放内存。解决方法为在声明handler内部类时,要声明静态内部类,并且使用软引用的方式,使handler持有外部类activity的软引用,在activity被销毁时,清空messagequeue里的消息。
sendmessageDelay实现是通过阻塞消息队列,存入messageQueue的消息都是按照要执行的先后顺序存入的,如果是不延时的消息,则直接处理,如果延时,就按照时间长短进行排序,如果消息队列的第一个消息需要延时则发生阻塞,当有新消息加入时,如果是不延时的消息则唤醒线程发送消息,如果是延时消息则按照处理时间先后插入消息队列。

所以其实Handler并不是只是用来实现线程间的通信,我之前很多延时操作都用到了Handler.postDelay()来实现,总结的时候却忽略了这个用法,但是不知道是不是使用handler.postDelay()来完成延时操作是不是大材小用,于是去查阅后发现实现延时执行有三种方法,

1.使用线程的休眠实现延时操作

new Thread() {
            @Override
            public void run() {
                super.run();
                Thread.sleep(3000);//休眠3秒
                /**
                 * 要执行的操作
                 */
                         }
        }.start();

2.使用TimerTask实现延时操作

TimerTask task = new TimerTask() {
            @Override
            public void run() {
              /**
               *要执行的操作
               */
            }
        };
        Timer timer = new Timer();
        timer.schedule(task, 3000);//3秒后执行TimeTask的run方法

3.使用Handler的postDelayed方法实现延时操作

Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
               /**
                *要执行的操作
                */
            }
        }, 3000);//3秒后执行Runnable中的run方法

作者:殇神马 来源:CSDN 原文:https://blog.csdn.net/mq2856992713/article/details/52005253

所以使用Handler.postDelay()来实现延时操作是一个比较方便的方法,第二个方法适合需要重复执行的代码片段,类似心跳连接操作,可以使用timer.schedule(TimerTask task, long delay, long period) 方法来完成,如果是在线程内部就像我之前遇到的情况,直接调用Thread.sleep()阻塞线程就可以了。

最后,关于Handler实现线程间的通信,非常容易造成内存泄漏

使用handler容易导致内存泄漏,因为handler一般是以内部类的形式定义的,内部类会持有对外部类的引用,如果还有子线程未关闭,那子线程就会持有hander对象,这样的话Activity也会被handler持有而无法进行回收释放内存。解决方法为在声明handler内部类时,要声明静态内部类,并且使用软引用的方式,使handler持有外部类activity的软引用,在activity被销毁时,清空messagequeue里的消息。

解决方案实例如下:

private static class MyHandler extends Handler {
    private final WeakReference<GameActivity> mActivity;

    MyHandler(GameActivity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final GameActivity activity = mActivity.get();
        if (activity != null) {
            if (msg.what == 1) {
                AlertDialog.Builder builder = new AlertDialog.Builder(activity);
                builder.setMessage("你已断开连接,请重新连接......");
                builder.setCancelable(false);
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        activity.startActivity(new Intent(activity,ClientActivity.class));
                        activity.finish();
                    }
                });
                builder.show();
            }
        }
    }
}

private final MyHandler mHandler = new MyHandler(this);

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值