Android的主线程与子线程
在上一篇文章Android的进程、线程与优先级中我们提到:
线程(thread):是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程至少对应一个线程。
CPU轮流为每个任务分配其占用时间。每个任务都觉得自己在一直占用CPU,但事实上CPU时间是划分成片段分配给了所有任务。
因而使用线程的行为应当受到限制:
进程资源有限,不能无限制地创建线程(创建和销毁线程都会有相应的开销)。
CPU的数量和轮转能力有限,过多的线程将导致CPU轮转周期变长,系统处理速度变慢。
在Android中,线程有更多一些的细分名目:主线程、子线程、HandlerThread、IntentService、AsyncTask。
主线程:进程拥有的默认线程。Androidd的UI访问是没有加锁的,这样在多个线程访问UI是不安全的。所以Android中规定只能在UI线程中访问UI,故而主线程的响应速度不应该受到影响,所以所有耗时操作都应该放置到子线程中进行。
子线程:进程中手动创建的线程,用于执行耗时任务。
HandlerThread:内部封装了Handler机制的子线程。运行时自动启动与子线程绑定的Looper。
IntentService:内部封装了HandlerThread的特殊Service。由于Service的优先级更高,所以适合执行一些高优先级的后台任务。
AsyncTask:一种内部封装了子线程和Handler机制的轻量级异步任务。
主线程(UI线程)
创建主线程:Android应用程序的入口
我们说“线程是程序执行的最小单元,主线程是进程拥有的默认线程”,那么创建进程的工作就必然包含了创建主线程的部分。同时,我们知道主线程的ID值与进程的进程号相同,因此可以推断:主线程就是进程默认的可执行部分,或者说:如果进程中总是只有一个线程,那么主线程就是进程本身。
既然我们希望了解主线程的创建过程的原因更多的是想知道应用程序从哪里开始运行。因此相比于了解创建主线程的过程,我们更应该关心的问题是:Android的应用程序入口在哪里?
我们知道Java中main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法。而Android应用程序的main()方法则在ActivityThread类中。
但并不是说,直到ActivityThread.main()方法得到执行,新起的应用程序才开始运行。上面我们提到线程是进程的一个实体,在执行main()方法前,系统已经将应用的进程率先孵化出来,并在内部初始化了运行时库,并启动了Binder线程池。
事实上,直到一个新的应用程序进程创建完毕之后,才调用RuntimeInit类的静态成员函数invokeStaticMain将ActivityThread类的静态成员函数main设置为新创建的应用程序的入口函数。
ActivityThrad管理主线程的执行,并根据AMS的要求(使用Binder机制通信,AMS为客户端,ActivityThread.ApplicationThread为服务端)通过Handler消息机制调度并执行Activity、BroadCast等组件以及其它操作(在我们关心的范围内,可以认为ActivityThread等同于主线程)。
\android\app\ActivityThread.main()public static void main(String[] args) {SamplingProfilerIntegration.start();… …// 创建/绑定主线程的Looper// Looper、Handler是构成Android应用消息循环机制的主要部分Looper.prepareMainLooper();// 创建ActivityThread的实例ActivityThread thread = new ActivityThread();//建立Binder通道 (创建新线程)thread.attach(false);// 创建一个Handlerif (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}// 开启Looper循环,这是一个死循环Looper.loop();// 除非Looper出现异常,否则该语句永远得不到执行throw new RuntimeException("Main thread loop unexpectedly exited");}
主线程如何在死循环中处理任务
新建一个应用,主Activity的代码如下:
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 发送一个Message,what=1handler.sendEmptyMessage(1);Log.d("GooDong","Exit onCreat------");}@Overrideprotected void onResume() {super.onResume();// 发送一个Message,what=2handler.sendEmptyMessage(2);Log.d("GooDong","Exit onResume------");}private Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {// 打印Message的标记值Log.d("GooDong","Message in ,msg.what = "+msg.what);return false;}});}
运行结果:
D/GooDong(1336): Exit onCreat------D/GooDong(1336): Exit onResume------D/GooDong(1336): Message in ,msg.what = 1D/GooDong(1336): Message in ,msg.what = 2
从上述运行结果我们可以看出:
- Looper.loop()执行之前,主Activity的生命周期已经结束了执行onResume()来到前台;
- Looper.loop()执行之后,主线程就进入消息循环机制的死循环。
那么问题来了:
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- Activity的生命周期是怎么实现在死循环体外能够执行起来的?
上面我们提到,Android中规定只能在主线程中访问UI,这就意味着,如果主线程结束运行,那么这些资源就会被回收,看起来就是程序崩溃了。因而在Android中使用了死循环确保主线程的可执行代码是能一直执行下去的,不会被退出。
真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。
在ActivityThread.main()中有这样一行代码:
// 创建ActivityThread的实例ActivityThread thread = new ActivityThread();// 建立Binder通道 (创建新线程)thread.attach(false);
进入attach方法:
private void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {… …// RuntimeInit获得一个ApplicationThread的Binder实例RuntimeInit.setApplicationObject(mAppThread.asBinder());final IActivityManager mgr = ActivityManagerNative.getDefault();try {// IActivityManager绑定了当前的ApplicationThreadmgr.attachApplication(mAppThread);} catch (RemoteException ex) {// Ignore}… …} else {… …}… …}
thread.attach(false)会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程。
尽管是处于死循环,实际上主线程大多数时候处于休眠状态,并不会消耗CPU资源。原因是,主线程在MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里。此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信:
private class H extends Handler {public static final int PAUSE_ACTIVITY = 101;public static final int RESUME_ACTIVITY = 107;… …public void handleMessage(Message msg) {switch (msg.what) {case PAUSE_ACTIVITY:// 执行Activity的onPause()流程… …case RESUME_ACTIVITY:// 执行Activity的onResume()流程… …}}
HandlerThread
在上一篇文章Android的进程、线程与优先级中我们给了一个Java子线程的简单示范,这里不再阐述子线程的相关内容。
HandlerThread继承了Thread,它是一种可以直接使用Handler的Thread。在它的run()方法烘通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。
public void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}
显然HandlerThread只是简单封装了Handler消息队列创建过程的Thread。HandlerThread只能通过消息机制来处理任务,这一点跟普通的Thread不同。在Android中的一个具体使用场景是IntentService,我们在将在下节介绍。
另外,无论我们是使用HandlerThread还是自行在线程中创建消息队列,承载该操作的子线程最终都陷入死循环,因此当明确不需要再使用该线程时应当手动调用Looper的quit或者quitSafely方法来终结这个线程。
IntentService
IntentService继承自Service。它比Service方便的地方在于,封装了HandlerThread和Handler:
// IntentService.onCreate() public void onCreate() {super.onCreate();// 创建了一个HandlerThread实例HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");//,并启动了该线程thread.start();// 获取了子线程的LoopermServiceLooper = thread.getLooper();// 创建一个Handler对象并将其与子线程的Looper关联起来mServiceHandler = new ServiceHandler(mServiceLooper);}
同时,相比一般的后台任务线程,IntentService拥有更高的优先级(更不容易被系统回收)。请注意:这不是说,在IntentService中开启的线程也有较高的优先级。
上述代码中我们看到,当IntentService第一次启动调用onCreate时,会创建一个HandlerThread和一个Handler,这样就可以通过Handler将任务发布到HanlderThread中处理了。IntentService会在onStart中处理每个后台任务:
// 每次启动IntentService,onStartCommand只会调用一次public int onStartCommand(Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}public void onStart(Intent intent, int startId) {Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}
最终启动IntentService的Intent会在ServiceHandler中得到处理:
private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}public void handleMessage(Message msg) {onHandleIntent((Intent)msg.obj);stopSelf(msg.arg1);}}// onHandleIntent是一个抽象类,需要子类实现protected abstract void onHandleIntent(Intent intent);
stopSelf(int startId)在尝试停止服务前会判断最近启动服务的次数是否和startId相等,如果相等就立刻停止服务。直到onHandlerIntent方法执行完最后一个任务,stopSelf(int startId)才能把IntentService停止。
由于Handler机制中的任务只能顺序处理,所以IntentService中的任务也只能顺序处理。
下面举一个例子说明IntentService的工作过程,Service是四大组件之一,必须在注册文件中注册。:
/** 继承IntentService自的类 */public class MyIntentService extends IntentService{private static final String TAG = "GooDong";// 如果是显示启动Service,一定要有一个参数为空的构造函数public MyIntentService() {super(TAG);}public MyIntentService(String name) {super(name);}// 覆写抽象方法,处理任务@Overrideprotected void onHandleIntent(Intent intent) {String actionCode = intent.getStringExtra("action_code");Log.d(TAG, "receive task , actionCode = "+actionCode);SystemClock.sleep(1000);Log.d(TAG, "handle task , actionCode = "+actionCode);}@Overridepublic void onDestroy() {Log.d(TAG, "Enter onDestroy---------");super.onDestroy();}}/** 在主Activity中启动任务 */public class MainActivity extends Activity {private static final String TAG = "GooDong";@Overrideprotected void onCreate(Bundle savedInstanceState) {Log.d(TAG, "Enter onCreate---------");super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent myService = new Intent(this , MyIntentService.class);myService.putExtra("action_code","task1");startService(myService);myService.putExtra("action_code","task2");startService(myService);myService.putExtra("action_code","task3");startService(myService);myService.putExtra("action_code","task4");startService(myService);}}
结果打印:
D/GooDong(1637): receive task , actionCode = task1D/GooDong(1637): handle task , actionCode = task1D/GooDong(1637): receive task , actionCode = task2D/GooDong(1637): handle task , actionCode = task2D/GooDong(1637): receive task , actionCode = task3D/GooDong(1637): handle task , actionCode = task3D/GooDong(1637): receive task , actionCode = task4D/GooDong(1637): handle task , actionCode = task4D/GooDong(1637): Enter onDestroy---------
可以看出,task1、task2、task3、task4按顺序执行,并且直到最后的task4执行完毕之后IntentService才被停止。
AsyncTask
使用AsyncTask
AsyncTask封装了Handler和Thread。使用AsyncTask可以方便地执行一些异步的任务,任务执行期间可以获取任务的进度,任务结束后可将结果更新到UI线程中。
AsyncTask是一个抽象的泛型方法,有三个泛型参量:
- Params:表示传入参数的类型
- Progress表示后台任务的执行进度的类型
- Result表示后台任务的返回结果
如果不需要具体的参数,则可以指定为Void。
AsyncTask提供了四个核心方法:
- onPreExecute(),在主线程中执行,在异步任务执行前调用
- donInBackGround(),在线程中执行,用于执行异步任务
- onProcessUpdate(),在主线程中执行返回当前任务执行进度
- onPostExecute(),在主线程中执行,异步任务完成后调用,在这里返回任务结果
AsyncTask的使用需要注意以下几点:
- AsyncTask的类必须在主线程中加载
- AsyncTask的对象必须在主线程中创建
- AsyncTask.execute()只能在主线程中调用
- 一个AsyncTask只能调用一次
AsyncTask的工作原理
AsyncTask采用了模板方法设计模式。这种模式指:
定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的执行结构即可重定义该算法的某些特定步骤。
其类图如下:
AsyncTask中的四个模板方法:
// 抽象方法,必须覆写protected abstract Result doInBackground(Params... params);//空方法,可选择性覆写protected void onPreExecute() {}protected void onPreExecute() {}protected void onProgressUpdate(Progress... values) {}
执行异步任务的入口:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {if (mStatus != Status.PENDING) {// 这说明AsyncTask任务只能执行一次switch (mStatus) {case RUNNING:throw new IllegalStateException("Cannot execute task:"+ " the task is already running.");case FINISHED:throw new IllegalStateException("Cannot execute task:"+ " the task has already been executed "+ "(a task can be executed only once)");}}mStatus = Status.RUNNING;// 这个方法显然是在主线程中调用onPreExecute();mWorker.mParams = params;// 启动线程池执行任务exec.execute(mFuture);return this;}
execute是一个final方法,最终调用到executeOnExecutor方法,在该方法中判断AsyncTask的状态,如果不是PENDING状态则抛出异常,这也就解释了为什么AyncTask只能执行一次。
接着调用onPreExecute()。因为AsyncTask要求在主线程中执行,所以onPreExecute()也就是在主线程中执行的。然后params参数传递给了mWorker对象的mParams字段,执行了exec.execute(mFuture)。
接下来看看mWorker和mFuture是什么:
public AsyncTask() {mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedreturn postResult(doInBackground(mParams));}};mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected void done() {try {postResultIfNotInvoked(get());} catch (InterruptedException e) {android.util.Log.w(LOG_TAG, e);} catch (ExecutionException e) {throw new RuntimeException("An error occured while executing doInBackground()", e.getCause());} catch (CancellationException e) {postResultIfNotInvoked(null);}}};} public class FutureTask<V> implements RunnableFuture<V>{public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW; }run(){Callable<V> c = callable;… …result = c.call();… …}}public interface RunnableFuture<V> extends Runnable, Future<V> {void run();}
mFuture有一个父类接口是Runnable,在实现的run()方法中执行了mWorker的call()方法,call()方法中调用了doInBackground(mParams)。所以当在线程池中执行mFuture时,其实是在子线程中执行了doInBackground(mParams)方法,并通过postResult()将执行结果返回。
// 定义了一个Handlerprivate static final InternalHandler sHandler = new InternalHandler();// postResult的实现private Result postResult(Result result) {// 注意这里不仅仅将消息封装入消息中,AsyncTask的对象引用也被封装了Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));message.sendToTarget();return result;}//Handler的具体实现private static class InternalHandler extends Handler {@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult result = (AsyncTaskResult) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one result// 结束任务执行result.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:// 通过onProgressUpdate返回任务执行进度result.mTask.onProgressUpdate(result.mData);break;}}}// 结束任务private void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {// 通过onPostExecute将结果回调给用户onPostExecute(result);}mStatus = Status.FINISHED;}
在postResult中将AsyncTask对象引用和执行结果封装在消息中,让消息处理返回的结果。InternalHandler有两个处理,一个是返回结果,另一个是返回执行进度。可以看到,onProgressUpdate()实际上是在主线程中执行的。最终在调用了AsyncTask的finish()方法结束执行任务。onPostExecute()也是在主线程中执行的。
Android的主线程与子线程相关推荐
- android子线程没有运行完,android假如主线程依赖子线程A的执行结果,如何让A执行完成,之后主线程再往下执行呢?...
/* String ObjectResult="原先的结果"; //使用VOLLY框架(与问题无关) JsonObjectRequest jsonObjectRequest = n ...
- android广播怎样运行在子线程,android假如主线程依赖子线程A的执行结果,如何让A执行完成,之后主线程再往下执行呢?...
抛开你这段代码不看,单根据你的标题来回答: android假如主线程依赖子线程A的执行结果,如何让A执行完成,之后主线程再往下执行呢? 需要在子线程执行完成的地方,通过主线程的Handler发送一条消 ...
- android 线程传递数据,Android Handle主线程向子线程发送数据
一.前言: 今天,下载apk的时候,下载进度回调到主线程,主线程刷新通知栏,造成页面阻塞掉,不能点击页面(下载结束后,可以正常点击页面). 所以,要在进度回调发送消息到子线程,在子线程的Handler ...
- Android,UI主线程与子线程
在一个Android 程序开始运行的时候,会单独启动一个Process.默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Co ...
- Android线程之主线程向子线程发送消息
和大家一起探讨Android线程已经有些日子了,谈的最多的就是如何把子线程中的数据发送给主线程进行处理,进行UI界面的更新,为什么要这样,请查阅之前的随笔.本篇我们就来讨论一下关于主线程向子线程如何发 ...
- Android之HandlerThread源码分析和简单使用(主线程和子线程通信、子线程和子线程通信)
1.先熟悉handler方式实现主线程和子线程互相通信方式,子线程和子线程的通信方式 如果不熟悉或者忘记了,请参考我的这篇博客 Android之用Handler实现主线程和子线程互相通信以及子 ...
- java中子线程与主线程通信_Android笔记(三十二) Android中线程之间的通信(四)主线程给子线程发送消息...
之前的例子都是我们在子线程(WorkerThread)当中处理并发送消息,然后在主线程(UI线程)中获取消息并修改UI,那么可以不可以在由主线程发送消息,子线程接收呢?我们按照之前的思路写一下代码: ...
- android主线程和子线程的区别
android 主线程和子线程有什么区别 本文较为深入的分析了android中UI主线程与子线程.分享给大家供大家参考. 具体如下:在一个Android 程序开始运行的时候,会单独启动一个Proces ...
- android判断主线程_Android主线程和子线程区别详解
主线程和子线程的区别 每个线程都有一个唯一标示符,来区分线程中的主次关系的说法. 线程唯一标示符:Thread.CurrentThread.ManagedThreadID; UI界面和Main函数均为 ...
最新文章
- 调整php-fpm,nginx调整php-fpm
- undertale人物_【undertale】传说之下精美人物图包 (Frisk篇)
- 配置2008的网络与工作组环境
- 机器学习方法三要素-阿里云大学
- JSP数据交互(二)
- ios::app与ios::ate打开方式有什么不同??
- Hadoop(MapR)分布式安装及自动化脚本配置
- 开发经验分享_05_葫芦画瓢
- 【python】python中execl的操作
- ASP.Net学习笔记006--Get和Post的区别
- 解决Gerrit的git unpack error问题
- 汽车域控制器架构和OTA的心脏:网关的四大豪门(上)
- QQ飞车手游设计分析
- UI设计,扁平化还是拟物化?
- 14家互联网公司裁员(1-2月裁员清单)
- 通达信 移动平均算法_通达信擒龙攻防战法主图指标公式
- android推箱子实验报告,android开发——推箱子小游戏(前序)
- 2012年上半年信息系统项目管理师 上午试题和参考答案及解析
- [附源码]Java计算机毕业设计SSM大学生健康管理系统的设计与实现
- CICD使用阿里云 云效实现自动发布代码