这是由一个故事引起的.
故事主人公发了一个帖子:
求解决android.os.NetworkOnMainThreadException
http://www.eoeandroid.com/thread-325472-1-1.html
这位楼主找到这个问题,然后明白在新版本里不应该在主线程里面做网络操作.
于是楼主找到了这篇ice的blog:
解决android.os.NetworkOnMainThreadException
http://my.eoe.cn/iceskysl/archive/4382.html
于是楼主的问题来了,"我明明根据这篇帖子提示的用runnable来做,怎么还有问题?"
我看了下,ice的代码是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.share_mblog_view);
new Thread(runnable).start();
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle data = msg.getData();
String val = data.getString("value");
Log.i("mylog","请求结果-->" + val);
}
}
Runnable runnable = new Runnable(){
@Override
public void run() {
//
// TODO: http request.
//
Message msg = new Message();
Bundle data = new Bundle();
data.putString("value","请求结果");
msg.setData(data);
handler.sendMessage(msg);
}
}
|
楼主的代码是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
hdnet = new Handler();
rbnet = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
int idnum = get_maxid();
String recv_msg = "" + idnum;
enet = readContentFromPost(recv_msg, RECV_TYPE);
} catch (Exception e) {
e.printStackTrace();
}
}
};
hdnet.post(rbnet);
|
先不说这个代码的问题,继续看回答.
一楼告诉楼主应该:new Thread(rbnet).start();这样
楼主反问道:我的的runnable可以启动。还需要用thread?
这里就回到我要说的主题了:
谁告诉你Runnable就是要开线程跑了?
或许很多人在学多线程的时候,老师就会跟他说:Java中创建多线程有两种方式,第一种是继承Thread类,第二种是实现Runnable接口.
前者是MyThread().start();来启动新线程,后者是new Thread(myRunnable).start();来启动新线程.
久而久之,大家接触到更多的线程运行方法就是后者,然后
理所当然地认为:Runnable就代表线程,没有别的用途.
错,很错,大错特错!
很多人根本没有想过,Runnable到底是个什么?
其实
它只是一个简单的接口,只定义了一个run方法,仅此而已,源码很简单:
1 2 3 |
public interface Runnable {
public abstract void run();
}
|
这跟我们平时定义的各种回调方法一个样,比如:
1 2 3 |
public interface MyCallback {
public abstract void onComplete();
}
|
光从Java语言的角度看Runnable,这只是一个再简单不过了的一个很小的interface.
那,为什么Runnable多用于线程方面?
这是
因为Java不支持多重继承,很多时候你的一个class早就继承了一个父类,无法再继承Thread类,所以**大都用实现Runnable接口来做的**(实际上,Thread也是实现了Runnable接口),一个线程start执行起来,他会去找到对应的run方法然后执行它的代码.
但是,线程会执行这个Runnable中的run方法,并不是说:这个Runnable中的run方法,只会在多线程中执行!记住,它只是一个接口方法,仅此而已.难道说,
喝水都用杯子,杯子就只能用来喝水了?特殊情况下用杯子装饭吃,你还要说现在杯子里面是水么?
所以说,Runnable只是一个恰好被Thread用到,大家都用它来跑多线程的,并不能想当然的以为,它出现就必须是多线程
下面是一个例子(不推荐):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class Test {
private Runnable mRunnable;
Test(Runnable runnable){
this.mRunnable = runnable;
}
public void say(){
mRunnable.run();
}
public static void main(String args[]){
System.out.println("Main thread,pid is:"+Thread.currentThread().getId());
new Test(new Runnable(){
@Override
public void run() {
System.out.println("I am running in the main thread,not other threads,thread id is:"+Thread.currentThread().getId());
}
}).say();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I am running in a new thread,not other main thread,thread id is:"+Thread.currentThread().getId());
}
}).start();
}
}
|
我这里故意用到这个Runnable接口去做一个简单的输出,对比一个Thread用这个Runnable做多线程输出
运行结果:
1 2 3 |
Main thread,pid is:1
I am running in the main thread,not other threads,thread id is:1
I am running in a new thread,not the main thread,thread id is:8
|
很显然,并不是所有在Runnable中的run方法中跑的代码都是开了线程跑的,开不开线程,看的是这个Runnable有没有被一个Thread对象构造使用
其实,
回归到Android,为什么说Handler能刷新主线程UI?就算你是在其他线程post的一个runnable,最终还是在主线程中执行呢?
这是因为这个runnable,你只能当他是一个简单的对象而已,只不过是带着一个run方法的对象,当别人需要执行你的逻辑的时候再去调用你的run方法,即可.而***不是所谓的把子线程并到主线程中执行***(的确有听过人这么说的).
Handler一直在main thread中执行,有一个不断循环的Looper,不断检索一个容器MessageQueue,这个容器,一旦有Message进来就去执行这个Message,如果这个Message有callback(一个Runnable,如果是handler.post(runnable)的话, 会生成一个Message对象,这个对象的callback参数被赋值成这个runnable对象),那么也去执行这个callback(也就是runnable对象)的run方法(这个就很类似我上面例子中的Test类中的Say方法了,仅仅只是调用这个run方法而已,不是去开线程).
直接看代码更明确一点:
post一个runnable
1 2 3 4 |
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
|
实际上是生成一个Message,这个Message对象的callback参数赋值为这个runnable对象
1 2 3 4 5 |
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
|
Looper中有一个无限循环,MessageQueue中一旦有新的消息,就去调用这个消息的dispatchMessage方法:
1 2 3 4 5 6 7 8 |
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
//余下很多不用写,只看这个关键代码
|
看看dispatchMessage方法,因为之前这个Message的callback实际上就是post过来的runnable对象,所以会执行到handleCallback(msg)
1 2 3 4 5 6 7 8 9 10 11 12 |
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
|
最后:
1 2 3 |
private static void handleCallback(Message message) {
message.callback.run();
}
|
很显然,这个,
仅仅只是执行了这个callback(post过来的runnable对象)的run方法,没有什么多线程,所以还是在主线程中运行的.
不知道为什么Android要这么设计,导致很多人根本就搞不懂了为什么Runnable不代表用多线程来跑,但是,既然这么设计了,那就得深入的弄懂他,不要被迷惑了.
声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息
原文作者: fortianwei