1.在java中,多线程一般有两种方式。简言之:一个是继承Thread类,另一个是实现runnable接口。他们之间的区别主要是在于,对于同时开启的多个对象去启动多线程的时候,继承Thread的各个对象之间不能实现数据的共享,而runnable可以。最经典的例子就是买票系统的实现。大家可以百度代码。
android 的多线程实际上就是java的多线程。android的UI线程又称为主线程。
Thread 和 Runnable:
Thread是一个线程,而Runnable可以理解为一个任务。这个任务只是一个接口。具体的任务执行是在 run()方法执行。
Thread thread = new Thread(Runnable);
把一个Runnable任务放到线程里面,当调用thread.start() 的时候,系统新开一个线程去执行,这个runnable任务是在多线程执行的。是在新开的线程执行的。
如果直接 new Runnable().run();那么实际上是直接在UI线程执行了这个任务没有进行一个多线程的操作。
总结:runnable()只是一个任务的抽象,并不是多线程。Thread.start()。才是新开一个多线程。并且在新开的线程执行Thread你们的run()方法。
2.继承和实现都需要重写里面的run方法,该方法就是你子线程运行的逻辑方法,同时new出来的对象也需要运行.start方法。特别要注意的是,实现runnable接口的new 出来的对象之后,其实此时根本没有产生一个多线程,我们仍然需要new Thread(new runnable)对象,然后将实现runnable接口对象作为Thread类一个构造方法的参数传进去。
2.1复制别人的代码。继承Thread类
<span style="font-size:18px;">package org.thread.demo;
class MyThread extends Thread{
private String name;
public MyThread(String name) {
super();
this.name = name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println("线程开始:"+this.name+",i="+i);
}
}
}
/* package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
mt1.run();
mt2.run();
}
} *///但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。还是单线程此时
//在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。下面启动start()方法启动线程:
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
mt1.start();
mt2.start();
}
}; //这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?
//在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,
</span>
2.2实现runnable接口
<span style="font-size:18px;">public interface Runnable{
public void run();
}
例子:
package org.runnable.demo;
class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this.name = name;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println("线程开始:"+this.name+",i="+i);
}
}
}; </span>
但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):
<span style="font-size:18px;">package org.runnable.demo;
import org.runnable.demo.MyThread;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
new Thread(mt1).start();
new Thread(mt2).start();
}
} </span>
此时thread对象没有被实现,不要和第一种方式中thread之类搞糊涂了。比较推荐使用后一种方法。
3.handler(和线程,Loop一一对应)。
Handler中分发消息的一些方法
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
post类方法允许你排列一个Runnable对象到主线程队列中
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
3.1handler是android中不同线程之间传递消息的机制。最常用的是用来更新系统的UI,因为UI操作只能在Main线程中执行,所以我们一旦在进行比如联网下载比较耗时的操作时,如果是在主线程中,就会报NRA错误,这对用户体验十分不友好,所以,此时如果我们有个在main线程中的handler对象,然后在子线程中进行耗时操作,结束后通过hangdler通知,再在main中更新就可以了。下面我们从handler的各种方法入手
send:(子线程向绑定handler中的main线程发消息)
首先new handler()对象,重写其handleMessager方法,该handler对象默认是在main中
3.1.1 handler.sendEmptyMessage(int what);该方法是发送空消息,用int型what参数区分不同的消息,同理,有其延迟方法 handler.sendEmptyMessageDelayed(what, delayMillis)。
3.1.2 handler.sendMessage(msg);发送消息msg,msg可以传递任意类型对象,同时其也有延迟方法方法。
<span style="font-size:18px;">public class ChangeTextActivity extends Activity {
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
num++;
tv.setText(num+"");
}
}
};
TextView tv;
int num=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.textView1);
new Thread() {
public void run() {
handler.sendEmptyMessage(1);
sleep(1000);}
}.start();
}
}
</span>
post:
3.2.1
public class QuesPostActivity extends Activity {
ImageView iv;
/*
* 通过post方法指定要执行的动作
* 如果是通过post方法指定要执行的动作,那么
* Handler对象不需要再重写handleMessage方法
* */
Handler handler = new Handler();
Runnable runnable ;
Random ran;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView)findViewById(R.id.imageView1);
ran = new Random();
runnable = new Runnable() {
@Override
public void run() {
int red = ran.nextInt(256);
int green = ran.nextInt(256);
int blue = ran.nextInt(256);
iv.setBackgroundColor(Color.rgb(red, green, blue));
//指定在500毫秒之后,运行当前类对象的run方法
handler.postDelayed(this, 500);
}
};
/*
* 一旦运行到post方法,那么马上执行该参数中的Runnable对象的run方法
* 并且注意:此处的Runnable对象中的run方法内可以调用改变UI的代码
* */
handler.post(runnable);
}
public void click (View v) {
//点击按钮停止颜色的切换
/*
* 停止参数指定的runnable对象的run方法的运行
* */
handler.removeCallbacks(runnable);
}
}
该代码和注释比较完整的说明了post的一个用法,首先,post中的关键参数就是一个实现了runnable的子类对象,这里是内部类来实现的,在该之类中run方法中写上逻辑,为什么此时的run方法可以更新UI呢,其实runna接口在没有Thread启动时,
还是在当前
线程也即main中的,所以这里最好不要进行一些耗时的操作。同时其也有延时方法,以及去runnable对象方法。 可以看到并没有多线程参与。
具体来说,这个函数的工作原理如下:
View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。
3.3HandlerThread(是用来产生子线程的,thread子类)
该类可以用来实现main线程中向子线程发送消息,此时最重要的就是在子线程中有一个handler对象以及loop对象(mian中的loop对象自己生成,不需要get)
步骤:
-
创建一个
HandlerThread
,即创建了一个包含Looper的线程。HandlerThread handlerThread = new HandlerThread("leochin.com");
handlerThread.start(); //创建HandlerThread后一定要记得start()
-
获取
HandlerThread
的LooperLooper looper = handlerThread.getLooper();//获得该子线程的loop对象。
-
创建Handler,通过Looper初始化
Handler handler = new Handler(looper);//将该子线程中的loop对象绑定到handler中,此时的handler就是该子线程的handler,可以在main中调用发送消息。
通过以上三步我们就成功创建HandlerThread
。通过handler发送消息,就会在子线程中执行。
如果想让HandlerThread
退出,则需要调用handlerThread.quit();
。
此时可以实现不同线程之间进行消息的传递了。
4.下图为handler在不同线程间消息传递的机制。
4.1首先明确,handler和loop需要有自己对应的线程,如下图,为main线程中
4.2其他线程通过main的handler对象将消息封装并存入main的MessgerQuuen中,通过main的loop对象来进行管理
4.3main的handler通过handleMeeager将消息取出在main中处理
一下代码可以加深对Loop的理解
<span style="font-size:18px;">new Thread() {
public void run() {
Looper.prepare();//在该子线程中生成其对应的loop对象
handler = new Handler(){
public void handleMessage(android.os.Message msg) {
Log.i("=====", "=======子线程1"+Thread.currentThread().getName());
}
};
};
Looper.loop();//才能将消息循环起来其作用
};
}.start();</span>
很多理解都是自己的理解,希望对大家的理解有帮助。
相关链接:http://blog.chinaunix.net/zt/1026/androidhandlerrunnablehand_1026281.shtml