0. 前言
- Service学习笔记01-启动方式与生命周期
- Service学习笔记02-实战 startService 与bindService
- Service学习笔记03- 前台服务实战
- Service学习笔记04- 其他服务实现方式与替代方式
1. 启动服务的两种方式区别
启动Service的两种方式是startService
和bindService
, 区别如下:
- 生命周期,
startService
调用Service后,会顺序执行onCreate()
和onStartCommand()
方法。且onCreate()
只有首次调用startService
才会执行,只有当调用stopSelf()
或者stopService
时Service才会停止运行
,
bindService
调用后会依次执行onCreate()
和onBind()
方法,且多次执行bindService时,onCreate()
和onBind()
方法并不会被多次调用,直到调用者的Context被销毁或者调用unBindService
,Service才会被销毁。 - 关联性,
onBind()
会返回一个IBinder
对象,由此调用者可以与Service进行关联,调用者可以据此获得Service对象,调用Service的方法。而startService
只能启动Service服务,然后onCreate
和onStartCommand()
依次执行,而调用者无法与Service产生任何关联。 - 跨进程,两种方式都支持跨进程,只要Service 在AndroidManifest.xml的声明的属性值
android:exported="false"
,不同的是,无论是进程内还是跨进程,startService调用方式都是一样的,而bindService()
如果是跨进程的,与进程内不一样,要使用AIDL 方式。
2. 启动服务实战
2.1 bindService()启动服务
通过绑定服务的方式可以在客户端获取对应Service的引用,从而完成与Service的交互。主要过程如下图所示:
- 首先,在继承Service的自定义服务中新建一个继承自IBinder的内部类,在IBinder中获取myService的引用
- 在服务的onBinder()方法中将Service中的IBinder对象注入(这个方法会在绑定服务成功的时候被调用,客户端可以通过这个方法获得IBinder对象,进而获取Service的引用)
- 在客户端声明一个ServiceConnection 对象,在ServiceConnection对象的onServiceContected() (这个方法就是在绑定服务成功的时候调用的)可以获得服务中的IBinder对象
- 通过获得的IBinder对象获取Service引用,可以获取Service中的数据和方法
2.1.1 代码实战
首先,我们先定义一个TestService,增加myBinder的内部类,在里面通过一个public类返回TestService 对象,然后重写onBind方法将Binder类返回给客户端,这是必要的一步,这样客户端就可以通过bind方式获取到我们的Service
public class TestService extends Service {
private static final String TAG = "TestService";
private static final Random generator = new Random();
//通过binder实现调用client与Service之间的通信
private MyBinder binder;
//要想自己的Service 支持bindService的启动方式,就必须在Service的OnBind方法中返回一个IBinder类型的实例
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "TestService -> onBind,Thread:" + Thread.currentThread().getName());
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "TestService -> onUnbind,Thread:" + intent.getStringExtra("from"));
return false;
}
public class MyBinder extends Binder {
public TestService getService() {
return TestService.this;
}
}
@Override
public void onCreate() {
Log.i(TAG, "TestService -> onCreate,Thread:" + Thread.currentThread().getName());
super.onCreate();
binder=new MyBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "TestService -> onStartCommand,StartId: " + startId + "Thread:" + Thread.currentThread().getName());
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
Log.i(TAG, "TestService -> onDestroy,Thread:" + Thread.currentThread().getName());
super.onDestroy();
}
//Service 暴露出去供client调用的公共方法
public int getRandomNumber() {
return generator.nextInt();
}
}
然后在Activity当中通过bindService来启动Service
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG="MainActivity";
private TestService service=null;
private boolean isBound=false;
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBound=true;
TestService.MyBinder myBinder=(TestService.MyBinder)binder;
service= myBinder.getService();
Log.i(TAG,"MainActivity onServiceConnected");
int num=service.getRandomNumber();
Log.i(TAG,"MainActivity 中调用TestService的getRandomNumber方法,结果:"+num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound=false;
Log.i(TAG,"MainActivity onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG,"MainActivity -> onCreate,Thread:"+Thread.currentThread().getName());
Button bindServiceButton=(Button)findViewById(R.id.btnBindService);
bindServiceButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Log.i(TAG,"onClick has into");
if(v.getId()==R.id.btnBindService){
Intent intent=new Intent(this,TestService.class);
intent.putExtra("from","ActivityA");
Log.i(TAG,"MainActivity 执行 bindService");
bindService(intent,connection,BIND_AUTO_CREATE);
}else if(v.getId()==R.id.btnUnBindService){
if(isBound){
Log.i(TAG,"MainActivity 执行 unBindService");
unbindService(connection);
}
}
}
}
在MainActivity 当中先是创建一个实现了ServiceConnection 的成员变量connection,里面主要需要重写onServiceConnected方法,在里面去创建IBinder 对象,并且获取Serivice对象
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG="MainActivity";
private TestService service=null;
private boolean isBound=false;
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBound=true;
TestService.MyBinder myBinder=(TestService.MyBinder)binder;
service= myBinder.getService();
Log.i(TAG,"MainActivity onServiceConnected");
int num=service.getRandomNumber();
Log.i(TAG,"MainActivity 中调用TestService的getRandomNumber方法,结果:"+num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound=false;
Log.i(TAG,"MainActivity onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG,"MainActivity -> onCreate,Thread:"+Thread.currentThread().getName());
Button bindServiceButton=(Button)findViewById(R.id.btnBindService);
bindServiceButton.setOnClickListener(this);
Button unBindServiceButton=(Button)findViewById(R.id.btnUnBindService);
unBindServiceButton.setOnClickListener(this);
Button startActivityButton=(Button)findViewById(R.id.btnStartActivity);
startActivityButton.setOnClickListener(this);
Button coastRandomNumButton=(Button)findViewById(R.id.btnCoastRandomNum);
coastRandomNumButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Log.i(TAG,"onClick has into");
if(v.getId()==R.id.btnBindService){
Intent intent=new Intent(this,TestService.class);
intent.putExtra("from","ActivityA");
Log.i(TAG,"MainActivity 执行 bindService");
bindService(intent,connection,BIND_AUTO_CREATE);
}else if(v.getId()==R.id.btnUnBindService){
if(isBound){
Log.i(TAG,"MainActivity 执行 unBindService");
unbindService(connection);
}
}else if(v.getId()==R.id.btnStartActivity){
Intent intent=new Intent(this,ActivityB.class);
Log.i(TAG,"MainActivity 启动ActivityB");
startActivity(intent);
}else if(v.getId()==R.id.btnFinish){
Log.i(TAG,"MainActivity 执行 finish");
this.finish();
}else if(v.getId()==R.id.btnCoastRandomNum){
Log.i(TAG,"MainActivity 执行 coastRandom");
Log.i(TAG,"random num :"+service.getRandomNumber());
}
}
@Override
protected void onDestroy() {
Log.i(TAG,"MainActivity -> onDestroy");
super.onDestroy();
}
}
- 问题1:在同一个Activity 重新执行bindService方法会如何?
重新执行ServiceConnection的onServiceConnected方法 - 问题2: bindService 或者unBindService 不成对出现会如何?
经过验证:重复执行bindService 方法只会执行一次Service的onBind 和onCreate 方法,bindService内部做了单例机制,但是如果多次执行unBindService方法会直接抛异常java.lang.IllegalArgumentException: Service not registered
- 问题3:为什么已经执行了unBindService方法还是可以引用绑定service 的方法?
问题3 从而引起来是否存在内存泄漏的问题,通过查看