什么是服务呢?
长期于后台运行的程序,如果是官方一点,首先它是一个组件,用于执行长期运行的任务,并且与用户没有交互。
通过Context.startService()来开启服务,通过Context.stop()来停止服务,还可以通过绑定来开启服务。
为什么要使用服务呢?
我们知道这个服务是用于执行长期后台运行的操作。有些时候,我们没有界面,但是程序仍然需要工作。比如说,我们播放音乐,在后台播放音乐。比如说,我们下载任务,在后台下载文件。这些都是没有界面 的后台运行程序,这些都是用服务做的。所以,服务就有它的用处了。
第二个原因是什么呢?
1、前台进程:可以理解为是最顶部的,直接跟用户交互的。比如说我们操作的Activity界面.
2、可见进程:可以见的,但是不操作的,比如说我们在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity则是可见进程。
3、服务进程:服务可以理解为是忙碌的后台进程,虽然是在后台,但是它很忙碌。
4、后台进程:后台进程就是退隐到后台,不做事的进程。
5、空进程:空进程是不做事的,没有任何东西在上面跑着,仅作缓存作用。
假设,内存不够用了,会先杀谁呢?
首先杀的是空进程,要是还不够就杀后台进程,要是还不够,那么就杀服务,但是服务被杀死以后,等内存够用了,服务又会跑起来了。
所以:如果我们需要长期后台操作的任务,使用Service就对了!其实Framework里多数是服务。如果我们进行音乐播放,即使退到了后台,也可以播放,我们使用服务完成吧!如果我们下载东西,退到后台也能下载,那么我们就使用服务吧!如果我们在不停地记录日志,那我们就用服务吧!
如果面试问到:服务用于执行耗时操作,这是对的吗?
如时服务直接执行耗时操作,也会出现anr.
在这里给大家补充一下anr的时长知识。首先ANR的意思是android no response,也就是无相应或者理解为操作超时。
在android系统中广播的ANR时长为:
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
前台广播为10秒,后台广播为60秒。
按键操作的anr时长为:按钮事件的时长为5秒,常指的是Activity的操作。
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
服务的话,前台服务为20秒超时,后台服务为200秒超时。
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
如果在服务中直接做耗时操作,也是会出现ANR异常的。服务可以长期在后台运行,所以你可以这么做:如果要做耗时操作,比如说网络的访问,数据库的读写之类的,可以开线程去做。
服务的生命周期
我们在startService之后,就执行了onCreate方法,接着是onStartCommand方法。当我们执行stopService的时候,就会走onDestroy方法了。这就是服务最基本的生命周期了。其实onStart也是服务的生命周期,只是这个方法已经过时了。
绑定启动服务
1、开启服务
优点:服务长期运行
缺点:服务不能通讯
2、绑定服务
优点:可以通讯
缺点:不能长期运行
绑定服务步骤:
1、注册BindSrevice继承自Service类
重写onCreate、onBind、onUnbind、onDestroy方法
package org.crazyit.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
/**
* 绑定本地服务
*/
public class BindService extends Service {
private static final String TAG = "BindService";
// 通过继承Binder,实现IBindService接口
private class MyBinder extends Binder implements IBindService {
@Override
public void callServiceInnerMethod() {
// 调用服务内部方法
serviceInnerMethod();
}
}
// Service被绑定时回调该方法
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind方法");
// 返回MyBinder对象,绑定服务的地方通过接口调用方法
return new MyBinder();
}
// Service被创建时回调该方法
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate方法");
}
// Service被断开连接时回调该方法
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind方法");
return true;
}
// Service被关闭之前回调该方法
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy方法");
}
private void serviceInnerMethod() {
Log.d(TAG, "serviceInnerMethod: ");
}
}
2、编写接口IBindService,绑定服务时,返回实现该接口和Binder类的类
package org.crazyit.service;
// 绑定服务要实现的接口
public interface IBindService {
void callServiceInnerMethod();
}
并在该类的实现接口方法中调用服务的内部方法serviceInnerMethod(),由此可实现服务内部的方法被绑定服务处调用。
3、在Activity中绑定服务、解绑服务以及调用服务内部的方法
bindService() 参数1:intent意图 参数2:serviceConnection 参数3:Service.BIND_AUTO_CREATE自动绑定
在ServiceConnection的onServiceConnected()方法中接收绑定服务返回的IBinder,此处用继承Binder类实现IBindService的IBindService接口进行接收
通过该接口调用实现了接口的服务内部方法iBindService.callServiceInnerMethod();
package org.crazyit.service;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
/**
* bindService后,unBindService,服务不再运行
* startService后,停止服务,服务仍在运行
*/
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private IBindService iBindService;
// 定义一个ServiceConnection对象
private ServiceConnection conn = new ServiceConnection() {
// 当该Activity与Service连接成功时回调该方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected方法");
// 获取Service的onBind方法所返回的MyBinder对象
iBindService = (IBindService) service;
}
// 当该Activity与Service断开连接时回调该方法
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected方法");
iBindService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取程序界面中的start、stop、getServiceStatus按钮
Button bindBn = findViewById(R.id.bind);
Button unbindBn = findViewById(R.id.unbind);
Button getServiceInnerMethod = findViewById(R.id.getServiceStatus);
// 创建启动Service的Intent
Intent intent = new Intent(this, BindService.class);
bindBn.setOnClickListener(view ->
// 绑定指定Service
bindService(intent, conn, Service.BIND_AUTO_CREATE));
unbindBn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 解除绑定Service
if (conn != null) {
unbindService(conn);
}
}
});
// 调用服务内部的方法
getServiceInnerMethod.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
iBindService.callServiceInnerMethod();
}
});
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<Button
android:id="@+id/bind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/bind"
android:textSize="20sp" />
<Button
android:id="@+id/unbind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/unbind"
android:textSize="20sp" />
<Button
android:id="@+id/getServiceStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/getServiceStatus"
android:textSize="20sp" />
</LinearLayout>
总结:
绑定服务时生命周期:onCreate -> onBind
解绑服务时生命周期:onUnbind -> onDestroy
如果在服务内的onBind方法中返回null,那么在ServiceConnection的onServiceConnected方法不会调用