本文参考Android Service两种启动方式详解(总结版)写成,在此致谢!
1. Service 的两种启动方式
1. startService()
完整生命周期函数如下:
onCreate() -> onStartCommand -> onDestroy()
调用 onStartService() 方法启动后,该 Service会一直运行下去,直到调用 *stopService() * 或 stopSelf(),该 Service 才会停止运行并被销毁。
- onCreate()
- Service 没有被创建,在调用 startService() 时会调用该方法
- Service 已经在运行,在调用 startService() 时不会调用该方法
- onStartCommand()
如果多次执行 startService() 方法,当前方法也会被多次调用,我们在该方法中根据传入的参数进行实际的操作。 - onDestroy()
在 Service 被销毁时执行该方法。
startService() 实例
- 创建 Service 实例
package com.example.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class ServiceByStart extends Service {
public ServiceByStart() {
}
@Override
public void onCreate() {
super.onCreate();
Log.i("karl", "onCreate - Thread ID = "+ Thread.currentThread().getId());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("karl", "onStartCommand -startId = "+ startId +", Thread ID = "+ Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i("karl", "onBind Thread ID = "+ Thread.currentThread().getId());
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("karl", "onDestroy Thread ID = "+ Thread.currentThread().getId());
}
}
- 在 Activity 中启动 Service
package com.example.servicetest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("karl", "Activity Thread ID = "+ Thread.currentThread().getId());
Log.i("karl", "before service started");
Intent intent = new Intent(this, ServiceByStart.class);
startService(intent);
Log.i("karl", "after service started");
stopService(intent);
Log.i("karl", "after service stopped");
}
}
2. bindService()
完整生命周期函数如下:
onCreate() -> onBind() -> onUnbind() -> onDestroy()
- bindService() 启动的服务与调用者之间的关系是 client-server 关系。Service 只能有一个,但绑定在 Service 上的 client 可以有若干个。这里的 client 指组件,比如某个 Activity 等。
- client 可以通过 IBinder 接口获取到 Service 实例,从而实现在 client 中调用 Service 中自定义方法的需求,实现灵活交互。
- bindService() 方式启动的服务,其生命周期与和其绑定的 client 息息相关。当 client 被销毁时,会自动解除与相应 Service 的绑定,client 也可以调用 unbindService() 方法,显式解除绑定。当没有任何 client 与 Service 绑定时,Service 就会被销毁。
bindService 代码实例
问题:创建一个 Sevice,然后创建两个 Activity MainActivity 与 TestActivity,从 MainActivity 跳转到 TestActivity
- 创建 bind 方式的 Service
要想让 Service 支持 bindService() 方式,需要做以下工作:
- 在 Sevice 的 onBind() 方法中返回 IBinder 类型的实例
- 在 onBind() 方法中返回的 IBinder 实例中,要能够返回当前 Service 实例。最简单的方法就是在 service 中创建 Binder 的自定义内部类,加入类似 getService() 的方法返回 Service 实例
package com.example.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import java.util.Random;
public class ServiceByBind extends Service {
public ServiceByBind() {
}
// client 通过 Binder 实例获取到 Service 实例
public class MyBinder extends Binder {
public ServiceByBind getService() {
return ServiceByBind.this;
}
}
// 通过 Binder 实例,实现 client 与 server 之间的通信
private Binder binder = new MyBinder();
private final Random random = new Random();
@Override
public void onCreate() {
super.onCreate();
Log.i("karl", "onCreate - Thread ID = "+ Thread.currentThread().getId());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("karl", "onStartCommand -startId = "+ startId +", Thread ID = "+ Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i("karl", "onBind Thread ID = "+ Thread.currentThread().getId());
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("karl", "onUnbind from : "+ intent.getStringExtra("from"));
return false;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("karl", "onDestroy Thread ID = "+ Thread.currentThread().getId());
}
/**
* 自定义方法,供外界调用
* @return 一个随机的正整数
*/
public int getRandomInt() {
return random.nextInt();
}
}
- client 端调用
client 端需要做如下工作:
- 创建 ServiceConnection 实例,并重写 onServiceConnected() 方法和 onServiceDisconnected() 方法
- 当执行到 onServiceConnected() 方法时,通过 IBinder 实例获取到 Service 实例,实现 client 端与 server 端的连接
- 当执行到 onServiceDisconnected() 方法时,client 端与 server 端断开连接
MainActivity.java 如下:
package com.example.servicetest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View .OnClickListener{
private Button bind, unbind, start, finish;
private ServiceByBind byBind = null;
private boolean isBind = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBind = true;
ServiceByBind.MyBinder myBinder = (ServiceByBind.MyBinder) service;
byBind = myBinder.getService();
Log.i("karl", "main - service bound ComponentName:"+ name.getShortClassName());
int num = byBind.getRandomInt();
Log.i("karl", "main - random int = "+ num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.i("karl", "main service unbound ComponentName:"+ name.getShortClassName());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("karl", "main - onCreate() Thread ID : "+ Thread.currentThread().getId());
/*
Log.i("karl", "Activity Thread ID = "+ Thread.currentThread().getId());
Log.i("karl", "before service started");
Intent intent = new Intent(this, ServiceByStart.class);
startService(intent);
Log.i("karl", "after service started");
stopService(intent);
Log.i("karl", "after service stopped");
*/
initView();
}
private void initView() {
bind = findViewById(R.id.bind);
unbind = findViewById(R.id.unbind);
start = findViewById(R.id.startActivity);
finish = findViewById(R.id.finish);
}
@Override
protected void onResume() {
super.onResume();
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
start.setOnClickListener(this);
finish.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind:
Intent intent = new Intent(this, ServiceByBind.class);
intent.putExtra("from", "main");
Log.i("karl", "=============================");
Log.i("karl", "main bindService");
bindService(intent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind:
if(isBind) {
Log.i("karl", "main unbindService");
unbindService(connection);
}
break;
case R.id.startActivity:
Intent intent1 = new Intent(this, TestActivity.class);
Log.i("karl", "start activity test");
startActivity(intent1);
break;
case R.id.finish:
Log.i("karl", "main finished");
finish();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("karl", "main destroyed");
}
}
TestActivity.java 如下:
package com.example.servicetest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class TestActivity extends AppCompatActivity implements View .OnClickListener{
private ServiceByBind byBind = null;
private boolean isBind = false;
private Button bind, unbind, finish;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBind = true;
ServiceByBind.MyBinder myBinder = (ServiceByBind.MyBinder) service;
byBind = myBinder.getService();
Log.i("karl", "test - service bound ComponentName:"+ name.getShortClassName());
int num = byBind.getRandomInt();
Log.i("karl", "test - random int = "+ num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.i("karl", "test service unbound ComponentName:"+ name.getShortClassName());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initView();
}
private void initView() {
bind = findViewById(R.id.bind);
unbind = findViewById(R.id.unbind);
finish = findViewById(R.id.finish);
}
@Override
protected void onResume() {
super.onResume();
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
finish.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind:
Intent intent = new Intent(this, ServiceByBind.class);
intent.putExtra("from", "test");
Log.i("karl", "=============================");
Log.i("karl", "test bindService");
bindService(intent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind:
if(isBind) {
Log.i("karl", "test unbindService");
unbindService(connection);
}
break;
case R.id.finish:
Log.i("karl", "test finished");
finish();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("karl", "test onDestroy");
}
}
2. 如何保证 Service 不被销毁
- 调整 onStartCommand() 方法中的返回值
- START_NOT_STICKY
表示当 Service 运行的进程被 Android 系统强制杀掉之后,不会重新创建该 Service。 - START_STICKY
表示当 Service 运行的进程被 Android 系统强制销毁后,会重建 Service,但 onStartCommand() 方法中 intent 参数值为 null。 - START_REDELIEVER_INTENT
表示当 Service 运行的进程被 Android 系统强制销毁后,会重建 Service,并且在销毁当前 Service 之前保存 intent 参数的参数值。
- 提高 Service 的优先级
在 AndroidManifest.xml 文件中的标签中可以通过 android:priority = "1000" 这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时适用于广播。 - 提升 Service 进程的优先级
可以使用 startForeground() 将 service 放到前台状态,这样低内存时,被杀死的概率会低一些。 - 在 onDestroy() 方法中重启 Service
当 service 走到 onDestroy() 时,发送一个自定义广播,当收到广播时,重新启动 service。