梳理一波Service

what

Android 四大组件之一,名为服务,可见与activity具有同等地位,看名字也只到,服务嘛,一般用于提供进行UI无关的等任务操作。和activity存在不一样,这家伙的存在是可以说是无声无息,甚至在有些流氓的软件中,你关掉了app,那还不一定关掉了服务[捂脸] ,不过这也正突出了service的特点,后台。开始知道是用来进行后台任务就行了,看看怎么用吧。

Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

特别:Service默认也是在主线程进行的。所以也会有ANR风险。

how

定义一个服务

继承Service

首先的肯定是要继承一个Service的咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TestService extends Service {

@Override//服务被第一次创建调用
public void onCreate() {
super.onCreate();
}

@Override//被调用服务调用
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

@Override//销毁时调用
public void onDestroy() {
super.onDestroy();
}

@Nullable
@Override//绑定时调用
public IBinder onBind(Intent intent) {
return null;
}
}

Manifest声明

1
2
3
4
5
6
7
8
9
10
<service android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>

开启服务

嗯,Intent又来了,哈哈,在持有context的地方调用,既然是用Intent,自然servie的也会有Intent-Filter标签来过滤。不过好像从来没用过类似activity的隐式启动,google发现原来是5.0以上就不允许。

1
2
Intent intent = new Intent(context, TestService.class);
context.startService(intent);

首先的话必然先调用onCreate,然后就是onStartCommmand,这个服务就算是开启了,活了。后面再startService也只会调用onStartCommand了。

停止服务

1
2
3
4
5
//第一种,通过context关闭
Intent intent = new Intent(context, TestService.class);
context.stopservcie(intent);
//第二种,在servie内部停止
stopSelf();

停止服务后系统会尽快销毁服务,当然也就会调用onDestroy咯

服务交互–绑定服务

好像之前使用开启服务和关闭服务,调用者和服务基本没什么耦合关系。各走各路了呃呃。为了能够保持调用者和服务之间的交互。使用绑定服务,这也是开启服务的另一种方式,话句话说通过绑定的方式来开启服务。这就要用到onBind的方法了。

绑定本地服务

服务端

除了基本的使用扩展Binder,或者使用Messenger,AIDL(其实也是Binder,233)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//服务端
public class TestService extends Service {

@Nullable
@Override
public IBinder onBind(Intent intent) {//被绑定时调用
//需要给出一个Binder对象,具体怎么给,都可以,内部类,匿名对象,只要是Binder就行
return new MyBinder();
}

class MyBinder extends Binder {
public void someActionA() {
System.out.println("do some action A");
}

public void someActionB() {
System.out.println("do some actin B");
}
}
}
客户端

准备ServiceConnection,发起绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private MyBinder mMyBinder;

private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("on connected");
mMyBinder = (MyBinder) service;
}

@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("on disconnected");

}
};

private void toBindService() {
Intent intent = new Intent(context, TestService.class);
context.bindService(intent, mServiceConnection, flag);
}
//过程为toBindService --> onBinder --> onServiceConnected

通过这样的方法我们就能够拿到一个Binder对象,或者其它东西,这样就算和Service建立起了交互

取消绑定
1
unBindService(serviceConnection);

Flag : 关于绑定服务的Flag

1
2
3
4
5
6
7
8
BIND_AUTO_CREATE,//若绑定服务时服务未启动,则会自动启动服务。 注意,这种情况下服务的onStartCommand,仍然未被调用(它只会在显式调用startService时才会被调用)。
BIND_DEBUG_UNBIND,//使用此标志绑定服务之后的unBindService方法会无效。 这种方法会引起内存泄露,只能在调试时使用。
BIND_NOT_FOREGROUND,//被绑定的服务进程优先级不允许被提到FOREGROUND级别
BIND_ABOVE_CLIENT,//服务比app重要,oom移除killer杀死service前杀死app
BIND_ALLOW_OOM_MANAGEMENT,
BIND_WAIVE_PRIORITY,//被绑定的服务进程不会被OOM列入猎杀对象中。
BIND_IMPORTANT,//被绑定的服务进程优先级会被提到FOREGROUND级别
BIND_ADJUST_WITH_ACTIVITY//允许activity调整重要性

一般使用第一个咯,这里我收集到的翻译的不明确,还是具体参看Context.java源码的英文注释

绑定远程服务

也就是IPC,在android 系统中很多服务都是可以直接跨进程调用的,而这些服务也都是由某些组件应用提供的。通过绑定远程的服务可以调用其它的功能,比如蓝牙,wifi等等。进程间通信,这个我放在单独的IPC后面详细归纳

  • Messenger
  • AIDL

前台服务

这个算是用户主动意识到的一种服务,即使内存不足,也不会考虑终止,优先级很高,其实说白点了,就是一个普通的服务通过开启通知栏的形式,讲明了服务在运行。

1
2
3
4
5
6
7
8
9
//创键通知
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
//服务开启前台,并且关联通知
startForeground(ONGOING_NOTIFICATION_ID, notification);

Intent服务

what

类:IntentService,通过扩展这个类实现的服务,里面操作会在子线程中进行。而且不需要手动关闭服务,它自己就会关闭。那么看看怎么用吧

how
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}

开启服务还是和普通的没区别,但是所有的操作可以在onHandleIntent中响应。而且此时这个方法内就是子线程了。还不用关心服务停止

why

那么为啥IntentService有这么大魔力呢,子线程,自带关闭,其实原理很简单,内部维护了简单的Looper和Handler操作(关于Handler和IntentService原理下次一起详细归纳下),使得所有的从onStartCommand的操作进入了一个HandlerThread中,然后每次调用都向提交任务给线程。最后也会执行完也会停止自己。

远程服务

在service配置声明中有一个属性process

1
<service android:process=":remote" android:enable="true" andorid:exported="true"/>

这样在运行的时候,这个service就和本应用位于不同的进程中了,那么就轮到IPC上场了。对咯,既然是IPC了,原来的开启服务的方式就行不通,那就的用AIDL咯

注意事项

  • 小心ANR:常态下Service的代码是运行在主线程滴

  • 不要过多的实现代码在这里,要把服务看成一个后台任务调用代理。。

  • 关于onStartCommand返回值

    1
    2
    3
    4
    5
    6
    7
    //该返回值描述了在杀死事件中如何继续这个服务
    START_NOT_STICKY
    杀死后不会重启
    START_STICKY
    杀死后会重启,但是第一次启动的startCommand()的Intent为null
    START_REDELIVER_INTENT
    杀死后会重启,并且会重传Intent

生命周期

普通启动

1
2
3
4
5
6
7
8
9
10
11
call to start service
|
onCreate
|
onStartCommand
|
service is running
|
onDestory
|
service shut down

绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
call to bind servie
|
onCreate
|
onBind
|
client are bound to servcie
|
onUnbind
|
onDesotry
|
service shut down

onStartCommand一定是在调用startServie才会调用的。即使是绑定服务,也不会调用它的。