文章开头奉送上本文用到的部分代码,方便大家对照学习。
1 简介
Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。我们可以用服务处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。作为四大组件之一的Service,在面试中也是是常考的内容,所以我们必须掌握。
注意 有一点希望大家注意,Service默认是执行在UI线程中,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。因此,千万不要在Service中执行耗时的操作。如果一定要处理耗时操作,应该创建新线程来完成这项工作,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。
2 service两种形式
2.1 非绑定式
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 仅当绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
2.2 绑定式
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。
2.3 非绑定方式使用步骤:
第一步继承service类,并重写onBind()方法.
onBind方法是针对绑定方式用的。如果你用的非绑定方式,这个方法大可不用管。代码如下:
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
/**
* 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。
* 如果服务已在运行,则不会调用此方法。
*/
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
/**
* 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。
* 一旦执行此方法,服务即会启动并可在后台无限期运行。
* 如果您实现此方法,则在服务工作完成后,需要由您通过调用 stopSelf() 或 stopService() 来停止服务。
* (如果您是用绑定方式,则无需实现此方法。)
*/
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
/**
* 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法,请务必实现此方法。
* 在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。
* 如果您并不使用绑定方式,则返回null即可。
*/
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
/**
* 当服务不再使用且将被销毁时,系统将调用此方法。
* 服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。
* 这是服务接收的最后一个调用。
*/
}
}
第二步在menifest中注册service
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
在注册的时候需要注意一下几点:
1.android:name 属性是唯一必需的属性,用于指定服务的类名
2.我们不建议使用隐式方式启动service,因为启动哪个服务存在一定的不确定性
3.通过添加 android:exported 属性并将其设置为 "false",确保服务仅适用于您的应用。这可以有效阻止其他应用启动您的服务,即便在使用显式 Intent 时也如此
第三步启动service
startService(new Intent(this,MyService.class));
有几点需要注意:
1. 当startService() 启动,这会导致调用服务的 onStartCommand() 方法。
根据这个特性,我们通过调用 startService() 方法并传递 Intent 对象,服务通过 onStartCommand() 方法接收此 Intent。
2.service应通过调用 stopSelf() 结束工作来自行停止运行或者由另一个组件通过调用 stopService() 来停止它。
2.4 绑定方式使用步骤:
第一步 创建服务类
这个是绑定方式,我们必须重写onBinder方法。
public class MyBinderService extends Service {
@Override
public void onCreate() {
Log.d("myapp","绑定onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("myapp","绑定onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
/**
* 绑定方式重点在这个方法,返回MyBinder引用。
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("myapp","绑定onBind");
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("myapp","绑定onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d("myapp","绑定onDestroy");
super.onDestroy();
}
/**
* 写一个类,继承Binder类。在onBind方法中返回MyBinder引用。
*/
public class MyBinder extends Binder{
public void showToast(){
Log.d("myapp","MyBinder-showToast");
}
public void showList(){
Log.d("myapp","MyBinder-showList");
}
}
}
我们需要写一个MyBinder类,继承Binder类,然后在onBind方法中返回MyBinder的引用。
第二步 注册service
注册方法参参考上面,这里省略
第三步 绑定service
ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyBinderService.MyBinder mb = (MyBinderService.MyBinder)service;
mb.showToast();
mb.showList();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(new Intent(MainActivity.this,MyBinderService.class),sc,BIND_AUTO_CREATE);
我们通过bindService()开启服务,通过unbindService()停止服务。
效果图
3 IntentService类
IntentService是专门用来解决Service中不能执行耗时操作这一问题的,创建一个IntentService也很简单,只要继承IntentService并覆写onHandlerIntent函数,在该函数中就可以执行耗时操作了。在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。代码如下:
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
//在这里面做耗时操作。当这个方法执行完毕,整个service就结束了,因此您永远不必调用 stopSelf()。
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4 扩展服务类
本节摘取官网-扩展服务类,使用 IntentService 显著简化了启动服务的实现。但是,若要求服务执行多线程(而不是通过工作队列处理启动请求),则可扩展 Service 类来处理每个 Intent。
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
与使用 IntentService 相比,上面service能执行更多工作。
注意 onStartCommand() 方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 onStartCommand() 返回的值必须是以下常量之一:
1.START_NOT_STICKY
如果系统在 onStartCommand() 返回后终止服务,则除非有 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
2.START_STICKY
如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
3.START_REDELIVER_INTENT
如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
4.TART_STICKY_COMPATIBILITY
START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
5 前台服务
由于后台服务优先级相对比较低,当系统出现内存不足的情况下,它就有可能会被回收掉,所以前台服务就是来弥补这个缺点的,它可以一直保持运行状态而不被系统回收。例如:墨迹天气在状态栏中的天气预报。代码如下:
public class ForeService extends Service{
@Override
public void onCreate() {
super.onCreate();
showNotifiction();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void showNotifiction(){
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("2017.2.15")
.setContentText("天气晴");
Intent resultIntent = new Intent(this, NotifictionActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = mBuilder.build();
mNotificationManager.notify(0, notification);
startForeground(0, notification);
}
}
前台服务创建很简单,其实就在Service的基础上创建一个Notification,然后使用Service的startForeground()方法即可启动为前台服务。
6 AccessibilityService无障碍服务
请参考我写的这篇文章《AccessibilityService讲解》和android拆轮子系列之一步一步教你写微信抢红包插件
7 系统服务
系统服务提供了很多便捷服务,可以查询Wifi、网络状态、查询电量、查询音量、查询包名、查询Application信息等等等相关多的服务,具体大家可以自信查询文档,这里举例几个常见的服务:
- 判断Wifi是否开启
WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
boolean enabled = wm.isWifiEnabled();
- 获取系统最大音量
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
- 获取当前音量
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int current = am.getStreamMaxVolume(AudioManager.STREAM_RING);
- 判断网络是否有连接
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
boolean isAvailable = info.isAvailable();
8 Service生命周期
service的生命周期分绑定方式和非绑定方式,下面给出的2张图大家可以对比的看,这里不多做介绍。
9 AIDL讲解
这个块知识我还没整理,大家可以参考这篇文章Android基础——初学者必知的AIDL在应用层上的Binder机制
10 用服务还是线程?
1)在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务。同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需要长时间运行的情况下使用线程。
2)如果任务占用CPU时间多,资源大的情况下,要使用线程。
3)最优的使用方法
创建并启动一个service,在Service里面创建、运行并控制该Thread(因为任何Activity都可以控制同一个Service,而系统只会创建一个对应的Service的实例)还有个办法是使用IntentService,它里面自带了子线程,使用它就不用创建新线程了。
IntentService并不适用于所有的场景,它的优点是使用方便、代码简洁,不需要我们自己像Service里面还有去创建线程,但由于任务需要排队,不适合大多数的多任务情况。
4)最后可以把Service想象成一种消息服务,而你可以在任何有Context的地方调用Context.startservice、Context.stopservice、Context.bindService,Context.unbindService,来控制它,也可以在Service里注册BroadcastReceiver,在其他地方通过发broadcast来控制它。
11 结尾
文章结尾奉送上本文用到的部分代码,方便大家对照学习。
好了就讲到这里吧,如果觉的我写的不错,请点个赞吧!谢谢!
在技术上我依旧是一个小渣渣,加油!勉励自己!
12 参考文档
【1】官网-服务
【2】Android Service不能再详细的教程
【3】Android基础——初学者必知的AIDL在应用层上的Binder机制