本文描述的是Android中的Runnable接口 。因Android中的线程源自于Java,所以首先需要了解Java中的线程,有关Java中的线程请看这篇文章Android(线程一) 线程  !

    Java开发中,我们实现多线程,有两种方式, 一种是继承Thread类,一种是实现Runnable接口。但是,我们真的理解Runnable?Runnable和Thread一样吗?都是开启新的线程吗? 为何明明在子线程使用Handler的post(Runnable),最终还是在主线程中执行呢?...带着这些疑问,我们来开始今天的博文。本文的例子是基于Android Studio。

一、首先通过例子实现这两种方式。

1、继承Thread类。

      Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了。

首先新建一个MyThread类继承自Thread类,重写run()方法,在控制输入传递的文本,

 

public class MyThread extends Thread {    private String name;    public MyThread(String name) {        this.name = name;    }    @Override    public void run() {        System.out.println("MyThread is " + name);    }}

接着创建该类,启动该线程(Thread类的start()方法),并输出线程的id,

 

 

public class Test1 {    public static void main(String[] args){        MyThread myThread1=new MyThread("线程1");        MyThread myThread2=new MyThread("线程2");        MyThread myThread3=new MyThread("线程3");        myThread1.start();        myThread2.start();        myThread3.start();        System.out.println("myThread1 id ="+myThread1.getId());        System.out.println("myThread1 id ="+myThread2.getId());        System.out.println("myThread1 id ="+myThread3.getId());    }}

 

控制台输出截图如下,

开启了三个线程。

PS:如果你也是使用Android Studio,控制台中文输出可能是乱码,那么可以参考这篇文章去解决,Android Studio中Java控制台中文输出乱码

2、实现Runnable接口。

      Runnable只是一个接口,它里面只有一个run()方法,没有start()方法,

 

    public interface Runnable{      public void run();      } 

      所以,即使实现了Runnable接口,那也无法启动线程,必须依托其他类。

 

      而Thread类,有一个构造方法,参数是Runnable对象,也就是说可以通过Thread类来启动Runnable实现多线程。

 

   public Thread(Runnable target) {        init(null, target, "Thread-" + nextThreadNum(), 0);    }

      所以,实现Runnable接口后,需要使用Thread类来启动。
下面还是上案例说明,

 

创建一个类MyRunnable,实现Runnable接口,

 

public class MyRunnable implements Runnable {    private String name;    public MyRunnable(String name) {        this.name = name;    }    @Override    public void run() {        System.out.println("MyRunnable is " + name);    }}

和继承Thread类的实现方法基本一样,其实Thread类也是实现了Runnable接口,

 

下面是调用以及启动线程并打印线程的id,启动线程还是调用Thread类的start()方法,

 

public class Test1 {    public static void main(String[] args){        MyRunnable myRunnable1=new MyRunnable("Runnable1");        MyRunnable myRunnable2=new MyRunnable("Runnable2");        MyRunnable myRunnable3=new MyRunnable("Runnable3");        Thread myThread1=new Thread(myRunnable1);        myThread1.start();        System.out.println("myThread1 id ="+myThread1.getId());        Thread myThread2=new Thread(myRunnable2);        myThread2.start();        System.out.println("myThread1 id ="+myThread2.getId());        Thread myThread3=new Thread(myRunnable3);        myThread3.start();        System.out.println("myThread1 id ="+myThread3.getId());    }}

 

控制台输出截图如下,

可以看到,启动了三个不同的线程。

小结:通过上面的两个小例子程序,我们可以得知,只是实现Runnable接口,并不能启动或者说实现一个线程。Runnable接口,并不能代表一个线程。Runnable接口和线程是两个不同的概念!

换句话说,一个类,实现Runnable接口,这个类可以做很多事情,不仅仅只被用于线程,也可以用于其他功能!

二、 为何明显使用Handler的post(Runnable),最终还是在主线程中执行呢?

1.我们都知道使用Handler更新UI,有时候会调用 Handler.post()方法更新UI, Handler.post()方法的源码如下,

 

   public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }

 

getPostMessage()方法是创建一个Message对象,并且将参数的Runnable对象赋值给了该Message对象的callback属性,

 

    private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

sendMessageDelayed()方法内部会继续调用其他方法(此处不再详说),而这一系列的方法最终的功能是,将创建的Message对象加入到消息队列中。详情请看 Android 源码解析Handler处理机制(二)。

 

2.执行Looper.loop()方法,该方法将会从消息循环中循环取出消息,取出消息后,会执行下面的代码,

 

  public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

参数是消息队列取出的消息,如果该消息的callback属性(Runnable对象)等于‘null’,则会执行handleMessage()方法,否则,将执行handleCallback()方法,我们重点看看handleCallback()方法,有关handleMessage()详情请看 Android 源码解析Handler处理机制(二)。

 

 

   private static void handleCallback(Message message) {        message.callback.run();    }

通过上面的分析,这一块是不是更加清晰、明白了!

message.callback.run();

很显然该方法仅仅是执行了消息的callback属性(Runnable对象)的run()方法,并没有开启子线程,它其实还是运行在Handler所在的线程即主线程中。

 

小结:使用Handler.post()方法更新UI,只是将消息加入到消息队列,并且设置消息的callback属性为参数Runnable对象的值;从消息循环中取出消息时,将执行消息对象的callback属性(Runnable对象)run()方法,还是在Handler所在的主线程中运行的,并没有开启新的子线程。

总结:读过本篇文章后,相信读者对Handler.post()方法更新UI理解会更清晰、完整、透彻,并且对Runnable接口会有新的不一样的认识。

 

PS:在使用Runnable时,可能会内存泄露。Runnable是一个匿名内部类,因此它对当前Activity有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。那么该怎么解决这种问题呢?代码如下,

 

    static class MyRunnable implements Runnable {        @Override        public void run() {       //执行任务        }    }

使用 静态内部类,避免了Activity的内存资源泄漏。

 

 

推荐文章: Android 更新UI的几种方法。

                 Android 源码解析Handler处理机制(一)。

                Android 源码解析Handler处理机制(二) 。

欢迎大家关注我的公众号

 

 

 

更多相关文章

  1. Android推送通知指南
  2. Android(安卓)消息处理 -- Looper 、Handler类
  3. Android的消息机制(java层)
  4. Android(安卓)Frameworks系列(一) startService启动
  5. Android自适应不同屏幕几种方法
  6. 简单解释Android中的任务、进程和线程
  7. Android(安卓)将 android view 的位置设为右下角的解决方法
  8. android使用mysql的方法总结
  9. Unity和Android互相调用方法

随机推荐

  1. 怎么在日常工作中践行逻辑思维能力?
  2. 永远不要在代码中使用「User」这个单词!
  3. tsconfig.json文件各字段吐血整理
  4. PHP中针对区域语言标记信息的操作
  5. Caffeine缓存的简单介绍
  6. Java 中 long 是不是原子操作?
  7. 小白学Python:Python的基本数据类型
  8. 10月18日作业-MYSQL新增表及导出
  9. 香港虚拟主机是什么?有哪些推荐?需要备案吗
  10. JSON与mysql