Android Service 启动方式,生命周期和应用场景详解
startService 和 bindService 区别:
startService 特点:一旦服务开启跟调用者(开启者)就没有任何关系了。开启者退出了,开启者挂了,服务还在后台长期的运行。开启者不能调用服务里面的方法。
bindService 特点:bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可以调用服务里面的方法。(说白了就和小程序一样,打开的时候使用,用完了就关闭拍屁股走人,一次性滴)注意:绑定服务不会调用onstart()
或者onstartcommand()
方法
一,Service 含义:
Service分为本地服务(LocalService)和远程服务(RemoteService):
1、本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,
也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。
2、远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,
不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。
Service启动方式(主要是1,2两种):
1、startService 启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;
2、bindService 启动的服务:该方法启动的服务可以进行通信。停止服务使用unbindService;
3、startService 同时也 bindService 启动的服务:停止服务应同时使用stepService与unbindService
二. Service的生命周期(第一次启动:onCreate()------》onStartCommand();第二次启动:nStartCommand();关闭:onDestory();)
通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法:
-
onCreate()
1.如果service没被创建过,调用startService()后会执行onCreate()回调;
2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作。 -
onStartCommand() ------》其中有四种int类型返回值的:
START_STICKY: 当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
START_NOT_STICKY:当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
START_REDELIVER_INTENT:当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。
onStartCommand()方法很重要,我们在该方法中根据传入的Intent参数进行实际的操作,比如会在此处创建一个线程用于下载数据或播放音乐等。 -
onBind()
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。 -
onDestory()
在销毁的时候会执行Service该方法。
这几个方法都是Service的回调方法,且在主线程中执行。
用startService 方式启动Service的时候重写onStartCommand()的方法。每次用该方式启动Service的时候都会调用改方法。
第二种方式:通过bindService启动Service
bindService启动服务特点:(和小程序一样,一次性滴,进入页面的时候绑定service,退出的时候关闭service)
1.bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
2.client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
3.bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。
当没有任何client与Service绑定时,Service会自行销毁。
bindService代码实例
1.创建一个FinnService继承Service(Server)
/** * Created by Finn on 2019-10-10. */ public class bindServiceA extends Service{ //client 可以通过Binder获取Service实例 public class MyBinder extends Binder { public bindService getService() { return bindServiceA .this; } } //通过binder实现调用者client与Service之间的通信 在onBind中返回该对象 private MyBinder binder = new MyBinder(); private final Random generator = new Random(); @Override public void onCreate() { Log.i("Finn","bindServiceA - onCreate - Thread = " + Thread.currentThread().getName()); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("Finn", "bindServiceA - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName()); return START_NOT_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { Log.i("Finn", "bindServiceA - onBind - Thread = " + Thread.currentThread().getName()); return binder; } @Override public boolean onUnbind(Intent intent) { Log.i("Finn", "bindService - onUnbind - from = " + intent.getStringExtra("from")); return false; } @Override public void onDestroy() { Log.i("Finn", "bindServiceA - onDestroy - Thread = " + Thread.currentThread().getName()); super.onDestroy(); } //getRandomNumber是Service暴露出去供client调用的公共方法 public int getRandomNumber() { return generator.nextInt(); }
2.创建ActivityA,可以通过bindService绑定服务(client)
public class ActivityA extends Activity { private bindServiceA service = null; private boolean isBind = false; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { isBind = true; bindService .MyBinder myBinder = (bindServiceA.MyBinder) binder; service = myBinder.getService();//通过ServiceConnection 中的IBinder获取 绑定的service对象 Log.i("Finn", "ActivityA - onServiceConnected"); int num = service.getRandomNumber();//通过service对象可对 bindServiceA中的函数进行操作 Log.i("Kathy", "ActivityA - getRandomNumber = " + num); } @Override public void onServiceDisconnected(ComponentName name) { isBind = false; Log.i("Finn", "ActivityA - onServiceDisconnected"); } }; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Log.i("Finn", "ActivityA - onCreate - Thread = " + Thread.currentThread().getName()); bindservice(); unbindservice(); } private void bindservice(){ Intent intent = new Intent(this, bindServiceA.class); intent.putExtra("from", "ActivityA"); Log.i("Finn", "ActivityA 执行 bindServiceA"); bindService(intent, conn, BIND_AUTO_CREATE);//通过该方法绑定服务(周期:onCreate()------》onbind()) } private void unbindservice(){ //单击了“unbindService”按钮 if (isBind) { Log.i("Finn", "ActivityA 执行 unbindService"); unbindService(conn);//通过该方法解除绑定服务 周期:----先执行 onunbind()---》onDestroy()
} } @Override protected void onDestroy() { super.onDestroy(); Log.i("Finn", "ActivityA - onDestroy"); } }
bindServices创建如下:
要想让Service支持bindService调用方式,需要做以下事情:
1.在Service的onBind()方法中返回IBinder类型的实例。
2.onBInd()方法返回的IBinder的实例需要能够返回Service实例本身。通常,最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得Service实例了。