声明: 本人菜鸟一枚, 本博客是本人自学的内容, 适用于初学者, 不喜勿喷, 谢谢大家

  • Handler介绍
  • Handler常用API
  • Handle内部实现原理
  • Handler内存泄漏问题分析


Handler介绍

对于像我这样的菜鸟来说, 刚开始学Android的时候, 如果想要实现类似下载的功能, 可能会这样写:

    public void downloadClick(View view) {        new Thread(new Runnable() {            @Override            public void run() {                //模拟下载                try {                    Thread.sleep(3000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                //下载完成后的操作                textView.setText("下载完成");            }        }).start();    }

这样写带来的后果是: 崩了

错误日志

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view

对于熟悉Android开发的人看来是非常可笑的事情, 因为在多线程操作时我们忽略了两点:

  1. 不允许阻塞UI线程(主线程)
  2. 不能在UI线程之外访问Android UI工具包

以上是Android开发不可逾越的红线, 必须遵守.

但是我们还需要在子线程和主线程之间传递数据, 该怎么办呢? 那就得用我们的Handler

Handler常用API

Handler可以完成下述两点工作:

  1. 消息调度和将来的某个时间点执行一个Runnable
  2. 多个任务加入到一个队列中

对于刚才的代码我们可以这样改

private Handler handler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what){                case 1:                    textView.setText("下载成功");                    break;            }        }    };    public void downloadClick(View view) {        new Thread(new Runnable() {            @Override            public void run() {                //模拟下载                try {                    Thread.sleep(3000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                handler.sendEmptyMessage(1);            }        }).start();    }

列举一下常用的API

handler.sendEmptyMessage(1);//发送空消息,Message msg = handler.obtainMessage();  //从全局的消息池中返回一个Message对象msg.what = 2;//设置标记handler.sendMessage(msg);handler.sendEmptyMessageAtTime(3, System.currentTimeMillis() + 3000);//表示在3s后发送一个空消息handler.sendEmptyMessageDelayed(4, 3000); //含义同时, 延迟发送消息//其他的都大同小异, 就不一一列举了

Handle内部实现原理

Handler实现机制:
1. Message对象, 表示要传递的一个消息, 内部使用数据结构实现消息池, 用于重复利用, 避免大量创建消息对象, 造成内存浪费
2. MessageQueue对象, 存放消息对象的消息队列, 先进先出原则
3. Looper对象负责管理当前线程的消息队列(MessageQueue), 用于循环检查消息队列, 从消息队列中一个一个的取出消息对象, 传入handlerMessage() 方法中
4. Handler对象负责把消息push到消息队列中, 以及接收并处理Looper从消息队列中取出的消息
图示说明:

Android启动程序时会在UI线程创建一个MessageQueue

Handler内存泄漏问题分析

如果我们仔细看我们之前写得程序在创建handler处有个黄色的叹号, 将详细信息调出来会是下面的样子

这就引出了我们所说的Handler的内存泄漏问题

到底哪儿出了问题呢??
大家仔细想想我们学习Java的时候, 在讲内部类的时候会讲到, 当我们创建一个内部类对象时, 我们的内部类对象默认会依附于外部类对象的存在而存在. 所以大家试想下面的例子:

handler.postDelayed(new Runnable() {    @Override    public void run() {        System.out.println("Test");    }}, 1000*60*5);finish();

在这种情况下,当执行完了postDelayed方法之后当前的Activity会立即finsh(), 但是大家要想到此时我们的handler是依附于外部类的 , 所以此时的Activity并没有真正的关掉

问题的解决
1. 定义一个内部类时会默认拥有外部类对象的引用, 所以最好我们定义一个静态的内部类
2. 使用弱引用, 即使用 引用的强弱分为: 强引用 => 软引用 => 弱引用 ,如果实在不清楚之间的关系请直接百度一下

    private MyHandler myHandler= new MyHandler(this);    private static class MyHandler extends Handler{        WeakReference weakReference;        public MyHandler(HandlerMemoryActivity activity) {            weakReference = new WeakReference(activity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            HandlerMemoryActivity activity = weakReference.get();            if (activity != null){                //这里写对Activity的操作            }        }    }

座右铭: 少说话, 多做事

更多相关文章

  1. Android(安卓)SurfaceFlinger中的SharedClient
  2. android Java StringBuffer对象的append方法
  3. Android系统中读写和显示图片
  4. Android(安卓)4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析
  5. Android(安卓)Volley 网络请求框架图
  6. Android(安卓)IntentService详解
  7. Process 'command '...SDK\build-tools\...\aidl.exe'' finis
  8. Android内存分析AndroidStudio的运用
  9. Flutter 集成极光推送jpush_flutter

随机推荐

  1. 安卓开发学习道路上觉得不错的学习网站
  2. Android(安卓)ROM研究---Android(安卓)bu
  3. Android的Btimap处理大图片解决方法
  4. Android中String的处理
  5. 用Android代码实现打开USB调试
  6. 【Android】TextView动态设置android:dra
  7. JetPack 之 DataBinding 的使用
  8. Android(安卓)canvas clip 参数解释
  9. Android中如何将dp,dip,sp与px相互转化
  10. Android权限问题