I'm trying to make my first system application for Android related to geolocation and local notifications.
I imagine it like this... There is basic activity MainActivity
. After start it launches a service TestService
which in case of change of coordinates sends them on the server, and in reply receives some message which will displayed as the local notification.
And I have some problems.
If I close the application (using task manager) then the service will stop, so after change of coordinates nothing happens. What I need do that service work all the time? Or it's impossible?
After activation of the local notification it launches NotifyActivity
which shows detailed information. There I click buttonDelete
- it will close NotifyActivity
and start MainActivity
. But if after that I switch to the OS screen (using Back
button) and back (using task manager) then instead of 'MainActivity' will be again displayed 'NotifyActivity'.
Why it occurs and how to avoid it?
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";
};
}
}
Geolocation service
[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());
}
}
local notifications
[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();
};
}
}
Example project here
The essential difference between local notifications and push notifications is simple: Local notifications are scheduled by an app locally and are delivered by the same device. Push notifications are sent by a remote server (its provider) which sends these notifications to devices on which the app is installed.
Local notifications ... to inform users when new data becomes available for your app, even when your app is not running in the foreground. For example, a messaging app might let the user know when a new message has arrived, and a calendar app might inform the user of an upcoming appointment.
When you enable geo-notifications the app will send you a notification when you leave home without arming the alarm or when you arrive home and the alarm is armed.
Local notifications reach users whether your app is running in the foreground or the background and require no external infrastructure to send, which is why they are aptly termed “local.” These notifications simply require that a user's device is on in order to be received.
In MainActivity add this code. This method will solve your second problem (bus still I don't know how to solve issue with application title in the list of recent apps).
public override bool OnKeyDown (Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.Back) {
MoveTaskToBack(true);
return true;
}
return base.OnKeyDown (keyCode, e);
}
Beeing hones activities in Android have a lot of pitfalls. Hence my suggestion is to launch MainActivity from service and then launch NotifyAcitivity. Then you can launch MainActivity again (in this case you'll need to set LaunchMode = LaunchMode.SingleTop for MainActivity).
For the first problem - have you tried defining [Service (Process = ":remote")]
instead of [Service]
?
I don't have experience with xamarin, but I can try to answer the questions as they relate generally to Android.
Question 1
When you close your app through the task manager, you are doing more than simply closing the app - you are killing the entire process. Although you can't control your users' behavior, generally this is not the way that an Android user is supposed to stop an app. The "correct" way to close an app is to simply back out of it, which does not destroy the process, and keeps your Service running in the background.
So to answer your question, I don't think there's anything you can do to get around this situation where a user forcefully kills your app's process. However, I don't think you need to worry about this edge case because under normal circumstances, your app's process won't be killed until the system deems it's ok or absolutely needs it. So the service should continue running if the app is closed in a normal way. The docs say:
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.
So your service will be OK. Generally, your app is not expected to handle cases where its process is forcefully killed.
More information about Processes
More information about the Service lifecycle
Question 2
I am not exactly sure what's going on here. However, looking at your linked project, I notice that your AndroidManifest.xml
file is barely filled out. My suspicion is that without this file, the android system doesn't know how to initialize your backstack, and it doesn't know which of your activities is the MAIN and LAUNCHER activity.
You should declare your activities in AndroidManifest.xml
, and in it you should specify that MainActivity
is your MAIN and LAUNCHER activity, and that it is a parent of NotifyActivity
. This way the system will know to open MainActivity
when the app is started, and it'll know to return to it when you hit "back" from NotifyActivity
. Something like this:
<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
Hopefully that helps you solve your problems!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With