目录
- 什么是服务
- 分类
- 更具是否在同一进程分类
- 更具运行类别分类
- 启动方式上分类
- 服务如何写
- 启动式服务
- 绑定式服务
- 前台服务
- 远程服务(AIDL 进程间通信)
- intentService
一:什么是服务?
Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。(其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行,你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作)
二:分类
更具是否在同一进程分类
类型 | 特点 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
本地服务 | 指的是服务和启动服务的activity在同一个进程中,开启的服务给本应用程序使用 (本app使用的) | 节约资源。通信方便。不需要AIDL和IPC | 限制大,主进程被杀死,服务也会停止 | 依附在某个进程的服务 如音乐播放器 |
远程服务 | 指的是服务和启动服务的activity不在同一个进程中。开启的服务给其他的应用程序使用 (给其他任何app使用的 基于IPC协议) | 灵活,服务常住在后台,不受其他Activity影响 | 消耗资源:单独进程,使用AIDL,和IPC | 系统级别服务 |
更具运行类别分类
类型 | 特点 | 适用场景 |
---|---|---|
前台服务 | 在通知栏通知(用户可以看到) | 服务运行时,让用户知道,并操作,音乐播放器 |
后台服务 | 处于后台的服务(用户无法看到) | 服务终止时,用户无法知道 |
启动方式上分类
类型 | 特点 |
---|---|
启动式 | 生命周期不依赖其他组件 |
绑定式 | 生命周期和其他组件绑定 |
三. 服务如何写
1. 启动式服务
生命周期
onCreate-->onStartCommand-->onDestroy
(1) 继承Service
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* 描述:本地服务
* <p>
* Created by allens on 2017/12/27.
*/
public class LocalService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("TAG", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG", "onDestroy");
}
}
onBind方法补充说明
onBind(...)函数是Service基类中的唯一抽象方法,子类都必须重写实现,此函数的返回值是针对Bound Service类型的Service才有用的,在Started Service类型中,此函数直接返回 null 即可
onStartCommand 方法补充说明
可以多次被调用。
这个方法的作用是处理服务和系统的关系,默认是
START_STICKY_COMPATIBILITY
, 返回值有四种具体如下
类型 | 名称 | 说明 |
---|---|---|
START_STICKY | 粘性服务 | 如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null |
START_NOT_STICKY | 非粘性 | 如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务 |
START_REDELIVER_INTENT | 粘性服务强化 | 如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。 |
START_STICKY_COMPATIBILITY | START_STICKY的兼容版本 | 不保证服务被kill后一定能重启,此值一般不会使用 |
强调一点
当Service因为内存不足而被系统kill后
一定要非常注意,因为此函数的返回值设定只是针对此种情况才有意义的,换言之,当认为的kill掉Service进程,此函数返回值无论怎么设定,接下来未来的某个时间内,即使系统内存足够可用,Service也不会重启。
onDestroy 方法补充说明
当用户强制kill掉进程时,onDestroy()是不会执行的
(2) 清单文件注册
<service android:name=".Service.LocalService" />
清单文件申明补充说明
属性 | 说明 |
---|---|
name | Service的类名 |
process | 设置具体的进程名称,不设置默认false为本地服务 |
exported | 该服务是否能够被其他应用程序所控制或连接 |
icon | Service的图标 |
label | Service的名字,若不设置,默认为Service类名 |
permission | 申明此Service的权限 |
(3) Activity 中启动
//启动服务
//生命周期 先执行onCreate 然后 onStartCommand
findViewById(R.id.local_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, LocalService.class);
startService(intent);
}
});
//关闭服务
//生命周期 onDestroy
findViewById(R.id.local_stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, LocalService.class);
stopService(intent);
}
});
2. 绑定式服务
生命周期
onCreate-->onBind-->onUnbind-->onDestroy
(1) 继承Service
public class BindService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG", "onBind");
//获取Acitivity传递的值
String value = intent.getStringExtra("data1");
Log.e("TAG", "获取的内容--->" + value);
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("TAG", "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e("TAG", "onDestroy");
super.onDestroy();
}
//自定义一个Binder类继承 Binder
public class MyBinder extends Binder {
//实现自己的逻辑
public int getNumber() {
//返回给Activity一个随机数,Activity显示就可以了
return new Random().nextInt(20);
}
}
}
(2) 清单文件注册
<service android:name=".Service.BindService" />
(3) Activity
//绑定服务
findViewById(R.id.bind_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, BindService.class);
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("TAG", "绑定服务成功了");
binder = (BindService.MyBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("TAG", "绑定服务断开了");
}
};
//通过intent的extras进行值的传递
intent.putExtra("data1", "前台的界面绑定服务了");
bindService(intent, connection, Service.BIND_AUTO_CREATE);
}
});
//下面是Log信息
//12-27 01:06:42.499 3300-3300/com.allens.servicedemo E/TAG: onCreate
//12-27 01:06:42.499 3300-3300/com.allens.servicedemo E/TAG: onBind
//12-27 01:06:42.499 3300-3300/com.allens.servicedemo E/TAG: 获取的内容--->前台的界面绑定服务了
//12-27 01:06:42.506 3300-3300/com.allens.servicedemo E/TAG: 绑定服务成功了
//获取服务中传过来的参数
findViewById(R.id.bind_getData).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (binder != null) {
int number = binder.getNumber();
Log.e("TAG", "NUMBER---->" + number);
}
}
});
//下面是Log信息
//12-27 01:08:23.518 3300-3300/com.allens.servicedemo E/TAG: NUMBER---->13
//解除绑定
findViewById(R.id.bind_stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//解除绑定的方法
if (connection != null) {
unbindService(connection);
//自己控制引用的myBinder对象
binder = null;
}
}
});
//下面是Log信息
//12-27 01:08:39.856 3300-3300/com.allens.servicedemo E/TAG: onUnbind
//12-27 01:08:39.856 3300-3300/com.allens.servicedemo E/TAG: onDestroy
注意
在四大基本组件中,需要注意的的是BroadcastReceiver
不能作为Bind Service
的Client
(依附对象),因为BroadcastReceiver
的生命周期很短,当执行完onReceive(..)
回调时,BroadcastReceiver
生命周期完结。而Bind Service
又与Client
本身的生命周期相关,因此,Android中不允许BroadcastReceiver
去bindService(..)
,当有此类需求时,可以考虑通过startService(..)
替代。
3. 前台服务
前台服务其实只是在启动式服务基础上,加上了Notification
通知
给出onCreate()
中的代码
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate");
//添加下列代码将后台Service变成前台Service
//构建"点击通知后打开MainActivity"的Intent对象
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
//新建Builer对象
Notification.Builder builer = new Notification.Builder(this);
//设置通知的标题
builer.setContentTitle("前台服务通知的标题")
//设置通知的内容
.setContentText("前台服务通知的内容")
//设置通知的图标
.setSmallIcon(R.mipmap.ic_launcher)
//设置点击通知后的操作
.setContentIntent(pendingIntent);
//将Builder对象转变成普通的notification
Notification notification = builer.getNotification();
//让Service变成前台Service,并在系统的状态栏显示出来
startForeground(1, notification);
}
效果
4. 远程服务
为了让远程Service与多个应用程序的组件(四大组件)进行跨进程通信(IPC),需要使用AIDL
- IPC:Inter-Process Communication,即跨进程通信
- AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
服务端 Service
(1)兴建一个AIDL 文件
建立好以后好会在与java同级的目录下看到一个aidl的文件夹,如下图所示
(2) Rebuild Project
一定不能忘了,
完成以后,可以在 build 中看到 已经编译好了
(3) 在Service子类中实现AIDL中定义的接口方法
package com.allens.servicedemo.Service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* 描述: AIDL 服务端service
* <p>
* Created by allens on 2017/12/27.
*/
public class ServerService extends Service {
private MyBinder myBinder;
//继承IServer.Stub 并实现其接口
public class MyBinder extends IServer.Stub {
@Override
public void onServer() throws RemoteException {
Log.e("TAG", "客户端与远程服务通信成功");
System.out.print("客户端与远程服务通信成功 " + Thread.currentThread().getName());
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG", "onBind");
return myBinder;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate");
myBinder = new MyBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("TAG", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG", "onDestroy");
}
}
(4) 清单文件注册信息
<!-- process 设置具体的进程名称-->
<!-- exported 该服务能被其他应用程序所控制或连接 -->
<service
android:name=".Service.ServerService"
android:exported="true"
android:process=":div">
<!--此处Intent的action必须写成“服务器端包名.aidl文件名”-->
<intent-filter>
<action android:name="com.allens.servicedemo.IServer" />
</intent-filter>
</service>
客户端 Service
(1) 将服务端的aidl 文件直接复制到 客户端
小提示
最好将AS 的目录切换到Project,在进行复制
(2) Activity
package com.allens.servicecliet;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.allens.servicedemo.Service.IServer;
public class MainActivity extends AppCompatActivity {
//创建ServiceConnection的匿名类
private ServiceConnection connection = new ServiceConnection() {
//重写onServiceConnected()方法和onServiceDisconnected()方法
//在Activity与Service建立关联和解除关联的时候调用
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("TAG", "onServiceDisconnected");
}
//在Activity与Service建立关联时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("TAG", "onServiceConnected");
//IService.Stub.asInterface()方法获取服务器端返回的IBinder对象
//将IBinder对象传换成了mAIDL_Service接口对象
IServer iServer = IServer.Stub.asInterface(service);
try {
//通过该对象调用在IService.aidl文件中定义的接口方法,从而实现跨进程通信
iServer.onServer();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_bind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG", "onClick");
//通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
//参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
Intent intent = new Intent("com.allens.servicedemo.IServer");
//Android5.0后无法只通过隐式Intent绑定远程Service
//需要通过setPackage()方法指定包名
intent.setPackage("com.allens.servicedemo");
//绑定服务,传入intent和ServiceConnection对象
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
}
}
最后看Log
当我们点击Client 中的Button时候,
12-27 02:52:06.702 5661-5661/com.allens.servicecliet E/TAG: onClick
12-27 02:52:06.979 5661-5661/com.allens.servicecliet E/TAG: onServiceConnected
然后去看Server中的log
12-27 02:52:06.905 5687-5687/com.allens.servicedemo:div E/TAG: onCreate
12-27 02:52:06.906 5687-5687/com.allens.servicedemo:div E/TAG: onBind
12-27 02:52:06.980 5687-5704/com.allens.servicedemo:div E/TAG: 客户端与远程服务通信成功
如果没看服务端的Log,请注意看一下,是否把进程切换到自己定义的进程下
最后 双手奉上代码
ServiceServerDemo 点击跳转
ServiceClietDemo 点击跳转
5. IntentService
特点
不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。
可以在
onHandleIntent
中做耗时操作当多次点击时候,会将任务添加到队列中,直到所有的任务都完成了,销毁服务
生命周期
onCreate-->onStartCommand-->onHandleIntent-->onDestroy
继承IntentService
/**
* 描述:intentService
* <p>
* Created by allens on 2017/12/27.
*/
public class MyIntentService extends IntentService {
/***
* 比较规范的action条件,包名+action+功能意义的描述
*/
private static final String ACTION_FOO = "com.allens.servicedemo.Service.FOO";
private static final String ACTION_BAZ = "com.allens.servicedemo.Service.BAZ";
/***
* 规范的key的写法
*/
private static final String EXTRA_PARAM1 = "com.allens.servicedemo.Service.PARAM1";
private static final String EXTRA_PARAM2 = "com.allens.servicedemo.Service.PARAM2";
public MyIntentService() {
//给IntentService内部创建的工作线程起名字
super("MyIntentService");
}
/**
* 外部直接调用该方法就可以启动该服务了(设置了action条件,传递的参数,并且启动了服务)
*
* @see IntentService
*/
public static void startActionFoo(Context context, String param1, String param2) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_FOO);
intent.putExtra(EXTRA_PARAM1, param1);
intent.putExtra(EXTRA_PARAM2, param2);
context.startService(intent);
}
public static void startActionBaz(Context context, String param1) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_BAZ);
intent.putExtra(EXTRA_PARAM1, param1);
context.startService(intent);
}
/**
* 进行耗时操作的处理 (当该方法执行完毕以后,服务会自动关闭)
* 通过action区分不同的耗时操作的逻辑,各自实现各自的逻辑
* <p/>
* 从核心代码入手,分析每一行代码表达意思,(可以中间涉及其他方法和成员变量的定义,以此扩展出去查看)
*
* @param intent
*/
@Override
protected void onHandleIntent(Intent intent) {
Log.e("TAG", "onHandleIntent");
if (intent != null) {
//获取action条件
final String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
//获取字符串内容
final String param1 = intent.getStringExtra(EXTRA_PARAM1);
final String param2 = intent.getStringExtra(EXTRA_PARAM2);
handleActionFoo(param1, param2);
} else if (ACTION_BAZ.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_PARAM1);
handleActionBaz(param1);
}
}
}
//模拟耗时
private void handleActionBaz(String param1) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("TAG", "param1 " + param1);
}
private void handleActionFoo(String param1, String param2) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
Log.e("TAG", "onCreate");
super.onCreate();
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.e("TAG", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e("TAG", "onDestroy");
super.onDestroy();
}
}
清单文件注册
<service android:name=".Service.MyIntentService" />
Activity
MyIntentService.startActionBaz(MainActivity.this,"test1");
写在最后
首先本文全部的DEMO 都可以在GitHub 上看到