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 线程。
进程线程总结:
1. 进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能。
2. 进程是操作系统进行资源分配的基本单位。
3. 线程是操作系统进行调度的基本单。
4. 线程的划分尺度小于进程,线程隶属于某个进程。
5. 进程是程序的一种动态形式,是CPU、内存等资源占用的基本单位,而线程是不能独立的占有这些资源的。
6. 进程之间相互独立,通信比较困难,而线程之间共享一块内存区域,通信比较方便。
7. 进程在执行过程中,包含比较固定的入口、执行顺序和出口,而线程的这些过程会被应用程序所控制。
二,线程的实现(两种方式)
两种方法,继承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的好处:
1. 开发中如果多次使用类似new Thread(){…}.start()这种方式开启一个子线程,会创建多个匿名线程,使得程序运行起来越来越慢,而HandlerThread自带Looper使他可以通过消息来多次重复使用当前线程,节省开支。
2. 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 方法等待计算完成,获取计算结果。简单的实例代码如下。
1. 单个任务:
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.bolts:bolts-android:1.2.0’)
2. 多个任务:
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下。
更多相关文章
- Android(安卓)异步更新UI----handler+thread
- Android(安卓)数据库的简单使用
- Android(安卓)中如何调节 TextView 的字间距
- android volatile的使用
- Android(安卓)彻底关闭WebView,防止WebView造成OOM
- 旅行青蛙(旅かえる)逆向笔记
- Android: 如何打开assets or raw文件夹下的数据库文件
- Android逆向之旅—Hook神器Frida使用详解
- Android(安卓)应用安装过程分析