企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### **Android中线程模型** [进程线程](http://note.youdao.com/noteshare?id=de140840a1da668b7f2f7f3facb34620) [Android多线程的四种方式](https://www.jianshu.com/p/2b634a7c49ec) [理解Android线程创建流程](https://www.kancloud.cn/alex_wsc/androidsystem/483949) [ActivityThread and ApplicationThread](https://www.2cto.com/kf/201312/269075.html) [Google官网介绍DDMS](https://developer.android.com/studio/profile/monitor.html) **所有Android 应用的线程都对应一个Linux 线程**,虚拟机因而可以更多地依赖操作系统的线程调度和管理机制。不同的应用在不同的进程空间里运行,加之对不同来源的应用都使用不同的Linux用户来运行,可以最大限度地保护应用的安全和独立运行。 **Android线程,一般地就是指Android虚拟机线程**,而虚拟机线程是通过系统调用而创建的Linux线程。纯粹的Linux线程与虚拟机线程的区别在于虚拟机线程具有运行Java代码的runtime. **除了虚拟机线程,还有Native线程和其他线程(可以从DDMS工具查看某一进程中的所有线程)**,对于Native线程又分为是否具有访问Java代码的两类线程。 #### **使用DDMS工具查看APP中进程中的线程** 如何更好地使用DDMS工具可参考以下文章 [Android Studio中怎么使用DDMS工具?](https://www.cnblogs.com/gaobig/p/5029381.html) [Android的DDMS中的Threads的各个字段的含义](http://blog.csdn.net/candyliuxj/article/details/7430056) [Android开发调试必备 - 使用DDMS](http://blog.csdn.net/stzy00/article/details/46554529) >[info] 如何验证一个进程中的线程数目和种类? > 可以通过IDE编译工具(eclipse或者AS)安装一个APP到模拟器或真机,打开DDMS工具,选择Thread菜单栏,可以看到当前进程中的线程种类和数目 **注意:下面查看的线程信息,是system_process进程中的信息,还有其他进程中信息,可以自行查看。** 运行AndroidDemo, **MainActivity.java** ~~~ public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } ~~~ **activity_main.xml** ~~~ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.wsc.androiddemo.MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> </RelativeLayout> ~~~ 打开DDMS,选择**system_process**,查看系统进程中的线程信息。 如下图所示: :-: ![DDMS分析App线程](https://box.kancloud.cn/4b551c7a274da38e9bbcda36d4f600ff_1173x501.jpg) 图1:DDMS分析App线程 :-: ![主线程的信息](https://box.kancloud.cn/3ee7ed8dd3c3c2e297982ac2f5c563c8_562x561.jpg) 图2 :主线程的信息 ![android.bg线程中的信息](https://box.kancloud.cn/e081cd07b1fa295a9f04c84acf5cd187_556x476.jpg) 图3:android.bg线程中的信息 ![ActivityManager线程中的信息](https://box.kancloud.cn/fc1f81feef439615bf6009eab9958c45_590x480.jpg) 图4:ActivityManager线程中的信息 类似图4中ActivityManager线程的信息的线程还有以下system_process中的线程信息 * android.fg * android.ui * android.io * android.display * PowerManagerService * PackageManager * PackageInstaller * MountService * WifiStateMachine * NetworkStats * NetworkPolicy * Wifi2pService * WifiService * ConnectivityServiceThread * NsdService * ranker * backup * WifiWatchdogStateMachine * WifiManager * WifiScanningSerice * WifiRttService * NetWorkTimeUpdateService * NetWorkMonitorNetWorkAgentInfo * DhcpStateMachine 从上面图4以及类似的24个线程的信息来看,除了主线程,一创建就会调用Looper类和Message Queue类的方法外,其他的线程(图4以及类似的24个线程,以及其他的线程)也会调用,**能否得出这些线程一诞生就有一个Looper类的对象和一个MQ(Message Queue)数据结构?这还有待验证**。 查看当前activity所在的进程中的线程的信息,如下图5所示 :-: ![App所在进程的线程信息](https://box.kancloud.cn/3b8e66b335f13ffc222df982958ea18b_1144x596.jpg) 图5:App所在进程的线程信息 **结论**: 1. 从上面系统进程和APP进程中线程信息来看,主线程和其他的一些线程的生命周期和它们所在的进程有关,进程被kill杀掉,那么这些线程也就不存在了;在主线程中创建的子线程的生命周期开始于start()方法,终止于run()函数运行结束。 守护线程的生命周期还和JVM有关系,当别的线程都dead时,JVM会kill掉所有守护线程然后退出。 2. 实验:在APP主线程创建一个子线程,主线程创建一个handler,用于更新UI;在子线程有一个点击事件,效果是更改主线程中UI的TextView文本内容改变,结果我们在DDMS中APP所在的进程中,没有看到这个子线程的创建和消失。 **每一个进程在诞生时,都会诞生一个主线程(Main Thread),以及诞生一个Looper类的对象和一个MQ(Message Queue)数据结构**。每当主线程作完事情,就会去执行Looper类。此时,不断地观察MQ的动态。如下图: **不同进程的地址空间是独立的** :-: ![](https://box.kancloud.cn/ad7b112bb4a165c597e19e259641b5ed_484x305.jpg) 图6:独立的进程空间 >[info] **备注**:然而,主线程创建一个子线程时,在预设情形下,子线程并不具有自己的Looper对象和MQ。由于没有Looper对象,就没有信息回圈(Message Loop),一旦工作完毕了,此子线程就结束了。 > 既然没有Looper对象也没有MQ,也就不能接受外来的Message对象了。则别的线程就无法透过MQ来传递信息给它了。 那么,如果别的线程(如主线程)需要与子线程通讯时(这里的意思是主线程传递消息 通过handler.sendMessage给子线程),该如何呢? 答案是:替它诞生一个Looper对象和一个MQ就行了。 > 主线程被创建的时候就会默认初始化Looper,因此在主线程中默认可以使用handler,不用手动创建Looper #### **Android 的单线程模型** 当第一次启动一个程序时, **Android 会同时启动一个对应的主线程(Main Thread)**。**主线程主要负责处理与UI 相关的事件**,如用户的按键事件,用户接触、屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理,所以**主线程通常又被叫作UI 线程**。 >[warning] **注意**: > * ActivityThread,不是主线程,只是运行在主线程的一个对象,从[ActivityThread.java源码](https://www.androidos.net.cn/android/6.0.1_r16/xref/frameworks/base/core/java/android/app/ActivityThread.java)来说,它只是一个final类。 > * ApplicationThread是ActivityThread的私有内部类,也是一个Binder对象。 * * * * * * * * * * **备注**:主线程中会构造这两个类的对象。但是**他们不是线程**,你看看继承关系就可以知道**ApplicationThread是binder, ActivityThread是一个final类也不是真正的线程**。 如果你用ddms查看某一个进程的所有线程,你会发现它只有一个main thread,当然它可能还有binder thread, jdwp, signal catcher等。   一个应用程序对应一个ActivityThread实例,应用程序由activityThread打开消息循环, 同时,一个应用程序也对应一个**ApplicationThread对象**,此对象**是ActivityThread 与 ActivityManagerService连接的桥梁**。 应该不是一个应用程序对应一个ActivityThread实例,而是一个进程对应一个ActivityThread实例,这个进程里面所有的activity对应这一个ActivityThread实例,你可以看看ActivityThread类包含了mActivities。 * * * * * * * * * * 在开发Android 应用程序时必须遵守单线程模型的原则: **Android UI 操作并不是线程安全的, 并且这些操作必须在UI 线程中执行。** 如果在非UI 线程中直接操作Ul 线程,会抛出异常。 工作线程-----要保证应用 UI 的响应能力,关键是不能阻塞 UI 线程。如果执行的操作不能很快完成,则应确保它们在单独的线程(“后台”或“工作”线程)中运行。 Android 的单线程模式必须遵守两条规则: * 不要阻塞 UI 线程 * 不要在 UI 线程之外访问 Android UI 工具包 由于UI 线程负责事件的监昕和绘图处理,因此**必须保证UI 线程能够随时响应用户的需求**,UI 线程里的操作应该像中断事件那样短小,费时的操作(如网络连接)需要另开线程, 否则,如果UI 线程超过5 s 没有响应用户请求, 会弹出对话框提醒用户终止应用程序。 如果在新开的线程中需要对Ul 线程进行设定,就可能违反单线程模型,因此**Android 采用一种复杂的Message Queue 机制保证线程间的通信。** #### **解析异步消息处理机制** 下图就是异步消息处理机制流程示意图 ![异步消息处理机制流程示意图](https://box.kancloud.cn/8111247adaf48b50e8c89c430e6d9c6f_720x510.jpg) 图7:异步消息处理机制流程示意图 >[info] **备注**:上图是Handler消息机制的处理流程,适用于所有的传递消息过程:工作线程传递消息给主线程,可以是主线程传递给工作线程,也可以是各个工作线程之间传递消息,还可以是主线程内部传递消息。 Android 中的异步消息处理主要由4 个部分组成: Message 、Handler 、MessageQueue 和Looper。 * **Message** Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。 * **Handler** Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler 的sendMessage ()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler 的handleMessage ()方法中。 * **MessageQueue** MessageQqeue 是消息队列的意思,它主要用于存放所有通过Handler 发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue 对象。 * **Looper** Looper 是每个线程中的MessageQueue 的管家,调用Looper 的loop ()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue 中存在一条消息,就会将它取出,并传递到Handler 的handleMessage ()方法中。每个线程中也只会有一个Looper 对象。 **子线程传递消息给UI线程(主线程)的整体流程如下**: 首先需要在主线程当中创建一个Handler 对象,并重写handleMessage ()方法。然后当子线程中需要进行UI 操作时,就创建一个Message 对象,并通过Handler 将这条消息发送出去。之后这条消息会被添加到MessageQueue 的队列中等待被处理,而Looper 则会一直尝试从MessageQueue 中取出待处理消息,最后分发回Handler 的handleMessage ()方法中。由于Handler 是在主线程中创建的,所以此时handleMessage ()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI 操作了。 示例代码如下所示: ~~~ public class MainActivity extends AppCompatActivity implements View.OnClickListener { public static final int UPDATE_TEXT = 1; private TextView text; private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: // 在这里可以进行UI操作 text.setText("Nice to meet you"); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView) findViewById(R.id.text); Button changeText = (Button) findViewById(R.id.change_text); changeText.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.change_text: new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what = UPDATE_TEXT; handler.sendMessage(message); // 将Message对象发送出去 } }).start(); break; default: break; } } } ~~~ #### **AsyncTask** 不过为了更加方便我们在子线程中对UI 进行操作, Android 还提供了另外一些好用的工具,比如AsyncTask。借助AsyncTask,即使你对异步消息处理机制完全不了解,也可以十分简单地从子线程切换到主线程。当然, AsyncTask 背后的实现原理也是基于异步消息处理机制的,只是Android 帮我们做了很好的封装而已。 AsyncTask 允许对用户界面执行异步操作。它会先阻塞工作线程中的操作,然后在 UI 线程中发布结果,而无需您亲自处理线程和处理程序。要使用它,必须**创建 AsyncTask 子类并实现 doInBackground() 回调方法**,该方法将在后台线程池中运行。要**更新 UI,必须实现 onPostExecute() 以传递doInBackground() 返回的结果并在 UI 线程中运行**,这样,您即可安全更新 UI。稍后,您可以**通过从 UI 线程调用 execute() 来运行任务**。 **AsyncTask 是一个抽象类**,所以如果我们想使用它,就**必须要创建一个子类去继承它**。在继承时我们可以为AsyncTask 类指定3个泛型参数,这3个参数的用途如下。 * Params:在执行AsyncTask 时需要传入的参数,可用于在后台任务中使用。 * Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。 * Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。 目前我们自定义的DownloadTask 还是一个空任务,并不能进行任何实际的操作,我们还需要去重写AsyncTask 中的几个方法才能完成对任务的定制。经常需要去重写的方法有以下4 个。 * onPreExecute() * doInBackground(Params ... ) * onProgressUpdate(Progress ... ) * onPostExecute(Result) AsyncTask 参考文档: * 可以使用泛型指定参数类型、进度值和任务最终值 * 方法 **doInBackground() 会在工作线程(非UI线程)上自动执行** * **onPreExecute()、onPostExecute() 和 onProgressUpdate() 均在 UI 线程中调用** * doInBackground() 返回的值将发送到 onPostExecute() * 您可以随时在 doInBackground() 中调用publishProgress(),以在 UI 线程中执行 onProgressUpdate() * 您可以随时取消任何线程中的任务 #### **Android多线程** 关于Android多线程的知识点补充: 当我们启动一个App的时候,Android系统会启动一个Linux Process,该Process包含一个Thread,称为UI Thread或Main Thread。通常一个应用的所有组件都运行在这一个Process中,当然,你可以通过修改四大组件在Manifest.xml中的代码块(`<activity><service><provider><receiver>`)中的**android:process**属性指定其运行在不同的process中。当一个组件在启动的时候,如果该process已经存在了,那么该组件就直接通过这个process被启动起来,并且运行在这个process的UI Thread中。 UI Thread中运行着许多重要的逻辑,如系统事件处理,用户输入事件处理,UI绘制,Service,Alarm等 :-: ![](https://upload-images.jianshu.io/upload_images/2839011-23a52dcb6f73550f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/493) 图8:UI Thread包含的逻辑 我们编写的代码则是穿插在这些逻辑中间,比如对用户触摸事件的检测和响应,对用户输入的处理,自定义View的绘制等。如果我们插入的代码比价耗时,如网络请求或数据库读取,就会阻塞UI线程其他逻辑的执行,从而导致界面卡顿。如果卡顿时间超过5秒,系统就会报ANR错误。所以,如果要执行耗时的操作,我们需要另起线程执行。 在新线程执行完耗时的逻辑后,往往需要将结果反馈给界面,进行UI更新。Android的UI toolkit(UI 工具包)不是线程安全的,不能在非UI线程进行UI的更新,所有对界面的更新必须在UI线程进行。 Android UI 工具包组件:来自 android.widget 和 android.view 软件包的组件。 Android提供了四种常用的操作多线程的方式,分别是: 1. Handler+Thread 2. AsyncTask 3. ThreadPoolExecutor 4. IntentService ##### **Handler+Thread** Android主线程包含一个消息队列(MessageQueue),该消息队列里面可以存入一系列的Message或Runnable对象。通过一个Handler你可以往这个消息队列发送Message或者Runnable对象,并且处理这些对象。每次你新创建一个Handle对象,它会绑定于创建它的线程(也就是UI线程)以及该线程的消息队列,从这时起,这个handler就会开始把Message或Runnable对象传递到消息队列中,并在它们出队列的时候执行它们。 :-: ![](https://upload-images.jianshu.io/upload_images/2839011-7036df5ffea97ec2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/465) 图9 :Handler Thread原理图 Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,Handler把压入消息队列有两类方式,Post和sendMessage: **Post方式**: Post允许把一个Runnable对象入队到消息队列中。它的方法有: ~~~ post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable,long) ~~~ 对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。 :-: ![](https://upload-images.jianshu.io/upload_images/2839011-b1d72fd4b937a29e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 图10:handler post用法 **sendMessage**: sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有: ~~~ sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message,long) sendMessageDelayed(Message,long) ~~~ Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。Message是一个final类,所以不可被继承。 :-: ![](https://upload-images.jianshu.io/upload_images/2839011-cdfe976cd627891b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 图11:handler定义 :-: ![](https://upload-images.jianshu.io/upload_images/2839011-2dd38fe2116901a0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 图12:handler sendMessage用法 **优缺点** 1. Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了 2. 处理单个异步任务代码略显多 **适用范围** 1. 多个异步任务的更新UI ##### **AsyncTask** [AsyncTask](https://www.androidos.net.cn/android/6.0.1_r16/xref/frameworks/base/core/java/android/os/AsyncTask.java)是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。 AsyncTask通过一个阻塞队列`BlockingQuery<Runnable>`存储待执行的任务,利用静态线程池THREAD_POOL_EXECUTOR提供一定数量的线程,默认128个。在Android 3.0以前,默认采取的是并行任务执行器,3.0以后改成了默认采用串行任务执行器,通过静态串行任务执行器SERIAL_EXECUTOR控制任务串行执行,循环取出任务交给THREAD_POOL_EXECUTOR中的线程执行,执行完一个,再执行下一个。 ~~~ class DownloadTask extends AsyncTask<Integer, Integer, String>{ // AsyncTask<Params, Progress, Result> //后面尖括号内分别是参数(例子里是线程休息时间),进度(publishProgress用到),返回值类型 @Override protected void onPreExecute() { //第一个执行方法 super.onPreExecute(); } @Override protected String doInBackground(Integer... params) { //第二个执行方法,onPreExecute()执行完后执行 for(int i=0;i<=100;i++){ publishProgress(i); try { Thread.sleep(params[0]); } catch (InterruptedException e) { e.printStackTrace(); } } return "执行完毕"; } @Override protected void onProgressUpdate(Integer... progress) { //这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数 //但是这里取到的是一个数组,所以要用progesss[0]来取值 //第n个参数就用progress[n]来取值 tv.setText(progress[0]+"%"); super.onProgressUpdate(progress); } @Override protected void onPostExecute(String result) { //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发 //这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕" setTitle(result); super.onPostExecute(result); } } ~~~ **优缺点** 1. 处理单个异步任务简单,可以获取到异步任务的进度 2. 可以通过cancel方法取消还没执行完的AsyncTask 3. 处理多个异步任务代码显得较多 **适用范围** 1. 单个异步任务的处理 ##### **ThreadPoolExecutor** ThreadPoolExecutor提供了一组线程池,可以管理多个线程并行执行。这样一方面减少了每个并行任务独自建立线程的开销,另一方面可以管理多个并发线程的公共资源,从而提高了多线程的效率。所以ThreadPoolExecutor比较适合一组任务的执行。Executors利用工厂模式对ThreadPoolExecutor进行了封装,使用起来更加方便。 :-: ![](https://upload-images.jianshu.io/upload_images/2839011-d3869fae4dff8272.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/520) 图13:ThreadPoolExecutor Executors提供了四种创建ExecutorService的方法,他们的使用场景如下: 1. Executors.newFixedThreadPool() 创建一个定长的线程池,每提交一个任务就创建一个线程,直到达到池的最大长度,这时线程池会保持长度不再变化 2. Executors.newCachedThreadPool() 创建一个可缓存的线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活的回收空闲的线程,当需要增加时, 它可以灵活的添加新的线程,而不会对池的长度作任何限制 3. Executors.newScheduledThreadPool() 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer 4. Executors.newSingleThreadExecutor() 创建一个单线程化的executor,它只创建唯一的worker线程来执行任务 **适用范围** 1. 批处理任务 ##### **IntentService** [IntentService](https://www.androidos.net.cn/android/6.0.1_r16/xref/frameworks/base/core/java/android/app/IntentService.java)继承自Service,是一个抽象类,是一个经过包装的轻量级的Service,用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。 特点 1. 一个可以处理异步任务的简单Service 它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。 扩展 IntentService 类(IntentService 类是一个抽象类,所以需要实现它的子类)特点 * 1、不需要开启线程, * 2、不需要关闭服务,它自己关闭 * 3、单线程下载 由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 IntentService 类实现服务也许是最好的选择。 IntentService 执行以下操作: * 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。 * 创建工作队列,用于将一个 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。 * 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。 * 提供 onBind() 的默认实现(返回 null)。 * 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。 * 综上所述,您只需实现 onHandleIntent() 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。) 示例: ~~~ public class HelloIntentService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public HelloIntentService() { super("HelloIntentService"); } /** * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */ @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } } } ~~~ 只需要一个构造函数和一个 onHandleIntent() 实现即可;不需要开线程。 如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现(return super.onStartCommand(intent,flags,startId);),以便 IntentService 能够妥善处理工作线程的生命周期。 例如,onStartCommand() 必须返回默认实现-----return super.onStartCommand(intent,flags,startId);(即,如何将 Intent 传递给 onHandleIntent()): ~~~ @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId); } ~~~ 除 onHandleIntent() 之外,您无需从中调用超类的唯一方法就是 onBind()(仅当服务允许绑定时,才需要实现该方法)。 IntentService 可用于**执行后台耗时的任务**,当任务执行后它会自动停止, 同时由于IntentService 是服务的原因,这导致它的优先级比单纯的线程要高很多,所以IntentService 比较**适合执行一些高优先级的后台任务**,因为它**优先级高不容易被系统杀死。** 在实现上, IntentService 封装了HandlerThread 和Handler,这一点可以从它的onCreate方法中看出来,