资讯专栏INFORMATION COLUMN

Handler 系列一:如何使用

ddongjian0000 / 2738人阅读

摘要:判断标志位获取数据,更新耗时操作从全局池中返回一个实例,避免多次创建如标志消息的标志存在的问题内存方面被作为引用,如果为非静态内部类,则会引用外部类对象。当时,可能并未执行完,从而引起的内存泄漏。

Handler 是什么?

为什么需要 Handler?

Handler 如何使用?

handler 存在的问题及改进?

Handler:是一个消息分发对象,进行发送和处理消息,并且其 Runnable 对象与一个线程的 MessageQueue 关联。
作用:调度消息,将一个任务切换到某个指定的线程中去执行。

为什么需要 Handler? 子线程不允许访问 UI

假若子线程允许访问 UI,则在多线程并发访问情况下,会使得 UI 控件处于不可预期的状态。
传统解决办法:加锁,但会使得UI访问逻辑变的复杂,其次降低 UI 访问的效率。

引入 Handler

采用单线程模型处理 UI 操作,通过 Handler 切换到 UI 线程,解决子线程中无法访问 UI 的问题。

Handler 使用 方式一: post(Runnable)

创建一个工作线程,实现 Runnable 接口,实现 run 方法,处理耗时操作

创建一个 handler,通过 handler.post/postDelay,投递创建的 Runnable,在 run 方法中进行更新 UI 操作。

new Thread(new Runnable() {
@Override
public void run() {

  /**
     耗时操作
   */
 handler.post(new Runnable() {
     @Override
     public void run() {
         /**
           更新UI
          */
     }
 });

}
}).start();

方式二: sendMessage(Message)

创建一个工作线程,继承 Thread,重新 run 方法,处理耗时操作

创建一个 Message 对象,设置 what 标志及数据

通过 sendMessage 进行投递消息

创建一个handler,重写 handleMessage 方法,根据 msg.what 信息判断,接收对应的信息,再在这里更新 UI。

private Handler handler = new Handler(){

@Override
public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (msg.what) {      //判断标志位
        case 1:
            /**
              获取数据,更新UI
             */
            break;
    }
 }

};

public class WorkThread extends Thread {

@Override

 public void run() {
   super.run();
/**
  耗时操作
 */
   

Message msg =Message.obtain(); //从全局池中返回一个message实例,避免多次创建message(如new Message)
msg.obj = data;
msg.what=1; //标志消息的标志
handler.sendMessage(msg);
}

new WorkThread().start();

Handler 存在的问题 内存方面

Handler 被作为 Activity 引用,如果为非静态内部类,则会引用外部类对象。当 Activity finish 时,Handler可能并未执行完,从而引起 Activity 的内存泄漏。故而在所有调用 Handler 的地方,都用静态内部类。

异常方面

当 Activity finish 时,在 onDestroy 方法中释放了一些资源。此时 Handler 执行到 handlerMessage 方法,但相关资源已经被释放,从而引起空指针的异常
避免

如果是使用 handlerMessage,则在方法中加try catch。

如果是用 post 方法,则在Runnable方法中加try catch。

Handler 的改进

内存方面:使用静态内部类创建 handler 对象,且对 Activity 持有弱引用

异常方面:不加 try catch,而是在 onDestory 中把消息队列 MessageQueue 中的消息给 remove 掉。

则使用如下方式创建 handler 对象:

        
    /**
     为避免handler造成的内存泄漏
     1、使用静态的handler,对外部类不保持对象的引用
     2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
    */
      private static class MyHandler extends Handler {
        private final WeakReference mActivityReference;    

        MyHandler(Activity activity) {
            this.mActivityReference = new WeakReference(activity);
        }

        @Override
            public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity activity = (MainActivity) mActivityReference.get();  //获取弱引用队列中的activity
            switch (msg.what) {    //获取消息,更新UI
                case 1:
                    byte[] data = (byte[]) msg.obj;
                    activity.threadIv.setImageBitmap(activity.getBitmap(data));
                    break;
            }
        }
    }

并在 onDesotry 中销毁:

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}
Handler 的使用实现

耗时操作采用从网络加载一张图片

继承 Thread 或实现 Runnable 接口的线程,与 UI 线程进行分离,其中 Runnable 与主线程通过回调接口进行通信,降低耦合,提高代码复用性。

在 Activity 中创建 handler 对象,调用工作线程执行
    
public class MainActivity extends AppCompatActivity {

ImageView threadIv;
ImageView runnableIv;
SendThread sendThread;
PostRunnable postRunnable;
private final MyHandler handler = new MyHandler(this);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    threadIv = (ImageView) findViewById(R.id.thread_iv);
    runnableIv = (ImageView) findViewById(R.id.runnable_iv);

    sendThread = new SendThread(handler);
    sendThread.start();

    postRunnable = new PostRunnable(handler);
    postRunnable.setRefreshUI(new PostRunnable.RefreshUI() {
        @Override
        public void setImage(byte[] data) {
            runnableIv.setImageBitmap(getBitmap(data));
        }
    });
    new Thread(postRunnable).start();
}

/**
  为避免handler造成的内存泄漏
  1、使用静态的handler,对外部类不保持对象的引用
  2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
 /
private static class MyHandler extends Handler {
    private final WeakReference mActivityReference;

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //获取弱引用队列中的activity
        switch (msg.what) {    //获取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

private Bitmap getBitmap(byte[] data) {
    return BitmapFactory.decodeByteArray(data, 0, data.length);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}
}
方式一:实现 runnable 接口,通过 post(Runnable)通信,并通过给定的回调接口通知 Activity 更新
public class PostRunnable implements Runnable {

private Handler handler;
private RefreshUI refreshUI;
byte[] data = null;

public PostRunnable(Handler handler) {
    this.handler = handler;
}

@Override
public void run() {
    /**
      耗时操作
     */
    final Bitmap bitmap = null;
    HttpClient httpClient = new DefaultHttpClient();
    HttpGet httpGet = new HttpGet("http://i3.17173cdn.com/2fhnvk/YWxqaGBf/cms3/FNsPLfbkmwgBgpl.jpg");
    HttpResponse httpResponse = null;
    try {
        httpResponse = httpClient.execute(httpGet);
        if (httpResponse.getStatusLine().getStatusCode() == 200) {
            data = EntityUtils.toByteArray(httpResponse.getEntity());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    //返回结果给UI线程
    handler.post(new Runnable() {
        @Override
        public void run() {
            refreshUI.setImage(data);
        }
    });
}

public interface RefreshUI {
    public void setImage(byte[] data);
}

public void setRefreshUI(RefreshUI refreshUI) {
    this.refreshUI = refreshUI;
}
}
方式二:继承Thread,通过handler的sendMessage通信
public class SendThread extends Thread {

private Handler handler;

public SendThread(Handler handler) {
    this.handler = handler;
}

@Override
public void run() {
    super.run();
    /**
      耗时操作
     */
    byte[]data=null;
    HttpClient httpClient = new DefaultHttpClient();
    HttpGet httpGet = new HttpGet("https://d36lyudx79hk0a.cloudfront.net/p0/descr/pc27/3095587d8c4560d8.png");
    HttpResponse httpResponse = null;
    try {
        httpResponse = httpClient.execute(httpGet);
        if(httpResponse.getStatusLine().getStatusCode()==200){
            data= EntityUtils.toByteArray(httpResponse.getEntity());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    //返回结果给UI线程
    doTask(data);
}

/**
  通过handler返回消息
  @param data
 */
private void doTask(byte[] data) {
    Message msg =Message.obtain();  //从全局池中返回一个message实例,避免多次创建message(如new Message)
    msg.obj = data;
    msg.what=1;   //标志消息的标志
    handler.sendMessage(msg);
}
}

本文发表于个人博客:http://lavnfan.github.io/,欢迎指教。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12963.html

相关文章

  • Handler 系列二:怎么通信

    摘要:承接上一篇系列一,上篇主要总结了如何通信,这篇来介绍怎么通信。消息入队列在中,通过入队列,并为该的赋值为当前的对象,记住很重要,之后取出该消息时,还需要由回调到该中处理消息。设置消息为空,可以通过的和方法通知消息队列退出。 承接上一篇Handler系列一,上篇主要总结了Handler如何通信,这篇来介绍Handler怎么通信。 Handler的通信机制 Handler,Looper,...

    VincentFF 评论0 收藏0
  • vue源码分析系列之响应式数据(四)

    摘要:执行当时传入的回调,并将新值与旧值一并传入。文章链接源码分析系列源码分析系列之环境搭建源码分析系列之入口文件分析源码分析系列之响应式数据一源码分析系列之响应式数据二源码分析系列之响应式数据三 前言 上一节着重讲述了initComputed中的代码,以及数据是如何从computed中到视图层的,以及data修改后如何作用于computed。这一节主要记录initWatcher中的内容。 ...

    GHOST_349178 评论0 收藏0
  • 【Mybatis系列】从源码角度理解Mybatis的数据转换器TypeHandler

    摘要:无论是在预处理语句中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成类型。这个抽象类实现了接口,这个接口主要定义了类型转换的几种操作。至于这个抽象类继承的,主要是提供了获取这个具体是哪个类型。 TypeHandlers 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用...

    Edison 评论0 收藏0
  • Vert.x Blueprint 系列教程(三) | Micro-Shop 微服务应用实战

    摘要:本教程是蓝图系列的第三篇教程,对应的版本为。提供了一个服务发现模块用于发布和获取服务记录。前端此微服务的前端部分,目前已整合至组件中。监视仪表板用于监视微服务系统的状态以及日志统计数据的查看。而服务则负责发布其它服务如服务或消息源并且部署。 本文章是 Vert.x 蓝图系列 的第三篇教程。全系列: Vert.x Blueprint 系列教程(一) | 待办事项服务开发教程 Vert....

    QiShare 评论0 收藏0
  • Vert.x Blueprint 系列教程(二) | 开发基于消息的应用 - Vert.x Kue

    摘要:本文章是蓝图系列的第二篇教程。这就是请求回应模式。好多属性我们一个一个地解释一个序列,作为的地址任务的编号任务的类型任务携带的数据,以类型表示任务优先级,以枚举类型表示。默认优先级为正常任务的延迟时间,默认是任务状态,以枚举类型表示。 本文章是 Vert.x 蓝图系列 的第二篇教程。全系列: Vert.x Blueprint 系列教程(一) | 待办事项服务开发教程 Vert.x B...

    elina 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<