Android 接口定义语言 (AIDL) 与其他 IDL: 您可以定义编程接口 同意使用 进程间通信 (IPC)。
在 Android 上,一个进程通常无法访问 另一个进程的内存区域为了进行语音通信,它们需要将对象分解为原语, 操作系统可以为您理解和编组跨越该边界的对象。用于 编写这类编组是一项繁琐的工作,因此 Android 会使用 AIDL 为您处理。
注意:仅当您让客户
不同的应用访问您的 IPC 服务,并且您想要在
服务。如果您不需要跨资源执行并发 IPC
创建界面时,您需要通过实现
Binder
。
如果您想执行 IPC,但不需要处理多线程处理,
使用 Messenger
实现接口。
无论如何,请务必先了解绑定服务,然后再
如何实现 AIDL
在开始设计 AIDL 接口之前,请注意,对 AIDL 接口的调用 直接函数调用。不要对调用所在的线程做假设 。实际情况因调用是否来自 本地进程或远程进程:
- 从本地进程发出的调用在进行调用的同一线程中执行。如果
这是您的主界面线程,该线程会继续在 AIDL 接口中执行。如果
另一个线程,也就是在服务中执行代码的线程。因此,如果仅本地
因此您可以完全控制哪些线程正在该服务中执行。但是
如果是这样,则完全不使用 AIDL;而是创建
来实施
Binder
。 - 来自远程进程的调用从平台内部维护的线程池中分派 自己的流程准备好应对来自未知线程的多次调用的来电 同时发生换言之,AIDL 接口的实现必须是 完全线程安全从一个线程对同一个远程对象发出的调用次数 在接收方端按顺序到达。
oneway
关键字用于修改远程调用的行为。使用它时,远程调用会 。它发送交易数据并立即返回。 该接口的实现最终会将此调用作为常规远程调用从Binder
线程池接收。如果使用oneway
拨打本地电话号码, 不会产生任何影响,并且调用仍是同步调用。
定义 AIDL 接口
使用 Java 在 .aidl
文件中定义 AIDL 接口
然后保存到源代码语法和 Python 2.1 的 src/
目录中,
托管服务的应用以及绑定到该服务的任何其他应用
当您构建每个包含 .aidl
文件的应用时,Android SDK 工具
根据 .aidl
文件生成 IBinder
接口,并将其保存在
项目的 gen/
目录下。该服务必须实现 IBinder
进行必要的更改然后,客户端应用便可绑定到该服务,并从
IBinder
以执行 IPC。
如需使用 AIDL 创建绑定服务,请按以下步骤操作, :
- 创建
.aidl
文件此文件定义带有方法签名的编程接口。
- 实现接口
Android SDK 工具会根据您的
.aidl
文件。此接口有一个名为Stub
的内部抽象类,用于扩展Binder
,并实现 AIDL 接口中的方法。您必须扩展Stub
类,并实现相应方法。 - 向客户端公开接口
注意:您在之后对 AIDL 接口所做的任何更改
您的第一个版本必须保持向后兼容性,以免中断其他应用
使用你的服务。也就是说,您的 .aidl
文件必须复制到其他应用
以便它们能够访问您的服务界面,因此,您必须保持对原始版本的支持
界面。
创建 .aidl 文件
AIDL 使用一种简单的语法,让您可以使用一种或多种方法来声明接口, 接受参数和返回值。参数和返回值可以是任意类型,甚至是 AIDL 生成的接口。
您必须使用 Java 编程语言构造 .aidl
文件。每.aidl
文件必须定义一个接口,并且只需要接口声明和方法
签名。
默认情况下,AIDL 支持下列数据类型:
- Java 编程语言中的所有基元类型(例如
int
、long
、char
、boolean
等) - 基元类型的数组,例如
int[]
String
CharSequence
List
List
中的所有元素都必须是此 列表或者您声明的某个其他 AIDL 生成的接口或 Parcelable。答 可以选择将List
用作参数化类型类,例如List<String>
。 尽管ArrayList
方法来使用List
接口。Map
Map
中的所有元素都必须是此 列表或者您声明的某个其他 AIDL 生成的接口或 Parcelable。参数化类型映射 例如以下形式的Map<String,Integer>
不受支持。模型另一边的 接收结果始终为HashMap
, 虽然生成的方法是使用Map
接口。建议将 使用Bundle
替代Map
。
您必须为前面未列出的每种其他类型添加一个 import
语句。
即使它们在与您的接口相同的软件包中定义。
定义服务接口时,请注意:
- 方法可以采用零个或多个参数,并且可以返回值或空值。
- 所有非基元参数都需要一个方向标记,用于指示数据走向:
in
、out
或inout
(请参阅下面的示例)。基元、
String
、IBinder
和 AIDL 生成的 接口的默认值为in
,不能是其他值。注意:请将方向限制为真实的 因为编组参数的开销很大。
.aidl
文件中包含的所有代码注释都包含在 生成时间:IBinder
在导入和打包之前添加的注释以外的界面 语句。- 字符串和 int 常量可以在 AIDL 接口(例如
const int VERSION = 1;
)中定义。 - 方法调用由
transact()
代码,通常基于接口中的方法索引。因为这 会让版本控制变得困难 可以手动将交易代码分配给某个方法:void method() = 10;
。 - 必须使用
@nullable
为可为 null 的参数和返回类型进行注解。
下面是一个 .aidl
文件示例:
// IRemoteService.aidl package com.example.android; // Declare any non-default types here with import statements. /** Example service interface */ interface IRemoteService { /** Request the process ID of this service. */ int getPid(); /** Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
将 .aidl
文件保存在项目的 src/
目录中。当您
构建应用时,SDK 工具会在您的IBinder
项目的 gen/
目录中。所生成文件的名称与 .aidl
文件的名称一致,但
扩展名为 .java
。例如,IRemoteService.aidl
的结果为 IRemoteService.java
。
如果您使用 Android Studio,增量构建几乎会立即生成 binder 类。
如果您不使用 Android Studio,则 Gradle 工具会在您下次使用 Binder 类时,
构建应用使用 gradle assembleDebug
构建项目
或 gradle assembleRelease
,.aidl
以便代码可以链接到生成的类。
实现接口
在构建应用时,Android SDK 工具会生成 .java
接口文件
以您的 .aidl
文件命名。生成的接口包含一个名为 Stub
的子类
它是其父接口的抽象实现(例如 YourInterface.Stub
),并声明了 .aidl
文件中的所有方法。
注意:Stub
也
定义了几个辅助方法(最值得注意的是 asInterface()
),该方法接受 IBinder
,通常是传递给客户端 onServiceConnected()
回调方法的方法;以及
返回存根接口的实例。如需详细了解如何进行此类转换,请参阅调用 IPC 部分
方法。
如需实现 .aidl
生成的接口,请扩展生成的 Binder
接口(例如 YourInterface.Stub
),并实现相应方法
继承自 .aidl
文件。
下面是名为 IRemoteService
的接口的实现示例,由前面定义的
使用匿名实例的 IRemoteService.aidl
示例:
Kotlin
private val binder = object : IRemoteService.Stub() { override fun getPid(): Int = Process.myPid() override fun basicTypes( anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String ) { // Does nothing. } }
Java
private final IRemoteService.Stub binder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing. } };
现在,binder
是 Stub
类 (Binder
) 的一个实例,
用于定义服务的 IPC 接口在下一步中,此实例会向
以便与服务进行交互
实现 AIDL 接口时,请注意几条规则:
- 来电不一定会在主线程上执行,因此您需要考虑 从一开始就介绍多线程以及如何正确构建线程安全服务
- 默认情况下,IPC 调用是同步的。如果您知道这项服务 以毫秒为单位完成请求,不要从 Activity 的主线程调用它。 它可能会挂起应用,导致 Android 显示“应用无响应” 对话框。从客户端中的单独线程调用它。
- 只有适用于 的参考文档下列出的异常类型
Parcel.writeException()
将会返回给调用方。
向客户端公开该接口
为服务实现了接口后,您需要将其公开给
以便进行绑定公开该接口
对于您的服务,请扩展 Service
并实现 onBind()
,以返回实现
生成的 Stub
(如上一部分中所述)。下面是一个示例
这项服务,它会向客户端公开 IRemoteService
示例接口。
Kotlin
class RemoteService : Service() { override fun onCreate() { super.onCreate() } override fun onBind(intent: Intent): IBinder { // Return the interface. return binder } private val binder = object : IRemoteService.Stub() { override fun getPid(): Int { return Process.myPid() } override fun basicTypes( anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String ) { // Does nothing. } } }
Java
public class RemoteService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { // Return the interface. return binder; } private final IRemoteService.Stub binder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing. } }; }
现在,当客户端(如 activity)调用 bindService()
以连接到此服务时,客户端的 onServiceConnected()
回调会收到
binder
实例(由服务的 onBind()
返回)
方法。
客户端还必须有权访问接口类。因此,如果客户端和服务
那么客户端应用必须拥有 .aidl
文件的副本
放在其 src/
目录中,这会生成 android.os.Binder
接口,从而为客户端提供对 AIDL 方法的访问权限。
当客户端收到 IBinder
时
在 onServiceConnected()
回调中,它必须调用
YourServiceInterface.Stub.asInterface(service)
,用于转换返回的
参数设置为 YourServiceInterface
类型:
Kotlin
var iRemoteService: IRemoteService? = null val mConnection = object : ServiceConnection { // Called when the connection with the service is established. override fun onServiceConnected(className: ComponentName, service: IBinder) { // Following the preceding example for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service. iRemoteService = IRemoteService.Stub.asInterface(service) } // Called when the connection with the service disconnects unexpectedly. override fun onServiceDisconnected(className: ComponentName) { Log.e(TAG, "Service has unexpectedly disconnected") iRemoteService = null } }
Java
IRemoteService iRemoteService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established. public void onServiceConnected(ComponentName className, IBinder service) { // Following the preceding example for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service. iRemoteService = IRemoteService.Stub.asInterface(service); } // Called when the connection with the service disconnects unexpectedly. public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "Service has unexpectedly disconnected"); iRemoteService = null; } };
如需更多示例代码,请参阅
<ph type="x-smartling-placeholder"></ph>
RemoteService.java
类中的
ApiDemos。
通过 IPC 传递对象
在 Android 10(API 级别 29 或更高级别)中,您可以定义
Parcelable
对象
AIDL。支持作为 AIDL 接口参数的类型以及其他 Parcelable 类型
此处受支持。这避免了手动编写编组代码和自定义
类。不过,这也会创建一个裸结构体。如果自定义访问器或其他功能
请改为实现 Parcelable
。
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect { int left; int top; int right; int bottom; }
上述代码示例会自动生成包含整数字段 left
的 Java 类,
top
、right
和bottom
。所有相关的编组代码
该对象无需添加任何代码即可直接使用,
实施。
您也可以通过 IPC 接口将自定义类从一个进程发送到另一个进程。不过,
确保类的代码对 IPC 通道的另一端可用,
您的类必须支持 Parcelable
接口。支持
Parcelable
非常重要
因为它允许 Android 系统将对象分解成可编组的基元
跨进程
如需创建支持 Parcelable
的自定义类,请执行以下操作:
以下:
- 让您的类实现
Parcelable
接口。 - 实现
writeToParcel
,该函数接受 对象的当前状态,并将其写入Parcel
。 - 将名为
CREATOR
的静态字段添加到您的类中,该字段是用来实现Parcelable.Creator
接口。 - 最后,创建一个声明 Parcelable 类的
.aidl
文件,如下所示Rect.aidl
文件。如果您使用的是自定义构建流程,请勿将
.aidl
文件添加到您的 build。此.aidl
文件与 C 语言中的头文件类似,并未经过编译,
AIDL 会在其生成的代码中使用这些方法和字段进行编组和解组 对象。
例如,以下 Rect.aidl
文件用于创建 Rect
类,
parcelable:
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect;
以下示例展示了 Rect
类如何实现
Parcelable
协议。
Kotlin
import android.os.Parcel import android.os.Parcelable class Rect() : Parcelable { var left: Int = 0 var top: Int = 0 var right: Int = 0 var bottom: Int = 0 companion object CREATOR : Parcelable.Creator<Rect> { override fun createFromParcel(parcel: Parcel): Rect { return Rect(parcel) } override fun newArray(size: Int): Array<Rect?> { return Array(size) { null } } } private constructor(inParcel: Parcel) : this() { readFromParcel(inParcel) } override fun writeToParcel(outParcel: Parcel, flags: Int) { outParcel.writeInt(left) outParcel.writeInt(top) outParcel.writeInt(right) outParcel.writeInt(bottom) } private fun readFromParcel(inParcel: Parcel) { left = inParcel.readInt() top = inParcel.readInt() right = inParcel.readInt() bottom = inParcel.readInt() } override fun describeContents(): Int { return 0 } }
Java
import android.os.Parcel; import android.os.Parcelable; public final class Rect implements Parcelable { public int left; public int top; public int right; public int bottom; public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { public Rect createFromParcel(Parcel in) { return new Rect(in); } public Rect[] newArray(int size) { return new Rect[size]; } }; public Rect() { } private Rect(Parcel in) { readFromParcel(in); } public void writeToParcel(Parcel out, int flags) { out.writeInt(left); out.writeInt(top); out.writeInt(right); out.writeInt(bottom); } public void readFromParcel(Parcel in) { left = in.readInt(); top = in.readInt(); right = in.readInt(); bottom = in.readInt(); } public int describeContents() { return 0; } }
Rect
类中的编组非常简单。查看
方法,以查看您可以写入的其他类型的值Parcel
转换为 Parcel
。
警告:请记住接收
来自其他进程的数据在此示例中,Rect
会从 Parcel
中读取四个数字,但您应确保这些数字在
值。如需详细了解如何保护应用免受恶意软件的侵扰,请参阅安全提示。
带软件包参数(包含 Parcelable 类型)的方法
如果某个方法接受了预期包含的Bundle
对象,
Parcelable,请确保通过以下方法设置 Bundle
的类加载器:
先调用 Bundle.setClassLoader(ClassLoader)
,然后再尝试读取
从 Bundle
获取。否则,即使在您的应用中正确定义了 Parcelable,您也会遇到 ClassNotFoundException
。
例如,请参考以下示例 .aidl
文件:
// IRectInsideBundle.aidl package com.example.android; /** Example service interface */ interface IRectInsideBundle { /** Rect parcelable is stored in the bundle with key "rect". */ void saveRect(in Bundle bundle); }如以下实现所示,
ClassLoader
为
在读取 Rect
之前在 Bundle
中明确设置:
Kotlin
private val binder = object : IRectInsideBundle.Stub() { override fun saveRect(bundle: Bundle) { bundle.classLoader = classLoader val rect = bundle.getParcelable<Rect>("rect") process(rect) // Do more with the parcelable. } }
Java
private final IRectInsideBundle.Stub binder = new IRectInsideBundle.Stub() { public void saveRect(Bundle bundle){ bundle.setClassLoader(getClass().getClassLoader()); Rect rect = bundle.getParcelable("rect"); process(rect); // Do more with the parcelable. } };
调用 IPC 方法
如需调用使用 AIDL 定义的远程接口,请在 您的调用类:
- 将
.aidl
文件添加到项目src/
目录中。 - 声明一个
IBinder
接口实例,该实例是根据 AIDL。 - 实现
ServiceConnection
。 - 致电
Context.bindService()
, 传入您的ServiceConnection
实现。 - 在您的
onServiceConnected()
实现中, 您会收到IBinder
实例命名为service
。致电YourInterfaceName.Stub.asInterface((IBinder)service)
至 将返回的参数转换为YourInterface
类型。 - 调用您在接口上定义的方法。一律设置陷阱
DeadObjectException
异常(在 连接中断。此外,请捕获SecurityException
异常,如果 IPC 方法调用所涉及的两个进程的 AIDL 定义存在冲突,系统会抛出此异常。 - 如需断开连接,请使用接口的实例调用
Context.unbindService()
。
调用 IPC 服务时,请牢记以下几点:
- 对象是跨进程计数的引用。
- 你可以发送匿名对象 作为方法参数
如需详细了解如何绑定到服务,请参阅绑定服务概览。
以下是一些示例代码,演示了如何调用 AIDL 创建的服务, 来自 ApiDemos 项目的远程服务示例。
Kotlin
private const val BUMP_MSG = 1 class Binding : Activity() { /** The primary interface you call on the service. */ private var mService: IRemoteService? = null /** Another interface you use on the service. */ internal var secondaryService: ISecondary? = null private lateinit var killButton: Button private lateinit var callbackText: TextView private lateinit var handler: InternalHandler private var isBound: Boolean = false /** * Class for interacting with the main interface of the service. */ private val mConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // This is called when the connection with the service is // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service) killButton.isEnabled = true callbackText.text = "Attached." // We want to monitor the service for as long as we are // connected to it. try { mService?.registerCallback(mCallback) } catch (e: RemoteException) { // In this case, the service crashes before we can // do anything with it. We can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText( this@Binding, R.string.remote_service_connected, Toast.LENGTH_SHORT ).show() } override fun onServiceDisconnected(className: ComponentName) { // This is called when the connection with the service is // unexpectedly disconnected—that is, its process crashed. mService = null killButton.isEnabled = false callbackText.text = "Disconnected." // As part of the sample, tell the user what happened. Toast.makeText( this@Binding, R.string.remote_service_disconnected, Toast.LENGTH_SHORT ).show() } } /** * Class for interacting with the secondary interface of the service. */ private val secondaryConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // Connecting to a secondary interface is the same as any // other interface. secondaryService = ISecondary.Stub.asInterface(service) killButton.isEnabled = true } override fun onServiceDisconnected(className: ComponentName) { secondaryService = null killButton.isEnabled = false } } private val mBindListener = View.OnClickListener { // Establish a couple connections with the service, binding // by interface names. This lets other applications be // installed that replace the remote service by implementing // the same interface. val intent = Intent(this@Binding, RemoteService::class.java) intent.action = IRemoteService::class.java.name bindService(intent, mConnection, Context.BIND_AUTO_CREATE) intent.action = ISecondary::class.java.name bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE) isBound = true callbackText.text = "Binding." } private val unbindListener = View.OnClickListener { if (isBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. try { mService?.unregisterCallback(mCallback) } catch (e: RemoteException) { // There is nothing special we need to do if the service // crashes. } // Detach our existing connection. unbindService(mConnection) unbindService(secondaryConnection) killButton.isEnabled = false isBound = false callbackText.text = "Unbinding." } } private val killListener = View.OnClickListener { // To kill the process hosting the service, we need to know its // PID. Conveniently, the service has a call that returns // that information. try { secondaryService?.pid?.also { pid -> // Note that, though this API lets us request to // kill any process based on its PID, the kernel // still imposes standard restrictions on which PIDs you // can actually kill. Typically this means only // the process running your application and any additional // processes created by that app, as shown here. Packages // sharing a common UID are also able to kill each // other's processes. Process.killProcess(pid) callbackText.text = "Killed service process." } } catch (ex: RemoteException) { // Recover gracefully from the process hosting the // server dying. // For purposes of this sample, put up a notification. Toast.makeText(this@Binding, R.string.remote_call_failed, Toast.LENGTH_SHORT).show() } } // ---------------------------------------------------------------------- // Code showing how to deal with callbacks. // ---------------------------------------------------------------------- /** * This implementation is used to receive callbacks from the remote * service. */ private val mCallback = object : IRemoteServiceCallback.Stub() { /** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here is * NOT running in our main thread like most other things. So, * to update the UI, we need to use a Handler to hop over there. */ override fun valueChanged(value: Int) { handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0)) } } /** * Standard initialization of this activity. Set up the UI, then wait * for the user to interact with it before doing anything. */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.remote_service_binding) // Watch for button taps. var button: Button = findViewById(R.id.bind) button.setOnClickListener(mBindListener) button = findViewById(R.id.unbind) button.setOnClickListener(unbindListener) killButton = findViewById(R.id.kill) killButton.setOnClickListener(killListener) killButton.isEnabled = false callbackText = findViewById(R.id.callback) callbackText.text = "Not attached." handler = InternalHandler(callbackText) } private class InternalHandler( textView: TextView, private val weakTextView: WeakReference<TextView> = WeakReference(textView) ) : Handler() { override fun handleMessage(msg: Message) { when (msg.what) { BUMP_MSG -> weakTextView.get()?.text = "Received from service: ${msg.arg1}" else -> super.handleMessage(msg) } } } }
Java
public static class Binding extends Activity { /** The primary interface we are calling on the service. */ IRemoteService mService = null; /** Another interface we use on the service. */ ISecondary secondaryService = null; Button killButton; TextView callbackText; private InternalHandler handler; private boolean isBound; /** * Standard initialization of this activity. Set up the UI, then wait * for the user to interact with it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_binding); // Watch for button taps. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(unbindListener); killButton = (Button)findViewById(R.id.kill); killButton.setOnClickListener(killListener); killButton.setEnabled(false); callbackText = (TextView)findViewById(R.id.callback); callbackText.setText("Not attached."); handler = new InternalHandler(callbackText); } /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service is // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service); killButton.setEnabled(true); callbackText.setText("Attached."); // We want to monitor the service for as long as we are // connected to it. try { mService.registerCallback(mCallback); } catch (RemoteException e) { // In this case the service crashes before we can even // do anything with it. We can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service is // unexpectedly disconnected—that is, its process crashed. mService = null; killButton.setEnabled(false); callbackText.setText("Disconnected."); // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); } }; /** * Class for interacting with the secondary interface of the service. */ private ServiceConnection secondaryConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // Connecting to a secondary interface is the same as any // other interface. secondaryService = ISecondary.Stub.asInterface(service); killButton.setEnabled(true); } public void onServiceDisconnected(ComponentName className) { secondaryService = null; killButton.setEnabled(false); } }; private OnClickListener mBindListener = new OnClickListener() { public void onClick(View v) { // Establish a couple connections with the service, binding // by interface names. This lets other applications be // installed that replace the remote service by implementing // the same interface. Intent intent = new Intent(Binding.this, RemoteService.class); intent.setAction(IRemoteService.class.getName()); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); intent.setAction(ISecondary.class.getName()); bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE); isBound = true; callbackText.setText("Binding."); } }; private OnClickListener unbindListener = new OnClickListener() { public void onClick(View v) { if (isBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mService != null) { try { mService.unregisterCallback(mCallback); } catch (RemoteException e) { // There is nothing special we need to do if the service // crashes. } } // Detach our existing connection. unbindService(mConnection); unbindService(secondaryConnection); killButton.setEnabled(false); isBound = false; callbackText.setText("Unbinding."); } } }; private OnClickListener killListener = new OnClickListener() { public void onClick(View v) { // To kill the process hosting our service, we need to know its // PID. Conveniently, our service has a call that returns // that information. if (secondaryService != null) { try { int pid = secondaryService.getPid(); // Note that, though this API lets us request to // kill any process based on its PID, the kernel // still imposes standard restrictions on which PIDs you // can actually kill. Typically this means only // the process running your application and any additional // processes created by that app as shown here. Packages // sharing a common UID are also able to kill each // other's processes. Process.killProcess(pid); callbackText.setText("Killed service process."); } catch (RemoteException ex) { // Recover gracefully from the process hosting the // server dying. // For purposes of this sample, put up a notification. Toast.makeText(Binding.this, R.string.remote_call_failed, Toast.LENGTH_SHORT).show(); } } } }; // ---------------------------------------------------------------------- // Code showing how to deal with callbacks. // ---------------------------------------------------------------------- /** * This implementation is used to receive callbacks from the remote * service. */ private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() { /** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here is * NOT running in our main thread like most other things. So, * to update the UI, we need to use a Handler to hop over there. */ public void valueChanged(int value) { handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0)); } }; private static final int BUMP_MSG = 1; private static class InternalHandler extends Handler { private final WeakReference<TextView> weakTextView; InternalHandler(TextView textView) { weakTextView = new WeakReference<>(textView); } @Override public void handleMessage(Message msg) { switch (msg.what) { case BUMP_MSG: TextView textView = weakTextView.get(); if (textView != null) { textView.setText("Received from service: " + msg.arg1); } break; default: super.handleMessage(msg); } } } }