Android四大组件——Service详解

2023-05-16

        Service 为后台运行,不可见,没有界面。优先级高于Activity(内存不足时先杀掉Activity),运行在主线程且不能做耗时操作。

一、Service 启动方式

1、startService()

        通过 startService 启动后,service会一直无限期运行下去,当外部调用了 stopService() 或stopSelf() 方法时,该Service才会停止运行并销毁。当系统资源不足时, 会回收一些不重要的service,service 被系统回收也会停止运行并被销毁。

生命周期

onCreate()

1、如果 service 没被创建过,调用 startService() 后会执行 onCreate() 回调;
2、如果 service 已处于运行中,调用 startService() 不会执行 onCreate() 方法。

        此方法适合完成一些初始化工作。

onStartCommand()

        如果多次执行了 startService() 方法,那么 Service 的 onStartCommand() 方法也会相应的多次调用。

onBind()

        Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。

         使用 startService 方法启动 Service,onBind() 方法基本用不到,重写时返回一个 null 即可。

onDestory()

        在销毁的时候会执行Service该方法。

代码实例

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 启动service
        Intent intent = new Intent(MainActivity.this, TestService.class);
        startService(intent);
    }
}

TestService.java

public class TestService extends Service {
    private static final String TAG = "TestService";

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

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xiaoxu.testdemo">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MainActivity ">
        <service
            android:name=".TestService"
            android:enabled="true"
            android:exported="true" />

        <activity
            android:name=".MainActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

2、bindService

        bindService 启动的服务和调用者之间是典型的 client-server 模式。调用者是 client,service则是 server 端。service只有一个,但绑定到 service 上面的 client 可以有一个或多个。这里所提到的 client 指的是组件,比如某个Activity。
        client 可以通过 IBinder 接口获取 Service 实例,从而实现在 client 端直接调用 Service 中的方法以实现灵活交互,这在通过 startService 方法启动中是无法实现的。
        bindService 启动服务的生命周期与其绑定的 client 息息相关。当 client 销毁时,client 会自动与 Service 解除绑定。当然,client 也可以明确调用 Context 的 unbindService() 方法与 Service 解除绑定。当没有任何 client 与 Service 绑定时,Service 会自行销毁。

生命周期

onCreate()

        当服务通过 onStartCommand() 和 onBind() 被第一次创建的时候,系统调用该方法。该调用要求执行一次性安装。

onBind()

        当其他组件想要通过 bindService() 来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回 IBinder 对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回 null。

onUnbind()

        当客户中断所有服务发布的特殊接口时,系统调用该方法。

onRebind()

        当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。

onDestroy()

        当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。

代码实例

MainAcivity.java

public class MainAcivity extends Activity{
    private TextService mService = null;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, TestService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }


    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            MyBinder mBinder = (MyBinder)binder;
            mService = mBinder.getService();
            String name = mService.getUserName();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

MyService.java

public class TestService extends Service{
    //通过 binder 实现调用者 client 与 Service 之间的通信
    private MyBinder binder = new MyBinder();

    // client 可以通过 Binder 获取 Service 实例
    public class MyBinder extends Binder {
        public MyService getService() {
            return TestService.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

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

    @Override
    public boolean onUnbind(Intent intent) {
        return false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    // getUserName 是 Service 暴露出去供 Client 调用的公共方法
    public int getUserName() {
        return "XiaoXu";
    }
}

3、startForegroundService

        Android 8.0 系统不允许后台应用创建后台服务,只能使用 startForegroundService() 启动服务,创建服务后,应用必须在 5 秒内调用该服务的 startForeground() 显示一条可见通知,声明有服务在挂着,不然系统会停止服务 + ANR 提示。
        系统的要求 Notification 要加 Channel,并且要在 onStartCommand 中执行 startForeground(),因为这个主要是针对后台保活的服务,如果在服务运行期间,再次使用 startForegroundService() 启动服务,那么这次就不会调用服务的 onCreate 方法,只会调用onStartCommand 方法。如果不在 onStartCommand 方法里再挂个通知的话,系统会认为你使用了 startForegroundService 却不在 5 秒内给通知,很傻地就停止服务 + ANR 提示。

代码实例

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 启动service
        Intent intent = new Intent(this, TestService.class) ;
        startForegroundService(intent);
    }
}

TestService.java

public class TestService extends Service {
    private static final String TAG = "TestService";

    private Notification notification;
    public static final String CHANNEL_ID_STRING = "service_01";

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

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_STRING,
                getString(R.string.app_name),
                NotificationManager.IMPORTANCE_LOW);
        manager.createNotificationChannel(mChannel);
        notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
        // 下面第一个参数不能为 0
        startForeground(1, notification);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

        可以看到使用  startForegroundService 启动方式与 startService 基本相同,只是在 Service 的 onStartCommand 增加弹出通知的操作。startForeground 方法中的第一个参数不能为 0,是因为这个通知不能与正常通知 id 相同,否则通知会被“隐藏”,这不是官方想看到的,就会出现 Context.startForegroundService() did not then call Service.startForeground() 异常。

二、其他

1、使用场景

startService

        前台应用调用 startService 启动 Service,Service 独立运行,不需要与 Activity 进行交互。

bindService

        前台应用调用 bindService 启动 Service,Android 与 Service 进行绑定,并且 Activity 可以得到 Service 的实例,实现与 Service 的交互。

startForegroundService

        后台应用调用 startForegroundService 启动 Service,Android 8.0 之后强制要求,以提示用户。例如:在收到开机广播时启动服务,使用 startService 会无法启动,只能使用 startForegroundService 进行启动并弹出通知提示用户。

2、隐藏通知

        有时在使用 startForegroundService 启动服务时,想隐藏常驻通知。

1)显示通知后马上调用 stopForeground(true) 取消通知。在 Android 11 中测试,服务被杀掉。

2)提前注册一个没有声音没有震动的通知channel,注册一个无声无振动的通知渠道。startForeground 时与该通知使用同一个 id,就可以隐藏我们的通知了。(未尝试)

参考:Android8 避免startForeground方法弹出通知

3、AIDL Service死亡监听

        服务的死亡监听我们使用第二种启动方式。

启动 Service

private void bindService(Context context) {
    Intent serviceIntent = new Intent();
    serviceIntent.setAction("com.cx.test");
    serviceIntent.setPackage("com.cx.test.MyService");
    boolean bindSuccess = context.bindService(serviceIntent, mServiceConnection, Service.BIND_AUTO_CREATE);
}

绑定结果回调

private IPushInterface mIPushInterface;
 
private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        if (iBinder != null) {
            //通过AIDL拿到服务
            mIPushInterface = IPushInterface.Stub.asInterface(iBinder);
            try {
                //服务死亡监听
                if (mIPushInterface != null) {
                    mIPushInterface.asBinder().linkToDeath(mDeathRecipient, 0);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
 
    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        //连接失败
    }
};

死亡监听回调

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        if (mContext != null) {
            //重新绑定服务
            bindService(mContext);
        }
    }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android四大组件——Service详解 的相关文章

  • STM32外围电路硬件解析

    复位电路 xff1a 复位 xff1a 让MCU回到最开始的状态 并且从头开始 xff0c 重新执行程序 我们什么时候需要复位 xff1f 1 烧录的时候 2 程序跑飞了的时候 3 上电复位 xff08 上电的时候需要复位 xff09 复位
  • stm32f103——中断——UART中断服务函数

    在程序中 xff0c CPU对外界突发事件进行处理的方式又两种 xff1a 1 轮询系统 xff1a xff08 在main中 xff0c 使用while循环 xff0c 进行循环判断外界事物是否发生 xff09 while xff08 1
  • Docker快速入门,看这个就够了

    Docker快速入门 一 Docker介绍1 1 Docker背景1 2 Docker概念1 3 Docker的优势1 4 Docker的三个基本概念 二 Docker的安装和使用2 1 安装依赖包2 2 安装Docker 三 启动Dock
  • Kubernetes(K8S)集群部署搭建图文教程(最全)

    Kubernetes 集群安装 前期准备集群安装系统初始化Harbor采取私有的仓库去镜像使用集群检测集群功能演示 前期准备 第一步 xff1a Router软路由构建 第二步 xff1a centos7安装 5台 xff08 自行安装 x
  • 【STM32-HAL库】一步步搭建出FOC矢量控制(附C代码)

    说明 本文为无刷电机或PMSM电机驱动的简易代码 xff0c 旨在分享一些个人调试过程的小心得 xff0c 提供一个demo文件 xff0c 程序仍有许多不完善的地方 xff0c 建立起个人的FOC底层驱动 xff0c 可以帮助快速熟悉FO
  • Ubuntu整个系统迁移到其他盘办法

    Ubuntu整个系统迁移到其他盘办法 一 xff1a 制作U盘启动器 xff0c 刻录Ubuntu系统 从U盘引导进入系统 xff0c 点击 xff1a try Ubuntu without install选项 二 xff1a 把需要迁移的
  • 四旋翼无人机飞控系统设计(基础知识篇)

    飞行原理 四旋翼的结构组装有十字模式和X模式之分 xff0c 两者的基本原理一致 xff0c 方向结构不同 xff0c 都是通过四个电机的组合状态进行控制姿态飞行 xff0c 而十字型四旋翼机头是对准其中一个电机的 xff0c X型的四旋翼
  • 大数据组件-Maxwell常用命令(持续更新完善)

    Download Download binary distro https github com zendesk maxwell releases download v1 33 0 maxwell 1 33 0 tar gz Sources
  • 【px4编译】make px4fmu-v3_default upload 不能使用

    今天遇到编译pixhawk2代固件无法upload的问题 xff0c 出现TypeError a bytes like object is required not str xff0c 在1代中是可以make upload的 ninja E
  • 【PX4代码】关于px4代码中timestamp与gps_itow的时间问题

    gps的输出频率为5hz xff0c 由此可见timestamp应该是113248090 1e 6转换为s xff0c 由次可见gps的itow时间应该为271998999 1e 3转换为s 总结 px4中的时间为cpu应该除以10 6 x
  • 【PX4代码】关于ekf2输出频率只有1ohz的问题

    问题 我在使用的是pixhawk 2代 imu的输出频率都在100hz以上 xff0c 使用的gps模块输出频率为5hz xff0c 使用log的local position csv 查看ekf2估计输出的点位信息只有10hz xff0c
  • Keil4中C51的debug调试步骤技巧

    1 选择相应的调试仿真连接器 xff1a 选择 xff1a project gt Options for Target 如下图所示 xff1a 然后在弹出对话框的Debug选项下选择仿真器的型号 xff1a 2 进入调试 xff1a 3 部
  • 怎么操作linux服务器

    Linux 服务器就是采用 Linux 系统的网络服务器 xff0c 同时也有采用 windows 的服务器 xff0c 作用是类似的 而 Linux 并不是一个特定的系统 xff0c 而是使用 Linux 内核的系统 xff0c 现在发行
  • 人工智能如何可以思考?

    近日在给同事讲人工智能的时候 xff0c 提到当数据量不够的时候 xff0c 必要时需要加入人工工程 xff0c 引导计算机 归纳 一些知识 xff0c 毕竟计算机智能比起人类智能 xff0c 最大的缺陷可能在于不懂得 举一反三 换句话说
  • bag文件内topic对应的frame_id查看指令

    启动ROS roscore 运行数据集 span class token comment 数据集小的话 xff0c 建议慢速播放 span rosbag play xxx span class token punctuation span
  • GPS数据类型(ROS)

    文章目录 一 传感器分类二 作用三 系统组成四 位置表示五 数据格式六 ROS中GPS数据格式sensor msgs NavSatFixROS中GPS数据主要包含这四类 xff0c 分别是gps 裸数据ros封装 xff0c 位置 xff0
  • cmake使用教程

    CMakeLists txt文档编写以及packsge xml文档介绍 CMakeLists txt文档一 cmake minimum required命令二 CMake中的编译类型三 cmake编译选项 xff08 与2对应 xff09
  • 四旋翼无人机飞控系统设计(闭环控制系统)

    对于一个简单的飞控程序来说 xff0c 控制器是它最核心的部分 xff0c 这里主要与大家讨论控制系统的基本理论知识 xff08 自控大佬请绕道 xff09 xff0c 包括控制系统概念 闭环控制系统的原理 下篇将侧重包含pid控制算法的具
  • 针对frame_id和child_frame_id的理解

    ros基础必看之各个frame的理解 ROS坐标系统 xff0c 常见的坐标系和其含义 ROS中TF 坐标系转换 原理与使用
  • IMU预积分学习

    IMU预积分学习 一 IMU状态传递方程 一 IMU状态传递方程 几种不同的表达形式 xff1a 1 lio mapping xff1a 参考大佬 xff1a lio mapping 及 VINS Mono代码及理论推导 xff08 2 x

随机推荐