Service总结

版权声明:很高兴认识你,我叫邵龙飞 原创文章,转载请注明 https://blog.csdn.net/qq_37482202/article/details/89057475

一、Service种类

按运行地点分类:

  1. 本地服务:运行在主进程中
  2. 远程服务:运行在独立的进程中

按运行类型分类:

  1. 前台服务:会有通知栏显示
  2. 后台服务:默认的服务,没有通知栏显示

按启动方式分类:

  1. startService启动:停止使用stopService
  2. bindService启动:停止使用unbindService
  3. 使用startService和bindService启动:同时使用stopService和unbindService停止

二、生命周期

 

onCreate()

系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作。

onStartCommand()

每次客户端调用startService()方法启动该Service都会回调该方法(多次调用)。一旦这个方法执行,service就启动并且在后台长期运行。通过调用stopSelf()或stopService()来停止服务。

onBind()

当组件调用bindService()想要绑定到service时系统调用此方法(一次调用,一旦绑定后,下次再调用bindService()不会回调该方法),提供一个返回一个IBinder来以使客户端能够使用它与service通讯,因为该方法在Service类中是抽象方法,所以你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null。

onUnbind()

当前组件调用unbindService(),想要解除与service的绑定时系统调用此方法(一次调用,一旦解除绑定后,下次再调用unbindService()会抛出异常)。

onDestory()

系统在service不再被使用并要销毁时调用此方法(一次调用)。service应在此方法中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一个调用。

注意点:

1.startService / stopService

生命周期顺序:onCreate->onStartCommand->onDestroy

①第一次 startService 会触发 onCreate 和 onStartCommand,以后在服务运行过程中,每次 startService 都只会触发 onStartCommand

②不论 startService 多少次,stopService 一次就会停止服务

2.bindService / unbindService

生命周期顺序:onCreate->onBind->onUnBind->onDestroy

如果一个Service在某个Activity中被调用bindService方法启动,不论bindService被调用几次,Service的onCreate方法只会执行一次,同时onStartCommand方法始终不会调用。

当建立连接后,Service会一直运行,除非调用unbindService来接触绑定、断开连接或调用该Service的Context不存在了(如Activity被Finish——即通过bindService启动的Service的生命周期依附于启动它的Context),系统在这时会自动停止该Service。

第一次 bindService 会触发 onCreate 和 onBind,以后在服务运行过程中,每次 bindService 都不会触发任何回调

3.混合型

当一个Service在被启动(startService)的同时又被绑定(bindService),该Service将会一直在后台运行,并且不管调用几次,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数一致(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会停止Service,必须调用stopService或Service自身的stopSelf来停止服务。

A bound service

被绑定的service是当其他组件(一个客户)调用bindService()来创建的。客户可以通过一个IBinder接口和service进行通信。客户可以通过 unbindService()方法来关闭这种连接。一个service可以同时和多个客户绑定,当多个客户都解除绑定之后,系统会销毁service。context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop,onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,当解除绑定时,只有完全没有Client与它绑定时才会调用onUnbind和onDestroy,否则都不会调用。

注意:由于TestService已经处于运行状态,所以ActivityB调用bindService时,不会重新创建TestService的实例,所以也不会执行TestService的onCreate回调方法,由于在ActivityA执行bindService的时候就已经执行了TestService的onBind回调方法而获取IBinder实例,并且该IBinder实例在所有的client之间是共享的,所以当ActivityB执行bindService的时候,不会执行其onBind回调方法,而是直接获取上次已经获取到的IBinder实例!!!并将其作为参数传入ActivityB的ServiceConnection的onServiceConnected方法中,标志着ActivityB与TestService建立了绑定连接,此时有两个客户单client(ActivityA和ActivityB)与TestService绑定。

三、前台服务

前台服务创建很简单,其实就在Service的基础上创建一个Notification,然后使用Service的startForeground()方法即可启动为前台服务。

大体流程:创建通知--》设置跳转--》获取通知服务--》显示通知--》启动前台服务

通知可以自定义视图,使用的是RemoteViews,其中只支持部分控件,使用IPC通信和反射实现,这里不多说

这里需要注意的一点就是如果你要给通知添加点击跳转,你最好为其指定一个父Activity,这样当从通知跳转到指定界面中后,按返回不会回到桌面而是应用的主视图,这样用户体验比较好

public class NotificationService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Notification.Builder mBuilder = new Notification.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentText("点击跳转");
        Intent intent2 = new Intent(this, SecondActivity.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(pendingIntent);
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = mBuilder.build();
        nm.notify(0, notification);
        startForeground(0, notification);
        return Service.START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

你还要在AndroidManifest中注册声明

        <activity
            android:name=".SecondActivity"
            android:parentActivityName=".MainActivity"/>
        <service android:name=".NotificationService" />

android:parentActivityName 的作用,就是为了左上角给子Activity加一个返回button
如果父 Activity 不在当前栈内(没有在当前栈有实例),这个属性不起效
如果父 Activity 在当前栈(在当前有实例),设置了该属性,父 Activity 会经历先销毁后创建的过程
如果设置父 Activity启动模式为 singleTask 或者 singleTop (两者效果一样的),那么父 Activity 就具有与 singTask 一样清理栈的 作用(清除在父 Activity 之上的那些Activity ),使得 父 Activity 得以重新独占设置与用于交互(可以走 onRsume 方法) 

四、怎么保证Service不被杀死

1)进程生命周期:

官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。

1. 如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。
2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.
3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。
4. 如果service可以使 用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
5. 如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。

解决方法:

方法一:onStartCommand中返回START_STICKY,如果内存不足被杀死,那么等内存足够是系统会自动重启Service;

方法二:Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

   1.前台进程( FOREGROUND_APP)
   2.可视进程(VISIBLE_APP )
   3. 次要服务进程(SECONDARY_SERVER )
   4.后台进程 (HIDDEN_APP)
   5.内容供应节点(CONTENT_PROVIDER)
   6.空进程(EMPTY_APP)

当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground将service放到前台状态。这样在低内存时被kill的几率会低一些。

         白色方法:放一个可见的Notification,使用startForeground

         灰色方法:它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。

方法四:监听锁屏事件或者主Activity被关闭时,显示一个1像素的透明Activity,让进程成为前台进程。

方法五:守护进程(Native层或者Java层),互相监听。

猜你喜欢

转载自blog.csdn.net/qq_37482202/article/details/89057475
今日推荐