Android Thread线程
相信大家对线程肯定不陌生,我们在编程的过程中经常要用到线程,Android提供了多线程支持,在Android程序中,VM采用抢占式调度模式对线程进行调度(基于线程优先级来决定CPU的使用权),android一个重要的机制就是线程+消息。
一,线程进程
既然说到线程,得先说说进程。一般来说一个android程序对于一个进程(组件元素activity、service、receiver、provider。都有一个process属性可以指定组件运行在哪个进程中,你可以在清单文件中manifest设置组件的进程)所有的组件都在特定进程的主线程中实例化。
线程:线程是属于进程的。同一个进程下的可以有多个线程。这些线程是共同享该进程占有的资源和地址空间的。线程是进程的一部分,一个没有线程的进程可以看做是单线程的。对于android应用来说,当一个应用启动的时候,系统会默认为它创建一个线程,称为“主线程”(UI线程)。这个线程很重要因为它负责处理调度事件到相关的 user interface widgets,包括绘制事件。你的应用也是在这个线程里面与来自Android UI toolkit (包括来自 android.widget 和 android.view 包的组件)的组件进行交互。因此,这个主线程有时候也被称为 UI 线程。
进程线程总结:
- 进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能。
- 进程是操作系统进行资源分配的基本单位。
- 线程是操作系统进行调度的基本单。
- 线程的划分尺度小于进程,线程隶属于某个进程。
- 进程是程序的一种动态形式,是CPU、内存等资源占用的基本单位,而线程是不能独立的占有这些资源的。
- 进程之间相互独立,通信比较困难,而线程之间共享一块内存区域,通信比较方便。
- 进程在执行过程中,包含比较固定的入口、执行顺序和出口,而线程的这些过程会被应用程序所控制。
二,线程的实现(两种方式)
两种方法,继承Thread类和实现Runnable接口
继承Thread类
extends Thread 重写run方法做我们想做的事情,start启动线程。 简单代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new CustomerThread("CustomerThread").start();
}
public class CustomerThread extends Thread {
public CustomerThread(String threadName) {
super(threadName);
}
@Override
public void run() {
/** do our things */
}
}
实现Runnable接口:run方法里面做我们要做的事情。简单代码如下
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SyncRunnable syncRunnable = new SyncRunnable();
Thread thread = new Thread(syncRunnable, "syncRunnable");
thread.start();
}
public class SyncRunnable implements Runnable {
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronizedloop " + i);
}
}
}
}
注:这两种方法的实现中我都有带上线程的名字。顺便说一点,我们用线程的时候最好给线程加上一个名字,这样我们在看log信息的时候能方便的知道是哪个线程打(默认会thread-0, thread-1什么的)。
三,线程退出
我们在使用线程的时候经常会碰到这种情况,我们的线程是要一直while执行的(如果不是要一直while执行的thread执行完了就会自动销毁)。这个时候当程序退出的时候我们想要这个while线程也去释放资源。主要有三种方式。
使用退出标志,使线程正常退出
这个最简单了设置标志位。简单代码如下:private boolean exit = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SyncThread syncRunnable = new SyncThread("syncThread");
syncRunnable.start();
}
@Override
protected void onDestroy() {
exit = true;
super.onDestroy();
}
public class SyncThread extends Thread {
public SyncThread(String threadName) {
super(threadName);
}
@Override
public void run() {
while(!exit) {
/** do our things */
}
}
}
在onDestroy方法里面退出。exit = true;
使用interrupt()方法中断线程 (当调用线程的interrupt()方法时,系统会抛出一个InterruptedException异常)
1)先捕获InterruptedException异常之后通过break来跳出循环,简单代码如下。SyncThread syncRunnable = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
syncRunnable = new SyncThread("syncThread");
syncRunnable.start();
}
@Override
protected void onDestroy() {
if (null != syncRunnable) {
syncRunnable.interrupt();
}
super.onDestroy();
}
public class SyncThread extends Thread {
public SyncThread(String threadName) {
super(threadName);
}
@Override
public void run() {
while(true) {
/** do our things */
try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
/** exit the while */
break;
}
}
}
}
2)使用isInterrupted()判断线程的中断标志来退出循环。简单代码如下
SyncThread syncRunnable = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
syncRunnable = new SyncThread("syncThread");
syncRunnable.start();
}
@Override
protected void onDestroy() {
if (null != syncRunnable) {
syncRunnable.interrupt();
}
super.onDestroy();
}
public class SyncThread extends Thread {
public SyncThread(String threadName) {
super(threadName);
}
@Override
public void run() {
while(!isInterrupted()) {
/** do our things */
}
}
}
3)两者结合使用(推荐) 在线程未进入阻塞的代码段时通过isInterrupted()判断中断来退出循环,在进入阻塞状态后通过通过捕获中断异常来退出循环,简单代码如下
SyncThread syncRunnable = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
syncRunnable = new SyncThread("syncThread");
syncRunnable.start();
}
@Override
protected void onDestroy() {
if (null != syncRunnable) {
syncRunnable.interrupt();
}
super.onDestroy();
}
public class SyncThread extends Thread {
public SyncThread(String threadName) {
super(threadName);
}
@Override
public void run() {
while(!isInterrupted()) {
/** do our things */
try{
Thread.sleep(5*1000);
}catch(InterruptedException e){
e.printStackTrace();
break;
}
}
}
}
使用stop方法强行终止线程(不推荐使用,可能发生不可预料的结果)
thread.stop() 不安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。
四,多线程通信
管道(pipe)属于linux范畴:一条“管道”为两个线程建立一个单向的通道。生产者负责写数据,消费者负责读取数据。简单代码如下
private PipedReader mRead;
private PipedWriter mWrite;
private Thread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
try {
mWrite.write("pipe");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mThread.interrupt();
try {
mRead.close();
mWrite.close();
} catch (IOException e) {
}
}
private void initData() {
mRead = new PipedReader();
mWrite = new PipedWriter();
try {
mWrite.connect(mRead);
} catch (IOException e) {
e.printStackTrace();
}
mThread = new Thread(new ThreadPipe(mRead), "ThreadPipe");
mThread.start();
}
private static class ThreadPipe implements Runnable {
private final PipedReader reader;
public ThreadPipe(PipedReader reader){
this.reader = reader;
}
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
try {
int i;
while((i = reader.read()) != -1){
char c = (char) i;
Log.d("vae_tag", "char = " + c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
先把PipedReader,PipedWriter关联起来。如mWrite.connect(mRead);然后这里是在UI线程里面写,ThreadPipe 线程里面读log出来。
- 共享内存:一个变量可以同时被多个线程所访问。这里要特别注意同步和原子操作的问题,这也共享内存容易出错的原因所在,容易出现死锁等。
- socket:这个应该简单。
- 消息队列(Hander和Message):这个也是android常用的线程间通信方式,网上也有好多关于这个的博文。主要要理清Thread, Handler,Message,Looper,MessageQueue 之间的关系。
* Looper:(相当于隧道) 一个线程可以产生一个Looper 对象,Looper负责的就是创建管理一个MessageQueue。
* MessageQueue(消息队列):里面放的就是Message对象,每一个线程最多只可以拥有一个MessageQueue,通常使用一个Looper对象对该线程的MessageQueue进行管理,每一个MessageQueue都不能脱离Looper而存在。主线程创建时会默认创建MessageQueue,其他的线程不会默认创建。
* Message(消息):被放入在MessageQueue中。
* Handler(消息的处理者):Handler负责将需要传递的信息封装成Message,通过调用Handler对象的obtainMessage()来实现,将消息传递给Looper,这是通过Handler对象的sendMessage()来实现的。继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该Handler对象收到该消息后,调用相应的Handler对象的handleMessage()方法对其进行处理。简单来说就是当要发送Message消息的时候Handler告诉对应Thread的Looper有消息到来,Looper把Message放入到MessageQueue当中去。当MessageQueue中有Message的时候该Thread的Looper告诉对应的Thread的Handler来处理消息。
几个简单例子代码。
1)UI线程给自己发送消息。
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new HandlerTest(getMainLooper());
Message msg = handler.obtainMessage(1, 1, 1, "UI thread send message");
handler.sendMessage(msg);
}
class HandlerTest extends Handler {
public HandlerTest(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("vae_tag", (String)msg.obj);
}
}
2)UI线程给其他线程发送消息。
private Button mButton;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.send_message_id);
new ChildThread("ChildThread").start();
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.obtainMessage(1, "UI thread send to child thread").sendToTarget();
}
});
}
class ChildThread extends Thread {
public ChildThread(String threadName) {
super(threadName);
}
public void run() {
Looper.prepare();/** init Looper */
handler = new ThreadHandler(Looper.myLooper());
Looper.loop(); /** start Looper */
}
class ThreadHandler extends Handler {
public ThreadHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
Log.d("vae_tag", "child thread receive the message :" + msg.obj);
}
}
}
3)其他线程给UI线程发送消息。
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new ChildThread("ChildThread").start();
}
class HandlerTest extends Handler {
public HandlerTest(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("vae_tag", (String) msg.obj);
}
}
class ChildThread extends Thread {
public ChildThread(String threadName) {
super(threadName);
}
public void run() {
handler = new HandlerTest(Looper.getMainLooper());
Message msg = handler.obtainMessage(1, 1, 1, "child thread send the message");
handler.sendMessage(msg);
}
}
4)其他线程给其他线程发
private Handler handler;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.send_message_id);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ChildThread2("ChildThread2").start();
}
});
new ChildThread1("ChildThread1").start();
}
class ChildThread1 extends Thread {
public ChildThread1(String threadName) {
super(threadName);
}
public void run() {
Looper.prepare();/** init Looper */
handler = new ThreadHandler(Looper.myLooper());
Looper.loop(); /** start Looper */
}
class ThreadHandler extends Handler {
public ThreadHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
Log.d("vae_tag", "receive message: " + msg.obj);
}
}
}
class ChildThread2 extends Thread {
public ChildThread2(String threadName) {
super(threadName);
}
public void run() {
Message msg = handler.obtainMessage(1, 1, 1, "child thread send the message");
handler.sendMessage(msg);
}
}
代码都是非常简单的代码,主要抓住Handler往哪个线程发就绑定到对应线程的Looper。
注:关于Message对象,虽然我们可以自己创建一个新的Message,但是更加推荐的是调用handler的obtainMessage方法来获取一个Message对象。这个方法的作用是从系统的消息池中取出一个Message,这样就可以避免Message创建和销毁带来的资源浪费了
五,子线程更新UI的几种方式
- handler:上面讲过。
Activity.runOnUIThread(Runnable):简单代码实现。
private Button mButton;
private TextView mTextView;
private Activity mActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActivity = this;
mTextView = (TextView) findViewById(R.id.text_view_id);
mButton = (Button) findViewById(R.id.send_message_id);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
public void run() {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText("click ite");
}
});
};
}.start();
}
});
}
View.Post(Runnable)或者View.PostDelayed(Runnabe,long)或者handler.post(new Runnable())三个是一个意思实际上是一样的View.Post(Runnable)源码里面会先获取到当前线程的handler,然后再调用post方法(ViewRootImpl.getRunQueue().post(action);):简单代码如下:
private Button mButton;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text_view_id);
mButton = (Button) findViewById(R.id.send_message_id);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ChileThread(mButton).start();
}
});
}
public class ChileThread extends Thread {
private Button mButton;
public ChileThread(Button textView) {
mButton = textView;
}
@Override
public void run() {
mButton.post(new Runnable() {
@Override
public void run() {
mTextView.setText("click ite");
}
});
}
}
在这里不管是mButton.post() 还是mTextView.post()都是可以的只要是继承View的对象就行。
- AsyncTask:异步任务,主要是四个重要的方法。
- onPreExecute:运行在UI线程,主要目的是为后台线程的运行做准备。当他运行完成后,他会调用doInBackground方法。
- doInBackground:运行在后台线程,他用来负责运行任务。他拥有参数Params,并且返回Result。在后台线程的运行当中,为了能够更新作业完成的进度,需要在doInbackground方法中调用PublishProgress方法。该方法拥有参数Progress。通过该方法可以更新Progress的数据。然后当调用完PublishProgress方法,他会调用onProgressUpdate方法用于更新进度。
- onProgressUpdate:运行在UI线程,主要目的是用来更新UI线程中显示进度的UI控件。他拥有Progress参数。在doInBackground中调用PublishProgress之后,就会自动调onProgressUpdate方法。
- onPostExecute:运行在UI线程,当doInBackground方法运行完后,他会调用onPostExecute方法,并传入Result。在onPostExecute方法中,就可以将Result更新到UI控件上。
六,HandlerThread
我们都知道Android中Handler的使用,一般都在UI主线程中执行,因此在Handler接收消息后,处理消息时,不能做一些很耗时的操作,否则将出现ANR错误。Android中专门提供了HandlerThread类,来解决该类问题。HandlerThread类是一个线程专门处理Hanlder的消息HandlerThread用于方便的创建一个含有Looper的线程类。Looper用来创建Handler类,实际上就一个Thread,只不过它比普通的Thread多了一个Looper。一般HandlerThread和Handler类配合使用, Handler将消息发往HandlerThread的消息队列, Handler处理消息。这里启动的是一个新线程 虽然不能直接操作UI 但可以通过Message发送消息来进行操作,其实就是把Handler里面要做的比较耗时的操作放到HandlerThread里面去做,如果有必要的话HandlerThread处理完之后可以再发回给UI线程。
使用HandlerThread的好处:
- 开发中如果多次使用类似new Thread(){…}.start()这种方式开启一个子线程,会创建多个匿名线程,使得程序运行起来越来越慢,而HandlerThread自带Looper使他可以通过消息来多次重复使用当前线程,节省开支。
android系统提供的Handler类内部的Looper默认绑定的是UI线程的消息队列,对于非UI线程又想使用消息机制,那么HandlerThread内部的Looper是最合适的,它不会干扰或阻塞UI线程。
简单的实例代码如下:public class MainActivity extends ActionBarActivity {
private Handler mHandler;
private MyHandlerThread mHandlerThread;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandlerThread = new MyHandlerThread("MyHandlerThread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mHandlerThread);
mButton = (Button) findViewById(R.id.send_message_id);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendEmptyMessage(1);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
}
public class MyHandlerThread extends HandlerThread implements Handler.Callback {
public MyHandlerThread(String name) {
super(name);
}
@Override
public boolean handleMessage(Message msg) {
Log.d("vae_tag", "get message");
return true;
}
}
}
七,线程池
对应数据库连接我们有数据库连接池,对于线程我们也有线程池。线程池是预先创建线程的一种技术。线程池在任务还没到来之前先创建一定数量的线程,放入空闲队列中。这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。从而达到减少了创建和销毁线程的次数,最大程度的复用对象。
包括三种单一线程池,固定线程池,缓存的线程池使用其他也非常的简单。
下面是固定线程池的简单实例,很简单的例子。
private Button mButton;
private ExecutorService mExecutorService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/** create three threads in the pool */
mExecutorService = Executors.newFixedThreadPool(3);
mExecutorService.submit(new ChileThread("ChileThread"));
mButton = (Button) findViewById(R.id.send_message_id);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mExecutorService.submit(new ChileThread("ChileThread"));
}
});
}
public class ChileThread extends Thread {
public ChileThread(String threadName) {
super(threadName);
}
@Override
public void run() {
Log.d("vae_tag", "aaaaaaaaaaaaaaaaa");
}
}
缓存的线程池 线程池的大小会根据执行的任务数动态分配
mExecutorService = Executors.newCachedThreadPool();
单一线程池
mExecutorService = Executors.newSingleThreadExecutor();
八,Callable,Future
Callable 源码
public interface Callable<V> {
/** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */
V call() throws Exception;
}
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务。Callable是一个泛型接口。call()函数返回的类型就是传递进来的V类型,Callable要配合ExecutorService来使用,采用ExecutorService的submit方法提交。
Callable 和 Runnable方法的区别:
1)Callable定义的方法是call,而Runnable定义的方法是run。
2)Callable的call方法可以有返回值,而Runnable的run方法不能有返回值。
3)Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常。
Future表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。Future的cancel方法可以取消任务的执行,它有一布尔参数,参数为 true 表示立即中断任务的执行,参数为 false 表示允许正在运行的任务运行完成。Future的 get 方法等待计算完成,获取计算结果。简单的实例代码如下。
单个任务:
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
signalTaskTest();
Log.d("vae_tag", "after signalTaskTest function");
}
private void signalTaskTest() {
Task.callInBackground(new Callable<Void>() {
@Override
public Void call() throws Exception {
/** start test */
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new Callable<String>() {
public String call() throws Exception {
Thread.sleep(2000);
return "hello";
}
});
Log.d("vae_tag", "start waiting result");
try {
Log.d("vae_tag", "get the result " + future.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
});
}
}
注:我是在一个线程里面测试 future.get()会等待获取结果(这里有用到Task.callInBackground 在gradle用引入compile ‘com.parse.bolts1.2.0’)
多个任务:
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
multipleTasksTest();
}
private void multipleTasksTest() {
Task.callInBackground(new Callable<Void>() {
@Override
public Void call() throws Exception {
/** start test */
ExecutorService threadPool2 = Executors.newFixedThreadPool(10);
CompletionService<Integer> compeletionService = new ExecutorCompletionService<Integer>(
threadPool2);
for (int i = 0; i <= 10; i++) {
final int seq = i;
compeletionService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(new Random().nextInt(5000));
return seq;
}
});
}
for (int i = 0; i < 10; i++) {
try {
Log.d("vae_tag", "result = " + compeletionService.take().get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
});
}
}
配合CompletionService使用。
九,推荐(EventBus 一个可以用于通信的开源库)
github项目地址 https://github.com/greenrobot/EventBus
这个也是我们在应用中经常用到的用于通信的一个类库。特别是在fragment 和fragment,fragment和activity等等之间通信非常的方便,用起来也非常的简单具体的可以去google下。
还没有评论,来说两句吧...