.net开发安卓入门 - Service
- Android Service 概述
- Service VS Thread (服务和线程之间进行选择)
- 前台服务
- 代码
- 启动前台服务方法
- 运行效果
- 后台服务
- 代码
- 启动代码
- 绑定服务
- AIDL
- 同系列文章推荐
Android Service 概述
移动应用不像桌面应用。 桌面具有大量资源,如屏幕空间、内存、存储空间和连接的电源,移动设备不会。 这些约束强制移动应用的行为方式不同。 例如,移动设备上的小屏幕通常意味着一次只显示一个应用 (,即活动) 可见。 其他活动将移动到后台,并推送到无法执行任何工作的挂起状态。 但是,仅仅因为 Android 应用程序处于后台并不意味着应用无法继续工作。
Android 应用程序至少由以下四个主要组件之一组成:活动、广播接收器、Intent和服务。 活动是许多出色的 Android 应用程序的基石,因为它们提供了允许用户与应用程序交互的 UI。 但是,当涉及到执行并发或后台工作时,活动并不总是最佳选择。
Android 中后台工作的主要机制是 服务。 Android 服务是一个组件,旨在在没有用户界面的情况下执行某些工作。
Service VS Thread (服务和线程之间进行选择)
根据桌面程序开发经验,很容易联想到多线程,因为看上去多线程也能够解决服务所面临的问题,那么在安卓中存在一定由他必然的原因,根据官方文档介绍,我理解成是安卓的一个设计漏洞导致衍生出来的Service这个东西。
官方解释如下(MSDN):
所有 Android 应用程序都有一个 主线程 (也称为 运行活动的 UI 线程) 。 若要使设备保持响应,Android 必须能够以每秒 60 帧的速度更新用户界面。 如果 Android 应用在主线程上执行过多工作,则 Android 会删除帧,这反过来又会导致 UI 显示为混蛋 (有时也称为 简) 。 这意味着,在两个帧之间的时间跨度中,UI 线程上执行的任何工作都应完成,大约 16 毫秒 (每 60 帧 1 秒)
为了解决此问题,开发人员可以使用活动中的线程来执行一些会阻止 UI 的工作。 但是,这可能会导致问题。 Android 可能会销毁并重新创建活动的多个实例。 但是,Android 不会自动销毁线程,这可能会导致内存泄漏。 其中的主要示例是 设备旋转 时 – Android 将尝试销毁活动的实例,然后重新创建一个新实例,这是潜在的内存泄漏 - 活动的第一个实例创建的线程仍将运行。
那我们看看Google Developer 怎么说的吧
Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
在服务和线程之间进行选择
简单地说,服务是一种即使用户未与应用交互也可在后台运行的组件,因此,只有在需要服务时才应创建服务,
如果您必须在主线程之外执行操作,但只在用户与您的应用交互时执行此操作,则应创建新线程。
前台服务
前台服务启动后,它必须通过调用 StartForeground 向 Android 注册自身。 如果服务以 Service.StartForegroundService 该方法启动,但不自行注册,则 Android 将停止该服务并将应用标记为非响应。
StartForeground 采用两个参数,这两个参数都是必需的:
- 用于标识服务的应用程序中唯一的整数值。
- 只要 Notification 服务正在运行,Android 就会在状态栏中显示的对象。
只要服务正在运行,Android 就会在状态栏中显示通知。 通知至少将为运行该服务的用户提供视觉提示。 理想情况下,通知应为用户提供应用程序的快捷方式,或者可能提供一些操作按钮来控制应用程序。 例如,这是一个音乐播放器 - 显示的通知可能具有暂停/播放音乐的按钮、回退到上一首歌曲或跳到下一首歌曲。
代码
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace 我的第一个安卓程序
{
[Service(Exported = true, Name = "com.lhd.service.TestService")]
public class TestService : Service
{
public override void OnCreate()
{
base.OnCreate();
Log.Info("XXXXAAA", $"日志信息:当前时间{DateTime.Now}");
}
public override IBinder OnBind(Intent intent)
{
return null;
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
Log.Info("XXXXAAA", $"StartCommandResult:当前时间{DateTime.Now}");
//DispatchNotificationThatServiceIsRunning();
//注册ForegroundService
RegisterForegroundService();
return StartCommandResult.NotSticky;
}
void DispatchNotificationThatServiceIsRunning()
{
Notification.Builder notificationBuilder = new Notification.Builder(this, "notice")
.SetSmallIcon(Resource.Drawable.notice_small)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText("TestService 服务!");
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(1000, notificationBuilder.Build());
}
private void RegisterForegroundService()
{
var notification = new Notification.Builder(this, "notice")
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText("卧槽!")
.SetSmallIcon(Resource.Drawable.notice_small)
.SetContentIntent(BuildIntentToShowMainActivity())
.SetOngoing(true)
.AddAction(BuildRestartTimerAction())
.AddAction(BuildStopServiceAction())
.Build();
// Enlist this instance of the service as a foreground service
StartForeground(1000, notification);
}
/// <summary>
/// Builds a PendingIntent that will display the main activity of the app. This is used when the
/// user taps on the notification; it will take them to the main activity of the app.
/// </summary>
/// <returns>The content intent.</returns>
PendingIntent BuildIntentToShowMainActivity()
{
var notificationIntent = new Intent(this, typeof(MainActivity));
notificationIntent.SetAction("com.lhd.GoMainAction");
notificationIntent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTask);
//notificationIntent.PutExtra(Constants.SERVICE_STARTED_KEY, true);
var pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, PendingIntentFlags.Immutable);
return pendingIntent;
}
/// <summary>
/// Builds a Notification.Action that will instruct the service to restart the timer.
/// </summary>
/// <returns>The restart timer action.</returns>
Notification.Action BuildRestartTimerAction()
{
var restartTimerIntent = new Intent(this, GetType());
//restartTimerIntent.SetAction(Constants.ACTION_RESTART_TIMER);
var restartTimerPendingIntent = PendingIntent.GetService(this, 0, restartTimerIntent, PendingIntentFlags.Immutable);
var builder = new Notification.Action.Builder(Resource.Drawable.ic_mtrl_chip_checked_black,
"周而复始!",
restartTimerPendingIntent);
return builder.Build();
}
/// <summary>
/// Builds the Notification.Action that will allow the user to stop the service via the
/// notification in the status bar
/// </summary>
/// <returns>The stop service action.</returns>
Notification.Action BuildStopServiceAction()
{
var stopServiceIntent = new Intent(this, GetType());
//stopServiceIntent.SetAction(Constants.ACTION_STOP_SERVICE);
var stopServicePendingIntent = PendingIntent.GetService(this, 0, stopServiceIntent, PendingIntentFlags.Immutable);
var builder = new Notification.Action.Builder(Android.Resource.Drawable.IcMediaPause,
"事了拂衣去,深藏身与名",
stopServicePendingIntent);
return builder.Build();
}
}
}
启动前台服务方法
[Java.Interop.Export]
public void StartService(View view)
{
service = new Intent(this, typeof(TestService));
StartForegroundService(service);
}
运行效果
后台服务
代码
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 我的第一个安卓程序
{
[Service(Exported = true, Name = "com.lhd.service.ConsoleWriteDateTimeService")]
public class ConsoleWriteDateTimeService : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
Task.Run(() =>
{
while (true)
{
Log.Info(nameof(ConsoleWriteDateTimeService), $"ConsoleWriteDateTimeService:当前时间{DateTime.Now}");
Task.Delay(3000).Wait();
}
});
return StartCommandResult.NotSticky;
}
}
}
启动代码
[Java.Interop.Export]
public void StartConsoleWriteDateTimeService(View view)
{
StartService(new Intent(this, typeof(ConsoleWriteDateTimeService)));
}
这两个暂时先不学习了,等有空在学吧!
↓↓↓↓↓↓↓↓↓
绑定服务
绑定服务是客户端-服务器接口中的服务器。借助绑定服务,组件(例如 Activity)可以绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。
AIDL
同系列文章推荐
.net开发安卓入门 - 环境安装
.net开发安卓入门 - Hello world!
.net开发安卓入门 - 基本交互(Button,输入EditText,TextView,Toast)
.net开发安卓入门 - 布局与样式
.net开发安卓入门 - Activity
.net开发安卓入门 - Notification(通知)
.net开发安卓入门 -记录两个问题处理办法