startService(): run indefinetly, 需要在适当时候stopSelf()
onBind(): 提供一个CS模式的服务,runs as long as the component bound to it. 当所有组件都unBind() 时,Service也就destroy了。
Service运行于主进程,因此勿做耗时工作,如果需要,可以开启新的线程来做耗时的work,以避免ANR。
声明周期lifecycle
onStartCommand()
当用户调用startService()请求启动服务,系统就会调用该方法。当该方法运行后,service就会在后台runs indefinitely.如果自己实现该方法,则需要自己调用stopSelf()或者stopService()来关闭服务。
onBind()
其他component调用bindService(),系统就会调用该方法。如不想被bind,则返回null,否则返回一个IBinder()。
onCreate()
当Service第一次被调用时,系统将调用该方法进行初始化,然后才会调用onStartCommand或者onBind. 如果该Service已经启动则跳过该步骤。
onDestory()
清理资源,诸如线程,注册的listeners,receivers等。
Caution: always use an explicit intent when starting or binding your Service
可将android:exported属性设为false, 这样就可以组织其他应用,即使是使用explicit intent也无法启动Service。
Traditionally,有两种Create Service的方式:
Service, 如有Intensive work,需要手动创建线程进行处理,否则容易造成ANR, as it is hosted in main thread
IntentService, Service的子类,使用worker thread 依次处理所有请求,需要继承onHandleIntent(),然后处理从onStartCommand收到的intent。
Extending the IntentService class
优点:
- create 默认的worker thread, 处理所有来自onStartCommand收到的Intent请求。
- Create a work queue, 依次传递收到intent到onHandleIntent(),依次处理。
- 在处理完所有请求后,就会自动销毁Service,无需自己调用stopSelf()
- 提供默认的onBind()实现,返回null。
- 提供一个默认的onStartCommand实现,它会将intent发送到work queue,并依次传递到onHandleIntent()
实例:
All you need: a constructor and an implementation of 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.
* 一个构造函数,且需调用super,并传入该service名
*/
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) {
}
}
}
}
}
注意,如果override 其他的方法,则必须调用super,让其帮助管理声明周期。比如对于onStartCommand()
@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);
}
Extending the Service class
如果需要同步处理多个请求,而不是按照队列依次处理,则继承Service,然后再对每个请求启动一个thread进行处理。
实例代码(有许多需要说明)
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();
}
}
第一点,新建HandleThread,与普通Thread无异,但是多了一个Looper成员。
第二点,正常来讲,Service是可以使用handler的,因为它是主线程,默认已经具有了Looper。但是官方demo还是根据新建的HandlerThread的成员looper,传入了Handler。
第三点,对于onStartCommand返回值START_STICKY,总共可以返回三个值,定义了在系统杀掉服务后系统如何继续服务。
START_NOT_STICKY
当系统杀掉Service后,不进行recreate.START_STICKY
当系统杀掉Service后,recreate and call onStartCommand(), 不传递last intent.适合于media play, 不执行commands,只是runs indefinitely.START_REDELIVER_INTENT
当系统杀掉Service后,recreate and call onStartCommand(), 携带last intent. 适合于正在执行任务的需要被立刻被恢复的,如下载。
Application Component 与Service交互,除了startService,无其他活动。若想得到Service的返回结果,可用PendingIntent 携带一个getBroadcast()对象,这样在Service使用完成后,即可发送发送广播。
Creating a Bound Service
可通过调用bindService()
To Create a Bound Service,必须实现onBind()返回一个IBinder对象,该对象定义了与Service通信的接口。
当Client不再需要Service,需要调用unbindService() 进行解绑,使得服务科正常关闭。
Sending Notifications to the User
使用Toast 或者状态栏notification。
状态栏Notification时best practise, 可在service结束后,发送通知,然后用户可点击通知栏的通知,然后启动一个Activity。
Running a Service in the Foreground
A foreground service必须提供一个notification for the status bar, 将被放置在正在运行的条目下,不可移除,除非Service运行完毕或者被移除前台。
实例:
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);
现在Notification已经使用Notification.Build进行建立了。
可使用stopForeground(),但不会停止Service。但是stopService()可以移除notification。