Android提供Service组件用来处理耗时的操作,不需要用户界面。
Service组件的优先级比不活跃的Activity高,这样被系统杀死的概率就降低了。当然,实际上即便运行中的Service被杀,但是当系统资源又足够的时候,Service又会被重启。
当必要的时候Service的优先级可能被提到与前台Activity相同的优先级(通过组件的标签)。这是极端的例子,当结束掉Service会直接影响用户体验的时候,比如音乐的播放被打断。
尽管Service跑起来不需要UI,但是它仍然在UI线程中执行。所以有些耗时的操作你需要放在其他的工作线程中,比如Thread和AsynTask类等。
介绍Service
Service主要做来:网络查询,处理数据,更新Content Provider,firing Intents,和触发Notification,主要做来处理耗时的操作。
若创建的Service只由自己的APP操纵,那么需要增加权限:
<service android:enabled= true
android:name= .MyService
android:permission= com.paad.MY_SERVICE_PERMISSION />
onStartCommand可能会被多次调用,当使用startService的时候触发。通过stopService或者stopSelf停止服务。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startBackgroundTask(intent, startId);
return Service.START_STICKY;
}
onStartCommand返回的参数是用来决定:当系统在Service运行时杀死了Service,资源足够的时候又重启这个Service,系统该如何回应。
下面介绍这个返回值:
1.START_STICKY 代表标准的行为。如果返回的是这个值,当Service任何时候的重启,onStartCommand都会被调用。当是注意的一点:传来的intent参数会丢失,也就是null。
2.START_NOT_STICKY 当运行时被杀死的时候,如果之前还有start请求未处理(注意是未处理,而不是处理中),Service才会被重启,否则服务自动停止。
3.START_REDELIVER_INTENT 如果Service在运行中被杀死,如果还有请求未被处理,或者在处理中。在后者情况,onStartCommand会被调用,传入之前传入的初始状态的Intent,因为它认为还没有完全处理好。
注意:以上行为只有在System kill event的情况下有效,stopSelf和stopService都不会过问onStartCommand的返回状态。
下面2个参数是用来判断你的Service是怎么启动的:
1.START_FLAG_REDELIVERY对应着START_REDELIVER_INTENT
2.START_FLAG_RETRY ------START_STICKY
启动和停止Service
显式和隐式启动:
private void explicitStart() {
// Explicitly start My Service
Intent intent = new Intent(this, MyService.class);
// TODO Add extras if required.
startService(intent);
}
private void implicitStart() {
// Implicitly start a music Service
Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA,United );
intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA,Pheonix );
startService(intent);
}
显式和隐式停止:
// Stop a service explicitly.
stopService(new Intent(this, MyService.class));
// Stop a service implicitly.
Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
stopService(intent);
Service与Activity绑定
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class MyBinder extends Binder {
MyMusicService getService() {
return MyMusicService.this;
}
}
private final IBinder binder = new MyBinder();
你还需要去实现一个ServiceConnection,重写它的onServiceConnected和onServiceDisconneted方法去获取一个Service实例的引用。
// Reference to the service
private MyMusicService serviceRef;
// Handles the connection between the service and activity
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Called when the connection is made.
serviceRef = ((MyMusicService.MyBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
// Received when the service unexpectedly disconnects.
serviceRef = null;
}
};
如果去执行绑定呢?
答:调用bindService。传入一个intent(隐式或者显式)和ServiceConnection的实例。你还可以指定绑定标示符:
Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class);
bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
Android 4.0(API 14) 引入了一些新的标示符:
1.BIND_ADJUST_WITH_ACTIVITYService的优先级将相对于其绑定的Activity,Activity到前台,则Service优先级相对提升,Activity到后台,则Servcie优先级相对降低。
2.BIND_ABOVE_CLIENT和BIND_IMPORTANT当你的客户端在前台,这个标示符下的Service也变得重要性相当于前台的Activity,优先级迅速提升。若是BIND_ABOVE_CLIENT,则优先级已经超过了Activity,也就是说Activity要比Service先死,当资源不够的时候。
3.BIND_NOT_FOREGROUND 你所绑定的Service优先级永远高不过前台Activity。
4.BIND_WAIVE_PRIORITY 绑定的服务不可调整自身的优先级。
创建前台Service
在进程优先级中,正在运行的Service优先级是老二的位置,仅次与运行在前台的Activity。
如果你的Service直接可以与用户交互的,那么考虑将其放在前台是必要的。通过startForeground.
startForeground通常必须指定一个不间断的Notification。这个Notification存在的时间与你的跑在前台的Service相同。
private void startPlayback(String album, String artist) {
int NOTIFICATION_ID = 1;
// Create an Intent that will open the main Activity
// if the notification is clicked.
Intent intent = new Intent(this, MyActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
// Set the Notification UI parameters
Notification notification = new Notification(R.drawable.icon,
Starting Playback , System.currentTimeMillis());
notification.setLatestEventInfo(this, album, artist, pi);
// Set the Notification as ongoing
notification.flags = notification.flags |
Notification.FLAG_ONGOING_EVENT;
// Move the Service to the Foreground
startForeground(NOTIFICATION_ID, notification);
}
结束前台的Service通过stopForeground:
public void pausePlayback() {
// Move to the background and remove the Notification
stopForeground(true);
}
Notification也会被取消。