Service服务
进程级别
在Android中,进程是有级别分类的,级别越高,越难杀死,保活就是提高当前应用进程优先级。
Foreground process前台进程
什么是前台进程呢,当你打开一个app就会启动一个activity,这个时候这个app就是前台进程;还有当广播接收者的onReceive方法执行的时候是前台进程。这个不会被杀死。
Visible process可视进程
当你开启activity声明周期执行了onPause方法,页面不可操作,但是还看得见,这时候就是可是进程。
Service process服务进程
在进程里面启动一个服务,听音乐的软件都启动了一个服务,当你退出软件的时候,同样能听音乐,就是因为播放音乐是放到服务里面执行的。
这个进程是看不到的。
Background process后台进程
相当于activity执行了onStop,就是不可见了,比如按下home键,这时候程序并没有被杀死,就是没有执行onDestroy,这时候就是后台进程了
Empty process空进程
这个一直按返回键,退出应用,执行了onDestroy,但是Android系统并不会马上杀死这个进程,这时候进程只一个空进程,当你一直开启其他app,开的太多了,这个空进程就被杀死了。
为啥不马上杀死呢,谷歌是这样解释的,为了提高下一次打开这个应用的速度。
这个进程最容易被杀死。
服务
Android设计服务这个组件,就是为了做一些需要在看不到的时候同样可以进行操作,就像Windows系统一样,任务管理器中有好多服务,但是你都看不到。常见的使用地方是,音乐播放器、后台网络下载,经常有些软件你点了更新,然后就开启服务下载了,你退出应用,还是在下载,就是因为这个下载实在服务中做的,当下载完成了,就开始安装。
服务是看不见的,服务也是四大组件之一,所以也要在清单文件配置一下,服务的声明周期只有三个,onCreate,onStartCommand,onDestroy。因为服务没有页面,所以服务的声明周期就很少只有三个了。
开启服务-----startService
代码怎么写呢,很简单,startService开启服务,只要你不手动关闭,就会长期在后台运行。
第一步:定义一个类,继承Service,清单文件配置一下,调用startService就可以了。
public class CustomService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.e(this.getClass().getName(),"onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(this.getClass().getName(),"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(this.getClass().getName(),"onDestroy");
}
}
下面在清单文件配置一下
<service android:name=".service.CustomService"/>
下面在activity中弄两个按钮,写两个点击事件,一个开始服务,一个关闭服务。
public void startServiceClick(View view) {
Intent intent = new Intent(this, CustomService.class);
startService(intent);
}
public void stopServiceClick(View view) {
Intent intent = new Intent(this,CustomService.class);
stopService(intent);
}
开启服务-----bindService
服务类还是上面这个服务类,就是开启方式不同了,需要注意的是,bindService开启的服务,当Activity被销毁了,被这个Activity用bindService开启的服务也就自动销毁了。
下面贴出开启的代码
private MyServiceConnection myServiceConnection;
public void startServiceClick(View view) {
Intent intent = new Intent(this, CustomService.class);
// startService(intent);
//第一个参数是意图
//第二个参数是监听服务连接的
//第三个是标记,BIND_AUTO_CREATE就是自动连接到服务
myServiceConnection = new MyServiceConnection();
bindService(intent,myServiceConnection,BIND_AUTO_CREATE);
}
public void stopServiceClick(View view) {
// Intent intent = new Intent(this,CustomService.class);
// stopService(intent);
unbindService(myServiceConnection);
}
/**
* 监听服务连接的类
*/
class MyServiceConnection implements ServiceConnection{
/**
* 当服务连接成功的时候调用
* 注意如果监听的服务的onBind没有返回值,这个方法就不会执行
* 其实这个监听类,监听的是IBinder
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
/**
* 当失去连接的时候调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
bindService特点:只能开启一次,只能关闭一次,开启多次或者关闭多次,都会崩溃。
bindService调用服务中的方法
上面说了bindService这种开启方式,但是这样开启的作用是还是什么呢,答案就是这种启动方式可以在服务的外部调用服务内部的方法。这时候就需要用到bindService开启的时候创建的监听类了,这个监听类时间上监听的是IBinder。
我就在上面的开启方式中改造一下吧。
- 在服务类中增加往外暴露的方法
public class CustomService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(this.getClass().getName(),"onBind");
return new MusicBind();
}
private void startMusic(){
Log.e(this.getClass().getName(),"startMusic");
}
private void stopMusic(){
Log.e(this.getClass().getName(),"stopMusic");
}
private void nextMusic(){
Log.e(this.getClass().getName(),"nextMusic");
}
private void previousMusic(){
Log.e(this.getClass().getName(),"previousMusic");
}
@Override
public void onCreate() {
super.onCreate();
Log.e(this.getClass().getName(),"onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(this.getClass().getName(),"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(this.getClass().getName(),"onDestroy");
}
public class MusicBind extends Binder{
public void startMusicBind(){
startMusic();
}
public void stopMusicBind(){
stopMusic();
}
public void nextMusicBind(){
nextMusic();
}
public void previousMusicBind(){
previousMusic();
}
}
}
- 开启服务,这步代码就不写了,开启服务就用bindService方法就行了
- 在监听服务的类中获取IBinder对象,注意把musicBind 提取到Activity中
/**
* 监听服务连接的类
*/
class MyServiceConnection implements ServiceConnection{
/**
* 当服务连接成功的时候调用
* 注意如果监听的服务的onBind没有返回值,这个方法就不会执行
* 其实这个监听类,监听的是IBinder
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
musicBind = (CustomService.MusicBind)service;
}
/**
* 当失去连接的时候调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
- 创建一个按钮调用服务中的方法
/**
* 操作音乐的方法,
* @param view
*/
public void operateMusic(View view){
musicBind.startMusicBind();
musicBind.stopMusicBind();
musicBind.previousMusicBind();
musicBind.nextMusicBind();
}
通过接口调用服务中的方法
通过接口操作服务中的任务
这时候可能你会有点疑问,直接创建服务对象调用服务方法不行吗?可以但是有个问题,这样使用,那这个服务类就和普通的类没区别了,就不处在服务的环境中了,也就是服务中的上下文(Context)就不能使用了,只能通过参数传递了,还有一些其他问题,所以正确的使用方式,还是在服务监听中通过IBinder来传递参数,调用方法。
还有一个方式可以操作服务中的方法,还是通过监听,不过这时监听中接收的就不是用CustomService.MusicBind这个内部类接收,而是用一个接口接收。
- 定义一个接口
public interface IMusicService {
void startMusicBind();
void stopMusicBind();
}
- 自定义服务中的Bind类实现这个接口
private class MusicBind extends Binder implements IMusicService{} - 监听接收这个接口
这一步就是java语言特性,多态的使用。
musicBind = (IMusicService)service; - 把接收到的接口提到Activity中调用
musicBind.startMusicBind();
musicBind.stopMusicBind();
这样加一层封装,算是面向接口编程。
开启一个长期运行,同时可操作的服务
还有一个疑问就是播放音乐这个功能,既是长期运行,又是可以操作的,所以应该如何实现呢?
其实挺简单的,先调用startService然后调用bindService。这样就实现了退出应用,还能操作服务中的任务。
Intent intent = new Intent(this, CustomService.class);
startService(intent);
//第一个参数是意图
//第二个参数是监听服务连接的
//第三个是标记,BIND_AUTO_CREATE就是自动连接到服务
myServiceConnection = new MyServiceConnection();
bindService(intent,myServiceConnection,BIND_AUTO_CREATE);
一般这种情况开启服务就可以了,不用主动关闭。但是不关闭就会报一个leak的问题,不是异常,就是谷歌打印的一个提醒。解决方案就是在页面销毁的时候解绑服务,但是并不是真的解绑,只是不打印日志了。
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(myServiceConnection);
}