概述
Service是一种没有UI界面,在后台执行长期任务的系统控件。
Service可以用于IPC。
android:exported false,Service只供自己应用使用;true 可以被其他应用使用
android:process 指定服务所在的进程,默认当前应用所在进程
Foreground Service
前台Service意味着用户知道Service的存在,在系统需要资源时不会kill它,前台Service必须在状态栏上提供Notification,该Notification不会消失,除非Service被停止或者其不再是前台Service。
开启 Service.startForeground(int id, Notification notification)
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
停止 Service.stopForeground(boolean removeNotification)
生命周期管理
- A Started Service 开始于startService(),结束于Service自己调用stopSelf(),或者其他组件调用stopService()
- A bound Service 开始于 bindService(),结束于所有client unbindService()
一个Service可能既被Start,又被bind,这样的情况,需要所有client unbindService,且stopSelf或者stopService。
A Started Service
其他组件调用startService()可以开启Started Service,如果该Service还未创建,执行onCreate(),否则onCreate不执行(也即只执行一遍),一个Service可以被多次start,每次都会执行onStartCommand()。
Service被start后,其与start它的组件就没有关系了,即使start它的组件被销毁了,Service也可能会存在。要停止Service,需要调用stopService()或者stopSelf(),如果Service被多个组件start,停止服务时要用stopSelf(int startId),因为如果startId不是最新的,那Service就不会被停掉,保证所有start Service的请求都能得到响应。
IntentService封装了worker-thread,可避免ANR,能满足同时只处理一个请求,继承它只需要实现onHandleIntent()即可。
onStartCommand()返回值用于描述当该方法返回时,如果服务被系统kill掉了,系统该如何对待该服务。
- START_NOT_STICKY 系统不会重新创建Service,除非有延迟的Intent(pending Intent)需要传递,对避免不必要地重新创建Service和应用重新开始未完成的任务(when your application can simply restart any unfinished jobs.),这是最安全的选择。
- START_STICKY 系统会重新创建Service,调用onStartCommand(),但不会传递最后的Intent,实际上,传递的是一个NULL的Intent,除非有pending Intent start Service(Service被kill后,有Intent startService),该选项适用与media player。
- START_REDELIVER_INTENT 重新创建Service,调用onStartCommand(),且传递最后的传递给Service的Intent,对pending Intent则按顺序传递,该选项适用于那些需要马上恢复的任务,比如文件下载。
Bound Service
- Extending the Binder class Binder实现了接口IBinder。这种方式适合Service只供自己应用使用,且运行在和client相同的进程中,通过创建Binder实例,在onBind()中返回给client,client使用返回的Binder实例可以操作Binder中的public方法和其他Binder实现类中的方法。如果Service只是供自身应用使用的后台工作线程,这是最好的技术方式。像这样创建的接口的唯一不使用的理由是Service将会被别的应用使用或者跨进程通信。
client和Service需要在同一个进程的原因是client能将返回的object类型转换,然后调用它的API。可以在返回的Binder对象中包含client可以访问的public方法,可以返回Service对象。
- Using a Messenger 这种是实现IPC的最简单的方式,因为Messenger把所有的请求放在一个队列中,由一个thread处理,不需要你处理多线程和线程安全的问题。
- Using AIDL AIDL完成所有将对象decompose到能被操作系统理解的primitives,然后在不同进程间使用(marshall)它们,其实Messenger也是基于AIDL的,只不过它同一时刻只处理一个请求,如果你需要同时处理多个请求,那就使用AIDL吧,这就需要你自己处理多线程和线程安全的问题。Messeger 和 AIDL 与 IntentService和自己实现Service的子类之间的关系类似。 使用AIDL的步骤是,创建定义了程序接口的.aidl文件,使用SDK提供的工具生成实现了接口和能够处理IPC的抽象类,在Service中继承抽象类。注意,大部分应用中,都不需要使用AIDL,因为它比较复杂并且用其他方式,比如Messenger,可以满足大部分应用的需要。
Messenger使用步骤
- 在Service中,创建Handler,用于接收client的请求,进行回调
- 使用Handler创建Messenger实例
- 在onBind()返回Messenger.getBinder()
- client用接收到的IBinder创建Messenger,用Messenger.send(Message msg)给Service发信息
- Service接收到Message后,在Handler的handleMessage()中处理
/**
* 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 object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
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;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
try {
Message msg = Message.obtain(null,
MessengerService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
msg = Message.obtain(null,
MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
mService.send(msg);
} 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.
}
AIDL
定义AIDL接口
// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, 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);
}
public interface IRemoteService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.demoproject.IRemoteService
{
...
实现接口并曝露给client,在Service中继承Stub,实现抽象方法,在onBind()返回Stub的对象。
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
}
};
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String packageName = null;
String[] packages = AIDLService.this.getPackageManager().getPackagesForUid(getCallingUid());
if(packages != null && packages.length > 0){
packageName = packages[0];
Log.d("AIDLService","onTransact packageName "+packageName);
if(!"com.package.test".equals(packageName)){
return false;
}
}
return super.onTransact(code, data, reply, flags);
}
Stub中的方法onTransact()可以用于权限管理,返回false表示权限认证失败,上面例子中如果请求访问的应用包名不是“com.package.test”,则不允许服务。
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = IRemoteService.Stub.asInterface(iBinder);
...
private int getPid(){
try {
return mService.getPid();
} catch (RemoteException e) {
e.printStackTrace();
}
}
IPC间可以传递对象,要求对象类型实现Parcelable接口,且用aidl文件中声明parcelable类,并且声明它的aidl文件要和parcelable类在同一包下。
package com.example.demoproject.aidl;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by abner on 8/13/15.
*/
public class Person implements Parcelable {
public int age;
public String name;
public Person(){
}
private Person(Parcel in){
readFromParcel(in);
}
public static Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
@Override
public Person createFromParcel(Parcel parcel) {
return new Person(parcel);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int i) {
out.writeInt(age);
out.writeString(name);
}
public void readFromParcel(Parcel in){
age = in.readInt();
name = in.readString();
}
}
package com.example.demoproject.aidl;
parcelable Person;
// IRemoteService.aidl
package com.example.demoproject.aidl;
import com.example.demoproject.aidl.Person;
// 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);
List<Person> getPerson();
void addPerson(in Person person);
}
aidl中支持的参数类型为:基本类型(int,long,char,boolean等),String,CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一个包里,比如上面的Person,尽管它和IRemoteService在同一个包中,但是还是需要显示的import进来。另外,除了基本数据类型,其他类型都需要用in,out,或者inout来表明参数是输入还是输出,抑或两者都是。基本数据类型默认in,且不能是其他,因为(You should limit the direction to what is truly needed, because marshalling parameters is expensive.)