Service是Android中实现程序后台运行的解决方案。
它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。
服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另一个程序,服务仍然能保持正常运行。
PS:服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。
当这个应用程序进程被杀掉时,所有依赖该进程的服务都会停止运行。
我们需要做什么:在服务的内部创建子线程,并在这里执行具体的任务。
2.1线程的基本用法
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1.最基本的线程继承启动 new MyThread().start(); // 2.实现Runnable接口启动 My2Thread my2Thread = new My2Thread(); new Thread(my2Thread).start(); // 3.匿名类直接启动,最常用 new Thread(new Runnable() { @Override public void run() { } }); } // 1.最基本的线程继承Thread,高耦合 class MyThread extends Thread{ @Override public void run() { super.run(); } } // 2.实现Runnable接口 class My2Thread implements Runnable{ @Override public void run() { } }
2.2在子线程中更新UI
和许多其他的GUI库一样,Android的UI也是线程不安全的。
也就是说:想要更新程序的UI,必须在主线程中进行,否则会报错。
但是有些时候我们必须在子线程里去执行一些耗时操作,然后根据操作的结果来更新UI,这个时候就需要用到异步消息处理机制。
2.3解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message 、Handler 、MessageQueue 和 Looper 。
- Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。 - Handler
Handler顾名思义也就是处理着的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发送的消息经过一系列地辗转处理后,最终会传递到Handler的handlerMessage()方法中。 - MessageQueue
MessageQueue是消息列队的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息列队中,等待被处理。每一个线程中只会有一个MessageQueue对象。 - Looper
Looper是每个线程中的MessageQueue管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handlerMessage()方法中。每个线程也只会有一个Looper对象。
异步消息处理的完整流程:
首先需要在主线程当中创建一个Handler对象,并重写handlerMessage()方法。
然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。
之后这条消息会被添加到MessageQueue的队列中等待被处理。
而Looper则会一直尝试从MessageQueue中取出待处理的消息。
最后分发会Handler的handlerMessage()方法中。
由于Handler实在主线程里创建的,所以此时handleMessage()方法中的代码也会在主线程中运行。
流程图:
2.4使用AsyncTask
为了更方便的在子线程中对UI进行操作的工具AsyncTask。
实现原理:封装好的异步消息处理机制,
AsyncTask的基本用法:AsyncTask是一个抽象类,必须创建一个子类去继承它。在继承的时候需要指定三个泛型参数。
- Params——在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
- Progress——后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
- Result——当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
AsyncTask
class MyTask extends AsyncTask<Void,Integer,Boolean>{ @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected Boolean doInBackground(Void... voids) { return null; } }
可以看到,给MyTask指定了三个泛型Void,Integer,Boolean
表示:在执行MyTask时不需要传入参数给后台任务,并用整型数据来作为进度条显示单位,和使用布尔类型数据来反馈执行结果。
这里重写了四个常用方法
- onPreExeute( )
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。 - doInBackground(Params . . . )
这个方法中的所有代码都会在子线程中运行,我们在这里处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回。如果AsyncTask的第三个泛型参数是Void就可以不返回处理结果。
PS:由于是子线程,所以无法进行UI操作,如需要则可调用publishProgress(Progress . . .)方法。 - onProgressUpdate(Progress . . .)
当在后台任务中调用了publishProgress(Progress . . .)方法后,onProgressUpdate(Progress . . .)很快就会被调用,该方法携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。 - onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法很快就会被调用。返回的数据会作为参数传递到此方法中。
简单来说,使用AsyncTask的诀窍就是,在doInBackground()中执行具体的耗时任务,在onProgressUpdate()方法中进行UI擦欧总,在onPostExecute()中进行收尾工作。
当然想要执行任务只需要: new MyTask().execute();
3.1服务的基本用法
定义一个服务