Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要长期运行的任务。
- Service生命周期
Service生命周期可以从两种启动Service的模式开始讲起,分别是context.startService()和context.bindService()。- startService的启动模式下的生命周期:当我们首次使用startService()启动一个服务时,系统会实例化一个Service实例,依次调用其onCreate()和onStartCommand()方法,然后进入运行状态。此后,如果再使用startService()启动服务时,不再创建新的服务对象,每个服务只会存在一个实例,系统会自动找到刚才创建的Service实例,直接调用了onStartCommand()方法(onStartCommand()中又调用了onStart()方法)。如果我们想要停掉一个服务,可使用stopService()方法,此时onDestroy()方法会被调用。需要注意的是,不管前面使用了多次startService(),只需一次stopService(),或自身的stopSelf()方法,即可停掉服务。
- bindService启动模式下的生命周期:在这种模式下,当调用者首次使用bindService()绑定一个服务时,系统会实例化一个Service实例,并依次调用其onCreate()方法和onBind()方法,不管之后调用 bindService() 几次,onCreate()方法都只会调用一次,同时onBind()方法始终不会被调用。如果我们需要解除与这个服务的绑定,可调用Context.unbindService() 断开连接或者之前调用bindService()的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,此时onUnbind()方法和onDestroy()方法会被调用。
两种方式的流程:
注:
- 当一个Service被终止(1、调用stopService();2、调用stopSelf();3、不再有绑定的连接(没有被启动))时,onDestroy()方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。
- 在调用 bindService 绑定到Service的时候,你就应当保证在某处调用unbindService 解除绑定(尽管 Activity 被 finish() 的时候绑定会自动解除,并且Service会自动停止)。
- 同时使用 startService() 与 bindService() 要注意到,Service的终止,需要unbindService()与stopService()同时调用,才能终止Service,不管 startService() 与 bindService() 的调用顺序,如果先调用 unbindService() 此时服务不会自动终止,再调用 stopService() 之后服务才会停止,如果先调用 stopService() 此时服务也不会终止,而再调用 unbindService() 或者 之前调用 bindService() 的 Context 不存在了(如Activity 被 finish() 的时候)之后服务才会自动停止;
- 当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
- 一般是重写onStartCommand方法,。
startService()示例:
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate called.");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand called.");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.i(TAG, "onStart called.");
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind called.");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind called.");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy called.");
}
}
每个Service必须在AndroidManifest.xml中 通过
上述代码表示一个在应用包service目录下的继承Service的Myservice服务。
点击事件:
/**
* 启动服务按钮方法
* @param view
*/
public void start(View view) {
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
/**
* 停止服务按钮方法
* @param view
*/
public void stop(View view) {
Intent intent = new Intent(this, MyService.class);
stopService(intent);
}
bindService()示例:
因为前面服务中的onBind方法返回值为null,因此,要想实现绑定操作,必须返回一个实现了IBinder接口类型的实例,该接口描述了与远程对象进行交互的抽象协议,有了它我们才能与服务进行交互。
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind called.");
return new Binder() {};
}
返回了一个Binder的实例,而这个Binder恰恰是实现了IBinder接口,这样就可以实现绑定服务的操作了。
public class MainActivity extends AppCompatActivity{
private static final String TAG = "MainActivity";
private boolean binded;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binded = true;
Log.i(TAG, "onServiceConnected called.");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
/**
* 绑定服务
* @param view
*/
public void bind(View view) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
/**
* 解除绑定
* @param view
*/
public void unbind(View view) {
unbindService();
}
//在MainActivity销毁之前如果没有进行解除绑定会导致后台出现异常信息
@Override
protected void onDestroy() {
super.onDestroy();
unbindService();
}
/**
* 解除服务绑定
*/
private void unbindService() {
if (binded) {
unbindService(conn);
binded = false;
}
}
}
在使用bindService()绑定服务时,我们需要一个ServiceConnection代表与服务的连接,使用bindService()启动服务后调用者和服务绑定到了一起,当调用者被销毁,服务也立即结终止。
- 创建前台服务
前台服务的优点:Service默认是运行在后台的,因此,它的优先级相对比较低,当系统出现内存不足的情况时,它就有可能被回收掉。如果将Service运行在前台,Service就可以一直保持运行状态,而不会被系统因内存不足回收。它可以在通知栏显示消息,当服务被终止的时候,通知栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如更新天气信息,音乐播放服务。设置服务为前台服务,使用的方法是 startForeground()与 stopForeground()
public class WeatherService extends Service{
private static final int NOTIFY_ID = 123;
@Override
public void onCreate(){
super.onCreate();
showNotification();
}
//在通知栏显示天气信息
private void showNotification(){
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.weather)
.setContentTitle(getText(R.string.the_day))
.setContentText(getText(R.string.weather));
//创建通知被点击时触发的Intent
Intent resultIntent = new Intent(this, MainActivity.class);
//创建任务栈Builder
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotification =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//构建通知
final Notification notification = mBuilder.builder();
//显示通知
mNotification.notify(NOTIFY_ID, notification);
//启动前台服务
startForeground(NOTIFY_ID, notification);
}
}
使用 startForeground(),如果NOTIFY_ID为 0 ,那么notification将不会显示。
另:
使用Notification通知栏的时候,用TaskStackBuilder来获取PendingIntent处理点击跳转到别的Activity。使用TaskStackBuilder来实现在通知栏中如果有Notification通知的话,点击它,然后会直接跳转到对应的应用程序的某个界面,这时如果按下Back键,会返回到该应用程序的主界面,而不是系统的主界面。addParentStack()是用来添加一个Activity,与这个Activity的AndroidManifest.xml文件中的parentActivityName的属性相关联。
- 进程内与服务通信
进程内与服务通信实际上就是通过bindService的方式与服务绑定,获取到通信中介Binder实例,然后通过调用这个实例的方法,完成对服务的各种操作。
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind called.");
return new MyBinder();
}
/**
* 绑定对象
* @author user
*
*/
public class MyBinder extends Binder {
/**
* 问候
* @param name
*/
public void greet(String name) {
Log.i(TAG, "hello, " + name);
}
}
}
我们创建了一个MyBinder的内部类,定义了一个greet方法,在onBind方法中就将这个MyBinder的实例返回,只要调用者获取到这个实例,就可以像拿着游戏手柄一样对服务进行操作。
public class MainActivity extends AppCompatActivity{
/**
* 绑定对象实例
*/
private MyService.MyBinder binder;
private boolean binded;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (MyService.MyBinder) service; //获取其实例
binder.greet("coder"); //调用其方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
/**
* 绑定服务
* @param view
*/
public void bind(View view) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
/**
* 解除绑定
* @param view
*/
public void unbind(View view) {
unbindService();
}
//在MainActivity销毁之前如果没有进行解除绑定会导致后台出现异常信息
@Override
protected void onDestroy() {
super.onDestroy();
unbindService();
}
/**
* 解除服务绑定
*/
private void unbindService() {
if (binded) {
unbindService(conn);
binded = false;
}
}
}
在绑定服务成功时将IBinder类型的service参数强转为MyService.MyBinder类型,获取绑定中介实例,然后调用其greet方法。绑定服务后,去操作binder对象,也许它还为null,这就容易引起空指针异常,正确的做法是把这些操作放到绑定成功之后。