Service的启动方式
startService()
- 当我们通过调用了Context的startService方法后,我们便启动了Service,通过startService方法启动的Service会一直无限期地运行下去,只有在外部调用Context的stopService或Service内部调用Service的stopSelf方法时,该Service才会停止运行并销毁。
bindService()
相比于用startService启动的Service,bindService启动的服务具有如下特点:
- bindService启动的服务在调用者和服务之间是典型的client-server的接口,即调用者是客户端,service是服务端,service就一个,但是连接绑定到service上面的客户端client可以是一个或多个。这里特别要说明的是,这里所提到的client指的是组件,比如某个Activity。
- 客户端client(即调用bindService的一方,比如某个Activity)可以通过IBinder接口获取Service的实例,从而可以实现在client端直接调用Service中的方法以实现灵活的交互,并且可借助IBinder实现跨进程的client-server的交互,这在纯startService启动的Service中是无法实现的。
- 不同于startService启动的服务默认无限期执行(可以通过Context的stopService或Service的stopSelf方法停止运行),bindService启动的服务的生命周期与其绑定的client息息相关。当client销毁的时候,client会自动与Service解除绑定,当然client也可以通过明确调用Context的unbindService方法与Service解除绑定。当没有任何client与Service绑定的时候,Service会自行销毁(通过startService启动的除外)。
- startService和bindService二者执行的回调方法不同:startService启动的服务会涉及Service的的onStartCommand回调方法,而通过bindService启动的服务会涉及Service的onBind、onUnbind等回调方法。
以上内容均摘自:孙群 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/iispring/article/details/48169339?utm_source=copy
Service的生命周期
onCreate()
源码注释:Called by the system when the service is first created. Do not call this method directly.
简单来说,这个方法其实就是系统在创建完Service之后给的回调函数。需要注意的是,这个方法只有在Service未创建,或者在该Service被销毁而调用onDestroy()方法之后,再度启用Service时才会调用。
该方法的调用时机: 在第一次执行startService或者bindService方法时,如果Service没有运行的时候会创建该Service并执行Service的onCreate回调方法;如果Service已经处于运行中,那么就不会执行Service的onCreate方法。也就是说如果多次执行了Context的startService/bindService方法启动Service,Service方法的onCreate方法只会在第一次创建Service的时候调用一次,以后均不会再次调用。我们可以在onCreate方法中完成一些Service初始化相关的操作。
onStartCommand()
在执行了startService方法之后,有可能会调用Service的onCreate方法(上面已经说过),在这之后一定会执行Service的onStartCommand回调方法。也就是说,如果多次执行了Context的startService方法,那么Service的onStartCommand方法也会相应的多次调用。
onStartCommand方法很重要,我们在该方法中根据传入的Intent参数进行实际的操作,比如会在此处创建一个线程用于下载数据或播放音乐等。
需要注意的是,这个方法只与startService这种启动方式有关,bindService是不会调用到这个方法的。
onBind()
Service中的onBind方法是抽象方法,所以Service类本身就是抽象类,也就是onBind方法是必须重写的,即使我们用不到。在通过startService使用Service时,onBind()方法并不会被调用,我们在重写onBind方法时,只需要将其返回null即可。onBind方法主要是用于给bindService方法调用Service时才会使用到。
这个方法更多的是用于Service和Activity之间的交互,以及进程间通信使用(这又是一个非常大的课题了)。
onUnbind()
通过bindService方法启动的Service会直接与Client绑定,在Client销毁或者Client主动调用unbindService(serviceConnection)后,如果没有其他Client存在,那么就会先执行onUnbind(),再执行onDestroy()。
可以这样说,对于一个通过bindService启动的Service而言,它的onUnbind()方法和onDestroy()必然是一起执行的。
onDestroy()
通过startService方法启动的Service会无限期运行,只有当调用了Context的stopService或在Service内部调用stopSelf方法时,Service才会停止运行并销毁,在销毁的时候会执行Service回调函数。
而通过bindService方法启动的Service,如上在onUnbind()所述,就不在赘言了。
代码演示
演示模板代码
MyService.java
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
LogUtils.e("onBind");
return null;
}
@Override
public void onCreate() {
super.onCreate();
LogUtils.e("onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.e("onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
LogUtils.e("onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
LogUtils.e("onDestroy");
super.onDestroy();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.e("serviceConnection in MainActivity");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startService(View view) {
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
public void bindService(View view) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
}
public void stopService(View view) {
Intent intent = new Intent(this, MyService.class);
stopService(intent);
}
public void unbindService(View view) {
unbindService(serviceConnection);
}
public void jump2BAct(View view) {
startActivity(new Intent(this, BActivity.class));
}
}
R.layout.activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startService"
android:text="startService"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="bindService"
android:text="bindService"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="stopService"
android:text="stopService"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="unbindService"
android:text="unbindService"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="jump2BAct"
android:text="jump2BAct"
android:textAllCaps="false" />
</LinearLayout>
BActivity.java
public class BActivity extends AppCompatActivity {
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.e("serviceConnection in BActivity");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
}
public void bindService(View view) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
}
public void unBindService(View view){
unbindService(serviceConnection);
}
}
R.layout.activity_b
<?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"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="bindService"
android:text="bindService"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="unBindService"
android:text="unBindService"
android:textAllCaps="false" />
</LinearLayout>
startService()方法启动Service,以及多次startService()
在MainActivity中点击三次startService(),再点击一次 stopService(), Log如下:
诚如以上所述,onCreate()只会在创建时调用,onStartCommand会响应每一次的startService。
bindService()方法启动Service相关测试
在MainActivity中bindService多次
在MainActivity中点击三次bindService,最后点击unbindService
对于bindService的启动方式,在Service创建完成并且绑定成功后,就不再调用onBind方法了。
在BActivity中bindService完成后,不调用unbindService直接返回
诚如之前描述所言,如果一个Service在client中以bindService启动,即便不主动unbind,最后也会随着client的销毁而自动unbind,不过需要注意内存泄露的问题。
在MainActivity中bindService完成后,进入BActivity再次bindService
从BActivity返回之前调用unBindService
从BActivity不调用unBindService,直接返回
当从上一步BActivity返回后,在MainActivity中调用unbindService方法
通过对多个client以bindService启动Service的操作来看,不管有多少client以bindService的方式启动Service,Service只会在第一次启动时调用onCreate()和onBind(),并且unbind这个方法的调用也只会在最后一个存活的client解绑时,才会调用,并在调用完unbind之后,调用onDestroy(),彻底结束Service。
Service被系统回收重建
以下内容均摘自: 孙群 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/iispring/article/details/47689819?utm_source=copy
当Android面临内存匮乏的时候,可能会销毁掉你当前运行的Service,然后待内存充足的时候可以重新创建Service,Service被Android系统强制销毁并再次重建的行为依赖于Service中onStartCommand方法的返回值。我们常用的返回值有三种值,START_NOT_STICKY、START_STICKY和START_REDELIVER_INTENT,这三个值都是Service中的静态常量。
START_NOT_STICKY
如果返回START_NOT_STICKY,表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,当然如果在其被杀掉之后一段时间又调用了startService,那么该Service又将被实例化。那什么情境下返回该值比较恰当呢?如果我们某个Service执行的工作被中断几次无关紧要或者对Android内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受,那么我们便可将 onStartCommand的返回值设置为START_NOT_STICKY。举个例子,某个Service需要定时从服务器获取最新数据:通过一个定时器每隔指定的N分钟让定时器启动Service去获取服务端的最新数据。当执行到Service的onStartCommand时,在该方法内再规划一个N分钟后的定时器用于再次启动该Service并开辟一个新的线程去执行网络操作。假设Service在从服务器获取最新数据的过程中被Android系统强制杀掉,Service不会再重新创建,这也没关系,因为再过N分钟定时器就会再次启动该Service并重新获取数据。
START_STICKY
如果返回START_STICKY,表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,但是onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一个用来播放背景音乐功能的Service就适合返回该值。
START_REDELIVER_INTENT
如果返回START_REDELIVER_INTENT,表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。只要返回START_REDELIVER_INTENT,那么onStartCommand重的intent一定不是null。如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。