安卓四大组件之Service


一、Service简介

Android Service 是 Android 系统中的一种组件,用于在后台执行长时间运行的操作或处理异步任务。Service 可以在不依赖于用户界面的情况下运行,并且可以在应用程序被关闭后继续运行。

Service 在 Android 应用程序中扮演着重要的角色,可以帮助应用程序执行一些耗时操作,同时保持用户界面的响应性。通过合理使用 Service,可以提高应用程序的性能和用户体验。


二、启动方式分类

Android Service 可以分为两种类型:Started Service 和 Bound Service。Started Service 是通过 startService() 方法启动的,它会在后台执行任务并在完成后停止。Bound Service 是通过 bindService() 方法绑定的,它与其他组件建立连接并可以进行通信。Service 可以执行网络请求、播放音乐、处理数据等任务,通常在 Service 中会创建一个单独的线程来执行这些操作,以避免阻塞主线程。

模式名称特点
startService一旦服务开启跟调用者(开启者)就没有任何关系了。开启者退出了,开启者挂了,服务还在后台长期的运行,只有通过调用停止服务的方法才能停止服务,或者是整个程序被kill掉,那么服务也会随之kill。开启者不能调用服务里面的方法
bindServicebind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可以调用服务里面的方法。(说白了就和小程序一样,打开的时候使用,用完了就关闭拍屁股走人,一次性滴)注意:绑定服务不会调用onstart()或者onstartcommand()方法

三、Service生命周期

官方说明图
在这里插入图片描述

3.1 常见生命周期使用

  • 1.被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
  • 2.被绑定的服务的生命周期:如果一个Service被基个Activity 调用 Context,bindservice 方法绑定启动,不管调用 bindservice 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用,
  • 3.被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
  • 4.当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里应当做一些清除工作,如停止在Service中创建并运行的线程。

特别注意:

  • 1.在调用 bindService 绑定到Service的时候,应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被finish 的时候绑定会自动解除,并且Service会自动停止);
  • 2.应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管是否使用bindService;
  • 3.同时使用 startService与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止Service,不管 startService与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;
  • 4.当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果 Activity 如果会自动旋转的话,旋转其实是Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
  • 5.在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 ontartCommand,不过之前的 onStart 任然有效。这意味着,如果开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么应当使用 onStartCommand 而不是 onStart。

3.2 生命周期方法具体介绍

方法作用自动调用的方法备注
startService()启动 Service 服务onCreate()、onStartCommand()startService()开启的Service,调用者退出后 Service仍然存在;
stopService()关闭 Service 服务onDestory()启动&绑定1个Service,若在无解绑的前提下调用 stopService()是无法停止服务的
bindService()绑定 Service 服务onCreate()、onBind()BindService()开启的Service,调用者退出后,Service随着调用者退出销毁
unbindService()解绑 Service 服务onUnbind()、onDestory()
onStartCommand()当另一个组件(如 Activity)通过调用 startService 请求启动服务时,系统将调用此方法。
onBind()当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须通过返回IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果并不希望允许绑定,则应返回 null。
onCreate()首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand)或 onBind 之前)。如果服务已在运行则不会调用此方法。
onDestroy()当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

onStartCommand()
onStartCommand() 是一个重要的方法,用于接收从其他组件(如 Activity 或其他 Service)传递过来的请求,并执行相应的操作。当通过 startService() 方法启动 Service 时,系统会调用 onStartCommand() 方法来传递启动指令,并且该方法需要返回一个整数值,用于指示系统如何处理 Service 的生命周期。

public int onStartCommand(Intent intent, int flags, int startId) {
    // 执行相应的操作
    return START_STICKY;
}

参数说明:

intent:包含启动 Service 的意图信息,可以通过它传递额外的数据。
flags:指示 Service 的启动方式,如 START_FLAG_REDELIVERY、START_FLAG_RETRY 等。
startId:表示 Service 的启动 ID,每次启动 Service 都会增加一个新的 ID。

onStartCommand() 方法需要返回一个整数值,该值用于指示系统如何处理 Service 的生命周期,常用的返回值包括:

START_STICKY:表示 Service 在被意外终止后会尝试重新启动,并且保留之前的 Intent 对象,适用于执行后台任务的 ServiceSTART_NOT_STICKY:表示 Service 在被意外终止后不会尝试重新启动,适用于执行一次性任务的 ServiceSTART_REDELIVER_INTENT:表示 Service 在被意外终止后会尝试重新启动,并且保留之前的 Intent 对象,适用于需要保持数据完整性的 Service

四、Service使用实例

Androidmanifest里Service的常见属性说明

属性说明备注
android:nameService的类名
android:labelService的名字若不设置,默认为Service类名
android:icon指定 Service 的图标用于在系统设置中显示。
android:permission指定调用 Service 需要的权限只有具有指定权限的应用程序或组件才能调用该 Service。
android:process指定 Service 所在的进程名可以为 Service 指定独立的进程,或者与应用程序的主进程共享一个进程。
android:enabled指定 Service 是否可用默认值为 true。如果设置为 false,则该 Service 将被禁用。
android:exported指定 Service 是否允许其他应用程序或组件调用默认值为 true。如果设置为 false,则只有与 Service 同一应用程序的组件可以调用它。

4.1 基础service

两个Button实现服务的开启和停止,并在服务的生命周期函数打log展示

MyService

public class MyService extends Service {


    //启动Service之后,就可以在onCreate()或onStartCommand()方法里去执行一些具体的逻辑
//由于这里作Demo用,所以只打印一些语句
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("henry","执行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("henry","执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("henry","执行了onDestory()");
    }


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

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/startService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="启动服务" />

    <Button
        android:id="@+id/stopService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/startService"
        android:layout_centerInParent="true"
        android:text="停止服务" />
</RelativeLayout>

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startService;
    private Button stopService;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService = (Button) findViewById(R.id.startService);
        stopService = (Button) findViewById(R.id.stopService);

        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.startService) {
            //构建启动服务的Intent对象
            Intent startIntent = new Intent(this, MyService.class);
            //调用startService()方法-传入Intent对象,以此启动服务
            startService(startIntent);
        } else if (v.getId() == R.id.stopService) {
            Intent stopIntent = new Intent(this, MyService.class);
            //调用stopService()方法-传入Intent对象,以此停止服务
            stopService(stopIntent);
        }

    }
}

在AndroidManifest.xml里注册Service
AndroidManifest.xml

      <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

依次点击两个按钮,观察log:
在这里插入图片描述

4.2 bindService demo

MyService

public class MyService extends Service {

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("henry", "执行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("henry", "执行了onDestory()");
    }

    @Override
    public IBinder onBind(Intent intent) {

        Log.d("henry", "执行了onBind()");
        //返回实例
        return mBinder;
    }


    @Override
    public boolean onUnbind(Intent intent) {

        Log.d("henry", "执行了onUnbind()");
        return super.onUnbind(intent);
    }

    //新建一个子类继承自Binder类
    class MyBinder extends Binder {

        public void service_connect_Activity() {

            Log.d("henry", "执行了service_connect_Activity");

        }
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/startService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="启动服务" />

    <Button
        android:id="@+id/stopService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/startService"
        android:layout_centerInParent="true"
        android:text="停止服务" />
    <Button
        android:layout_centerInParent="true"
        android:layout_below="@id/stopService"
        android:id="@+id/bindService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@id/bindService"
        android:id="@+id/unbindService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务"
        />
</RelativeLayout>

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startService;
    private Button stopService;
    private Button bindService;
    private Button unbindService;

    private MyService.MyBinder myBinder;


    //创建ServiceConnection的匿名类
    private ServiceConnection connection = new ServiceConnection() {

        //重写onServiceConnected()方法和onServiceDisconnected()方法
        //在Activity与Service建立关联和解除关联的时候调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        //在Activity与Service解除关联的时候调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //实例化Service的内部类myBinder
            //通过向下转型得到了MyBinder的实例
            myBinder = (MyService.MyBinder) service;
            //在Activity调用Service类的方法
            myBinder.service_connect_Activity();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        startService = (Button) findViewById(R.id.startService);
        stopService = (Button) findViewById(R.id.stopService);

        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);

        bindService = (Button) findViewById(R.id.bindService);
        unbindService = (Button) findViewById(R.id.unbindService);

        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.startService) {
            //点击启动Service
            Intent startIntent = new Intent(this, MyService.class);
            startService(startIntent);
        } else if (v.getId() == R.id.stopService) {
            //点击停止Service
            Intent stopIntent = new Intent(this, MyService.class);
            stopService(stopIntent);
        } else if (v.getId() == R.id.bindService) {
            //点击绑定Service
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTO_CREATE);
            //这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service
            //这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行
        } else if (v.getId() == R.id.unbindService) {
            //点击解绑Service
            unbindService(connection);
        }

    }
}

依次点击按钮观察log打印:
在这里插入图片描述

4.3 前后台服务

安卓前台服务和后台服务都是安卓系统中的服务,但它们在使用场景和运行方式上有所不同。

  • 前台服务是指用户可以看到的服务,通常会在通知栏中显示一个通知,用户可以通过通知栏来控制和交互。前台服务通常用于需要用户注意和交互的场景,比如音乐播放器、导航应用等。前台服务在运行时会占用一定的系统资源,并且有较高的优先级,系统会尽力保持其运行状态。
  • 后台服务是指用户看不到的服务,通常在后台默默地运行,不会显示通知。后台服务通常用于一些不需要用户交互的场景,比如数据同步、定时任务等。后台服务在运行时会尽量减少对系统资源的占用,以避免影响用户体验和系统性能。

这里重点看下前台服务,因为平常大部分都是后台服务。
什么是前台服务

前台服务是那些被认为用户知道(用户所认可的)且在系统内存不足的时候不允许系统杀死的服务。前台服务必须给状态栏提供一个通知,它被放到正在运行(Ongoing)标题之下——这就意味着通知只有在这个服务被终止或从前台主动移除通知后才能被解除。

为什么要使用前台服务?

在一般情况下,Service几乎都是在后台运行,一直默默地做着辛苦的工作。但这种情况下,后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收。
  那么,如果我们希望Service可以一直保持运行状态且不会在内存不足的情况下被回收时,可以选择将需要保持运行的Service设置为前台服务。
例:
App中的音乐播放服务应被设置在前台运行(前台服务)——在App后台运行时,便于用户明确知道它的当前操作、在状态栏中指明当前歌曲信息、提供对应操作。

前台Service优先级较高,不会由于系统内存不足而被回收;后台Service优先级较低,当系统出现内存不足情况时,很有可能会被回收

只需要在原有的Service类进行稍微修改即可

public class MyService extends Service {

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("henry", "执行了onCreat()");
        // 将该服务转为前台服务
        startForeground();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("henry", "执行了onDestory()");
    }

    @Override
    public IBinder onBind(Intent intent) {

        Log.d("henry", "执行了onBind()");
        //返回实例
        return mBinder;
    }


    @Override
    public boolean onUnbind(Intent intent) {

        Log.d("henry", "执行了onUnbind()");
        return super.onUnbind(intent);
    }

    //新建一个子类继承自Binder类
    class MyBinder extends Binder {

        public void service_connect_Activity() {

            Log.d("henry", "执行了service_connect_Activity");

        }
    }

    /**
     * 启动前台服务
     */
    private void startForeground() {
        String channelId = null;
        // 8.0 以上需要特殊处理
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            channelId = createNotificationChannel("kim.hsl", "ForegroundService");
        } else {
            channelId = "";
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId);
        Notification notification = builder.setOngoing(true)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setPriority(PRIORITY_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(1, notification);
    }

    /**
     * 创建通知通道
     *
     * @param channelId
     * @param channelName
     * @return
     */
    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(String channelId, String channelName) {
        NotificationChannel chan = new NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        service.createNotificationChannel(chan);
        return channelId;
    }
}

在这里插入图片描述

4.4 跨进程通信service+AIDL

如果你有aosp源码注册过系统服务看这个就是小菜一碟了。

demo使用两个工程,一个模拟服务器,一个模拟客户端
服务器端(Service)
步骤1:新建定义AIDL文件,并声明该服务需要向客户端提供的接口
步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind())
步骤3:在AndroidMainfest.xml中注册服务

客户端(Client)
步骤1:拷贝服务端的AIDL文件到目录下
步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service

本质:使用AIDL的binder机制进行跨进程通信。
参考链接:远程服务Service(含AIDL & IPC讲解)

五、总结

  • Service是四大组件之一,常用于在后台执行长时间运行的操作,不会直接与用户交互。
  • Service可以通过startService()方法启动,也可以通过bindService()方法绑定到一个Activity上。
  • Service可以在后台运行,即使应用被切换到后台或者关闭,Service仍然可以继续运行。
  • Service可以通过onStartCommand()方法接收来自其他组件的请求,并在后台执行相应的操作。
  • Service可以与Activity进行通信,可以通过Binder对象进行跨进程通信。
  • Service可以通过onDestroy()方法来释放资源,停止服务的运行。
  • Service可以通过在AndroidManifest.xml文件中声明来注册,并可以设置不同的启动模式和优先级。
  • 28
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值