Android开发学习之探究服务
Posted 哈喽喔德
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发学习之探究服务相关的知识,希望对你有一定的参考价值。
(学习参考书:第一行代码第2版)
服务是android中实现程序后台运行的解决方案,适合去执行那些不需要和用户交互而且还需要长时间运行的任务。服务的运行不依赖任何用户界面,及时程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
服务不是运行在一个独立的线程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,依赖于该进程的服务也会停止运行。
服务也不会自动开启线程,所有的代码默认运行在主线程当中。需要在服务的内部创建子线程,并在这执行具体的任务。
一、Android多线程
Android多线程跟Java多线程采用相同的语法,有三种方式使用线程
- 定义线程时只需要新建一个类继承自Thread,然后重写父类的run()方法,并且在里面编写耗时逻辑即可。创建Thread子类的实例后,调用它的start()方法就可以让run方法中的代码在子线程中运行了。
- 新建类实现Runnable接口,再重写run()方法。调用时创建实例传入Thread类的构造函数里,再调用Thread的start()方法即可。
- 采用匿名类的方式,直接new Thread(new Runnable(){}).start。
二、在子线程中更新UI
与许多其他的GUI库一样,Android的UI也是线程不安全的。也就是说,想要更新应用程序中的UI元素,则必须在主线程中进行,否则会出现异常。
为了解决有时候必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件。Android提供了一套异步消息处理机制,完美的解决了在子线程中进行UI操作的问题。
在子线程中进行UI操作的问题。
(1)基本用法
- 在主线程中创建一个Hanlder对象,并重写handleMessage()方法
- 当子线程需要进行UI操作时,就创建一个Message对象,并通过Hanlder将这条消息发送出去
- 之后这条消息会被添加到MessageQueue的队列中等待被处理
- Looper会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。
public static final int UPDATE_TEXT=1;
private TextView text;
private Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case UPDATE_TEXT:
text.setText("Hello Android!");
break;
default:
break;
}
}
};
……
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what =UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
(2)解析异步消息处理机制
Android中的异步消息处理机制主要由4个部分组成:
Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
Handler
主要用于发送和处理消息。发送消息一般是Handler的sendMessage()方法,发出的消息经过一系列的辗转处理后,最终会传递到Handler的hanleMessage()方法中。
MessageQueue
消息队列,主要用于存放所有通过Hanler发送的消息。这部分消息会一直存放在消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
Looper
Looper是每个线程中MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环中,然后每当发现MessageQueue中存在一条消息,就会将他取出,并传递到Handler的handleMessage()方法中,每个线程也只会有一个Looper对象。
(3)使用AsyncTask
AsycnTask可以更简单的从子线程切换到主线程。其背后的实现原理也是基于异步消息处理机制的。
AsycnTask是一个抽象类,如果想使用它,就必须创建一个子类去继承它。在继承时可以为AsycnTask类指定三个泛型参数,用途如下:
Params 在执行AsycnTask时需要传入的参数,可用于在后台任务中使用
Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
Result 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型
AsycnTask子类通常需要重写的方法:
onPreExecute() 该方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框
doInBackground(Params…) 该方法的所有代码都会在子线程中运行,应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsycnTask的第三个泛型参数指定为Void,可以不返回。该方法中不能进行UI操作;如需更新,如反馈任务进度,可以调用pubishProgress(Progress)方法完成。
onProgressUpdate(Progress…) 当在后台任务中调用了pubishProgress(Progress)方法后,该方法会很快被调用。该方法携带的参数就是后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
onPostExecute(Result) 当后台任务执行完毕并通过return语句进行返回时,这个方法很快被调用。返回的数据作为参数传递到此方法中,可以利用返回的数据进行UI操作。
简单来说,AsycnTask的使用诀窍是:在doInBackground()方法中执行具体的耗时操作,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。只需要调用publishProgress()方法,就可以轻松从子线程切换到UI线程了。
三、服务的基本用法
(1)定义服务
自定义的服务类是继承于Service类的,onBind方法时Service中唯一一个抽象方法,必须要在子类中实现。除此之外,还可以重写onCreate()每次服务创建时调用,onStartComand()每次服务启动时调用,onDestory()服务销毁时调用等方法。
(2)启动和停止服务
启动和停止服务是 借助Intent实现的。
- 启动服务:创建Intent对象,传入参数上下文和服务类实例;调用startService()方法,传入intent对象
- 停止服务:创建Intent对象,传入参数上下文和服务类实例;调用stopService()方法,传入intent对象
(3)活动与服务间通信
要实现活动和服务间的通信,需要使用到Service类重写的onBind()方法,具体方法如下:
- 创建一个专门继承Binder的类来对需要实现的功能进行管理
- 在Binder子类内部实现需要的功能
- 创建新建类的实例,在onBind()中返回
- 在活动中绑定服务,首先创建一个ServiceConnection的匿名类
- 在匿名类中重写onServiceConnection()和onServiceDisconnection()方法,这两个方法分别会在活动与服务成功绑定和连接断开的时候调用
- 在onServiceConnection()方法中通过下转型获得Binder子类的实例,这样在活动中根据具体场景可以调用Binder子类中任何public()方法,即实现指挥服务实现相应功能
- 通过构建和服务相关的Intent对象,调用bindService()方法将活动和服务绑定起来。该方法接收三个参数:Intent对象、ServiceConnection对象、标志位
- 如果需要解绑服务,调用unbindService()方法即可
(4)服务的生命周期
一旦项目在任何位置调用了startService()
方法,相应的服务就会启动起来,并回调onStartCommand()
方法。如果这个服务之前没有被创建过,onCreate()
方法会先于onStartCommand()
方法执行。服务启动后会一直保持运行状态,直到stopService()
或stopSelf()
方法被调用。
还可以调用bindService()
方法来获取一个服务的持久连接,这时就会回调服务中的onBind()
方法。如果服务未被创建,onCreate()
方法会先于onBind()
执行。之后,调用方可以获取到onBind()
方法力返回的IBinder对象实例,就可以自由与服务通信了。只要调用方和服务的链接没有断开,服务就会一直保持运行状态。
当调用startService()
方法后,又去调用stopService()
方法,这是服务中的onDestory()
方法就会执行,表示服务已被销毁。类似的,调用了bindService()方法后,又调用unbindService()
方法,onDestory()
方法也会执行。当两个启动方法都被调用,则只有同时调用两个停止方法才能让onDestory()
方法执行。
区别辨析:
stopSelf() 服务执行完成onCreate()后,停止service,调用onDestory()方法
stopService() 立即停止服务,调用onDestory()方法
(5)使用前台服务
服务几乎都是在后台运行的,优先级较低,容易被系统回收。如果希望服务一直保持运行状态,可以考虑使用前台服务。前台服务会一直有一个正在运行的图标在系统的状态栏显示,类似于通知。创建前台服务的方法如下:
- 在AndroidManifest.xml中添加前台服务的权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- 创建一个通知
- 使用startForeground()方法传入id和Notification对象
(6)在子线程运行服务
服务中的代码默认运行在主线程当中,如果直接在服务里处理一些耗时操作,很容易出现程序无响应的情况。这时就需要在服务的每个具体方法里开启一个子线程,然后在子线程里处理耗时操作。故一个标准服务可写为:
public class MyService extends Service{
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//具体操作逻辑
stopself();
}
})
return super.onStartCommand(intent, flags, startId);
}
}
处于子线程的服务一旦启动后,会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来。
以上是关于Android开发学习之探究服务的主要内容,如果未能解决你的问题,请参考以下文章
Android 学习之《第一行代码》第三版 笔记Kotlin 继承时的括号到底写不写