您的位置:首页 > 移动开发 > Android开发

【Android开发】Android跨进程通讯(AIDL)官方文档及官方Demo讲解

2016-02-25 22:42 666 查看

第一章、 关于Android跨进程的思考

先来回顾一下操作系统中的一些概念。

 同一个进程中可以有多个线程,线程间通信可以直接取得地址。因为Java程序的内存分配在连续的地址空间。

 默认一个Java程序会开启一个进程,运行在JVM中。但是一个JVM是可以开启多个进程的。

 一个或多个操作系统可以开启多个JVM,多个JVM之间依赖TCP通讯的方法调用即Java的RMI(Remote Method Interface)机制,即分布式计算的模型。

具体到Android的应用场景:假如一个app中有个Service用于TCP通讯,不断接受数据和图像等,则可以将该Service设置为新的进程,这样,即便程序出现Bug死掉,后台服务仍然在运行,当程序重启时,可以继续接受数据和图像。

另外一个应用场景:将用于TCP通讯的Service独立开发成一个app,其它多个app可以通过跨进程连接到该Service,并接受数据。包含Servie的这个app就可以管理多个与之相连的app。

注意,Android不允许没有UI即没有Activity的Service出现。所以,如果Service与Activity之间需要解耦,则可以将Service放到新的线程中。Android中跨进程机制有Messenger和AIDL,应用场景如下:

 如果Servie和Activity在同一APK中,可以使用Messenger和AIDL,如图1;

 如果Servie和Activity在不同APK,只能使用AIDL,如图2;

当然,广播也是一种方式,但是广播是操作系统级的,资源消耗大,对于大量且频繁的数据通信,并不推荐使用。



图1,跨进程模式,1个app中将Service放到新的进程中



图2,跨进程模式,多个app连接远程服务(没有必要Service和Activy再放在不同的进程中,当然,也可以)。

这里的第二章到第五章来自于Android官方Training,如果无法访问,可以这里看;否则直接跳到第六章。

第二章、 前言

AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked with. It allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC). On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose(分解) their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious(单调沉闷的) to write, so Android handles it for you with AIDL.

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

Before you begin designing your AIDL interface, be aware that calls to an AIDL interface are direct function calls. You should not make assumptions about the thread in which the call occurs.(方法中不能出现线程) What happens is different depending on whether the call is from a thread in the local process or a remote process. Specifically:

• Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn’t be using AIDL at all, but should instead create the interface by implementing a Binder).方法请求和方法执行是在同一个线程中。

• Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.请求的方法被分派到不同的线程,所以这些AIDL接口必须是线程安全的。

• The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. If oneway is used with a local call, there is no impact and the call is still synchronous.使用oneway关键字修饰方法,该方法不能阻塞。假如用于本地调用,该关键词没有效力,该方法仍然能使用同步。

第三章、 Defining an AIDL Interface

You must define your AIDL interface in an .aidl file using the Java programming language syntax, then save it in the source code (in the src/ directory) of both the application hosting the service and any other application that binds to the service.

When you build each application that contains the .aidl file, the Android SDK tools generate an IBinder interface based on the .aidl file and save it in the project’s gen/ directory. The service must implement the IBinder interface as appropriate. The client applications can then bind to the service and call methods from the IBinder to perform IPC.

To create a bounded service using AIDL, follow these steps:

1. Create the .aidl file

This file defines the programming interface with method signatures.

2. Implement the interface

The Android SDK tools generate an interface in the Java programming language, based on your .aidl file. This interface has an inner abstract class named Stub that extends Binder and implements methods from your AIDL interface. You must extend the Stub class and implement the methods.

3. Expose the interface to clients

Implement a Service and override onBind() to return your implementation of the Stub class.

Caution: Any changes that you make to your AIDL interface after your first release must remain backward compatible in order to avoid breaking other applications that use your service. That is, because your .aidl file must be copied to other applications in order for them to access your service’s interface, you must maintain support for the original interface.

1. Create the .aidl file

AIDL uses a simple syntax that lets you declare an interface with one or more methods that can take parameters and return values. The parameters and return values can be of any type, even other AIDL-generated interfaces.

You must construct the .aidl file using the Java programming language. Each .aidl file must define a single interface and requires only the interface declaration and method signatures.

By default, AIDL supports the following data types:

• All primitive types in the Java programming language (such as int, long, char, boolean, and so on)

• String

• CharSequence

• List

All elements in the List must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you’ve declared. A List may optionally be used as a “generic” class (for example, List). The actual concrete class that the other side receives is always an ArrayList, although the method is generated to use the List interface.

• Map

All elements in the Map must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you’ve declared. Generic maps, (such as those of the form Map

// 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);
}


Simply save your .aidl file in your project’s src/ directory and when you build your application, the SDK tools generate the IBinder interface file in your project’s gen/ directory. The generated file name matches the .aidl file name, but with a .java extension (for example, IRemoteService.aidl results in IRemoteService.java).

If you use Eclipse, the incremental build generates the binder class almost immediately. If you do not use Eclipse, then the Ant tool generates the binder class next time you build your application—you should build your project with ant debug (or ant release) as soon as you’re finished writing the .aidl file, so that your code can link against the generated class.

2. Implement the interface

When you build your application, the Android SDK tools generate a .java interface file named after your .aidl file. The generated interface includes a subclass named Stub that is an abstract implementation of its parent interface (for example, YourInterface.Stub) and declares all the methods from the .aidl file.

Note: Stub also defines a few helper methods, most notably asInterface(), which takes an IBinder (usually the one passed to a client’s onServiceConnected() callback method) and returns an instance of the stub interface. See the section Calling an IPC Method for more details on how to make this cast.

To implement the interface generated from the .aidl, extend the generated Binder interface (for example, YourInterface.Stub) and implement the methods inherited from the .aidl file.

Here is an example implementation of an interface called IRemoteService (defined by the IRemoteService.aidl example, above) using an anonymous instance:

private final IRemoteService.Stub mBinder = 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
}
};


Now the mBinder is an instance of the Stub class (a Binder), which defines the RPC interface for the service. In the next step, this instance is exposed to clients so they can interact with the service.

There are a few rules you should be aware of when implementing your AIDL interface:

• Incoming calls are not guaranteed to be executed on the main thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.入境请求不能保证在主线程中执行,所以,你一开始就该考虑多线程,并使得你的service是线程安全的。

• By default, RPC calls are synchronous. If you know that the service takes more than a few milliseconds to complete a request, you should not call it from the activity’s main thread, because it might hang the application (Android might display an “Application is Not Responding” dialog)—you should usually call them from a separate thread in the client.

• No exceptions that you throw are sent back to the caller.不能给caller抛出异常

3. Expose the interface to clients

Once you’ve implemented the interface for your service, you need to expose it to clients so they can bind to it. To expose the interface for your service, extend Service and implement onBind() to return an instance of your class that implements the generated Stub (as discussed in the previous section). Here’s an example service that exposes the IRemoteService example interface to clients.

public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}

private final IRemoteService.Stub mBinder = 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 } };
}


Now, when a client (such as an activity) calls bindService() to connect to this service, the client’s onServiceConnected() callback receives the mBinder instance returned by the service’s onBind() method.

The client must also have access to the interface class, so if the client and service are in separate applications, then the client’s application must have a copy of the .aidl file in its src/ directory (which generates the android.os.Binder interface—providing the client access to the AIDL methods).

When the client receives the IBinder in the onServiceConnected() callback, it must call YourServiceInterface.Stub.asInterface(service) to cast the returned parameter to YourServiceInterface type. For example:

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;
}
};


For more sample code, see the RemoteService.java class in ApiDemos.

第四章、 Passing Objects over IPC

If you have a class that you would like to send from one process to another through an IPC interface, you can do that. However, you must ensure that the code for your class is available to the other side of the IPC channel and your class must support the Parcelable interface. Supporting the Parcelable interface is important because it allows the Android system to decompose objects into primitives that can be marshalled across processes.

To create a class that supports the Parcelable protocol, you must do the following:

1. Make your class implement the Parcelable interface.

2. Implement writeToParcel, which takes the current state of the object and writes it to a Parcel.

3. Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface.

4. Finally, create an .aidl file that declares your parcelable class (as shown for the Rect.aidl file, below). If you are using a custom build process, do not add the .aidl file to your build. Similar to a header file in the C language, this .aidl file isn’t compiled.

AIDL uses these methods and fields in the code it generates to marshall and unmarshall your objects.

For example, here is a Rect.aidl file to create a Rect class that’s parcelable:

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;


And here is an example of how the Rect class implements the Parcelable protocol.

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) {
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();
}
}


The marshalling in the Rect class is pretty simple. Take a look at the other methods on Parcel to see the other kinds of values you can write to a Parcel.

Warning: Don’t forget the security implications of receiving data from other processes. In this case, the Rect reads four numbers from the Parcel, but it is up to you to ensure that these are within the acceptable range of values for whatever the caller is trying to do. See Security and Permissions for more information about how to keep your application secure from malware.

第五章、 Calling an IPC Method

Here are the steps a calling class must take to call a remote interface defined with AIDL:

1. Include the .aidl file in the project src/ directory.

2. Declare an instance of the IBinder interface (generated based on the AIDL).

3. Implement ServiceConnection.

4. Call Context.bindService(), passing in your ServiceConnection implementation.

5. In your implementation of onServiceConnected(), you will receive an IBinder instance (called service). Call YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter to YourInterface type.

6. Call the methods that you defined on your interface. You should always trap DeadObjectException exceptions, which are thrown when the connection has broken; this will be the only exception thrown by remote methods.

7. To disconnect, call Context.unbindService() with the instance of your interface.

A few comments on calling an IPC service:

• Objects are reference counted across processes.

• You can send anonymous objects as method arguments.

For more information about binding to a service, read the Bound Services document.

Here is some sample code demonstrating calling an AIDL-created service, taken from the Remote Service sample in the ApiDemos project.

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.
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, 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);
}
}

};
}


第六章、 官方Demo –RemoteService

官方讲解中的例子都来自于官方Demo的一个实例。官方Demo Activity和Service是在同一个apk中,在SDK中位置是sdk\samples\android-15\ApiDemos\src\com\example\android\apis\app,文件是RemoteServic.java以及3个AIDL接口文件。

效果是Activity中绑定后台服务,并将AIDL回调对象传递给后台服务,后台服务是在新的进程中,不断递增一个int类型的数,并调用传递进来的AIDL对象中的方法,即显示在了Activity中了。可以开启模拟器(api=15)查看该Demo的运行效果。

1. AIDL设置

共有3个AIDL接口。IremoteService的参数也是AIDL接口

/**
* Example of defining an interface for calling on to a remote service
* (running in another process).
*/
interface IRemoteService {
/**
* Often you want to allow a service to call back to its clients.
* This shows how to do so, by registering a callback interface with
* the service.
*/
void registerCallback(IRemoteServiceCallback cb);

/**
* Remove a previously registered callback interface.
*/
void unregisterCallback(IRemoteServiceCallback cb);
}


在Client中实现该接口,并作为IremoteService接口中方法的参数。注意,这里的参数可以是实现了Parcelable接口的复杂对象。



/**

* Example of a callback interface used by IRemoteService to send

* synchronous notifications back to its clients. Note that this is a

* one-way interface so the server does not block waiting for the client.

*/

oneway interface IRemoteServiceCallback {

/**

* Called when the service has a new value for you.

*/

void valueChanged(int value);

}

```

*************************************************************************
可以通过AIDL获得远程服务的进程号,进而可以kill该进程。


interface ISecondary {

/**

* Request the PID of this service, to do evil things with it.

*/

int getPid();

/**
* This demonstrates the 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.  远程服务
第1步:分别实现IRemoteService和ISecondary


/**

* The IRemoteInterface is defined through IDL

*/

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {

public void registerCallback(IRemoteServiceCallback cb) {

if (cb != null) mCallbacks.register(cb);//将Client中实现的AIDL接口加入到Service的链表中

}

public void unregisterCallback(IRemoteServiceCallback cb) {

if (cb != null) mCallbacks.unregister(cb);

}

};

/**
* A secondary interface to the service.
*/
private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
public int getPid() {
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
}


};

*************************************************************************
这里要注意,Client端实现的IRemoteServiceCallback接口被添加到了链表中:
private final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();
*************************************************************************

第2步:设置服务被连接时的返回类型


@Override

public IBinder onBind(Intent intent) {

// TODO Auto-generated method stub

// Select the interface to return. If your service only implements

// a single interface, you can just return it here without checking

// the Intent.

if (IRemoteService.class.getName().equals(intent.getAction())) {

return mBinder;

}

if (ISecondary.class.getName().equals(intent.getAction())) {

return mSecondaryBinder;

}

return null;

}

第3步:服务中数据的模拟,这里是不断累加一个数,然后通知Client


/**

* Our Handler used to execute operations on the main thread. This is used

* to schedule increments of our value.

*/

private final Handler mHandler = new Handler() {

@SuppressLint(“HandlerLeak”)

@Override public void handleMessage(Message msg) {

switch (msg.what) {

// It is time to bump the value!
case REPORT_MSG: {
// Up it goes.
int value = ++mValue;

// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).valueChanged(value);//回调所有需要通知的Client接口
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();

// Repeat every 1 second.
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
} break;
default:
super.handleMessage(msg);
}
}


};

*************************************************************************
注意,这里一旦更改了值,就通知所有的Client,即调用IRemoteServiceCallback中的方法,这样,Client中就收到了通知。

第4步:其他,在Manifest中修改Service的属性,添加:remote,并加入3个Action。Action值最好采用对应的AIDL全名称。


*************************************************************************
3.  Client端
Client端最好先通过Start的方式启动Service,再通过Bind的方式绑定该服务。这样,一旦Client挂掉,Service仍然在运行。否则,仅仅通过Bind的方式启动并绑定服务,一旦Client挂掉,Service也会一起死掉(尽管是不同进程)。
第1步,初始化连接类


/* The primary interface we will be calling on the service. /

IRemoteService mService = null;

/* Another interface we use on the service. /

ISecondary mSecondaryService = null;

/**

* 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(BindingActivity.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(BindingActivity.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);
}
};


第2步:Start方式启动Service


@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
startService(new Intent(BindingActivity.this, RemoteService.class));
}


第3步:Bind方式绑定Service


bindService(new Intent(IRemoteService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(ISecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);


第4步:实现IRemoteServiceCallback


/**
* 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));
}
};


4.  完整代码


/*

* Copyright (C) 2007 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the “License”);

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an “AS IS” BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package com.example.android.apis.app;

import android.app.Activity;

import android.app.Notification;

import android.app.NotificationManager;

import android.app.PendingIntent;

import android.app.Service;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Bundle;

import android.os.RemoteException;

import android.os.Handler;

import android.os.IBinder;

import android.os.Message;

import android.os.Process;

import android.os.RemoteCallbackList;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

// Need the following import to get access to the app resources, since this

// class is in a sub-package.

import com.example.android.apis.R;

/**

* This is an example of implementing an application service that runs in a

* different process than the application. Because it can be in another

* process, we must use IPC to interact with it. The

* {@link Controller} and {@link Binding} classes

* show how to interact with the service.

*

*
Note that most applications do not need to deal with

* the complexity shown here. If your application simply has a service

* running in its own process, the {@link LocalService} sample shows a much

* simpler way to interact with it.

*/

public class RemoteService extends Service {

/**

* This is a list of callbacks that have been registered with the

* service. Note that this is package scoped (instead of private) so

* that it can be accessed more efficiently from inner classes.

*/

final RemoteCallbackList mCallbacks

= new RemoteCallbackList();

int mValue = 0;
NotificationManager mNM;

@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

// Display a notification about us starting.
showNotification();

// While this service is running, it will continually increment a
// number. Send the first message that is used to perform the
// increment.
mHandler.sendEmptyMessage(REPORT_MSG);
}

@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(R.string.remote_service_started);

// Tell the user we stopped.
Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();

// Unregister all callbacks.
mCallbacks.kill();

// Remove the next pending message to increment the counter, stopping
// the increment loop.
mHandler.removeMessages(REPORT_MSG);
}

@Override
public IBinder onBind(Intent intent) {
// Select the interface to return. If your service only implements
// a single interface, you can just return it here without checking
// the Intent.
if (IRemoteService.class.getName().equals(intent.getAction())) {
return mBinder;
}
if (ISecondary.class.getName().equals(intent.getAction())) {
return mSecondaryBinder;
}
return null;
}

/**
* The IRemoteInterface is defined through IDL
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public void registerCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
};

/** * A secondary interface to the service. */ private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() { public int getPid() { return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { }};

@Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(this, "Task removed: " + rootIntent, Toast.LENGTH_LONG).show();
}

private static final int REPORT_MSG = 1;

/**
* Our Handler used to execute operations on the main thread. This is used
* to schedule increments of our value.
*/
private final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {

// It is time to bump the value!
case REPORT_MSG: {
// Up it goes.
int value = ++mValue;

// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).valueChanged(value);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();

// Repeat every 1 second.
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
} break;
default:
super.handleMessage(msg);
}
}
};

/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.remote_service_started);

// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());

// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, Controller.class), 0);

// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.remote_service_label),
text, contentIntent);

// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
mNM.notify(R.string.remote_service_started, notification);
}

// ----------------------------------------------------------------------

/**
* <p>Example of explicitly starting and stopping the remove service.
* This demonstrates the implementation of a service that runs in a different
* process than the rest of the application, which is explicitly started and stopped
* as desired.</p>
*
* <p>Note that this is implemented as an inner class only keep the sample
* all together; typically this code would appear in some separate class.
*/
public static class Controller extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.remote_service_controller);

// Watch for button clicks.
Button button = (Button)findViewById(R.id.start);
button.setOnClickListener(mStartListener);
button = (Button)findViewById(R.id.stop);
button.setOnClickListener(mStopListener);
}

private OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
// Make sure the service is started. It will continue running
// until someone calls stopService().
// We use an action code here, instead of explictly supplying
// the component name, so that other packages can replace
// the service.
startService(new Intent(
"com.example.android.apis.app.REMOTE_SERVICE"));
}
};

private OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
// Cancel a previous call to startService(). Note that the
// service will not actually stop at this point if there are
// still bound clients.
stopService(new Intent(
"com.example.android.apis.app.REMOTE_SERVICE"));
}
};
}

// ----------------------------------------------------------------------

/**
* Example of binding and unbinding to the remote service.
* This demonstrates the implementation of a service which the client will
* bind to, interacting with it through an aidl interface.</p>
*
* <p>Note that this is implemented as an inner class only keep the sample
* all together; typically this code would appear in some separate class.
*/

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);
}
}

};
}

// ----------------------------------------------------------------------

/**
* Examples of behavior of different bind flags.</p>
*/

public static class BindingOptions extends Activity {
ServiceConnection mCurConnection;
TextView mCallbackText;

class MyServiceConnection implements ServiceConnection {
final boolean mUnbindOnDisconnect;

public MyServiceConnection() {
mUnbindOnDisconnect = false;
}

public MyServiceConnection(boolean unbindOnDisconnect) {
mUnbindOnDisconnect = unbindOnDisconnect;
}

public void onServiceConnected(ComponentName className,
IBinder service) {
if (mCurConnection != this) {
return;
}
mCallbackText.setText("Attached.");
Toast.makeText(BindingOptions.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}

public void onServiceDisconnected(ComponentName className) {
if (mCurConnection != this) {
return;
}
mCallbackText.setText("Disconnected.");
Toast.makeText(BindingOptions.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
if (mUnbindOnDisconnect) {
unbindService(this);
mCurConnection = null;
Toast.makeText(BindingOptions.this, R.string.remote_service_unbind_disconn,
Toast.LENGTH_SHORT).show();
}
}
}

/**
* 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_binding_options);

// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind_normal);
button.setOnClickListener(mBindNormalListener);
button = (Button)findViewById(R.id.bind_not_foreground);
button.setOnClickListener(mBindNotForegroundListener);
button = (Button)findViewById(R.id.bind_above_client);
button.setOnClickListener(mBindAboveClientListener);
button = (Button)findViewById(R.id.bind_allow_oom);
button.setOnClickListener(mBindAllowOomListener);
button = (Button)findViewById(R.id.bind_waive_priority);
button.setOnClickListener(mBindWaivePriorityListener);
button = (Button)findViewById(R.id.bind_important);
button.setOnClickListener(mBindImportantListener);
button = (Button)findViewById(R.id.bind_with_activity);
button.setOnClickListener(mBindWithActivityListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);

mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}

private OnClickListener mBindNormalListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection();
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE)) {
mCurConnection = conn;
}
}
};

private OnClickListener mBindNotForegroundListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection();
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) {
mCurConnection = conn;
}
}
};

private OnClickListener mBindAboveClientListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection();
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE | Context.BIND_ABOVE_CLIENT)) {
mCurConnection = conn;
}
}
};

private OnClickListener mBindAllowOomListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection();
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
mCurConnection = conn;
}
}
};

private OnClickListener mBindWaivePriorityListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection(true);
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY)) {
mCurConnection = conn;
}
}
};

private OnClickListener mBindImportantListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection();
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) {
mCurConnection = conn;
}
}
};

private OnClickListener mBindWithActivityListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
ServiceConnection conn = new MyServiceConnection();
if (bindService(new Intent(IRemoteService.class.getName()),
conn, Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY
| Context.BIND_WAIVE_PRIORITY)) {
mCurConnection = conn;
}
}
};

private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mCurConnection != null) {
unbindService(mCurConnection);
mCurConnection = null;
}
}
};
}


}

“`
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: