Android Binder机制完全解析

行是知之始,知是行之成。这篇文章主要讲述Android Binder机制完全解析相关的知识,希望能为你提供帮助。
概述 之前我写过一篇文章Android Service全面解析, 简单实现了如何通过AIDL实现Service的跨进程通信( IPC) , 其实是通过Binder机制来实现的, 本文我们就重点来看看Binder机制的原理。
Binder可以提供系统中任何程序都可以访问的全局服务。这个功能当然是任何系统都应该提供的, 下面我们简单看一下android的Binder的框架:
Android Binder框架分为服务器接口Binder驱动、以及客户端接口; 简单想一下, 需要提供一个全局服务, 那么全局服务那端即是服务器接口, 任何程序即客户端接口, 它们之间通过一个Binder驱动访问。

  • 服务器接口: 实际上是Binder类的对象, 该对象一旦创建, 内部则会启动一个隐藏线程, 接收Binder驱动发送的消息, 收到消息后, 会执行Binder对象中的onTransact()函数, 并按照该函数的参数执行不同的服务器端代码。
  • Binder驱动: 该对象也为Binder类的实例, 客户端通过该对象访问远程服务。
  • 客户端接口: 获得Binder驱动, 调用其transact()发送消息至服务器。
实例实现 如果你觉得上面的描述太抽象了, 没关系, 下面我们通过一个具体的例子来看看Binder机制的原理。例子我仍然使用上一篇文章的例子, 不过之前我是使用Eclipse创建工程, 今天我们使用Studio来创建项目, 效果都是一样的。
( 1) Studio创建两个Module, app代表客户端程序, binder_server代表服务器端程序。
Android Binder机制完全解析

文章图片

( 2) 创建aidl文件
在app目录上右键, NEW-> AIDL-> AIDL File, 创建一个aidl文件( IMyAidlInterface.aidl) , 同时必须要指明包名, 包名必须和java目录下的包名一致。此aidl文件会默认生成到aidl目录下, aidl目录和java目录同级别。
Android Binder机制完全解析

文章图片

IMyAidlInterface.aidl文件内容:
interface IMyAidlInterface { int plus(int a, int b); String toUpperCase(String str); }

build一下, 会自动生成IMyAidlInterface.java文件, 不同于Eclipse的gen目录, studio下的java文件目录为:
* (项目名)\\app\\build\\generated\\source\\aidl\\debug\\com\\hx\\binder\\IMyAidlInterface.java
关于IMyAidlInterface.java文件内容, 我们后面会具体分析, 这里先省略。
( 3) 将aidl文件连同目录一起拷贝到服务器端
Android Binder机制完全解析

文章图片

【Android Binder机制完全解析】( 4) 服务器端新建服务MyRemoteService
public class MyRemoteService extends Service {@ Override public void onCreate() { super.onCreate(); MainActivity.showlog(" onCreate()" ); }@ Override public int onStartCommand(Intent intent, int flags, int startId) { MainActivity.showlog(" onStartCommand()" ); return super.onStartCommand(intent, flags, startId); }@ Override public void onDestroy() { super.onDestroy(); MainActivity.showlog(" onDestroy()" ); }@ Override public IBinder onBind(Intent intent) { MainActivity.showlog(" onBind()" ); return mBinder; }@ Override public boolean onUnbind(Intent intent) { MainActivity.showlog(" onUnbind()" ); return super.onUnbind(intent); }IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @ Override public String toUpperCase(String str) throws RemoteException { if (str != null) { return str.toUpperCase(); } return null; }@ Override public int plus(int a, int b) throws RemoteException { return a + b; } }; }

在Manifest中进行注册:
< service android:name= " .MyRemoteService" android:exported= " true" > < intent-filter> < action android:name= " com.hx.action.remoteService" /> < /intent-filter> < /service>

( 4) 编写客户端代码
public class MainActivity extends Activity implements View.OnClickListener {private Button bindService; private Button unbindService; private Button plus; private Button toUpperCase; private IMyAidlInterface myAIDLInterface; private ServiceConnection connection = new ServiceConnection() { @ Override public void onServiceDisconnected(ComponentName name) { myAIDLInterface = null; Toast.makeText(MainActivity.this, " onServiceDisconnected" , Toast.LENGTH_SHORT).show(); }@ Override public void onServiceConnected(ComponentName name, IBinder service) { myAIDLInterface = IMyAidlInterface.Stub.asInterface(service); Toast.makeText(MainActivity.this, " onServiceConnected" , Toast.LENGTH_SHORT).show(); } }; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService = (Button) findViewById(R.id.bind_service); unbindService = (Button) findViewById(R.id.unbind_service); plus = (Button) findViewById(R.id.plus); toUpperCase = (Button) findViewById(R.id.toUpperCase); //button点击事件 bindService.setOnClickListener(this); unbindService.setOnClickListener(this); plus.setOnClickListener(this); toUpperCase.setOnClickListener(this); }@ Override public void onClick(View v) { switch (v.getId()) { case R.id.bind_service: Intent intent = new Intent(" com.hx.action.remoteService" ); //5.0以上安卓设备, service intent必须为显式指出 Intent eintent = new Intent(getExplicitIntent(this,intent)); bindService(eintent, connection, Context.BIND_AUTO_CREATE); //bindService(intent, connection, BIND_AUTO_CREATE); break; case R.id.unbind_service: if(myAIDLInterface != null){ unbindService(connection); } break; case R.id.plus: if (myAIDLInterface != null) { try { int result = myAIDLInterface.plus(13, 19); Toast.makeText(this, result + " " , Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } } else { Toast.makeText(this, " 服务器被异常杀死, 请重新绑定服务端" , Toast.LENGTH_SHORT).show(); } break; case R.id.toUpperCase: if (myAIDLInterface != null) { try { String upperStr = myAIDLInterface.toUpperCase(" hello aidl service" ); Toast.makeText(this, upperStr + " " , Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } } else { Toast.makeText(this, " 服务器被异常杀死, 请重新绑定服务端" , Toast.LENGTH_SHORT).show(); } break; default: break; } }public static Intent getExplicitIntent(Context context, Intent implicitIntent) { // Retrieve all services that can match the given intent PackageManager pm = context.getPackageManager(); List< ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); // Make sure only one match was found if (resolveInfo = = null || resolveInfo.size() != 1) { return null; } // Get component info and create ComponentName ResolveInfo serviceInfo = resolveInfo.get(0); String packageName = serviceInfo.serviceInfo.packageName; String className = serviceInfo.serviceInfo.name; ComponentName component = new ComponentName(packageName, className); // Create a new intent. Use the old one for extras and such reuse Intent explicitIntent = new Intent(implicitIntent); // Set the component to be explicit explicitIntent.setComponent(component); return explicitIntent; } }

< ?xml version= " 1.0" encoding= " utf-8" ?> < LinearLayout xmlns:android= " http://schemas.android.com/apk/res/android" android:id= " @ + id/activity_main" android:layout_width= " match_parent" android:layout_height= " match_parent" android:paddingBottom= " @ dimen/activity_vertical_margin" android:paddingLeft= " @ dimen/activity_horizontal_margin" android:paddingRight= " @ dimen/activity_horizontal_margin" android:paddingTop= " @ dimen/activity_vertical_margin" android:orientation= " vertical" > < Button android:id= " @ + id/bind_service" android:layout_width= " match_parent" android:layout_height= " wrap_content" android:text= " bind service" /> < Button android:id= " @ + id/unbind_service" android:layout_width= " match_parent" android:layout_height= " wrap_content" android:text= " unbind service" /> < Button android:id= " @ + id/plus" android:layout_width= " match_parent" android:layout_height= " wrap_content" android:text= " 13 + 19" /> < Button android:id= " @ + id/toUpperCase" android:layout_width= " match_parent" android:layout_height= " wrap_content" android:textAllCaps= " false" android:text= " hello aidl service" /> < /LinearLayout>

运行程序, 看效果:
Android Binder机制完全解析

文章图片

我们首先点击BIND SERVICE按钮, 绑定服务, 会弹出“onServiceConnected”的Toast, 说明服务绑定成功, 获取到了服务器端的Binder驱动。
服务端Log:
Android Binder机制完全解析

文章图片

然后分别点击13+ 19和hello aidl service按钮, 可以通过Binder驱动调用服务端的代码并返回正确的计算结果。
最后点击UNBIND SERVICE按钮, 我们的期望是弹出“onServiceDisconnected”的Toast, 解除绑定, 实际上呢? 很遗憾没有弹出。
服务端Log:
Android Binder机制完全解析

文章图片

由于我们当前只有一个客户端绑定了此Service, 所以Service调用了onUnbind和onDestory。当我们继续点击13+ 19按钮, 发现依然可以正确执行得到结果, 也就是说即使onUnbind被调用, 连接也是不会断开的, 那么什么时候会断开连接呢?
即当服务端被异常终止的时候, 比如我们现在在手机的正在执行的程序中找到该服务, 并强行停止它:
Android Binder机制完全解析

文章图片

可以看到这时弹出了“onServiceDisconnected”的Toast, 说明连接被断开。之后再次点击13+ 19按钮, 则会弹出Toast提示“服务器被异常杀死, 请重新绑定服务端”。
原理分析 还记得我们上面根据aidl文件生成的Java文件吗? 我们来看看它的结构吧:
public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = " com.hx.binder.IMyAidlInterface" ; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); }/** * Cast an IBinder object into an com.hx.binder.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.hx.binder.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj = = null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) & & (iin instanceof com.hx.binder.IMyAidlInterface))) { return ((com.hx.binder.IMyAidlInterface) iin); } return new com.hx.binder.IMyAidlInterface.Stub.Proxy(obj); }@ Override public android.os.IBinder asBinder() { return this; }@ Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_plus: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.plus(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_toUpperCase: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.toUpperCase(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }private static class Proxy implements com.hx.binder.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; }@ Override public android.os.IBinder asBinder() { return mRemote; }public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; }@ Override public int plus(int a, int b) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; }@ Override public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(str); mRemote.transact(Stub.TRANSACTION_toUpperCase, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } }static final int TRANSACTION_plus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_toUpperCase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); }public int plus(int a, int b) throws android.os.RemoteException; public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException; }

代码比较长, 但思路还是比较清晰的, IMyAidlInterface.java文件包含两个静态内部类—StubProxy( 其中Proxy是Stub的内部类) 。
public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface

其中Stub是个抽象类, 它继承了Binder, 并实现了IMyAidlInterface接口。Stub提供了几个方法: asInterfaceasBinderonTransact, 但并没有实现IMyAidlInterface接口的方法, 所以需要交给Stub的实现类去实现。
private static class Proxy implements com.hx.binder.IMyAidlInterface

Proxy是Stub的内部类, 也实现了IMyAidlInterface接口。并提供了几个方法: asBindergetInterfaceDescriptor, 并实现了IMyAidlInterface接口的方法plustoUpperCase
接下来看看服务端和客户端是如何和这个文件建立关联的吧。
服务端:
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @ Override public String toUpperCase(String str) throws RemoteException { if (str != null) { return str.toUpperCase(); } return null; }@ Override public int plus(int a, int b) throws RemoteException { return a + b; } };

可以看到我们服务端提供的服务是由IMyAidlInterface.Stub来执行的, 上面分析过, Stub这个类是Binder的子类, 是不是符合我们文章开头所说的服务端其实是一个Binder类的实例。而且mBinder实现了IMyAidlInterface接口的方法。
接下来看Stub的onTransact()方法:
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_plus: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.plus(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_toUpperCase: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.toUpperCase(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }

文章开头也说到服务端的Binder实例会根据客户端依靠Binder驱动发来的消息, 执行onTransact方法, 然后由其参数决定执行服务端的代码。
可以看到onTransact有四个参数: code , data , replay , flags
  • code: 是一个整形的唯一标识, 用于区分执行哪个方法, 客户端会传递此参数, 告诉服务端执行哪个方法
  • data: 客户端传递过来的参数
  • reply: 服务器返回回去的值
  • flags: 标明是否有返回值, 0为有( 双向) , 1为没有( 单向)
我们仔细看case TRANSACTION_plus中的代码:
data.enforceInterface(DESCRIPTOR);

与客户端的writeInterfaceToken对应, 标识远程服务的名称
int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt();

接下来分别读取了客户端传入的两个参数
int _result = this.plus(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result);

然后执行this.plus, 即我们服务端实现的plus方法; 返回result由reply写回。
toUpperCase同理, 可以看到服务端通过AIDL生成的Stub类, 封装了服务端本来需要写的代码。
客户端
客户端主要通过ServiceConnected与服务端连接
private ServiceConnection connection = new ServiceConnection() { @ Override public void onServiceDisconnected(ComponentName name) { isConnected = false; Toast.makeText(MainActivity.this, " onServiceDisconnected" , Toast.LENGTH_SHORT).show(); }@ Override public void onServiceConnected(ComponentName name, IBinder service) { isConnected = true; myAIDLInterface = IMyAidlInterface.Stub.asInterface(service); Toast.makeText(MainActivity.this, " onServiceConnected" , Toast.LENGTH_SHORT).show(); } };

其实这个onServiceConnected中的IBinder实例, 其实就是我们文章开头所说的Binder驱动, 也是一个Binder实例。
在IMyAidlInterface.Stub.asInterface中最终调用了:
return new com.hx.binder.IMyAidlInterface.Stub.Proxy(obj);

这个Proxy实例传入了我们的Binder驱动, 并且封装了我们调用服务端的代码, 文章开头说, 客户端会通过Binder驱动的transact()方法调用服务端代码。
直接看Proxy中的plus方法
public int plus(int a, int b) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; }

首先声明两个Parcel对象, 一个用于传递数据, 一个用户接收返回的数据
_data.writeInterfaceToken(DESCRIPTOR);

与服务器端的enforceInterfac对应
_data.writeInt(a); _data.writeInt(b);

写入需要传递的参数
mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);

终于看到了我们的transact方法, 第一个对应服务端的code,_data,_reply分别对应服务端的data, reply, 0表示是双向的
_reply.readException(); _result = _reply.readInt();

最后读出我们服务端返回的数据, 然后return。可以看到和服务端的onTransact基本是一行一行对应的。
注意:
  • 当客户端调用transact方法发起RPC( 远程过程调用) 请求后, 当前线程会挂起, 等待服务器端的返回结果。所以如果一个远程方法很耗时, 那么不能再UI线程中调用此远程方法。
  • 服务端的Binder方法( onTransact) 运行在Binder线程池中, 所以Binder方法不管是否耗时, 都应该采取同步机制, 因为它已经运行在一个线程中了。
到此, 我们已经通过AIDL生成的代码解释了Android Binder框架的工作原理。Service的作用其实就是为我们创建Binder驱动, 即服务端与客户端连接的桥梁。
AIDL其实通过我们写的aidl文件, 帮助我们生成了一个接口, 一个Stub类用于服务端, 一个Proxy类用于客户端调用。
Android Binder机制完全解析

文章图片

不依赖AIDL实现IPC通信 那么我们是否可以不通过写aidl文件来实现远程的通信呢? 下面向大家展示如何完全不依赖AIDL来实现客户端与服务端的通信。
服务端代码:
public class MyRemoteService extends Service { private static final String DESCRIPTOR = " MyRemoteService" ; private static final int TRANSACTION_plus = 0x110; private static final int TRANSACTION_toUpperCase = 0x111; @ Override public void onCreate() { super.onCreate(); MainActivity.showlog(" onCreate()" ); }@ Override public int onStartCommand(Intent intent, int flags, int startId) { MainActivity.showlog(" onStartCommand()" ); return super.onStartCommand(intent, flags, startId); }@ Override public void onDestroy() { super.onDestroy(); MainActivity.showlog(" onDestroy()" ); }@ Override public IBinder onBind(Intent intent) { MainActivity.showlog(" onBind()" ); return mBinder; }@ Override public boolean onUnbind(Intent intent) { MainActivity.showlog(" onUnbind()" ); return super.onUnbind(intent); }private MyBinder mBinder = new MyBinder(); private class MyBinder extends Binder { @ Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_plus: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = _arg0 + _arg1; reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_toUpperCase: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = _arg0.toUpperCase(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }}; }

< service android:name= " .MyRemoteService" android:exported= " true" > < intent-filter> < action android:name= " com.hx.action.remoteService" /> < /intent-filter> < /service>

客户端代码:
public class MainActivity extends Activity implements View.OnClickListener { private Button bindService; private Button unbindService; private Button plus; private Button toUpperCase; private IBinder myBinder; private static final String DESCRIPTOR = " MyRemoteService" ; private static final int TRANSACTION_plus = 0x110; private static final int TRANSACTION_toUpperCase = 0x111; private ServiceConnection connection = new ServiceConnection() { @ Override public void onServiceDisconnected(ComponentName name) { myBinder = null; Toast.makeText(MainActivity.this, " onServiceDisconnected" , Toast.LENGTH_SHORT).show(); }@ Override public void onServiceConnected(ComponentName name, IBinder service) { myBinder = service; Toast.makeText(MainActivity.this, " onServiceConnected" , Toast.LENGTH_SHORT).show(); } }; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService = (Button) findViewById(R.id.bind_service); unbindService = (Button) findViewById(R.id.unbind_service); plus = (Button) findViewById(R.id.plus); toUpperCase = (Button) findViewById(R.id.toUpperCase); //button点击事件 bindService.setOnClickListener(this); unbindService.setOnClickListener(this); plus.setOnClickListener(this); toUpperCase.setOnClickListener(this); }@ Override public void onClick(View v) { switch (v.getId()) { case R.id.bind_service: Intent intent = new Intent(" com.hx.action.remoteService" ); //5.0以上安卓设备, service intent必须为显式指出 Intent eintent = new Intent(getExplicitIntent(this,intent)); bindService(eintent, connection, Context.BIND_AUTO_CREATE); //bindService(intent, connection, BIND_AUTO_CREATE); break; case R.id.unbind_service: if(myBinder != null){ unbindService(connection); } break; case R.id.plus: if (myBinder != null) { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(78); _data.writeInt(95); myBinder.transact(TRANSACTION_plus, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); Toast.makeText(this, _result + " " , Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } finally { _reply.recycle(); _data.recycle(); } } else { Toast.makeText(this, " 服务器被异常杀死, 请重新绑定服务端" , Toast.LENGTH_SHORT).show(); } break; case R.id.toUpperCase: if (myBinder != null) { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(" my new programe" ); myBinder.transact(TRANSACTION_toUpperCase, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); Toast.makeText(this, _result + " " , Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } finally { _reply.recycle(); _data.recycle(); } } else { Toast.makeText(this, " 服务器被异常杀死, 请重新绑定服务端" , Toast.LENGTH_SHORT).show(); } break; default: break; } }public static Intent getExplicitIntent(Context context, Intent implicitIntent) { // Retrieve all services that can match the given intent PackageManager pm = context.getPackageManager(); List< ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); // Make sure only one match was found if (resolveInfo = = null || resolveInfo.size() != 1) { return null; } // Get component info and create ComponentName ResolveInfo serviceInfo = resolveInfo.get(0); String packageName = serviceInfo.serviceInfo.packageName; String className = serviceInfo.serviceInfo.name; ComponentName component = new ComponentName(packageName, className); // Create a new intent. Use the old one for extras and such reuse Intent explicitIntent = new Intent(implicitIntent); // Set the component to be explicit explicitIntent.setComponent(component); return explicitIntent; } }

< ?xml version= " 1.0" encoding= " utf-8" ?> < LinearLayout xmlns:android= " http://schemas.android.com/apk/res/android" android:id= " @ + id/activity_main" android:layout_width= " match_parent" android:layout_height= " match_parent" android:paddingBottom= " @ dimen/activity_vertical_margin" android:paddingLeft= " @ dimen/activity_horizontal_margin" android:paddingRight= " @ dimen/activity_horizontal_margin" android:paddingTop= " @ dimen/activity_vertical_margin" android:orientation= " vertical" > < Button android:id= " @ + id/bind_service" android:layout_width= " match_parent" android:layout_height= " wrap_content" android:text= " bind service" /> < Button android:id= " @ + id/unbind_service" android:layout_width= " match_parent"

    推荐阅读