地理位置、服务和本地通知
Geolocation, service and local notifications
我正在尝试为 Android 制作我的第一个与地理定位和本地通知相关的系统应用程序。
我想象是这样的……有基本的activityMainActivity
。启动后,它会启动一个服务 TestService
,如果坐标发生变化,它会将它们发送到服务器上,并作为回复接收一些消息,这些消息将显示为本地通知。
我有一些问题。
如果我关闭应用程序(使用任务管理器),则服务将停止,因此坐标更改后什么也不会发生。
我需要什么服务才能一直工作?还是不可能?
激活本地通知后,它会启动 NotifyActivity
,显示详细信息。我在那里单击 buttonDelete
- 它会关闭 NotifyActivity
并启动 MainActivity
。但是,如果之后我切换到 OS 屏幕(使用 Back
按钮)并返回(使用任务管理器),那么将再次显示 'NotifyActivity' 而不是 'MainActivity'。
为什么会发生以及如何避免?
主要活动
[Activity(Label = "LocationTest", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
StartService(new Intent(this, typeof(TestService)));
button.Text = "Started";
};
}
}
地理定位服务
[Service]
public class TestService : Service, ILocationListener
{
// Start service
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
locManager = (LocationManager)GetSystemService(LocationService);
locationCriteria = new Criteria();
locationCriteria.Accuracy = Accuracy.Coarse;
locationCriteria.PowerRequirement = Power.Low;
string locationProvider = locManager.GetBestProvider(locationCriteria, true);
// Preferences.MinTime, for example, 60 (seconds)
// Preferences.MinDist, for example, 100 (meters)
locManager.RequestLocationUpdates(locationProvider, Preferences.MinTime * 1000, Preferences.MinDist, this);
return StartCommandResult.Sticky;
}
public void OnLocationChanged(Location loc)
{
// Send coordinates to the server, receive a response, and show local notification
var msg = new ReceivedMessage(counter++, "Test Title", loc.ToString());
ShowNotification(msg);
}
// show local notification
void ShowNotification(ReceivedMessage msg)
{
var myContainer = new Bundle();
myContainer.PutLong("msg_id", Convert.ToInt64(msg.Id));
myContainer.PutStringArray("msg_data", new [] { msg.Title, msg.Text });
var resultIntent = new Intent(this, typeof(NotifyActivity));
resultIntent.PutExtras(myContainer);
TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotifyActivity)));
stackBuilder.AddNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(Convert.ToInt32(msg.Id), PendingIntentFlags.UpdateCurrent);
Notification.Builder builder = new Notification.Builder(this)
.SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate)
.SetAutoCancel(true)
.SetContentIntent(resultPendingIntent)
.SetContentTitle(msg.Title)
.SetContentText(msg.Text)
.SetSmallIcon(Resource.Drawable.Icon);
var nm = (NotificationManager)GetSystemService(NotificationService);
nm.Notify(Convert.ToInt32(msg.Id), builder.Build());
}
}
本地通知
[Activity(Label = "NotifyActivity")]
public class NotifyActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.NotifyActivity);
var msg_id = Intent.Extras.GetLong("msg_id");
var msg_data = Intent.Extras.GetStringArray("msg_data");
FindViewById<TextView>(Resource.Id.textTitle).Text = msg_data[0];
FindViewById<TextView>(Resource.Id.textDescription).Text = msg_data[1];
FindViewById<Button>(Resource.Id.buttonDelete).Click += delegate {
StartActivity(typeof(MainActivity));
Finish();
};
}
}
示例项目here
我没有使用 xamarin 的经验,但我可以尝试回答一般与 Android 相关的问题。
问题 1
当您通过任务管理器关闭您的应用程序时,您所做的不仅仅是关闭应用程序 - 您正在终止整个进程。虽然您无法控制用户的行为,但通常这不是 Android 用户停止应用程序的方式。 "correct" 关闭应用程序的方法是简单地退出它,这不会破坏进程,并使您的服务 运行 处于后台。
因此,为了回答您的问题,我认为您无法采取任何措施来解决用户强行终止您的应用程序进程的情况。但是,我认为您不必担心这种边缘情况,因为在正常情况下,您的应用程序的进程不会被终止,直到系统认为它没问题或绝对需要它。因此,如果应用程序以正常方式关闭,服务应该继续 运行。文档说:
the system will keep the service running as long as either it is
started or there are one or more connections to it with the
Context.BIND_AUTO_CREATE flag. Once neither of these situations hold,
the service's onDestroy() method is called and the service is
effectively terminated.
这样你的服务就OK了。通常,您的应用不应处理其进程被强制终止的情况。
More information about Processes
More information about the Service lifecycle
问题二
我不太确定这里发生了什么。但是,查看您的链接项目,我注意到您的 AndroidManifest.xml
文件几乎没有填写。我怀疑如果没有这个文件,android 系统不知道如何初始化你的 backstack,也不知道你的哪个活动是 MAIN 和 LAUNCHER activity.
您应该在 AndroidManifest.xml
中声明您的活动,并在其中指定 MainActivity
是您的 MAIN 和 LAUNCHER activity,并且它是 [=14] 的父项=].这样系统就会知道在应用程序启动时打开 MainActivity
,并且当您从 NotifyActivity
中点击 "back" 时它会知道 return 到它。像这样:
<application android:label="LocationTest" android:icon="@drawable/icon">
<activity android:name="MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="NotifyActivity"
android:parentActivityName="MainActivity"/>
</application>
More information about how to use your Android Manifest
希望能帮到您解决问题![=21=]
在 MainActivity 中添加此代码。此方法将解决您的第二个问题(总线我仍然不知道如何解决最近应用列表中的应用程序标题问题)。
public override bool OnKeyDown (Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.Back) {
MoveTaskToBack(true);
return true;
}
return base.OnKeyDown (keyCode, e);
}
Android 中的蜜蜂活动有很多陷阱。因此我的建议是从服务启动 MainActivity,然后启动 NotifyAcitivity。然后您可以再次启动 MainActivity(在这种情况下,您需要为 MainActivity 设置 LaunchMode = LaunchMode.SingleTop)。
对于第一个问题 - 您是否尝试过定义 [Service (Process = ":remote")]
而不是 [Service]
?
我正在尝试为 Android 制作我的第一个与地理定位和本地通知相关的系统应用程序。
我想象是这样的……有基本的activityMainActivity
。启动后,它会启动一个服务 TestService
,如果坐标发生变化,它会将它们发送到服务器上,并作为回复接收一些消息,这些消息将显示为本地通知。
我有一些问题。
如果我关闭应用程序(使用任务管理器),则服务将停止,因此坐标更改后什么也不会发生。 我需要什么服务才能一直工作?还是不可能?
激活本地通知后,它会启动
NotifyActivity
,显示详细信息。我在那里单击buttonDelete
- 它会关闭NotifyActivity
并启动MainActivity
。但是,如果之后我切换到 OS 屏幕(使用Back
按钮)并返回(使用任务管理器),那么将再次显示 'NotifyActivity' 而不是 'MainActivity'。 为什么会发生以及如何避免?
主要活动
[Activity(Label = "LocationTest", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
StartService(new Intent(this, typeof(TestService)));
button.Text = "Started";
};
}
}
地理定位服务
[Service]
public class TestService : Service, ILocationListener
{
// Start service
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
locManager = (LocationManager)GetSystemService(LocationService);
locationCriteria = new Criteria();
locationCriteria.Accuracy = Accuracy.Coarse;
locationCriteria.PowerRequirement = Power.Low;
string locationProvider = locManager.GetBestProvider(locationCriteria, true);
// Preferences.MinTime, for example, 60 (seconds)
// Preferences.MinDist, for example, 100 (meters)
locManager.RequestLocationUpdates(locationProvider, Preferences.MinTime * 1000, Preferences.MinDist, this);
return StartCommandResult.Sticky;
}
public void OnLocationChanged(Location loc)
{
// Send coordinates to the server, receive a response, and show local notification
var msg = new ReceivedMessage(counter++, "Test Title", loc.ToString());
ShowNotification(msg);
}
// show local notification
void ShowNotification(ReceivedMessage msg)
{
var myContainer = new Bundle();
myContainer.PutLong("msg_id", Convert.ToInt64(msg.Id));
myContainer.PutStringArray("msg_data", new [] { msg.Title, msg.Text });
var resultIntent = new Intent(this, typeof(NotifyActivity));
resultIntent.PutExtras(myContainer);
TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotifyActivity)));
stackBuilder.AddNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(Convert.ToInt32(msg.Id), PendingIntentFlags.UpdateCurrent);
Notification.Builder builder = new Notification.Builder(this)
.SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate)
.SetAutoCancel(true)
.SetContentIntent(resultPendingIntent)
.SetContentTitle(msg.Title)
.SetContentText(msg.Text)
.SetSmallIcon(Resource.Drawable.Icon);
var nm = (NotificationManager)GetSystemService(NotificationService);
nm.Notify(Convert.ToInt32(msg.Id), builder.Build());
}
}
本地通知
[Activity(Label = "NotifyActivity")]
public class NotifyActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.NotifyActivity);
var msg_id = Intent.Extras.GetLong("msg_id");
var msg_data = Intent.Extras.GetStringArray("msg_data");
FindViewById<TextView>(Resource.Id.textTitle).Text = msg_data[0];
FindViewById<TextView>(Resource.Id.textDescription).Text = msg_data[1];
FindViewById<Button>(Resource.Id.buttonDelete).Click += delegate {
StartActivity(typeof(MainActivity));
Finish();
};
}
}
示例项目here
我没有使用 xamarin 的经验,但我可以尝试回答一般与 Android 相关的问题。
问题 1
当您通过任务管理器关闭您的应用程序时,您所做的不仅仅是关闭应用程序 - 您正在终止整个进程。虽然您无法控制用户的行为,但通常这不是 Android 用户停止应用程序的方式。 "correct" 关闭应用程序的方法是简单地退出它,这不会破坏进程,并使您的服务 运行 处于后台。
因此,为了回答您的问题,我认为您无法采取任何措施来解决用户强行终止您的应用程序进程的情况。但是,我认为您不必担心这种边缘情况,因为在正常情况下,您的应用程序的进程不会被终止,直到系统认为它没问题或绝对需要它。因此,如果应用程序以正常方式关闭,服务应该继续 运行。文档说:
the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated.
这样你的服务就OK了。通常,您的应用不应处理其进程被强制终止的情况。
More information about Processes
More information about the Service lifecycle
问题二
我不太确定这里发生了什么。但是,查看您的链接项目,我注意到您的 AndroidManifest.xml
文件几乎没有填写。我怀疑如果没有这个文件,android 系统不知道如何初始化你的 backstack,也不知道你的哪个活动是 MAIN 和 LAUNCHER activity.
您应该在 AndroidManifest.xml
中声明您的活动,并在其中指定 MainActivity
是您的 MAIN 和 LAUNCHER activity,并且它是 [=14] 的父项=].这样系统就会知道在应用程序启动时打开 MainActivity
,并且当您从 NotifyActivity
中点击 "back" 时它会知道 return 到它。像这样:
<application android:label="LocationTest" android:icon="@drawable/icon">
<activity android:name="MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="NotifyActivity"
android:parentActivityName="MainActivity"/>
</application>
More information about how to use your Android Manifest
希望能帮到您解决问题![=21=]
在 MainActivity 中添加此代码。此方法将解决您的第二个问题(总线我仍然不知道如何解决最近应用列表中的应用程序标题问题)。
public override bool OnKeyDown (Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.Back) {
MoveTaskToBack(true);
return true;
}
return base.OnKeyDown (keyCode, e);
}
Android 中的蜜蜂活动有很多陷阱。因此我的建议是从服务启动 MainActivity,然后启动 NotifyAcitivity。然后您可以再次启动 MainActivity(在这种情况下,您需要为 MainActivity 设置 LaunchMode = LaunchMode.SingleTop)。
对于第一个问题 - 您是否尝试过定义 [Service (Process = ":remote")]
而不是 [Service]
?