2017-11-29 由runnable说起Android中的子线程和主线程
1.首先纠正一个观点,就是runnable运行在子线程中是错误的观念。runnable只是创建了一个执行任务的对象,但是它本身并不会创建一个新的子线程,Runable只是给你接口让你实现工作线程的工作事务,然后附加到你new thread的线程上或post的线程中,其本身并不会创建线程。
几个常识性的概念需要清楚的:
(1)Runnable中的run方法:run()方法在每个线程启动时都会首先执行,启动几个线程就有几个线程去执行这个run()方法。 run()方法是Runnabl接口的抽象方法。实现Runnabl接口就必须实现它的方法,而这个方法就是线程的入口。
(2)实现了Runnable的类只是定义了一个任务,一般还要把实现了Runnable的类的对象传递给一个Thread对象才能开启线程。开启线程的两种方式:
一:
Runnable r=new Runnable(){ public void run() { while(true){ System.out.println("1111"); } } } Thread t = new Thread(r); t.start();
或者下面的形式也可以开启一个线程,也就是重写Thread类的run方法:
二:
class First extends Thread{ public void run(){ while(true){ System.out.println("11111111"); } } } public class Test{ public static void main(String []args){ Thread t=new First(); t.start(); } }
(3)跟进源码一探究竟:
你可以再看一下Thread类的源代码:
private Runnable target; public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { //其它代码 this.target = target; //其它代码 } public void run() { if (target != null) { target.run(); } }
所以使用第一种方式开启线程的时候,target就是构造方法传过来的Runnable对象,就会去执行target的run方法,也就是Runnable对象的任务,如果是第二种方式,则执行已经重写过的run方法里的任务。
再看一下Thread类的start方法:
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } private native void start0();
start0方法用的native关键字,百度了一下,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。
可以再看一下JDK的文档,关于Thread类的start方法的介绍:
public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
所以我觉得start方法里应该就是做了让虚拟机调用该线程的 run 方法,也就是Thread类的run方法,也就执行了run方法里的任务
Thread的run方法是调用Runnable的run方法的。Thread类是implements了Runnable接口;调用的start()方法,其实就是Thread类中的start()方法;代理设计模式。
2.handler.post在主线程中执行,具体代码:
自定义的线程中是不能更新UI的,但是如果遇到更新UI的事情,我们可以用handler的post()方法来将更新UI的方法体,直接传送到主线程中,这样就能直接更新UI了。Handler的post()方法就是将Runnable中的代码段传送到主线程。
public class MainActivity extends Activity { TextView valueTv; public Handler mHandler; private MyThread thread; // 定义一个自己的线程 class MyThread extends Thread { @Override public void run() { System.out.println("线程开始运行"); Runnable r = new Runnable() { @Override public void run() { valueTv.setTextColor(Color.RED); valueTv.setTextSize(30); valueTv.setText("从线程中传过来的代码段"); System.out.println("执行runnable代码的线程:"+Thread.currentThread().getName()); } }; //上面代码中的runnable线程体经过post后会直接传送到主线程中执行修改字体的操作。 //post直接可以把一段代码当做变量一样传递,但是请不要传送耗时操作的代码到主线程中 mHandler.post(r); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); valueTv = (TextView)findViewById(R.id.vale_textView); mHandler = new Handler(); thread = new MyThread(); // 启动线程 thread.start(); } }
方法的官方解释是:
The runnable will be run on the thread to which this handler is attached.
既是说,这个开启的runnable会在这个handler所依附线程中运行,而这个handler是在UI线程中创建的,所以
自然地依附在主线程中了。
postDelayed(new Runnable()) 而没有重新生成新的 New Thread()
3.View.post(Runnalbe)方法,在post(Runanble action)方法中,View获得当前主线程(即UI线程)的handler,然后将action对象post到handler里面去,在Handler里,它将传递过来的action对象封装成一个Message(Message 的callback为action),然后将其投入到UI线程的消息循环中,在handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法,而此时,已经路由到UI线程里,因此我们可以毫无顾虑来更新UI。
4.主要参考如下博客:
http://blog.sina.com.cn/s/blog_77c6324101016jp8.html
https://www.cnblogs.com/huaqing-wkc/p/4940190.html
常见的新建线程的方法是:
Thread thread = new Thread();
thread.start();
HandlerThread thread = new HandlerThread("string");
thread.start();