最近,我开始着手在 Android 上制作专门的语音助手。至少可以说我与 Java 关系密切,而且我还没有时间玩 Kotlin,NativeScript 似乎是显而易见的选择。

现在这是一项正在进行的工作,但我已经了解了很多关于 Android 的知识,我想与您分享一些我的发现。

首先,对于这项任务,我需要不断地听唤醒词并做出相应的反应。在任何平台上实现此功能的明显选择是某种后台服务或守护程序。

当谷歌搜索 nativescript 和后台服务时,一个优秀的教程和一个示例 repo 出现在顶部(我正在谈论这个)。

唉,这是使用 IntentService ,它只按计划运行并在任务完成后退出。
虽然创建一个连续的后台服务非常容易,但缺少关于这个主题的示例(本文旨在解决这个问题)。

设置

对于本文,我假设我们正在使用
typescript hello_world 模板:

tns create ServiceExample --ts --appid tk.ozymandias.ServiceExample

适应其他模板/技术应该不难。

服务

首先在 下新建一个子文件夹app/,我们调用它service。这纯粹是为了保持您的项目结构干净整洁。现在使用这些内容
创建一个新文件app/service/continuous_service.android.ts

export const CONTINUOUS_SERVICE_CLASSNAME = "tk.ozymandias.ServiceExample.Continuous_Service";@JavaProxy("tk.ozymandias.ServiceExample.Continuous_Service")
class Continuous_Service extends android.app.Service {private timerId: number;onBind(): android.os.IBinder {return null;}onCreate(): void {super.onCreate();console.log("SERVICE CREATED");if (!this.timerId) {this.timerId = setInterval(() => {console.log("PING");}, 1000)}}onStartCommand(intent: android.content.Intent, flags: number, startId: number): number {console.log("SERVICE STARTED");return android.app.Service.START_REDELIVER_INTENT;}onDestroy(): void {console.log("SERVICE DESTROYED");super.onDestroy();clearInterval(this.timerId);}
}

现在这是一个非常基本的服务,它只是在后台运行并每秒向控制台打印“PING”。

在顶部,我们将服务名称导出为常量,稍后将在几个地方使用它。
唉,您至少需要在另外两个地方将服务名称指定为字符串文字。

第一个在这里很明显:@JavaProxy注释。
在此处使用变量将引发关于现有扩展的错误,而不是未定义的变量值。

第二个将在清单中。稍后再谈。

onCreate实例化服务时调用一次,onStartCommand每次服务启动onDestroy时调用,服务退出时调用。

服务如何启动和重新启动取决于
您从onStartCommand. 您可能很想回到START_STICKY这里,但这会在您的应用程序被终止时导致崩溃,因为系统会尝试重新启动您的服务null。

使其连续

到目前为止,我们有一个从您的应用程序开始的功能服务!但是当应用程序退出或被杀死时,我们如何让它继续运行呢?

让我们从制作广播接收器开始。

import { CONTINUOUS_SERVICE_CLASSNAME } from "./continuous-service.android";export const RESTART_RECEIVER_CLASSNAME = "tk.ozymandias.ServiceExample.Restart_Receiver";@JavaProxy("tk.ozymandias.ServiceExample.Restart_Receiver")
class Restart_Receiver extends android.content.BroadcastReceiver {onReceive(context: android.content.Context, intent: android.content.Intent): void {console.log("RESTART INTENT RECEIVED");const serviceIntent = new android.content.Intent();serviceIntent.setClassName(context, CONTINUOUS_SERVICE_CLASSNAME);context.startService(serviceIntent);}
}

然后让我们稍微修改一下我们的服务以在退出时调用广播接收器,以便它可以重新启动我们的服务。

// At the top
import { RESTART_RECEIVER_CLASSNAME } from "./restart-receiver.android";// In the onDestroy method in our serviceonDestroy(): void {// ...const restartIntent = new android.content.Intent();restartIntent.setClassName(this, RESTART_RECEIVER_CLASSNAME);this.sendBroadcast(restartIntent);}

您还应该onTaskRemoved在我们的服务中实现方法。
当用户从最近视图中滑动您的应用程序时调用它。
在这种情况下(可能还有其他情况)onDestroy,默认情况下不会调用。
所以让我们通过调用onDestroy来调用stopSelf!

// ...onTaskRemoved(intent: android.content.Intent): void {console.log("TASK REMOVED");this.stopSelf();}

现在我们有一个持续运行的服务!当应用程序退出或被杀死时,我们调用广播接收器,
然后重新启动我们的服务。

不幸的是,在较新版本的 Android 中,当系统
由于内存不足或电池优化而终止您的应用程序时,onDestroy不能保证会被调用。

前台服务

幸运的是,有一种官方的方法可以解决这个问题。
我们需要的是让我们的服务成为前台服务。
缺点是我们必须提供持久通知,但是从 Oreo 开始,此通知可以从系统设置中隐藏,
而不会影响我们的服务。

我们需要再次修改我们的服务,这次是
onCreate方法:

// In the onCreate method in our serviceonCreate(): void {// ...const builder: android.app.Notification.Builder = new android.app.Notification.Builder(app.android.context);// Need to check api level, NotificationChannel is required but only available on Oreo and aboveif (android.os.Build.VERSION.SDK_INT >= 26) {const channel: android.app.NotificationChannel = new android.app.NotificationChannel("persistence", "Service running indicator", android.app.NotificationManager.IMPORTANCE_LOW);const manager: android.app.NotificationManager = (<android.app.Activity>app.android.context).getSystemService(android.content.Context.NOTIFICATION_SERVICE);channel.enableLights(false);channel.enableVibration(false);manager.createNotificationChannel(channel);builder.setChannelId("persistence");}const notification: android.app.Notification = builder.build();this.startForeground(13, notification);}

这使得持续的前台服务具有
持久的通知,
无论如何都会保持运行(它仍然可以从设置中强制停止)。

收尾工作

现在,如果您尝试到目前为止的代码,它将崩溃。
那是因为我们没有在
AndroidManifest.xml!
我们需要声明的是我们需要的权限(仅在最新版本的 Android 上)、服务和接收者。

事不宜迟,这是清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="__PACKAGE__"android:versionCode="1"android:versionName="1.0"><supports-screensandroid:smallScreens="true"android:normalScreens="true"android:largeScreens="true"android:xlargeScreens="true"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.FOREGROUND_SERVICE" /><applicationandroid:name="com.tns.NativeScriptApplication"android:allowBackup="true"android:icon="@drawable/icon"android:label="@string/app_name"android:theme="@style/AppTheme"><activityandroid:name="com.tns.NativeScriptActivity"android:label="@string/title_activity_kimera"android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode"android:theme="@style/LaunchScreenTheme"><meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" /><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name="com.tns.ErrorReportActivity"/><service android:enabled="true" android:name="tk.ozymandias.ServiceExample.Continuous_Service" /><receiverandroid:name="tk.ozymandias.ServiceExample.Restart_Receiver"android:enabled="true"android:exported="true"android:label="ContinuousServiceRestarter" /></application>
</manifest>

小彩蛋

您可能已经注意到,我们收到的通知是
通用的“应用程序正在运行”通知,点击时会转到设置。
我们可以做得更好!

// In the onCreate method in our serviceonCreate(): void {// ...const appIntent: android.content.Intent = new android.content.Intent(app.android.context, com.tns.NativeScriptActivity.class);const pendingIntent: android.app.PendingIntent = android.app.PendingIntent.getActivity(app.android.context, 0, appIntent, 0);const builder: android.app.Notification.Builder = new android.app.Notification.Builder(app.android.context);builder.setContentText("Custom notification, F'Yeah!").setSmallIcon(android.R.drawable.btn_star_big_on).setContentIntent(pendingIntent);// ...}

使用 NativeScript 的 Android 持续后台服务相关推荐

  1. android杀死 后台服务,android怎么样做到相似于微信那样后台服务不会被杀死

    正在做一款锁屏应用. 做锁屏肯定用到了service,可是本人发现每日手动点击自带的内存清理按钮的时候,本人的那个service总是会被杀死. 而微信的后台服务却是一直正常的运行,不会被杀掉. 360 ...

  2. 使用ASP.NET做android的后台服务

    毕业设计的后台使用学过的C#语言来搭建好了,使用Sqlserver 2008作为系统的数据库,android端采用ksoap2-android.jar包进行连接.记录下搭建服务器及发布IIS. 1.搭 ...

  3. Android判断后台服务(Service)是否运行

    今天在项目中需要判断某个服务是否在后台运行,因此写了一个工具类方便大家调用,话不多说,上代码. *** 服务工具类* * @author Administrator* */ public class ...

  4. android 后台自动拍照,Android实现后台服务拍照功能

    一.背景介绍 最近在项目中遇到一个需求,实现一个后台拍照的功能.一开始在网上寻找解决方案,也尝试了很多种实现方式,都没有满意的方案.不过确定了难点:即拍照要先预览,然后再调用拍照方法.问题也随之而来, ...

  5. Android官方开发文档Training系列课程中文版:后台服务之IntentService的创建

    原文地址:http://android.xsoftlab.net/training/run-background-service/index.html 引言 除非特别指定,否则所有的操作都是在UI线程 ...

  6. android 测试屏幕触点,如何检测Android Studio中的后台服务是否触摸了屏幕?

    我正在研究 android studio上的一个项目.我需要在后台服务中检测屏幕是否被触摸(并弹出一条消息).但是,我在后台服务中检测屏幕是否被触摸有问题而不影响使用智能手机的用户. 当我说" ...

  7. Android移动开发之【Android实战项目】后台服务Service

    桌面应用程序:可见 服务:不可见 长期在后台运行 帮助应用执行耗时的操作 安卓的服务:安卓四大组件之一 不可见 后台长期运行 界面与服务有时候要执行数据交互 文章目录 如何创建服务 创建一个类 继承S ...

  8. android 发送前台广播,使用IntentService与BroadcastReceiver实现后台服务(Android7.0可用)...

    IntentService的优点 IntentService会创建单独的线程处理所有的Intent请求, 会处理onHandleIntent方法实现的代码, 隐藏开发者无须处理多线程问题, 当所有请求 ...

  9. android api文档中文版_什么骚操作,用Android能写后台服务?

    事情是这样子的, 前段时间在浏览github的时候,偶然间发现了一个有趣的项目AndServer,说是能够用Android写一个可运行的后台服务,并且写法还和当前最流行的SpringBoot很类似,于 ...

最新文章

  1. php函数多个参数_php中,用函数,如果有很多个参数,只使用最后一个参数,有什么优雅的写法?...
  2. 创建Vue实例传入的options||Vue的生命周期
  3. SAP CRM和Cloud for Customer的扩展字段元数据
  4. 浅谈 Spark 的多语言支持
  5. flash挂载到系统 spi_jffs2根文件系统挂载不上spi flash
  6. java 内置web服务器_Webstorm 2016内置web服务器配置
  7. 【CDH】 kafkaServer-gc.log日志太多
  8. 3- 基于代理 Dao 实现 CRUD 操作
  9. Python官方软件包存储库成恶意软件大本营?
  10. javascript实现图片轮播_手撸一个简易版轮播图(上)
  11. atitit.获取connection hibernate4
  12. 网易博客 android,android编译环境
  13. 显著性水平与p值的区别
  14. 微商史上最全软文标题写作套路(收藏版)
  15. workbench中施加预紧力进行模态分析
  16. 为什么商家有了收款二维码还要使用聚合支付?
  17. 基于FPGA的信号发生器
  18. Couldnt check the working tree for unmerged files because of an error. bad signature index file cor
  19. dll是什么呢?dll丢失如何解决?
  20. LoRa无线工业智能手表

热门文章

  1. 赛码网输入注意事项 Python
  2. 2016.03.31,英语,《Vocabulary Builder》Unit 08
  3. 解决jdbcTemplate和jpa查询TINYINT(1)返回BOOLEAN的问题
  4. 微信启动图片6年来的首次更换;网信办对微博、贴吧作出处罚;谷歌Play成为全球最大手机应用平台丨价值早报
  5. 基于JSP的在线书店的设计
  6. 常态C的5个错误和相应的解决方案
  7. 三层交换机与路由器的异同
  8. 不固定图片宽高瀑布流_类百度图片的固定高度横向瀑布流js方法及纯css实现的方法记录...
  9. 福大计算机在职研究生,福州大学在职研究生上课时间介绍
  10. 冰冰学习笔记:二叉树的功能函数和OJ练习题