1 Service是什么?
Service是一个可以在后台执行长时间运行操作而不操作用户界面的应用组件。服务可由其它应用组件启动,而且即使用户切换到其它应用,服务仍将在后台继续运行。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。例如,服务可以处理网络事物,播放音乐,执行文件I/O或与内容提供程序交互,而所有这一切均可在后台进行。
1.1 启动
当应用组件(activity)通过调用startService()启动服务时,服务即处于“启动状态”。一旦启动,服务即可在后台无限期运行,即时启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。操作完成后,服务会自行停止运行。
1.2 绑定
当应用组件通过调用bindService()绑定服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务端接口,允许组件与服务进行交互,发送请求,获取结果,甚至是利用进程间通信跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
1.3 应该是使用服务还是线程?
- 服务是一种即使用户未与应用交互也可在后台运行的组件,因此,仅在必要时才创建服务。
- 如果确实要使用服务,则默认情况下,它仍会在应用的主线程中运行,因此,如果服务执行的是密集型或阻止性操作,则您仍应在服务内创建新线程。
- 服务和线程完全是两个概念,服务是Android系统的一种组件,一般情况下,服务是可以长期运行的,是有生命周期的,而线程更多是为了执行某个任务设计出的类,也是服务于Service,而且服务的很多功能在线程上都不具备。
- 常见业务:
- 维护长连接,维护应用的一些消息队列,安卓组件间通信,那么需要Service
- 网络请求,需要非UI线程执行的任务,那么需要Thread
2 基础知识
2.1 生命周期
- onStartCommand():当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
- onBind():当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC,远程调用)时,系统将调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回
null
。 - onCreate(): 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已在运行,则不会调用此方法。
- onDestroy():当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
- 如果组件通过调用 startService() 启动服务(这会导致对 onStartCommand() 的调用),则服务将一直运行,直到服务使用 stopSelf() 自行停止运行,或由其他组件通过调用 stopService() 停止它为止;如果组件是通过调用 bindService() 来创建服务(且未调用 onStartCommand(),则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它
2.2 使用清单文件声明服务
要声明服务,请添加 service 元素,元素作为application的子元素
service的属性
. . .
- android:description:向用户描述服务的字符串(以字符串资源引用的方式)
- android:directBootAware:是否该服务是直接引导意识;也就是说,它是否可以在用户解锁设备之前运行。默认是FALSE
- android:enabled:要启用服务,application和service属性必须都为“true”(默认情况下都为true)
- android:exported:其他应用程序的组件是否可以调用服务或与其交互,当值为“false”时,只有同一个应用程序或具有相同用户ID的应用程序的组件可以启动该服务或绑定到该服务,如果有过滤器,默认值为true,反之则无。
- android:icon:表示服务的图标,这可能在查看应用管理的时候看得到。
- android:isolatedProcess:如果设置为true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限。唯一的沟通是通过Service API(绑定和启动)
- android:label:可以向用户显示的服务的名称
- android:name:用户定义的服务类
- android:permission:实体必须具有以启动服务或绑定到其的权限的名称,如果startService(),bindService()或stopService()的调用者未被授予此权限,则该方法将不会工作,并且Intent对象将不会传递到服务,如果未设置此属性,则由application元素的权限属性设置的权限将应用于服务。如果未设置任何属性,则服务不受权限保护。
- android:process:要运行服务的进程的名称。通常情况下,应用程序的所有组件在为应用程序创建的默认进程中运行,它与应用程序包具有相同的名称;如果分配给此属性的名称以冒号(':')开头,则在需要并且服务在该进程中运行时,将创建应用程序专用的新进程;如果进程名称以小写字符开头,则该服务将在该名称的全局进程中运行,前提是它具有这样做的权限。这允许不同应用程序中的组件共享进程,减少资源使用。
2.3 服务分类
2.3.1 按运行地点分类
类别 | 特点 | 优点 | 缺点 | 应用场景 |
---|---|---|---|---|
本地服务 | 默认运行在主线程;所在应用主进程被终止后,服务也会停止 | 节约资源;通信方便,不需要考虑IPC | 限制性大 | 需要依附在某个进程的服务(必须音乐播放) |
远程服务 | 运行在独立进程;服务常驻在后台,不受其他组件影响 | 扩展性要比本地服务强 | 消耗资源大,占单独进程;需要使用AIDL或Messenger实现IPC,使用起来较复杂 | 系统级别服务(比如推送服务,一直维护长连接的服务) |
2.3.2 按运行类型分类
类别 | 特点 |
---|---|
前台服务 | 前台服务在手机息屏后,服务不会被自动杀死;通知栏有通知条提示(用户可以看见服务在运行), 服务使用时,需要让用户知道或进行相关操作,如音乐播放服务,服务终止时,通知栏也会消失;如果是需要一直维护一个长连接,但是是非系统应用,用前台服务也是一种方案; |
后台服务 | 服务使用时不需要用户关心,比如天气更新,日期同步;但是如果是非系统级服务,手机息屏后,这样的后台服务很容易被终止。 |
2.3.3 按功能分类
类别 | 特点 | 应用场景 |
---|---|---|
通信要求不高,不用绑定的服务 | 用startService()启动;调用者退出后,服务仍在运行 | 一般使用Intent来和Service通信,服务受其他组件的影响很小,比如用于维护长连接的服务。 |
通信要求高,需要用到绑定的服务 | 1 用bindService()启动,调用者退出后,服务也随着退出 2 先用startService启动,然后用bindService()绑定,调用者退出后,服务也随着退出 | 通过绑定,组件可以直接调用服务开放的方法。 |
2.4 服务常见类
2.4.1 IntentService 类
这是 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
已经封装好了一个工程线程,而且任务结束后,服务会自动销毁。
2.4.2 Service 类
这是适用于所有服务的基类。扩展此类时,考虑创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有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
传递至任何其他绑定的客户端。 - 当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
创建绑定服务的三种方式
必须提供IBinder
,用以提供客户端与服务进行交互的编程接口
- 1 扩展
Binder
类
绑定服务允许应用组件通过调用bindService()
与其绑定,以便创建长期连接(通常不允许组件通过调用startService()
来启动它);如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。 - 2 使用
Messenger
如需让接口跨不同的进程工作,则可使用Messenger
为服务创建接口。服务可以这种方式定义对应于不同类型Message
对象的Handler
。此Handler
是Messenger
的基础,后者随后可与客户端分享一个IBinder
,从而让客户端能利用Message
对象向服务发送命令。此外,客户端还可定义自有Messenger
,以便服务回传消息。这是执行进程间通信 (IPC) 的最简单方法,因为Messenger
会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。 - 3 使用AIDL
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
的代理角色
如图:
例子:使用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
的功能。
大概步骤:
- 服务器端:
- 1 定义AIDL,生命服务端可以向客户端提供的接口
- 2 在Service中扩展实现声明的接口方法
- 3 在AndroidManifest.xml注册服务&声明为远程服务
- 客户端:
- 1 拷贝服务端的AIDL文件
- 2 使用Stub.adInterface接口获取服务器端的Binder,根据需要调用服务提供的接口方法
- 3 通过Intent指定服务的服务名称和所在包,绑定远程服务
注意事项:
在AIDL文件中,支持的数据类型:
- 基本数据类型(int,long,char,boolean,float,double等)
- String和CharSequence
- List:只支持ArrayList,且每个元素都必须能够被AIDL支持
- Map:只支持HashMap,且每个元素都必须能够被AIDL支持
- Parcelalbe:所有实现了Parcelable接口的对象
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
- 引用其它Parcelable或AIDL文件,必须在文件中显示引用
- AIDL除了基本数据类型,其它类型的参数必须标上方向:in,out或者inout
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);
}
}