参考: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); } } }; }
原理:
http://chenfeng0104.iteye.com/blog/1255302
1.IRemoteService.Stub.asInterface(service)在本地创建了一个代理
2.当使用remoteService调用方法时,其实是调用了本地com.example.aidl.IRemoteService.Stub.Proxy对象的方法,从Proxy方法中可以看到,每个方法都执行了transact。这一过程是把Client端的参数转换成Parcel(_data)传递到Server端。
3.transact 会调用onTransact方法,,而在Server端又会把返回数据保存到_reply中,这就形成了一次交互
说明:
1.来自本地进程的调用会在与调用者相同的线程中被执行。如果这是主UI线程,那么线程会在AIDL接口中继续执行。如果是另外一个线程,那么调用例程是在服务中要执行代码之一。因此,只有正在访问服务的本地线程,才能够完全控制调用会在哪个线程中执行(但是,这个中情况下,完全不需要使用AIDL,而是应该通过实现Binder来创建接口)。
2.来自远程进程的调用会被分配到一个由平台维护的进程内部的线程池中。你必须为来自未知的、同时发生的多次调用的线程做好准备。换句话说,AIDL接口的实现必须是线程安全的。
3.oneway关键次是用来修饰远程调用行为的。当使用该关键词时,远程调用不是阻塞的,它只是发送事物数据并立即返回。接口的实现最终实现是把普通的远程调用按照Binder线程池的调用规则来接收的。如果oneway是使用在本地调用上,那么不会有任何影响,并且调用依然是异步的