【Android开发基础】四大组件之一Service(服务)的应用场景及使用(以实时聊天为例)

news2025/1/10 10:29:45

文章目录

    • 一、引言
      • 1、什么是服务?
      • 2、应用场景
      • 3、其他类同
    • 二、生命周期
    • 三、基础使用
      • 1、创建服务
      • 2、注册服务
      • 3、启动服务
    • 四、进阶使用(实时聊天)
      • 1、实现效果
      • 2、数据流图
      • 3、服务部分

一、引言

1、什么是服务?

       Service(服务)是一种可以在后台长时间执行的运行操作,没有用户界面的应用组件。主要处理用户长时间不用的功能,但又不得不时刻在后台待命的一些指令。

2、应用场景

  • (1)网络事务:聊天(等待他人回复短信)、地图定位(熄屏后实时定位的播报语音)等
  • (2)本地资源:播放音乐(读取音乐文件)、文件IO(后台上传文件、下载文件)等
  • (3)定时任务:订单超时(未支付情况下,一定时间后自动销毁订单)、闹钟提醒等

3、其他类同

       根据我对其他框架的学习,类同的方法、组件都大同小异,就比如:

  • 前端(不用框架时):,就是定时器嵌套定时器
  • Vue:Web Socket
  • Spring:Spring Task

       其实这些组件、方法、库的设计思想和针对的问题都离不开一种设计模式 - Observer观察者模式(发布-订阅)
       主要是完成定时任务、通知反馈这类业务需求而存在。

二、生命周期

在这里插入图片描述

  • onCreate() :
    首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。

  • onStartCommand() :
    当另一个组件通过调用startService()请求启动服务时,系统将调用此方法。

  • onDestroy() :
    当服务不再使用且将被销毁时,系统将调用此方法。

  • onBind() :
    当另一个组件通过调用bindService()与服务绑定时,系统将调用此方法。

  • onUnbind() :
    当另一个组件通过调用unbindService()与服务解绑时,系统将调用此方法。

  • onRebind() :
    当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,系统将调用此方法。

三、基础使用

1、创建服务

简单写一个测试服务,每隔一秒打印一次时间。

public class TestService extends Service {

	//线程的声明
    private Thread thread = null;
    Calendar ca = null;

    @Override
    public void onCreate() {
        super.onCreate();
        // 公共时间对象
        ca = Calendar.getInstance();
        //创建服务时,创建线程对象
        thread = new Thread(null, background ,"TestThread");
    }

    @SuppressWarnings("deprecation")
    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        if (!thread.isAlive()) {
            thread.start();
        }
    }

	/**
	*服务关闭时,销毁线程
	*/
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (thread.isAlive()) {
            thread.interrupt();
        }
    }

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

	private Runnable background = new Runnable() {
        @Override
        public void run() {
            try{
                while (!Thread.interrupted()) {
                    int mYear = ca.get(Calendar.YEAR);
        			int mMonth = ca.get(Calendar.MONTH) + 1;
        			int mDay = ca.get(Calendar.DAY_OF_MONTH);
        			int mHour = ca.get(Calendar.HOUR_OF_DAY);
        			int mMinute = ca.get(Calendar.MINUTE);
        			time = mYear + "-" + mMonth + "-" + mDay + " " + mHour + ":" + mMinute;
        			Log.i("当前时间", time)
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

}

2、注册服务

如果不在AndroidManifest.xml里注册服务,使用后,应用启动会出现报错

在这里插入图片描述

<!-- 服务 -->
<service android:name=".service.TestService" />

3、启动服务

public class MainActivity extends AppCompatActivity {

	static Intent serviceTest = null;   // 测试服务
	
	 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       	init_service(getBaseContext())
    }

	private static void init_service(Context context) {
        serviceTest = new Intent(context, TestService.class);
        context.startService(serviceTest);
    }
    
}

四、进阶使用(实时聊天)

这里我会大致提供实现实时聊天功能的服务部分代码

1、实现效果

使用service实现的聊天功能

2、数据流图

很久没有画数据流图,有点生疏。

在这里插入图片描述

3、服务部分

为了展示方便,我就将代码综合在一起了,没有分三层架构。

/**
 * 聊天服务
 */
public class ChatService extends Service {

    private Thread thread = null;      // 线程的声明
    static SqlChat sqlChat;            // 预显示聊天信息表
    static SqlChatInfo sqlChatInfo;    // 聊天信息内容表
    public static SqlFriend sqlFriend; // 好友信息表

    static String uid;                 // 当前用户ID,主要是用于定向连接服务器
    static String time;                // 当前时间

    static SharedPreferences user;

    @Override
    public void onCreate() {
        super.onCreate();
        // 公共时间
        Calendar ca = Calendar.getInstance();
        // 获取到用户ID
        user = getBaseContext().getSharedPreferences("user", MODE_PRIVATE);
        uid = user.getString("id", "");
        // 初始化数据库对象
        sqlChat = new SqlChat(getBaseContext(), uid);
        sqlFriend = new SqlFriend(getBaseContext(), Long.valueOf(uid).longValue());
        // 创建服务时,创建线程对象
        thread = new Thread(null, getChatInfo ,"ChatThread");
    }

    @SuppressWarnings("deprecation")
    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        if (!thread.isAlive()) {
            thread.start();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //服务关闭时,销毁线程
        if (thread.isAlive()) {
            thread.interrupt();
        }
    }

    private Runnable getChatInfo = new Runnable() {
        @Override
        public void run() {
            try{
                while (!Thread.interrupted()) {
                    // 监听聊天信息 服务器控制层的@GetMapper("chat/{id}")
                    // id可以为雪花临时编号也可以是指定长久编号
                    String jsonChatInfo = okHttp.get(getBaseContext(), "chat/" + uid);
                    // 数据转换
                    Gson gson = new Gson();
                    Result<List<ChatInfo>> result = gson.fromJson(jsonChatInfo, new TypeToken<Result<List<ChatInfo>>>(){}.getType());
                    if (result.getCode() == 1) {
                    	int mYear = ca.get(Calendar.YEAR);
        				int mMonth = ca.get(Calendar.MONTH) + 1;
        				int mDay = ca.get(Calendar.DAY_OF_MONTH);
        				int mHour = ca.get(Calendar.HOUR_OF_DAY);
        				int mMinute = ca.get(Calendar.MINUTE);
        				time = mYear + "-" + mMonth + "-" + mDay + " " + mHour + ":" + mMinute;
//                        Log.i("service", result.getData().toString());
                        // 获取服务器存储的数据
                        for (ChatInfo chatInfo:result.getData()) {
                            insertChatInfo(getBaseContext(), chatInfo);
                        }
                    }
                    Thread.sleep(3000);   // 每隔三秒实现一次访问
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

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

    public static void insertChatInfo(Context context, ChatInfo chatInfo) throws IOException {
        // 初始化聊天信息数据库
        sqlChatInfo = new SqlChatInfo(context, uid, String.valueOf(chatInfo.getId()));
        Chat ch = sqlChat.select(Long.valueOf(chatInfo.getId()).longValue());
        Friend friend = sqlFriend.select(chatInfo.getId());
        // 异常判断
        if (friend == null)
            throw new IOException("短信指向异常");
//        Log.i("test", ch.toString());
        if (ch.getId() == null) {
            ch.setId(friend.getId());
            ch.setImage(friend.getImage());
            ch.setNotes(friend.getNotes());
            ch.setNext(chatInfo.getText());
            ch.setState(1);   // 参数state 0 -> 不显示 1 -> 显示(可以实现聊天信息删除以及数据恢复)
            ch.setService(1);
            ch.setTime(time);
            sqlChat.insert(ch);
        } else {
            ch.setId(friend.getId());
            ch.setNext(chatInfo.getText());
            ch.setService(ch.getService() + 1);    // 小红点加一
            ch.setTime(time);
            sqlChat.update(ch);
        }
        // 添加聊天信息
        ChatInfo chatInfo1 = new ChatInfo();
        chatInfo1.setId(friend.getId());
        chatInfo1.setState(1);
        chatInfo1.setText(chatInfo.getText());
        chatInfo1.setTime(time);
//        Log.i("test", chatInfo1.toString());
        if (sqlChatInfo.insert(chatInfo1)) {
            Log.i("insert", "ChatInfo添加成功!");
            ShowNotifications.showN(context, ch);
            if (UserInfoActivity.chatInfoViewModel != null) {   // 如果在聊天界面就更新数据
                UserInfoActivity.chatInfoViewModel.setListChatInfo();
            }
            if (ChatFragment.chatViewModel != null) {    // 如果在主界面Chat更新界面
                ChatFragment.chatViewModel.renew();
            }
        } else {
            Log.i("insert", "ChatInfo添加失败!");
        }
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

路径规划算法:基于海洋捕食者优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于海洋捕食者优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于海洋捕食者优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能…

nginx系列第五篇:nginx中相关通信总结

目录 1.master进程监听socket 2.master和worker进程通信机制 2.1通信渠道 2.2通信方法 2.3通信内容 2.4子进程事件处理 3.epoll封装 4.linux系统下信号查看 1.master进程监听socket nginx在master进程socket bind listen&#xff0c;accept在通过epoll在子进程中控制&a…

ThreeJS 炫酷特效旋转多面体Web页 Demo 01《ThreeJS 炫酷特效制作》

本案例为一个 threejs 的特效网页&#xff0c;大小球体进行包裹&#xff0c;外球体为透明材质&#xff0c;但是进行了线框渲染&#xff0c;使其能够通过外球踢查看其内球体。 注&#xff1a;案例参考源于互联网&#xff0c;在此做代码解释&#xff0c;侵删 本案例除 ThreeJS 外…

Kafka原理

生产者原理解析 生产者工作流程图&#xff1a; 一个生产者客户端由两个线程协调运行&#xff0c;这两个线程分别为主线程和 Sender 线程 。 在主线程中由kafkaProducer创建消息&#xff0c;然后通过可能的拦截器、序列化器和分区器的作用之后缓存到消息累加器&#xff08;Rec…

uniApp 页面通讯统一解决方案

文章目录 往期回顾统一解决方案uni.on和eventChannel之间的选择如何设置触发器最终范例距离 往期回顾 uniapp 踩坑记录 uni.$on为什么不能修改data里面的数据 uniApp页面通讯大汇总&#xff0c;如何页面之间传值 统一解决方案 uni.on和eventChannel之间的选择 uni.on和eve…

61082-041502PLF(0.80mm)40 位置 连接器 插座,G846A050210T1HR 集管和线壳 WTB 1.00 PITCH

61082-041502PLF&#xff08;0.80mm&#xff09;FCI紧凑型Bergstak连接器提供广泛的堆叠高度和电路尺寸&#xff0c;以支持广泛的夹层&#xff0c;板堆叠通信&#xff0c;数据和工业应用。 连接器类型&#xff1a;插座&#xff0c;外罩触点 针位数&#xff1a;40 间距&#xff…

浅谈互联网搜索之召回

一、背景 在搜索系统中&#xff0c;一般会把整个搜索系统划分为召回和排序两大子系统。本文会从宏观上介绍召回系统&#xff0c;并着重介绍语义召回。谨以此文&#xff0c;希望对从事和将要从事搜索行业的工作者带来一些启发与思考。 二、搜索系统召回方法 不同于推荐系统&…

6月6号软件资讯更新合集......

Yao 0.10.3 正式发布&#xff0c;拥抱 AIGC 时代&#xff01; ChatGPT 解锁了新的人机交互方式&#xff0c;人类可以与电脑直接交流了&#xff01;AIGC 时代已经到来&#xff0c;万千应用正在升级或重构&#xff0c;Yao 提供了一个开箱即用的解决方案&#xff0c;可以快速开发…

迷茫了3年:做完这个测试项目,我终于决定辞职!

2023年早已过半&#xff0c;来个迟到的年中总结&#xff0c;说实话&#xff0c;2023&#xff0c;很迷茫&#xff0c;然后过的非常不如意&#xff0c;倒不是上一年的职业目标没达到&#xff0c;而是接下来的路根本不知道如何走。在没解决这个问题之前&#xff0c;或者说没搞清楚…

Web3.0概念

学习web3您需要先掌握 JavaScript node React 后续 我们将学习一门新的语言 叫 Solidity 他是一种只能合约语言开发 我们利用web3将不再依赖后端 而是连接只能合约开发 首先 我们先不用急着写代码 还是要概念为先 首先 我们来对比 WEB1.0到3.0的概念 首先 web1.0 更多处于信…

AI实战营第二期——第一次作业:基于RTMPose的耳朵穴位关键点检测

题目&#xff1a;基于RTMPose的耳朵穴位关键点检测 背景 根据中医的“倒置胎儿”学说&#xff0c;耳朵的穴位反映了人体全身脏器的健康&#xff0c;耳穴按摩可以缓解失眠多梦、内分泌失调等疾病。耳朵面积较小&#xff0c;但穴位密集&#xff0c;涉及耳舟、耳轮、三角窝、耳甲…

Unity - 从RG中解压法线贴图

文章目录 环境目的问题解决References 环境 Unity : 2020.3.37f1 Pipeline : BRP 目的 备忘便于索引 问题 之前使用 GPA 还原一些效果的时候&#xff0c;发现 法线贴图的 Y 通道数值不对&#xff0c;感觉被 翻转了 比方说&#xff0c;下面是 GPA 中的法线 这个法线是 DX …

Ubuntu20.04安装EVO工具教程

EVO工具全名为“Python package for the evaluation of odometry and SLAM”&#xff0c;使用Python写的轨迹评估工具&#xff0c;目前在SLAM领域论文中的“使用率”逐渐上升&#xff0c;可以说已经成为了作为SLAMer一定要会用的工具。最近需要使用evo工具评测SLAM算法性能并可…

Dell服务器安装Ubuntu系统

1、下载镜像&#xff0c;做启动盘 镜像链接 http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-live-server-amd64.iso 版本可以根据自己要求选择。 做启动盘 我用的是ultraiso 记得先格式化&#xff0c;再写入。 2、 设置BIOS启动 按F11&#xff0c;进入BIOS…

光线追踪是怎么影响渲染速度的,什么显卡可以支持?

在 3D 世界中&#xff0c;慢慢地人们倾向于让它尽可能逼真。他们可以应用许多技术和技巧&#xff0c;但有一种技术可以为您提供很多帮助&#xff0c;称为光线追踪。然而&#xff0c;众所周知&#xff0c;它是非常计算密集型的。在本文中&#xff0c;让我们进一步探讨它&#xf…

Java JUC并发编程

前言 1、JUC是指有关 java.util.concurrent包以及其子包&#xff0c;这些包都是有关线程操作的包 2、HTTPS服务请求中&#xff0c;WEB服务只负责创建主线程来接收外部的HTTPS请求&#xff0c;如果不做任何处理&#xff0c;默认业务逻辑是通过主线程来做的&#xff0c;如果业务…

Linux文件基础IO

目录 C文件IO相关操作 介绍函数 文件相关系统调用接口 接口介绍 fd文件描述符 重定向 缓冲区 inode 软硬链接 动静态库 库的制作 制作静态库 制作动态库 使用库 使用静态库 使用动态库 C文件IO相关操作 介绍函数 打开文件 参数介绍&#xff1a; const char*…

MySQL的explain字段解释

MySQL的explain字段解释 ,type类型含义:1.id 2.select_type 3.table 4.type(重要) 5.possible_keys 6.possible_keys 7. key 8.key_len 9. ref 10. rows(重要) 11. filtered 12. Extra(重要) 如下: Explain命令是查看查询优化器是如何决定执行查询的主要方法。这个功…

Firewalld防火墙详解

文章目录 Firewalld防火墙什么是防火墙Firewalld防火墙的概念Firewalld防火墙运行模式Firewalld防火墙的命令Firewalld防火墙的高级规则 Firewalld防火墙 什么是防火墙 防火墙&#xff1a;防范一些网络攻击。有软件防火墙、硬件防火墙之分。 硬件防火墙和软件防火墙的主要区…

【软件开发】MyBatis 理论篇

MyBatis 理论篇 1.MyBatis 是什么&#xff1f; MyBatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。…