android service aidl 理解
参考:http://developer.android.com/guide/components/aidl.html
http://developer.android.com/guide/topics/manifest/service-element.html
service 属性基础
SYNTAX: <service android:enabled=["true" | "false"] android:exported=["true" | "false"] android:icon="drawable resource" android:isolatedProcess=["true" | "false"] android:label="string resource" android:name="string" android:permission="string" android:process="string" > . . . </service> CONTAINED IN: <application> CAN CONTAIN: <intent-filter> <meta-data> DESCRIPTION: Declares a service (a Service subclass) as one of the application's components. Unlike activities, services lack a visual user interface. They're used to implement long-running background operations or a rich communications API that can be called by other applications. All services must be represented by <service> elements in the manifest file. Any that are not declared there will not be seen by the system and will never be run. ATTRIBUTES: android:enabled Whether or not the service can be instantiated by the system — "true" if it can be, and "false" if not. The default value is "true". The <application> element has its own enabled attribute that applies to all application components, including services. The <application> and <service> attributes must both be "true" (as they both are by default) for the service to be enabled. If either is "false", the service is disabled; it cannot be instantiated. android:exported Whether or not components of other applications can invoke the service or interact with it — "true" if they can, and "false" if not. When the value is "false", only components of the same application or applications with the same user ID can start the service or bind to it. The default value depends on whether the service contains intent filters. The absence of any filters means that it can be invoked only by specifying its exact class name. This implies that the service is intended only for application-internal use (since others would not know the class name). So in this case, the default value is "false". On the other hand, the presence of at least one filter implies that the service is intended for external use, so the default value is "true". This attribute is not the only way to limit the exposure of a service to other applications. You can also use a permission to limit the external entities that can interact with the service (see the permission attribute). android:icon An icon representing the service. This attribute must be set as a reference to a drawable resource containing the image definition. If it is not set, the icon specified for the application as a whole is used instead (see the <application> element's icon attribute). The service's icon — whether set here or by the <application> element — is also the default icon for all the service's intent filters (see the <intent-filter> element's icon attribute). android:isolatedProcess If set to true, this service will run under a special process that is isolated from the rest of the system and has no permissions of its own. The only communication with it is through the Service API (binding and starting). android:label A name for the service that can be displayed to users. If this attribute is not set, the label set for the application as a whole is used instead (see the <application> element's label attribute). The service's label — whether set here or by the <application> element — is also the default label for all the service's intent filters (see the <intent-filter> element's label attribute). The label should be set as a reference to a string resource, so that it can be localized like other strings in the user interface. However, as a convenience while you're developing the application, it can also be set as a raw string. android:name The name of the Service subclass that implements the service. This should be a fully qualified class name (such as, "com.example.project.RoomService"). However, as a shorthand, if the first character of the name is a period (for example, ".RoomService"), it is appended to the package name specified in the <manifest> element. Once you publish your application, you should not change this name (unless you've set android:exported="false"). There is no default. The name must be specified. android:permission The name of a permission that that an entity must have in order to launch the service or bind to it. If a caller of startService(), bindService(), or stopService(), has not been granted this permission, the method will not work and the Intent object will not be delivered to the service. If this attribute is not set, the permission set by the <application> element's permission attribute applies to the service. If neither attribute is set, the service is not protected by a permission. For more information on permissions, see the Permissions section in the introduction and a separate document, Security and Permissions. android:process The name of the process where the service is to run. Normally, all components of an application run in the default process created for the application. It has the same name as the application package. The <application> element's process attribute can set a different default for all components. But component can override the default with its own process attribute, allowing you to spread your application across multiple processes. If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the service runs in that process. If the process name begins with a lowercase character, the service will run in a global process of that name, provided that it has permission to do so. This allows components in different applications to share a process, reducing resource usage.
android:process=":remote" 全局进程 android:process="remote" 每个应用都是一个新进程 不写 默认应用本身一个进程
activity,service通信
核心思想:相互传递数据,activity中获取service的实例或者代理,调用相关方法传递数据
步骤:
1.aidl建立 在gen\路径下生成java文件
// 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, to do evil things with it. */ 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); }
2.service中创建binder,实现onBinder方法
public class RemoteService extends Service{
@Override
publicvoid onCreate(){
super.onCreate();
}
@Override
publicIBinder onBind(Intent intent){
// Return the interface
return mBinder;
}
privatefinalIRemoteService.Stub mBinder =newIRemoteService.Stub(){
publicint getPid(){
returnProcess.myPid();
}
publicvoid basicTypes(int anInt,long aLong,boolean aBoolean,
float aFloat,double aDouble,String aString){
// Does nothing
}
};
}
3.activity中创建serviceConnect方法 得到 代理对象
IRemoteService mIRemoteService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Following the example above for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service mIRemoteService = 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"); mIRemoteService = null; } };
....bindService
bindService(new Intent(IRemoteService.class.getName()),mConnection, Context.BIND_AUTO_CREATE);
....unbindService
unbindService(mConnection);
4.调用接口方法得到所需要的
int pid = mSecondaryService.getPid();
扩展:service调用activity方法的实现
1.create callback aidl
2.service 中 activity绑定后 将 callback对象存入
final RemoteCallbackList<IRemoteServiceCallback> mCallbacks
= new RemoteCallbackList<IRemoteServiceCallback>();
3.activity 中创建 callback对象,在connectService 中传递callback对象
4.service中调用callback对象
public static class Binding extends Activity { /** The primary interface we will be calling on the service. */ IRemoteService mService = null; /** Another interface we use on the service. */ ISecondary mSecondaryService = null; Button mKillButton; TextView mCallbackText; private boolean mIsBound; /** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_binding); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); mKillButton = (Button)findViewById(R.id.kill); mKillButton.setOnClickListener(mKillListener); mKillButton.setEnabled(false); mCallbackText = (TextView)findViewById(R.id.callback); mCallbackText.setText("Not attached."); } /** * 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 has been // 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); mKillButton.setEnabled(true); mCallbackText.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 has crashed before we could 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 has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mKillButton.setEnabled(false); mCallbackText.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 mSecondaryConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // Connecting to a secondary interface is the same as any // other interface. mSecondaryService = ISecondary.Stub.asInterface(service); mKillButton.setEnabled(true); } public void onServiceDisconnected(ComponentName className) { mSecondaryService = null; mKillButton.setEnabled(false); } }; private OnClickListener mBindListener = new OnClickListener() { public void onClick(View v) { // Establish a couple connections with the service, binding // by interface names. This allows other applications to be // installed that replace the remote service by implementing // the same interface. bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE); bindService(new Intent(ISecondary.class.getName()), mSecondaryConnection, Context.BIND_AUTO_CREATE); mIsBound = true; mCallbackText.setText("Binding."); } }; private OnClickListener mUnbindListener = new OnClickListener() { public void onClick(View v) { if (mIsBound) { // 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 // has crashed. } } // Detach our existing connection. unbindService(mConnection); unbindService(mSecondaryConnection); mKillButton.setEnabled(false); mIsBound = false; mCallbackText.setText("Unbinding."); } } }; private OnClickListener mKillListener = 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 will return // to us that information. if (mSecondaryService != null) { try { int pid = mSecondaryService.getPid(); // Note that, though this API allows us to request to // kill any process based on its PID, the kernel will // still impose standard restrictions on which PIDs you // are actually able to 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 will also be able to kill each // other's processes. Process.killProcess(pid); mCallbackText.setText("Killed service process."); } catch (RemoteException ex) { // Recover gracefully from the process hosting the // server dying. // Just for purposes of the 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 will * NOT be 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) { mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0)); } }; private static final int BUMP_MSG = 1; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case BUMP_MSG: mCallbackText.setText("Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } }; }
扩展:如果要传递的参数不是基础类型,那就需要对其进行包装,成为parcelable的实例,再用aidl接口申明一下。
package com.mobogenie.assistant.entity; import android.os.Parcel; import android.os.Parcelable; import com.mobogenie.assistant.io.client.manager.ClientAuthAllowStatusManager; import com.mobogenie.assistant.io.client.manager.ClientAuthAllowStatusManager.AuthAllowStatus; import com.mobogenie.assistant.io.client.manager.update.BaseUpdateEntityManager.ClientUpdateStatus; public class SocketClientRemoteInfo implements Parcelable { // name private String name; // 是否是wifi连接 private boolean isWifiClient; //ip地址 private String ipAdd; //是否验证通过 boolean isEnable = true; AuthAllowStatus authStatus = AuthAllowStatus.WAITING; ClientUpdateStatus clientUpdateStatus = ClientUpdateStatus.None; public SocketClientRemoteInfo(String name, boolean isWifiClient,String ipAdd,AuthAllowStatus authStatus,ClientUpdateStatus clientUpdateStatus) { this.name = name; this.isWifiClient = isWifiClient; this.ipAdd = ipAdd; this.authStatus = authStatus; this.clientUpdateStatus = clientUpdateStatus; this.isEnable = (authStatus==AuthAllowStatus.ALLOW); } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isWifiClient() { return isWifiClient; } public void setWifiClient(boolean isWifiClient) { this.isWifiClient = isWifiClient; } public String getIpAdd() { return ipAdd; } public void setIpAdd(String ipAdd) { this.ipAdd = ipAdd; } public boolean isEnable() { return isEnable; } public void setEnable(boolean isEnable) { this.isEnable = isEnable; } public AuthAllowStatus getAuthStatus() { return authStatus; } public void setAuthStatus(AuthAllowStatus authStatus) { this.authStatus = authStatus; } public ClientUpdateStatus getClientUpdateStatus() { return clientUpdateStatus; } public void setClientUpdateStatus(ClientUpdateStatus clientUpdateStatus) { this.clientUpdateStatus = clientUpdateStatus; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(isWifiClient ? 0 : 1); dest.writeString(ipAdd); dest.writeString(authStatus.name()); dest.writeString(clientUpdateStatus.name()); } // 添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口 public static final Parcelable.Creator<SocketClientRemoteInfo> CREATOR = new Parcelable.Creator<SocketClientRemoteInfo>() { @Override public SocketClientRemoteInfo createFromParcel(Parcel source) {// 从Parcel中读取数据 String name = source.readString(); boolean isWifiClient = source.readInt() == 0 ? true : false; String ipAdd = source.readString(); String authStatusName = source.readString(); AuthAllowStatus authStatus= AuthAllowStatus.WAITING; try { authStatus = AuthAllowStatus.valueOf(authStatusName); }catch(Exception e){ e.printStackTrace(); } String clientUpdateStatusName = source.readString(); ClientUpdateStatus clientUpdateStatus= ClientUpdateStatus.None; try { clientUpdateStatus = ClientUpdateStatus.valueOf(clientUpdateStatusName); }catch(Exception e){ e.printStackTrace(); } return new SocketClientRemoteInfo(name,isWifiClient,ipAdd,authStatus,clientUpdateStatus); } @Override public SocketClientRemoteInfo[] newArray(int size) { return new SocketClientRemoteInfo[size]; } }; }
SocketClientRemoteInfo.aidl文件声明如下
package com.mobogenie.assistant.entity; parcelable SocketClientRemoteInfo;
则在其他aidl接口中就可以正常使用了
interface IRemoteServiceManager { /** * 获取所有连接状态的clients */ List<SocketClientRemoteInfo> getConnectClients(); }
扩展:如果绑定本地服务调用则不需要用的aidl
在service中:继承Binder建立BaseBinder类 建立方法 getService 返回service实例。
public class BaseBinderService extends Service { ... ... BaseBinder mBindler; public class BaseBinder extends Binder { public BaseBinderService getService() { return BaseBinderService.this; } } @Override public IBinder onBind(Intent intent) { if (intent.getAction().contains( IBaseBinderService.intentAction_BINDERSERVICE)) { return mBindler; } } ... ... }
Activity中 调用 bindService 实现 serviceConnection 类 中 连接方法中可取得Service实例(通过上面自己写的getServcie方法)可以随意调用service的方法了 想实现service到activity的回调 则自己建立回调函数 注册(赋值传递)到service里 ,servcie里自行调用即可
private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mService = null; Log.i("The connection with the MiguService has been unexceptedly disconnected!"); // update the task Message message = mHandler.obtainMessage(); message.what = IBaseBinderService.MESSAGE_UNBIND_SERVICE; mHandler.sendMessage(message); } @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = ((BaseBinderService.BaseBinder) service).getService(); Log.i("The connection with the MiguService has been connected!"); if (mService == null) { return; } // update the task Message message = mHandler.obtainMessage(); message.what = IBaseBinderService.MESSAGE_BIND_SERVICE; mHandler.sendMessage(message); } };
原理:
http://chenfeng0104.iteye.com/blog/1255302
http://www.cnblogs.com/noTice520/archive/2012/11/01/2750209.html
public interface ISecondary extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.android.apis.app.ISecondary { private static final java.lang.String DESCRIPTOR = "com.example.android.apis.app.ISecondary"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an * com.example.android.apis.app.ISecondary interface, generating a proxy * if needed. */ public static com.example.android.apis.app.ISecondary asInterface( android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = (android.os.IInterface) obj .queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.android.apis.app.ISecondary))) { return ((com.example.android.apis.app.ISecondary) iin); } return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj); } 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_getPid: { data.enforceInterface(DESCRIPTOR); int _result = this.getPid(); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 != data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.android.apis.app.ISecondary { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } /** * Request the PID of this service, to do evil things with it. */ public int getPid() 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); mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } /** * This demonstrates the basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean) ? (1) : (0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } /** * Request the PID of this service, to do evil things with it. */ public int getPid() throws android.os.RemoteException; /** * This demonstrates the basic types that you can use as parameters and * return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; }
分析:
类结构:
接口ISecondary{
抽象类Stub 继承 Binder 实现 ISecondary{
类Proxy 实现Isecondary{
}
}
}
在Activity SeviceConnect 调用 ISencond.stub.asInteface 接口获取ISencond实例 分析 asInteface方法:
public static com.example.android.apis.app.ISecondary asInterface( android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = (android.os.IInterface) obj .queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.android.apis.app.ISecondary))) { return ((com.example.android.apis.app.ISecondary) iin); } return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj); }
查询是本地调用直接返回stub,如果是远程调用返回代理类Proxy。
至于以后调用如:mSecondaryService.getPid(),basicTypes等,本地调用直接返回实现stup抽象函数实现的方法体,远程调用看Proxy中的实现的函数。
1.将参数实例化Parcel(_data)对象,传递到Binder驱动。让客户端进程休眠,并且将传过来的pacel数据从客户端进程映射到服务端进程。
2.服务端线程调用ontransact方法解包执行相应方法操作并将返回值写入parcel(_reply)。
3.服务端线程将返回值传递给binder驱动,binder驱动重新唤醒客户端进程并把返回值传递给proxy对象,会被调用并最后被解包并作为proxy方法的返回值。
说明:
1.来自本地进程的调用会在与调用者相同的线程中被执行。如果这是主UI线程,那么线程会在AIDL接口中继续执行。如果是另外一个线程,那么调用例程是在服务中要执行代码之一。因此,只有正在访问服务的本地线程,才能够完全控制调用会在哪个线程中执行(但是,这个中情况下,完全不需要使用AIDL,而是应该通过实现Binder来创建接口)。
2.来自远程进程的调用会被分配到一个由平台维护的进程内部的线程池中。你必须为来自未知的、同时发生的多次调用的线程做好准备。换句话说,AIDL接口的实现必须是线程安全的。
3.oneway关键词是用来修饰远程调用行为的。当使用该关键词时,远程调用不是阻塞的,它只是发送事物数据并立即返回。接口的实现最终实现是把普通的远程调用按照Binder线程池的调用规则来接收的。如果oneway是使用在本地调用上,那么不会有任何影响,并且调用依然是同步的