Service是一个长时间操作的后台服务,也可以做IPC操作。
Service有两种启动模式:Started和Bound。所谓”started”就是通过调用startService()而Bound就是通过调用bindService()。
通过Service的生命周期可以得到Server的几个重要的回调函数:
A started service
Service从startService()开始启动,然后独立运行,必须调用stopSelf()或者stopService()来停止Service。当Service停止后,系统会销毁它。
A bound service
Service通过其它足交调用bindService()启动。client通过IBinder 接口来调用service的方法。client可以通过unbindService()来关闭连接。多个clients可以同时bind到同一个Service,当所有的client都unbind后,系统就会销毁这个Service。
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
<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>
Service中的标签说明:
android:name
你所编写的服务类的类名,可填写完整名称,包名+类名,如com.example.test.ServiceA,也可以忽略包名,用.开头,如.ServiceA,因为在manifest文件开头会定义包名,它会自己引用。
一旦你发布应用,你就不能改这个名字(除非设置android:exported=”false”),另外name没有默认值,必须定义。
android:enabled
是否可以被系统实例化,默认为true
因为父标签也有enable属性,所以必须两个都为默认值true的情况下服务才会被激活,否则不会激活。
android:exported
其他应用能否访问该服务,如果不能,则只有本应用或有相同用户ID的应用能访问。当然除了该属性也可以在下面permission中限制其他应用访问本服务。
这个默认值与服务是否包含意图过滤器intent filters有关。如果一个也没有则为false
android:isolatedProcess
设置true意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(binding and starting)。
android:label
可以显示给用户的服务名称。如果没设置,就用的lable。不管怎样,这个值是所有服务的意图过滤器的默认lable。定义尽量用对字符串资源的引用。
android:icon
类似label,是图标,尽量用drawable资源的引用定义。
android:permission
是一个实体必须要运行或绑定一个服务的权限。如果没有权限,startService(),bindService()或stopService()方法将不执行,Intent也不会传递到服务。
如果属性未设置,会由权限设置情况应用到服务。如果两者都未设置,服务就不受权限保护。
android:process
服务运行所在的进程名。通常为默认为应用程序所在的进程,与包名同名。元素的属性process可以设置不同的进程名,当然组件也可设置自己的进程覆盖应用的设置。
如果名称设置为冒号:开头,一个对应用程序私有的新进程会在需要时和运行到这个进程时建立。如果名称为小写字母开头,服务会在一个相同名字的全局进程运行,如果有权限这样的话。这允许不同应用程序的组件可以分享一个进程,减少了资源的使用。
写自己的Service有两种方法,继承Service或者IntentService,IntentService是Service的子类,由于Service运行在主线程中,所以如果继承Service就必须要新建一个线程去做耗时的工作。而IntentService就是这个作用,会使用一个工作线程来处理多个请求:
继承IntentService
大多数服务不需要同时处理多个请求,继承IntentService是最好的选择。
IntentService做了以下的工作:
这些都加入到IntentService中了,你需要做的就是实现构造方法和onHandleIntent(),如下:
public class HelloIntentService extends IntentService {
/** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */
public HelloIntentService() {
super("HelloIntentService");
}
/** * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
如果需要重写其他回调方法,如onCreate(),onStartCommand()等,一定要调用super()方法,保证IntentService正确处理worker线程,只有onHandleIntent()和onBind()不需要这样。如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
返回一个整型值,用来描述系统在杀掉服务后是否要继续启动服务,返回值有三种:
前台服务是被认为是用户已知的正在运行的服务,当系统需要释放内存时不会优先杀掉该服务。前台Service必须在状态栏上提供一个notification,这个Notification不会消失除服Service销毁了或者被从前台移除。
比如,音乐播放器通过一个Service在播放音乐就需要被设为在前台运行,因为用户需要明确的知道它的操作。在状态栏的notification 需要显示当前的歌曲并且允许用户启动音乐播放器。
想要请求Service在前台运行,需要调用startForeground()。比如:
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);
The integer ID you give to startForeground() must not be 0.
想要把Service从前台移除,需要调用stopForeground()。这个方法需要一个boolean参数,指明是否需要从状态把Notification移除。这个方法不会stop service。然而,如果前台service被stop了,那么对应的Notification也会被移除。
Andorid总结 - Bound Services
Andorid总结 - AIDL
Bound Service允许其他应用组件通过bindService()来绑定它,从而建立一个长链接,可以提供交互和返回值。
建立一个绑定的服务需要实现onBind()方法返回一个定义了与服务通信接口的IBinder对象。其他应用程序组件可以调用bindService()方法获取接口并且调用服务上的方法。
创建一个绑定的服务,第一件事就是定义一个说明客户端与服务通信方式的接口。这个接口必须是IBinder的实现,并且必须要从onBind()方法返回。一旦客户端接收到了IBinder,就可以通过这个接口进行交互。
多个客户端可以绑定到一个服务,可以用unbindService()方法解除绑定,当没有组件绑定在服务上,这个服务就会被销毁。
想要创建一个提供binding的service,必须提供一个IBinder给client与service进行交互。有三种方式可以定义接口:
例子,通过onBinder返回Service对象:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
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();
}
}
一个调用Messenger实现的Service的客服端实例:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/** * 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();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
想要绑定Service,Client需要实现:
实现ServiceConnection的实例:
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Because we have bound to an explicit
// service that is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false;
}
};
调用bindService():
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
bindService的第三个参数指示binding时的选项。通常应该设置为BIND_AUTO_CREATE 。还可以为BIND_DEBUG_UNBIND 和BIND_NOT_FOREGROUND, 或者设为0代表什么都不选.
binding to a service的时候有几个注意事项:
使用AIDL的必要条件是你允许来自不同应用的client来访问你的service做IPC的操作,并且需要处理多线程的情况。
如果你不要做跨进程的IPC,那么你应该使用“Extending the Binder class”方法,参考Andorid总结 - Bound Services
如果需要跨进程IPC但是不会有多线程的操作,那么你应该使用“Using a Messenger”方法,参考Andorid总结 - Bound Services
在使用AIDL时,client与service在同一进程和不在同一进程的区别:
你必须要定义AIDL接口在 “.aidl”文件中,语法跟Java类似。“.aidl”可以通过Android Studio来帮助创建,Android Studio会默认创建一个文件夹来保存.aidl的文件。
在应用程序编译之后,Android SDK tools 会根据”.aidl”生成一个IBinder的接口,这个接口保存在build目录下。service必须实现IBinder,然后把这个IBinder传给client调用。
具体步骤如下:
创建.aidl文件
“.aidl”文件中定义一个接口,接口里的方法提供给client调用。
AIDL使用很简单的Java语法,里面什么的方法可以带有参数和返回值。参数和返回值的类型可以为任意的类型,甚至可以用AIDL生成的接口做参数。只能定义方法,不能有变量。
AIDL中可以用的默认类型有:
使用额外的数据类型时,必须使用import来导入,即使在同一个包中。
// 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. 实现接口
如上图,当编译完成后,Android SDK tools会给我们在generated/source中生成对应的java接口文件。生成的接口包含了一个叫“Stub”的子类,比如“YourServiceInterface.Stub”,这个子类同事也继承了Binder,而我们的Servcie就是要实现这个Stub子类,作为Binder来传递给client使用。
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
}
};
在实现AIDL接口时,有几个小规格要注意:
client端不用收到任务异常消息。
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
}
};
}
而client的onServiceConnected() callback 就会接受到这个mBinder 对象。接收到之后通过调用 YourServiceInterface.Stub.asInterface(service) 把mBinder转换为 YourServiceInterface类型. 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;
}
};
跟Bound Sercie类似,唯一需要注意的就是调用方法的时候,需要捕获一个 异常 DeadObjectException。当连接断开的时候会抛出这个异常。
可以在IPC中传递对象,但是这个对象必须实现Parcelable 接口。快速实现Parcelable 可以通过Android Studio的插件“android-parcelable-intellij-plugin”