Android中线程和线程之间的通信问题(网络请求数据后)
前提引入
关于进程和线程
运行中的程序称作进程,它是动态的。
它有并发性,也就是通常情况下,我们操作系统中都会运行多个进程(跑很多个程序),那么这些进程都是并发执行的,一般采用分时操作,即每个进程分时获取到时间片,在该时间段内可以获得CPU的资源来执行,但通常情况下时间片的大小是很短的,所以给人的感觉就是每个进程都很流畅地运行。
(因为关于进程和线程的一些具体知识不是这篇的重点,所以可以参考一些操作系统的教材,这里就只做简单概述)
操作系统中进程的三种基本状态:就绪态、执行态、阻塞态
线程的生命周期:创建、就绪、执行、(阻塞)、死亡
创建(new Thread):
Thread thread = new Thread();
此时线程被创建,但并未进入就绪队列等待执行。
就绪(runnable):
thread.start();
此时线程已被启动,处于就绪状态,但并未得到CPU资源。正在等待分配到的时间片获得CPU资源。线程只有获得了CPU资源才会执行里面的代码。也就是说现在该线程正在一个就绪队列里等着,等CPU资源。
执行(running):
线程得到CPU资源了,那么它就开始执行了,此时该线程处在执行状态。除非时间片耗尽、或者有更高优先级的线程进入抢占该线程的CPU资源、或者该线程被阻塞,否则该线程会一直执行直到线程中编写的代码执行完毕后结束。
注: 说明一下,时间片耗尽一般感觉不到。因为非常短。人为上感觉是一直在执行的,像是独享CPU资源的,但实际上一般情况下就绪队列里会有好多个线程在排队等着要CPU资源,那么CPU通过分时操作目的是为了让每个在就绪队列里的线程都有机会得到执行。但是人是感觉不到的,因为时间片非常短。
死亡(dead):
进程执行完毕或者被杀死时,线程就进入了死亡状态。等待CPU回收资源。
自然终止:线程正常执行完所有代码退出,结束。
异常终止:调用stop()方法让一个线程终止
阻塞(blocked):
由于某种原因(耗时操作、网络请求数据等)导致让正在运行的线程让出自己的资源暂缓执行,此时该线程进入阻塞状态。
比如:sleep()睡眠、readline()(在读不到数据的时候会阻塞该线程)
注:一个阻塞状态下的线程要想重新获得CPU资源执行代码,必须要先被唤醒,进入阻塞态后,再等待CPU分配时间片给它获得CPU资源后才能进入执行态。
本文重点
关于Android的线程和线程之间的通信
Android的UI线程是一个Android程序的主线程。也就是说这带来一个问题。UI线程是不能够被阻塞或者长时间不运行的(比如进行网络请求数据、等待什么操作执行完毕后等等,一般情况下认为网络请求是比较耗时的)。因为如果在UI线程(主线程)中长时间不运行或者被阻塞的话,会造成UI视图界面被卡死。
那么这个时候如果想在UI线程(主线程)中等待网络请求后的数据然后对UI视图进行操作、显示等等该怎么办呢?
新建线程 new Thread();
新建线程,让网络请求等耗时操作在该线程(非主线程)中执行。这样就避免了UI界面卡死的情况。在网络请求得到数据后再通知UI线程(主线程),再在UI线程(主线程)中更新视图、添加数据源等等操作。(P.S.好像说UI线程是循环往复一直执行的,所以也再次证明它不能被阻塞或者长时间不运行)。
那么如果在其他线程中得到数据后去通知UI线程(主线程)并把数据传递过去让UI线程(主线程)去更新视图呢?
Android中Handler机制可以做到这一点
Handler机制:
- 在UI线程(主线程)中创建Handler对象(即该Handler对象绑定了主线程)
- 在UI(主线程)中new Thread(创建别的线程)让他们做一些耗时操作(比如得到一些后台的数据等等),然后再这些线程运行结束或者拿到数据后,通过调用主线程中Handler对象的sendMessage()方法或者sendEmptyMessage()方法给主线程发送“消息”(Message)并把你得到的数据传递过去。
- 在Handler对象中的handleMessage()方法中接受到消息和数据,进行相应的UI操作。
例子
在UI线程(主线程)中开启两个线程,模拟耗时操作(此时可以去后端访问获取数据),在等待两个耗时操作都完成后即可更新UI、设置数据源等操作。
public TestActivity extends Activity{
//一些视图控件的声明
private Thread thread1;//模拟一个耗时操作的线程
private Thread thread2;//模拟另一个耗时操作的线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//一些视图控件的声明及事件绑定
//这里就是UI线程(主线程)
//创建线程,以实现耗时操作
thread1 = new Thread(new Runnable() {
@Override
public void run() {
Log.e("Thread1","这是线程一,将耗时5秒");
try {
Thread.sleep(5000);
MyHandler.sendEmptyMessage(0);//调用UI线程(主线程)中的Handler对象的sendEmptyMessage方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread2 = new Thread(new Runnable() {
@Override
public void run() {
Log.e("Thread2","这是线程二,将耗时3秒");
try {
Thread.sleep(3000);
MyHandler.sendEmptyMessage(1); //调用UI线程(主线程)中的Handler对象的sendEmptyMessage方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
}
//在UI线程(主线程)中创建Handler对象MyHandler(继承自Handler)
private Handler MyHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
Log.e(TAG,"收到Thread1的返回");
if (thread2.isAlive()){
Log.e("Thread1","Thread2还活着");
}else{
Log.e("Thread1","Thread2死了");
}
//可进行UI操作
break;
case 1:
Log.e(TAG,"收到Thread2的返回");
if (thread1.isAlive()){
Log.e("Thread2","Thread1还活着");
}else{
Log.e("Thread2","Thread1死了");
}
//可进行UI操作
break;
}
}
};
}
总结
首先讲了进程和线程的一些基本定义和知识及其基本状态,具体进程和线程的关系这里不做细讲。然后这篇文章的重点就是要讲的是Android中线程之间的通信问题。最重要的一点就是UI线程(主线程)是不能被阻塞或长时间不执行任何操作的。然后Android中的Handler机制可以满足Android中线程之间的通信问题。通过传递Message对象可以把数据给带过去。