android中的Handler和Runnable

时间:2023-03-10 03:49:26
android中的Handler和Runnable

最近在做一个项目,在网络请求时考虑用Handler进行处理,然后就研究了一下Handler和Runnable

首先在看一下java中的Runnable

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.

This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example, Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.

In addition, Runnable provides the means for a class to be active while not subclassing Thread. A class that implements Runnable can run without subclassing Thread by instantiating a Thread instance and passing itself in as the target. In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

这个官方文档中的描述,看原版文档还是很有好处的,就不翻译了。

在Java中Runnable可以实现资源共享的多线程,网上多拿卖票的例子来讲,但是看上去有点模糊不清,对于这样一个代码

MyRunnable runnable = new MyRunnable;
new Thread(runnable,"a").start();
new Thread(runnable,"b").start();

它们之间资源共享到底是怎么个共享呢?

现在我们来看一下例子

public class Main {

    public static void main(String[] args)  {
        new Main().test();
    }

    void test(){
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable,"a").start();
        new Thread(runnable,"b").start();
    }
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i=0;i<5;i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }

    }
}

执行结果

a0
b0
b1
b2
b3
b4
a1
a2
a3
a4

可以看到这两个线程相互之间并没有影响得都执行了5次

然后在我们的Runnable类里边加一个全局变量

public class Main {

    public static void main(String[] args)  {
        new Main().test();
    }

    void test(){
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable,"a").start();
        new Thread(runnable,"b").start();
    }

    public class MyRunnable implements Runnable{
        int num=0;
        @Override
        public void run() {
            for (int i=0;i<5;i++) {
                ++num;
                System.out.println(Thread.currentThread().getName() + i+"--------"+num);
            }
        }
    }
}

执行结果

b0--------2
b1--------3
a0--------2
b2--------4
a1--------5
b3--------6
a2--------7
b4--------8
a3--------9
a4--------10

可以发现这个全局变量是在两个线程之间共享的

这说明什么?当一个Runnable对象传递到多个线程执行时,Runnable对象的run()方法会在多个线程中执行,而全局变量是在这些线程*享的

因此,我们可以通过一个全局变量充当线程锁比如:

public class Main {

    public static void main(String[] args)  {
        new Main().test();
    }

    void test(){
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable,"a").start();
        new Thread(runnable,"b").start();
    }

    public class MyRunnable implements Runnable{
        int num=0;
        final  String string ="";
        @Override
        public void run() {
            synchronized (string){
                for (int i=0;i<5;i++) {
                    ++num;
                    System.out.println(Thread.currentThread().getName() + i+"--------"+num);
                }
            }
        }
    }
}

这样的执行结果

a0--------1
a1--------2
a2--------3
a3--------4
a4--------5
b0--------6
b1--------7
b2--------8
b3--------9
b4--------10

这样只有当一个线程中的run()方法执行完之后才会执行另一个

然后把同步锁的地方变一下

public class Main {

    public static void main(String[] args) {
        new Main().test();
    }

    void test() {
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable, "a").start();
        new Thread(runnable, "b").start();
    }

    public class MyRunnable implements Runnable {
        int num = 0;
        final String string = "";
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                synchronized (string) {
                    ++num;
                    System.out.println(Thread.currentThread().getName() + i + "--------" + num);
                }
            }
        }
    }
}

执行结果

a0--------1
b0--------2
b1--------3
b2--------4
b3--------5
b4--------6
a1--------7
a2--------8
a3--------9
a4--------10

可以看到,两个线程交替执行,但只有一个线程的

++num;

System.out.println(Thread.currentThread().getName() + i + "--------" + num);

执行完之后才会执行另一个线程的,所以全局变量的值是依次增加的。


再来看Android中的Handler的post(Runnale)这个方法

public class MainActivity extends AppCompatActivity {

    Handler handler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyRunnable myRunnable = new MyRunnable();
        handler.post(myRunnable);
    }

    class MyRunnable implements  Runnable{
        @Override
        public void run() {
            for (int i=0;i<5;++i){
                System.out.println(Thread.currentThread().getName()+"---"+i);
            }
        }
    }
}

android中的Handler和Runnable

可以看到,这个Runnable实在主线程上运行的,因为Handler是在主线程上创建的,与主线程绑定。一些新手可能就会以为这样是新建了一个线程,把耗时操作放里边造成UI卡顿。api说明如下:

Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

那么我们post两个呢,代码修改为

MyRunnable myRunnable = new MyRunnable();
handler.post(myRunnable);
handler.post(myRunnable);

那相应的结果

android中的Handler和Runnable

可以看到是依次运行的,这也体现了Handler的消息队列的思想

再改一下

MyRunnable myRunnable = new MyRunnable();
handler.post(myRunnable);
handler.post(myRunnable);
System.out.println("post finish");

android中的Handler和Runnable

可以看到在post之后并不是立即执行。

那么如何post一个runnable对象到子线程执行呢?这就要将Handler与一个子线程绑定

public class MainActivity extends AppCompatActivity {

    HandlerThread thread = new HandlerThread("subThread");
    Handler handler = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        thread.start();
        handler = new Handler(thread.getLooper());
        MyRunnable myRunnable = new MyRunnable();
        handler.post(myRunnable);
        System.out.println("post finish");
    }

    class MyRunnable implements  Runnable{
        @Override
        public void run() {
            for (int i=0;i<5;++i){
                System.out.println(Thread.currentThread().getName()+"---"+i);
            }
        }
    }
}

执行结果:

android中的Handler和Runnable