Handler类中发送消息-post()和postDelay()方法精炼详解

一、前期知识储备

(1)上官方文档:参见Handler类中的描述

 

 

首先,post和postDelay都是Handler的方法,用以在子线程中发送Runnable对象的方法;

其次,Android中post()方法可以直接在非UI线程中更新UI,不同与Handelr的Send类方法,需要进行切换;

最后,两个方法在实现UI线程事件的时间上有所区别,postDelayed()方法用以延期执行,post则是立即执行;

(2)Handler类的post类方法和send类方法联系与区别

post类方法,以匿名内部类的形式发送Runnable对象,在Runnable对象重写的run()方法中直接对UI进行更新;

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

三种切回主线程的实例:

        final Handler handler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 素描算法处理 耗时操作
                final Bitmap bitmap1 = SketchUtil.testGaussBlur(finalBitmap,1,1);
                final Bitmap bitmap2 = SketchUtil.testGaussBlur(finalBitmap,10,10);
                final Bitmap bitmap3 = SketchUtil.testGaussBlur(finalBitmap,20,20);

                // 三种切回主线程更新UI的方法
                imageView.post(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bitmap1); // 素描图
                    }
                });

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        orignView.setImageBitmap(bitmap2); // 素描图
                    }
                });

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        threeView.setImageBitmap(bitmap3); // 素描图
                    }
                });
            }
        }).start();

注意:使用handler方法切回主线程时,注意handler的实例化要放在主线程中,而不能在新开的子线程中,否则报错:

RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

这是因为,Handler在哪里创建,就获得哪里的Looper。主线程创建的Handler,即默认使用主线程的Looper。

常见常用的post()类方法汇总:

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

send类方法,比如sendMessage()方法,使用该方法发送构造好的Message,然后用Handler的handleMessage()方法接收发送出来的消息,在方法中对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实例,避免多次创建message(如new Message)
        Message msg =Message.obtain();  
        msg.obj = data;
        msg.what=1;   //标志消息的标志
        handler.sendMessage(msg);
    } 
}

常见常用的send类方法汇总:

 

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

分析:谷歌为Android系统提供了一系列的post类方法用以发送Runnable对象,又提供了一系列的send类方法用以发送Message对象,其实二者并不矛盾也不重复,打开post类方法的源码,就会发现最终发送的Runnable对象也会转变成Message对象进行发送。谷歌提供两类方法应该是分别处理不同的场景,发送的消息较为复杂时,且每种消息对应一种UI的更新时选择使用send类方法;而当子线程中只发出一种消息时,则直接使用post方法发送消息,且直接在post方法的内部实现UI的更新。

(3)Message的构造

public final class Message implements Parcelable {
    public int what;
    public int arg1; 
    public int arg2;
    public Object obj;
    ...
    }

Message类中有这几个成员变量描述消息,其中what是我们定义的消息码,为了让接收者能知道消息是关于什么的。arg1和arg2用于发送一些integer类型的值。obj用于传输任意类型的值。

(4)Handler接收消息的方法-handleMessage()方法

Handler是Android系统为处理异步消息机制所提供的类,我们在实际开发中最常使用handler的场景即是在子线程中处理耗时操作后利用Handler的post类方法或者send类方法来发送消息;而当使用的是send类方法时,需要使用Handler来接收消息,同时使用Handler的handleMessage()方法来处理消息。

private static class MyHandler extends Handler{

        //对Activity的弱引用
        private final WeakReference<HandlerActivity> mActivity;

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

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity = mActivity.get();
            if(activity==null){
                super.handleMessage(msg);
                return;
            }
            switch (msg.what) {
                case DOWNLOAD_FAILED:
                    Toast.makeText(activity, "下载失败", Toast.LENGTH_SHORT).show();
                    break;
                case DOWNLOAD_SUCCESS:
                    Toast.makeText(activity, "下载成功", Toast.LENGTH_SHORT).show();
                    Bitmap bitmap = (Bitmap) msg.obj;
                    activity.imageView.setVisibility(View.VISIBLE);
                    activity.imageView.setImageBitmap(bitmap);
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    }

这里为避免出现内存泄漏,使用了静态内部类和弱引用结合的方式来实现。

二、上代码,看具体实现

view.post(new Runnable() {

    @Override

    public void run() {

view.performClick();//需要处理是事件,Android开发中常见的是UI操作

}});

注:post的方法意在main主线程执行完后立即调用。

view.postDelayed(new Runnable() {

    @Override

    public void run() {

view.performClick();//需要处理是事件,Android开发中常见的是UI操作

}},3000);//延迟3秒注:postDelayed的方法意在延迟执行,在main主线程执行完后延迟3秒后开始调用。

三、和Handler结合使用—开发中常见

实际开发中,最常见的就是和Handler结合使用,开启异步任务

下面实现从网络中加载一张图片,并且显示在UI界面上

 

(1)在 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<Activity> mActivityReference;

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(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
}
}

(2)实现 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;
    }
}

文章后期更改过,将文章主旨内容从post()方法和postDelayed()方法的联系与区别转到Android中使用Handler进行异步消息的处理,以几段代码为例,分析了Handler的发送消息的方法:①post类方法;②send类方法;及Handler类接收消息和处理消息的方法handleMessage();最后模拟了从网上下载图片进而在UI界面中进行更新。

提供一些其他Android消息机制分析,帮助理解读者理解:

①Handler是Android消息机制的上层接口,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行,该线程既可以是主线程,也可以是子线程,要看构造Handler时使用的构造方法中传入的Looper位于哪里;

②Handler的运行需要底层的MessageQueue和Looper的支撑,Handler创建的时候会采用当前线程的Looper来构造消息循环系统,而线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper;

③上述代码中的第一个Handler-mainHandler,实例化的时候,直接在onCreate()方法中new出了实例,其实是其已经在主线程中了,主线程-ActivityThread,ActivityThread被创建时就会初始化Looper,这就是主线程中默认可以直接使用Handler的原因;

④上述代码中的第二个Handler-workHandler,它在实例化的时候,参数传入了 mHandlerThread.getLooper() ,注意,这个Handler使用的就不是主线程的Looper了,而是子线程的Looper,HandlerThread在调用start()方法之后,就可以获取到子线程的Looper,然后将其传入workHandler的构造方法中,那么此时的workHandler就会运行在子线程中,用于处理耗时操作。

⑤Handler的工作原理:Handler创建时会采用当前线程的Looper来构建内部消息循环系统,如果当前线程没有Looper,那么就会报错“Can`t create handler inside thread that has not called Looper.prepare()”解决方法有两个:为当前线程创建Looper即可,像上述代码中workHandler,或者在一个有Looper的线程中创建Handler也行,就像上述代码中的mainHandler一样;

⑥调用Handler的post方法会将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法来发送一个消息,这个消息同样会在Looper中去处理。其实post方法最终也是通过send方法来完成的。每当Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable的run方法或者Handler的handleMessage方法就会被调用。注意Looper是运行在创建Handler所在的线程中的,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中去执行了;

⑦Looper的工作原理:Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。注意关注一些重要的Looper的方法:

  • Looper.prepare()-为当前线程创建一个Looper;
  • Looper.loop()-开启消息循环,只有调用该方法,消息循环系统才会开始循环;
  • Looper.prepareMainLooper()-为主线程也就是ActivityThread创建Looper使用;
  • Looper.getMainLooper()-通过该方法可以在任意地方获取到主线程的Looper;
  • Looper.quit() Looper.quitSafely()-退出Looper,自主创建的Looper建议在不使用的时候退出

⑧ActivityThread主线程通过ApplicationThread和AMS进行进程间通信

  • 12
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值