参考:
一. 什么是服务
服务是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
二. 服务的两种启动方式
- start模式启动
- 当应用组件(如 Activity)通过调用
startService()
启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
- bind模式启动
- 当应用组件通过调用
bindService()
绑定到服务时,服务即处于“绑定”状态(与活动绑定)。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
三. Start模式启动服务
- 该模式会回调的方法
/**
* 该方法是必须实现的抽象方法,用于bind模式启动,在调用bindService之后会调用,用于将服务和调用组件进行绑定。在start模式的时候返回null即可
* @param intent 用来绑定该服务的intent
* @return 返回的就是自定义的用于操作服务的接口
*/
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
Log.i(TAG, "onBind: intent "+intent);
Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
return mDownloadBinder;
}
/**
* 该方法在服务只在第一次启动/绑定的时候会调用
*/
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
super.onCreate();
}
/**
*
* 该方法在每次调用startService方法的时候都会调用,
* 一旦执行此方法,服务即会启动并可在后台无限期运行。
* 服务工作完成后,需要通过调用 stopSelf() 或 stopService() 来停止服务。
*
* @param intent 就是用来启动该服务的Intent,传递的数据可以通过该intent拿出来
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
Log.i(TAG, "onStartCommand: "+intent);
Log.i(TAG, "onStartCommand: data "+intent.getStringExtra("data"));
Log.i(TAG, "onStartCommand: flags "+flags);
Log.i(TAG, "onStartCommand: startId "+startId);
return super.onStartCommand(intent, flags, startId);
}
/**
* 该方法在调用stopService/unbindService方法的时候会调用,服务停止运行
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
- 使用该模式启动服务的步骤
- 新建sevice类继承自Service
- 至少实现onStartCommand回调方法
- 注册服务
- 调用startService(Intent)方法启动该服务,该服务就会一直在后台运行,直到调用stopService(Intent)或stopSelf(Intent)方法
四. bind方式启动服务
- 该模式会回调的方法
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
Log.i(TAG, "onBind: intent "+intent);
Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
return mDownloadBinder;
}
/**
* 当与服务连接的所有组件都断开连接的时候调用(Called when all clients have disconnected
* from a particular interface published by the service.
* The default implementation does nothing and returns false.)
*
* @param intent 用来绑定服务的intent
* @return 默认返回false
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: ");
Log.i(TAG, "onUnbind: intent "+intent.getStringExtra("data"));
return super.onUnbind(intent);
}
/**
* 当有新的组件绑定到服务的时候会调用该方法
* @param intent 用来绑定服务的intent
*/
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: ");
super.onRebind(intent);
}
- 使用bind方式启动服务的步骤
- 创建Binder类作为操作服务的接口
/**
* 用于Activity操作Service的类,给与服务绑定的组件一个操作服务的接口
*/
class DownloadBinder extends Binder{
public void startDownload(){
Log.i(TAG, "startDownload: ");
}
public void getProgress(){
Log.i(TAG, "getProgress: ");
}
}
- 实现onBind方法并返回Binder对象
- Activity实现ServiceConnection类,重写
onServiceConnected
和onServiceDisconnected
方法
private MyService.DownloadBinder mDownloadBinder;//Binder对象,用来操作服务
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 活动与服务绑定的时候调用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: name " + name);
mDownloadBinder = (MyService.DownloadBinder) service;//将IBinder参数强转,实例化自定义的Binder对象
mDownloadBinder.startDownload();//调用Binder的方法操作服务
mDownloadBinder.getProgress();
}
/**
* 活动与服务解绑的时候调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
- 调用bindService方法进行绑定
/*参数:Intent,ServiceConnection,标志位(BIND_AUTO_CREATE表示活动与服务绑定之后自动创建服务)*/
bindService(startService, mConnection, BIND_AUTO_CREATE);
五. 前台服务
- 通过调用startForeground方法(借助通知)使服务显示在通知栏就是前台服务(如通知栏显示的QQ这种)
- 使用方法(该段代码写在Service类的onCreate方法中)
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is title")
.setContentText("This is text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
六. IntentService
- 什么是IntentService?
- 因为服务是运行在主线程的,所以当需要进行耗时操作的时候,就需要在服务内开启子线程,然后在运行结束的时候调用stopSelf方法结束该服务。为了避免这一大堆麻烦事,IntentService就诞生了。
- IntentService做的事(来自官网)
- 创建默认的工作线程,用于在应用的主线程外执行传递给 [onStartCommand()](https://developer.android.com/reference/android/app/Service.html?hl=zh-cn#onStartCommand(android.content.Intent, int, int))的所有 Intent。
- 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent()实现,这样您就永远不必担心多线程问题。
- 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
- 提供 onBind()的默认实现(返回 null)。
- 提供 [onStartCommand()](https://developer.android.com/reference/android/app/IntentService.html?hl=zh-cn#onStartCommand(android.content.Intent, int, int))的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent()实现。
- 综上所述,您只需实现 onHandleIntent()来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)
- 使用方法
- 新建一个类继承自IntentService
- 实现无参构造函数并调用父类的有参构造函数(必须)
- 实现onHandleIntent方法(该方法运行在子线程),在该方法内进行想要的操作
public class MyIntentService extends IntentService {
private final String TAG = getClass().getSimpleName();
/**
* 必须实现的无参构造函数,且必须调用父类有参构造方法
*/
public MyIntentService(){
super("MyIntentService");
}
/**
* 处理具体逻辑的抽象方法,该方法运行在子线程
* @param intent 启动服务用的intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent: ");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 服务销毁时会调用
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- 正常调用startService方法启动服务
七. 源码
- MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
/**
* start service
*/
private Button mBtStartService;
/**
* stop service
*/
private Button mBtStopService;
private final String TAG = getClass().getSimpleName();
/**
* bind service
*/
private Button mBindService;
/**
* unbind service
*/
private Button mUnbindService;
private MyService.DownloadBinder mDownloadBinder;//Binder对象,用来操作服务
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 活动与服务绑定的时候调用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: name " + name);
mDownloadBinder = (MyService.DownloadBinder) service;//将IBinder参数强转,实例化自定义的Binder对象
mDownloadBinder.startDownload();//调用Binder的方法操作服务
mDownloadBinder.getProgress();
}
/**
* 活动与服务解绑的时候调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
/**
* start service
*/
private Button mBtBindService;
/**
* start activity
*/
private Button mBtStartActivity;
/**
* start intentservice
*/
private Button mStartIntentService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
@Override
public void onClick(View v) {
Intent startService = new Intent(MainActivity.this, MyService.class);
startService.putExtra("data", "data");
switch (v.getId()) {
case R.id.bt_bind_service:
Log.i(TAG, "onClick: " + startService);
startService(startService);
break;
case R.id.bt_stop_service:
//Log.i(TAG, "onClick: "+startService);
stopService(startService);
break;
case R.id.bind_service:
/*参数:Intent,ServiceConnection,标志位(BIND_AUTO_CREATE表示活动与服务绑定之后自动创建服务)*/
bindService(startService, mConnection, BIND_AUTO_CREATE);
//mDownloadBinder.startDownload();
break;
case R.id.unbind_service:
unbindService(mConnection);
break;
case R.id.bt_start_activity:
startActivity(new Intent(MainActivity.this, SecondActivity.class));
break;
case R.id.start_intent_service:
startService(new Intent(MainActivity.this,MyIntentService.class));
break;
}
}
private void initView() {
mBtStartService = (Button) findViewById(R.id.bt_bind_service);
mBtStartService.setOnClickListener(this);
mBtStopService = (Button) findViewById(R.id.bt_stop_service);
mBtStopService.setOnClickListener(this);
mBindService = (Button) findViewById(R.id.bind_service);
mBindService.setOnClickListener(this);
mUnbindService = (Button) findViewById(R.id.unbind_service);
mUnbindService.setOnClickListener(this);
mBtBindService = (Button) findViewById(R.id.bt_bind_service);
mBtBindService.setOnClickListener(this);
mBtStartActivity = (Button) findViewById(R.id.bt_start_activity);
mBtStartActivity.setOnClickListener(this);
mStartIntentService = (Button) findViewById(R.id.start_intent_service);
mStartIntentService.setOnClickListener(this);
}
}
- MyService
public class MyService extends Service {
private final String TAG = getClass().getSimpleName();
private DownloadBinder mDownloadBinder = new DownloadBinder();
/**
* 用于Activity操作Service的类,给与服务绑定的组件一个操作服务的接口
*/
class DownloadBinder extends Binder{
public void startDownload(){
Log.i(TAG, "startDownload: ");
}
public void getProgress(){
Log.i(TAG, "getProgress: ");
}
}
public MyService() {
}
/**
* 该方法是必须实现的抽象方法,用于bind模式启动;
* 在调用bindService之后会调用,用于将服务和调用组件进行绑定。
* 在start模式的时候返回null即可
* @param intent 用来绑定该服务的intent
* @return 返回的就是自定义的用于操作服务的接口
*/
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
Log.i(TAG, "onBind: intent "+intent);
Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
return mDownloadBinder;
}
/**
* 当与服务连接的所有组件都断开连接的时候调用(Called when all clients have disconnected
* from a particular interface published by the service.
* The default implementation does nothing and returns false.)
*
* @param intent 用来绑定服务的intent
* @return 默认返回false
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: ");
Log.i(TAG, "onUnbind: intent "+intent.getStringExtra("data"));
return super.onUnbind(intent);
}
/**
* 当有新的组件绑定到服务的时候会调用该方法
* @param intent 用来绑定服务的intent
*/
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: ");
super.onRebind(intent);
}
/**
* 该方法在服务只在第一次启动/绑定的时候会调用
*/
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
/*//将该服务变成前台服务
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is title")
.setContentText("This is text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);*/
super.onCreate();
}
/**
*
* 该方法在每次调用startService方法的时候都会调用,
* 一旦执行此方法,服务即会启动并可在后台无限期运行。
* 服务工作完成后,需要通过调用 stopSelf() 或 stopService() 来停止服务。
*
* @param intent 就是用来启动该服务的Intent,传递的数据可以通过该intent拿出来
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
Log.i(TAG, "onStartCommand: "+intent);
Log.i(TAG, "onStartCommand: data "+intent.getStringExtra("data"));
Log.i(TAG, "onStartCommand: flags "+flags);
Log.i(TAG, "onStartCommand: startId "+startId);
return super.onStartCommand(intent, flags, startId);
}
/**
* 该方法在调用stopService/unbindService方法的时候会调用,服务停止运行
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- MyIntentService
public class MyIntentService extends IntentService {
private final String TAG = getClass().getSimpleName();
/**
* 必须实现的无参构造函数,且必须调用父类有参构造方法
*/
public MyIntentService(){
super("MyIntentService");
}
/**
* 处理具体逻辑的抽象方法,该方法运行在子线程
* @param intent 启动服务用的intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent: ");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 服务销毁时会调用
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.foxnickel.servicedemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
<service android:name=".MyIntentService"/>
<activity android:name=".SecondActivity">
</activity>
</application>
</manifest>
七. 注意
- 服务并不是运行在独立的进程中的,而是依赖于创建服务的应用程序进程,当创建该服务的进程被杀掉时,服务也会停止。
- 服务的所有代码都运行在主线程中,并不会自己开启线程。