重要:本文不会教你什么是 Android 后台服务,以及如何使用它们。如果你想了解服务,请前往Android API 指南。
大家好,由于这是我的第一篇博客文章,所以需要做一个适当的介绍。我的名字是 Peter,我是一名软件开发人员,并且是 NativeScript 核心 Android 运行时团队的一员。今天我将向您展示 Android 后台服务在 NativeScript 应用程序中的行为,并提供编写您自己的实现的指南。
当您的应用程序需要执行繁重的非 UI 工作时,您必须注意不要阻塞 UI 线程并破坏其他平滑的用户体验。在 Android 上,使用后台服务是这项工作的正确技术。
繁重的非 UI 工作的常见示例包括
* - 核心模块包已经包含了用于异步 HTTP 客户端、图像的异步处理等的现成解决方案
** - NativeScript 开源社区创建了插件,使开发人员能够使用 SQLite 和其他 NOSQL 数据库
几乎所有在使用 Android 开发时可以做的事情,您也可以在 NativeScript 中完成。由于 NativeScript Android 运行时的性质 - JavaScript 代码在主 UI 线程上运行,在使用 Android 服务时,存在某些已知限制。
以下是如何使用 Android Alarm Manager 来安排调用 IntentService 的示例,该服务将创建通知,即使应用程序的 Activity 已被销毁(应用程序已关闭)。我们将利用 NativeScript 的一项优势 -直接使用 JavaScript 访问原生 (Java) API,以便编写尽可能接近你在网上查找 Android 指南时找到的代码。原始代码SO
tns create my-app
cd my-app
android.app.IntentService.extend(
"com.tns.notifications.NotificationIntentService"
/* 为你的类指定一个有效的名称,因为它需要在 AndroidManifest 中声明 */
, {
onHandleIntent:
function
(intent) {
var
action = intent.getAction();
if
(
"ACTION_START"
== action) {
postNotification();
}
else
if
(“ACTION_STOP” == action) {
/* 获取系统 Alarm Manager 并取消所有挂起的警报,这将阻止服务定期执行 */
}
android.support.v4.content.WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
});
function
postNotification() {
// 做一些事情。例如,从后端获取最新数据以创建丰富的通知?
var
utils = require(
"utils/utils"
);
var
context = utils.ad.getApplicationContext();
// 获取 Android 中应用程序上下文的引用
var
builder =
new
android.app.Notification.Builder(context);
builder.setContentTitle(
"Scheduled Notification"
)
.setAutoCancel(
true
)
.setColor(android.R.color.holo_purple)
// 可选
.setContentText(
"This notification has been triggered by Notification Service"
)
.setVibrate([100, 200, 100])
// 可选
.setSmallIcon(android.R.drawable.btn_star_big_on);
// 当通知被按下时,将打开主 NativeScript Activity
var
mainIntent =
new
android.content.Intent(context, com.tns.NativeScriptActivity.class);
var
pendingIntent = android.app.PendingIntent.getActivity(context,
1,
mainIntent,
android.app.PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
builder.setDeleteIntent(getDeleteIntent(context));
var
manager = context.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
manager.notify(1, builder.build());
}
/* 仅适用于从通知屏幕中删除通知 */
function
getDeleteIntent(context) {
var
intent =
new
android.content.Intent(context, com.tns.broadcastreceivers.NotificationEventReceiver.class);
intent.setAction(
"ACTION_DELETE_NOTIFICATION"
);
return
android.app.PendingIntent.getBroadcast(context, 0, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
}
android.support.v4.content.WakefulBroadcastReceiver.extend(
"com.tns.broadcastreceivers.NotificationEventReceiver"
, {
onReceive:
function
(context, intent) {
var
action = intent.getAction();
var
serviceIntent;
if
(
"ACTION_START_NOTIFICATION_SERVICE"
== action) {
serviceIntent = createIntentStartNotificationService(context);
}
else
if
(
"ACTION_DELETE_NOTIFICATION"
== action) {
serviceIntent = createIntentDeleteNotification(context);
}
if
(serviceIntent) {
android.support.v4.content.WakefulBroadcastReceiver.startWakefulService(context, serviceIntent);
}
}
})
var
Intent = android.content.Intent;
function
createIntentStartNotificationService(context) {
var
intent =
new
Intent(context, com.tns.notifications.NotificationIntentService.class);
intent.setAction(
"ACTION_START"
);
return
intent;
}
function
createIntentDeleteNotification(context) {
/* 与上面类似,只是动作不同 */
}
function
getStartPendingIntent(context) {
var
alarmIntent =
new
android.content.Intent(context, com.tns.broadcastreceivers.NotificationEventReceiver.class);
intent.setAction(
"ACTION_START_NOTIFICATION_SERVICE"
);
return
android.app.PendingIntent.getBroadcast(context, 0, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
}
function
setupAlarm(context) {
var
alarmManager = context.getSystemService(android.content.Context.ALARM_SERVICE);
var
alarmIntent = getStartPendingIntent(context);
alarmManager.setRepeating(android.app.AlarmManager.RTC_WAKEUP,
java.lang.System.currentTimeMillis(),
1000 * 60 * 60 * 24,
/* 闹钟将每 24 小时发送一次 `alarmIntent` 对象 */
alarmIntent);
}
函数 cancelAlarm(context) { … }
module.exports.setupAlarm = setupAlarm;
function
createViewModel() {
var
utils = require(
"utils/utils"
);
var
services = require(
""./service-helper"
);
/* … */
viewModel.onTap =
function
() {
services.setupAlarm(utils.ad.getApplicationContext());
}
/* 在用户交互时插入您取消闹钟管理器代码 */
return
viewModel;
}
<?
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-screens
android:smallScreens
=
"true"
android:normalScreens
=
"true"
android:largeScreens
=
"true"
android:xlargeScreens
=
"true"
/>
<
uses-sdk
android:minSdkVersion
=
"17"
android:targetSdkVersion
=
"__APILEVEL__"
/>
<!-- 所有用户权限都在上面声明 -->
<!-- 使用 WakefulBroadcastReceiver 需要授予 WAKE_LOCK 权限 -->
<
uses-permission
android:name
=
"android.permission.WAKE_LOCK"
/>
<
application
... >
<
activity
...
</intent-filter>
</
activity
>
<
activity
android:name
=
"com.tns.ErrorReportActivity"
/>
<
service
android:name
=
"com.tns.notifications.NotificationIntentService"
android:enabled
=
"true"
android:exported
=
"false"
/>
<
receiver
android:name
=
"com.tns.broadcastreceivers.NotificationEventReceiver"
/>
</
application
>
</
manifest
>
现在您已经准备好测试您的通知调度程序了!
我们使用 *IntentService* 在没有前台活动的情况下执行操作。 请记住,代码在 NativeScript Android 应用程序的主线程上执行。 一个更密集的操作,例如下载图像,可能会阻塞前台活动 - 从而冻结用户界面,或者因违反 Android 中的 严格线程策略 而引发异常。 考虑使用后台服务进行几乎立即产生结果的操作。
注意:在开发发送通知的应用程序时要非常小心。 一些用户可能会发现他们很烦人,如果他们经常收到它们,并且因此 - 删除您的应用程序。
注意:当您希望在不同的线程上卸载长时间运行的作业时,建议的方法是在 Java 类中编写实现,并在 NativeScript 中调用该作业。
在即将发布的版本中,我们将推出对 NativeScript 中 Web Workers 功能的支持,允许您在后台线程中执行代码,所有这些都通过 Angular/JavaScript 进行。