Service是一个可以在后台执行长时间运行操作而不操作用户界面的应用组件。服务可由其它应用组件启动,而且即使用户切换到其它应用,服务仍将在后台继续运行。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。例如,服务可以处理网络事物,播放音乐,执行文件I/O或与内容提供程序交互,而所有这一切均可在后台进行。
当应用组件(activity)通过调用startService()启动服务时,服务即处于“启动状态”。一旦启动,服务即可在后台无限期运行,即时启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。操作完成后,服务会自行停止运行。
当应用组件通过调用bindService()绑定服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务端接口,允许组件与服务进行交互,发送请求,获取结果,甚至是利用进程间通信跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
Service生命周期
null
。要声明服务,请添加 service 元素,元素作为application的子元素
service的属性
. . .
类别 | 特点 | 优点 | 缺点 | 应用场景 |
---|---|---|---|---|
本地服务 | 默认运行在主线程;所在应用主进程被终止后,服务也会停止 | 节约资源;通信方便,不需要考虑IPC | 限制性大 | 需要依附在某个进程的服务(必须音乐播放) |
远程服务 | 运行在独立进程;服务常驻在后台,不受其他组件影响 | 扩展性要比本地服务强 | 消耗资源大,占单独进程;需要使用AIDL或Messenger实现IPC,使用起来较复杂 | 系统级别服务(比如推送服务,一直维护长连接的服务) |
类别 | 特点 |
---|---|
前台服务 | 前台服务在手机息屏后,服务不会被自动杀死;通知栏有通知条提示(用户可以看见服务在运行), 服务使用时,需要让用户知道或进行相关操作,如音乐播放服务,服务终止时,通知栏也会消失;如果是需要一直维护一个长连接,但是是非系统应用,用前台服务也是一种方案; |
后台服务 | 服务使用时不需要用户关心,比如天气更新,日期同步;但是如果是非系统级服务,手机息屏后,这样的后台服务很容易被终止。 |
类别 | 特点 | 应用场景 |
---|---|---|
通信要求不高,不用绑定的服务 | 用startService()启动;调用者退出后,服务仍在运行 | 一般使用Intent来和Service通信,服务受其他组件的影响很小,比如用于维护长连接的服务。 |
通信要求高,需要用到绑定的服务 | 1 用bindService()启动,调用者退出后,服务也随着退出 2 先用startService启动,然后用bindService()绑定,调用者退出后,服务也随着退出 | 通过绑定,组件可以直接调用服务开放的方法。 |
这是 Service 的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。
例子:
public class CustomService extends IntentService {
private static final String TAG = "CustomService>>>>>>>";
/**
* name是命名工作线程的,对debug有帮助
*/
public CustomService(String name) {
super(name);
}
public CustomService() {
super("hello");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 服务创建时调用,服务正在运行不会调用
*/
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: !!!!!!!!!!!!");
}
/**
* 其它组件通过startServer后调用
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: !!!!!!");
return super.onStartCommand(intent, flags, startId);
}
/**
* 服务启动时调用
*/
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.e(TAG, "onStart: !!!!!!!!");
}
/**
* 这个方法是在工作线程里执行的
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.e(TAG, "onHandleIntent: 当前线程:" + Thread.currentThread().getName());
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("服务后台计数:》》" + i);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
/**
* 服务销毁前的回调
*/
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("服务已完成!");
}
}
启动服务结果
由启动服务结果可知:onCreate()
,onStartCommand()
等等服务的生命周期函数都是在应用的主线程执行的,intentService
封装的onHandleIntent
是工作在工作线程的,证明intentService
已经封装好了一个工程线程,而且任务结束后,服务会自动销毁。
这是适用于所有服务的基类。扩展此类时,考虑创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有Activity
的性能,所以一般我们要自己实现一个workThread
,推荐用IntentThread
和handler
实现。
启动服务
Intent
(指定要启动的服务)传递给 startService()
,从 Activity
或其他应用组件启动服务。Android
系统将调用服务的onStartCommand()
方法,并向其传递 Intent
。onStartCommand()
进行相应的调用。但是,要停止服务,只需一个服务停止请求(使用 stopSelf()
或 stopService()
)即可。停止服务
onStartCommand()
返回后会继续运行。因此,服务必须通过调用 stopSelf()
自行停止运行,或者由另一个组件通过调用 stopService()
来停止它。一旦请求使用 stopSelf()
或 stopService()
停止服务,系统就会尽快销毁服务。绑定服务
绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity
)绑定到服务、发送请求、接收响应,甚至执行进程间通信 (IPC)。 绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。
Service
类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现 onBind()
回调方法。该方法返回的 IBinder
对象定义了客户端用来与服务进行交互的编程接口。bindService()
绑定到服务。调用时,它必须提供 ServiceConnection
的实现,后者会监控与服务的连接。bindService()
方法会立即无值返回,但当 Android
系统创建客户端与服务之间的连接时,会对 ServiceConnection
调用 onServiceConnected()
,向客户端传递用来与服务通信的 IBinder
。onBind()
方法来检索 IBinder
。系统随后无需再次调用 onBind()
,便可将同一 IBinder
传递至任何其他绑定的客户端。创建绑定服务的三种方式
必须提供IBinder
,用以提供客户端与服务进行交互的编程接口
Binder
类bindService()
与其绑定,以便创建长期连接(通常不允许组件通过调用 startService()
来启动它);如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。Messenger
如需让接口跨不同的进程工作,则可使用 Messenger
为服务创建接口。服务可以这种方式定义对应于不同类型 Message
对象的 Handler
。此 Handler
是 Messenger
的基础,后者随后可与客户端分享一个 IBinder
,从而让客户端能利用 Message
对象向服务发送命令。此外,客户端还可定义自有 Messenger
,以便服务回传消息。这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger
会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。AIDL
(Android
接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。 之前采用 Messenger
的方法实际上是以 AIDL
作为其底层结构。 如上所述,Messenger
会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果您想让服务同时处理多个请求,则可直接使用 AIDL
。 在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。如需直接使用 AIDL
,您必须创建一个定义编程接口的 .aidl
文件。Android SDK
工具利用该文件生成一个实现接口并处理 IPC
的抽象类,您随后可在服务内对其进行扩展。1 扩展Binder
类
此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效。 例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。
例子
public class LocalService extends Service {
private static final String TAG = "LocalService>>>>>>>>";
private final IBinder mBinder = new LocalBinder();
private final Random mGenerator = new Random();
/**
* 供客户端使用的类,因为我们知道服务通常是和客户端运行在一个线程, * 所以我们不需要处理IPC,这里这个Binder类很像MVP里的中介者或者是Service的代理
*/
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public void onCreate() {
Log.e(TAG, "onCreate: !!!!!!!!!!");
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: !!!!!!!");
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: !!!!!!!!!!");
return super.onStartCommand(intent, flags, startId);
}
/**
* 供客户端调用的接口
*/
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: !!!!!!!!!!");
}
}
绑定服务
// 实际还是不要用这种匿名类实现
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: !!!!!!!!!!");
mBound = true;
LocalService.LocalBinder binder = (LocalService.LocalBinder) service; // 取得对service实例的引用 localService=binder.getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: !!!!!!!!!");
mBound = false;
localService = null;
}
};
Intent intent = new Intent(MainActivity.this, LocalService.class);
// 绑定服务
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
取消绑定
if (mBound){
unbindService(mConnection);
mBound=false;
}
所觉得这里的Service
并没有多大的卵用,只是个分层的作用,而且接口很多,适合做那些长时间运行又不需要界面的服务。
2 使用Messenger
如需让服务与远程进程通信,则可使用 Messenger
为您的服务提供接口。利用此方法,您无需使用 AIDL
便可执行进程间通信 (IPC
)。
实现client
向service
发message
,单向过程
Handler
,由其接收来自客户端的发来的每个Message
Messenger
,前面的Handler
就是用于创建Messenger
对象的(含有对 Handler
的引用)Messenger
内 创建一个 IBinder
,服务通过 onBind()
使其返回客户端IBinder
将 Messenger
(引用服务的 Handler
)实例化,然后使用后者将 Message
对象发送给服务Handler
中(具体地讲,是在 handleMessage()
方法中)接收每个 Message
。如果要实现service
对client
做出回应,则需要修改service
里实现的Handle
r,就是通过收到的Message
的replyTo
获取client
的Messenger
代理对象,响应的client
也需要实现一个Handler
用于处理Service
回应的Message
所以看起来是Messenger
就是充当了Service
或Client
里Handler
的代理角色
如图:
image
例子:使用Messager实现Service与客户端的跨进程双向通信
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final int TYPE_REPLY_MESSAGE = 200;
private static final String TAG = "MainActivity";
private ActivityMainBinding mainBinding;
private Messenger remoteMessenger = null;
private boolean isBinded = false;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: >>>");
remoteMessenger = new Messenger(service);
isBinded = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
remoteMessenger = null;
isBinded = false;
}
};
private MainHandler mainHandler = new MainHandler(this);
private Messenger mainMessenger = new Messenger(mainHandler);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
// 绑定服务
Intent intent = new Intent(this,MessageService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
mainBinding.mainBtnSendMsg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bundle bundle = new Bundle();
bundle.putString("msg","hello ,how are you !");
Message message = Message.obtain();
message.what = MessageService.TYPE_SEND_MSG;
message.setData(bundle);
message.replyTo = mainMessenger;
try {
remoteMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解绑服务
unbindService(serviceConnection);
mainBinding.unbind();
}
private static class MainHandler extends Handler{
private WeakReference weakReference;
public MainHandler(MainActivity mainActivity){
this.weakReference = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
if (weakReference.get() != null){
switch (msg.what){
case TYPE_REPLY_MESSAGE:
String replyMsg = msg.getData().getString("msg");
Log.d(TAG, "handleMessage: >>>>"+replyMsg);
break;
default:super.handleMessage(msg);
}
}else {
super.handleMessage(msg);
}
}
}
}
MessageService.java
public class MessageService extends Service {
private static final String TAG = "MessageService";
public static final int TYPE_SEND_MSG = 100;
private final Messenger messenger = new Messenger(new ProcessHandler());
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: >>>>");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: >>>>");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: >>>>");
return messenger.getBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: >>>>");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: >>>>");
}
/**
* @className MessageService
* @createDate 2019/5/28 19:33
* @author newtrekWang
* @email [email protected]
* @desc 定义一个Handler,用来处理客户端发来的消息
*
*/
class ProcessHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case TYPE_SEND_MSG:
Bundle bundle = msg.getData();
String st = bundle.getString("msg");
Log.d(TAG, "handleMessage: >>>> 收到客户端发的say Hello消息"+st);
st +=",too";
bundle.putString("msg",st);
Message message = Message.obtain();
message.what = MainActivity.TYPE_REPLY_MESSAGE;
message.setData(bundle);
try {
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
}
AndroidManifest.xml
点击界面的按钮后,测试结果:
05-28 20:18:25.889 18041-18041/me.newtrekwang.inputdevicedemo D/MainActivity: handleMessage: >>>>hello ,how are you !,too
3 使用AIDL
为了让远程Service与多个应用程序的组件进行跨进程通信,需要使用AIDL。在多进程通信中,存在两个进程角色:服务器端和客户端
IPC:
Intet-Process Communication
,跨进程通信
AIDL:Android Interface Definition Language
,Android
接口定义语言,用于让某个Service
与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享一个Service
的功能。
大概步骤:
注意事项:
在AIDL文件中,支持的数据类型:
AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
例子:
1.客户端获取服务端的图书列表
2.客户端让服务端添加书籍,并有操作回调
3.客户端可以注册新书到达监听器
客户端,服务端都要用的类文件,注意文件所在的包名也要一样。
Book.java
public class Book implements Parcelable {
private String name;
private String author;
private int page;
public Book(String name, String author, int page) {
this.name = name;
this.author = author;
this.page = page;
}
public Book() {
}
protected Book(Parcel in) {
this.name = in.readString();
this.author = in.readString();
this.page = in.readInt();
}
public static final Creator CREATOR = new Creator() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(author);
dest.writeInt(page);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", page=" + page +
'}';
}
}
Book.aidl
// Book.aidl
package me.newtrekwang.remoteservice;
// Declare any non-default types here with import statements
parcelable Book;
IOperationCallback.aidl
// IOperationCallback.aidl
package me.newtrekwang.remoteservice;
// Declare any non-default types here with import statements
interface IOperationCallback {
void callback(int code,String msg);
}
IOnNewBookArrivedListener.aidl
// IOnNewBookArrivedListener.aidl
package me.newtrekwang.remoteservice;
// Declare any non-default types here with import statements
import me.newtrekwang.remoteservice.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
IBookManager.aidl
// IBookManager.aidl
package me.newtrekwang.remoteservice;
// Declare any non-default types here with import statements
import me.newtrekwang.remoteservice.Book;
import me.newtrekwang.remoteservice.IOperationCallback;
import me.newtrekwang.remoteservice.IOnNewBookArrivedListener;
interface IBookManager {
List getBookList();
void addBook(in Book book, IOperationCallback callback);
void registerNewBookArrivedListener(IOnNewBookArrivedListener onNewBookArrivedListenner);
void unRegisterNewBookArrivedListener(IOnNewBookArrivedListener onNewBookArrivedListenner);
}
服务端服务实现:
AIDLService.java
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
/**
* CopyOnWriteArrayList 支持并发读写,因为可能会遇到多个客户端访问的情况
*/
private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
private RemoteCallbackList onNewBookArrivedListeners = new RemoteCallbackList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book, IOperationCallback callback) throws RemoteException {
synchronized (this){
mBookList.add(book);
callback.callback(200,"添加成功!");
int N = onNewBookArrivedListeners.beginBroadcast();
for (int i=0;i>>>>>>>>>>>");
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book("Android开发艺术探索","任玉刚",200));
mBookList.add(new Book("Kotlin实战","fdad",234));
Log.d(TAG, "onCreate: >>>>>");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: >>>>>>");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: >>>>>");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: >>>>>");
}
}
AndroidManifest.xml
...
...
客户端实现:
Main2Activity.java
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "Main2Activity";
private ActivityMain2Binding activityMain2Binding;
private IBookManager iBookManager;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iBookManager = IBookManager.Stub.asInterface(service);
try {
iBookManager.registerNewBookArrivedListener(onNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
iBookManager = null;
}
};
private IOnNewBookArrivedListener.Stub onNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
Log.d(TAG, "onNewBookArrived: >>>"+newBook);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMain2Binding = DataBindingUtil.setContentView(this,R.layout.activity_main2);
Intent intent = new Intent();
ComponentName componentName = new ComponentName("me.newtrekwang.remoteservice","me.newtrekwang.remoteservice.AIDLService");
intent.setComponent(componentName);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
initView();
}
private void initView() {
activityMain2Binding.mainBtnGetBookList.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (iBookManager != null){
try {
List books = iBookManager.getBookList();
if (books != null){
activityMain2Binding.mainTvContent.setText(books.toString());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
activityMain2Binding.mainBtnAddBook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (iBookManager != null){
String name = activityMain2Binding.etName.getText().toString();
String author = activityMain2Binding.etAuthor.getText().toString();
String page = activityMain2Binding.etPage.getText().toString();
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(author) || TextUtils.isEmpty(page)){
return;
}
Book book = new Book(name,author,Integer.parseInt(page));
try {
iBookManager.addBook(book, new IOperationCallback.Stub() {
@Override
public void callback(int code, String msg) throws RemoteException {
Log.d(TAG, "callback: code:"+code+" msg:"+msg);
ToastUtils.showShort(">>>>"+msg);
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
activityMain2Binding.unbind();
if (iBookManager != null && iBookManager.asBinder().isBinderAlive()){
try {
iBookManager.unRegisterNewBookArrivedListener(onNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(serviceConnection);
}
}
作者:newtrek
链接:https://www.jianshu.com/p/e57d77ce506e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。