I am making use of Prism in my xamarin forms project.I am also making use of background services to push long running tasks in the background.The problem is when the app is killed the service is also killed.And by "killed" I mean press home-button -> see all running apps -> swipe my app aside -> app killed .I want to keep the service alive even if the app is killed.I have read many posts which say that it can be done.However I was not able to get it working.
This is what I have tried :-
This is Android MainActivity.cs
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
try
{
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App(new AndroidInitializer()));
WireUpLongRunningTask();
}
catch(Exception)
{
}
}
public void WireUpLongRunningTask()
{
MessagingCenter.Subscribe<StartSyncBackgroundingTask>(this, "StartSyncBackgroundingTask", message => {
var intent = new Intent(this, typeof(AndroidSyncBackgroundService));
StartService(intent);
});
}
This is AndroidSyncBackgroundService class :-
[Service]
public class AndroidSyncBackgroundService : Service
{
CancellationTokenSource _cts;
private ISyncBackgroundService _isyncBackgroundService;
private App _app => (App)Xamarin.Forms.Application.Current;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
_cts = new CancellationTokenSource();
Task.Run(() => {
try {
//INVOKE THE SHARED CODE
_isyncBackgroundService = _app.Container.Resolve<ISyncBackgroundService>();
_isyncBackgroundService.RunBackgroundingCode(_cts.Token).Wait();
}
catch (System.OperationCanceledException) {
}
finally {
if (_cts.IsCancellationRequested) {
var message = new CancelledTask();
Device.BeginInvokeOnMainThread(
() => MessagingCenter.Send(message, "CancelledTask")
);
}
}
}, _cts.Token);
return StartCommandResult.Sticky;
}
public override void OnDestroy()
{
if (_cts != null) {
_cts.Token.ThrowIfCancellationRequested();
_cts.Cancel();
}
StartService(new Intent("com.xamarin.AndroidSyncBackgroundService"));
base.OnDestroy();
}
public override void OnTaskRemoved(Intent rootIntent)
{
Intent restartServiceIntent = new Intent(Xamarin.Forms.Forms.Context, typeof(AndroidSyncBackgroundService));
PendingIntent restartServicePendingIntent = PendingIntent.GetService(Xamarin.Forms.Forms.Context, 1, restartServiceIntent,PendingIntentFlags.OneShot);
AlarmManager alarmService = (AlarmManager)Xamarin.Forms.Forms.Context.GetSystemService(Context.AlarmService);
alarmService.Set(
AlarmType.ElapsedRealtime,
1000,
restartServicePendingIntent);
base.OnTaskRemoved(rootIntent);
}
}
This is SyncBackgroundService class :-
public class SyncBackgroundService: ISyncBackgroundService
{
private ISqliteCallsService _iSqliteCallsService;
private IFeedBackSqliteService _feedBackSqliteService;
private ISettingApiService _isettingApiService;
private ISettingSqliteService _isettingSqliteService;
private IWebApiService _iwebApiService;
private App _app => (App)Xamarin.Forms.Application.Current;
public async Task RunBackgroundingCode(CancellationToken token)
{
_iSqliteCallsService= _app.Container.Resolve<ISqliteCallsService>();
await Task.Run(async () => {
token.ThrowIfCancellationRequested();
App.bRunningBackgroundTask = true;
await Task.Run(async () =>
{
await Task.Delay(1);
_iSqliteCallsService.ftnSaveOnlineModeXMLFormat("Offline", 0);
_iSqliteCallsService.SyncEmployeeTableData();
_iSqliteCallsService.SaveOfflineAppCommentData();
_iSqliteCallsService.SaveOfflineAdditionToFlowData();
await Task.Delay(500);
//MessagingCenter.Send<SyncBackgroundService>(this, "StopSyncBackgroundingTask");
});
}, token);
}
}
}
As can be seen in the code snippet I have made use of StartCommandResult.Sticky and still the service gets killed and does not restart.
Also i'm making use of Alarm Manager in OnTaskRemoved method,which gets fired when the app is killed according to its documentation.But in my case the service does not restart atall.Can somebody point out what is the mistake in my code? Or provide a working solution so that I can implement it in my app.
Thanks in advance!
Try this after you call StartService
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat)
{
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext, typeof(AndroidSyncBackgroundService )), 0);
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(Context.AlarmService);
alarm.Cancel(pintent);
}
The reason this might work is because Android schedules your service to be killed after your app is killed. Doing this removes that scheduled task.
Related
I need to open my popup screen when my method is called, but it is being ignored by my await and does not show the popup.
This is for an android device and I am using rg.plugins.popup to display my popup page and I'm using VS17 as the IDE.
So far I've also tried calling my method synchronously but that doesn't seem to work either.
public async void PegaValor(bool retry)
{
await PopupNavigation.PushAsync(new Paginas.PopupTentarNovamente());
Paginas.PopupTentarNovamente tentarNovamente = new Paginas.PopupTentarNovamente();
if (tentarNovamente.resultado)
{
retry = false;
}
else
{
retry = true;
}
}
I have a suspicion that the pop up needs to be ran on the main UI Dispatcher, I'd suggest trying the following:
public async void PegaValor(bool retry)
{
Activity.RunOnUiThread(async () => {
await PopupNavigation.PushAsync(new Paginas.PopupTentarNovamente());
});
Paginas.PopupTentarNovamente tentarNovamente = new Paginas.PopupTentarNovamente();
if (tentarNovamente.resultado)
{
retry = false;
}
else
{
retry = true;
}
}
EDIT:
Based on your comments and taking a quick look at your github link here are a couple of ways you can do what I suggested, although I still have no clue how your instantiating the class from the git repo.
So for a quick example, this way assumes your using that class inside of an activity, so you could try the following
// Create an instance of metodosEmpenho in your activity and pass through the Activity as a parameter to the constructor.
public class MyActivity : Activity
{
private MetodosEmpenho metodosEmpenho;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
metodosEmpenho = new MetodosEmpenho(Context as Activity);
metodosEmpenho.VerOperador("");
}
}
// Create a consturctor in the MetodosEmpenho class that will take the Activity paramter and store it for later use.
public class MetodosEmpenho
{
private readonly Activity _activity
public MetodosEmpenho(Activity currActivity)
{
_activity = currActivity;
}
public async void PegaValor(bool retry)
{
_activity.RunOnUiThread(async () => {
await PopupNavigation.PushAsync(new Paginas.PopupTentarNovamente());
});
Paginas.PopupTentarNovamente tentarNovamente = new Paginas.PopupTentarNovamente();
if (tentarNovamente.resultado)
{
retry = false;
}
else
{
retry = true;
}
}
}
I am totally new in Xamarin. I am trying to run the service when the application gets closed. But as I closed the app from the recent item service getting stop and getting message projectname.android has stopped. I want service should run as in whatsapp after closed the app then also service run. Anyone know the answer please help.
Here is my service class Code. This code sent the notification each 5 sec. using local notification plugin library. but this code running fine in android 5 version
class AndroidService : Service
{
private Timer _check_Timer_Data;
public override IBinder OnBind(Intent intent)
{
return null;
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
base.OnStartCommand(intent, flags, startId);
var t = new Thread(() =>
{
_check_Timer_Data = new Timer((o) =>
{
Random rdno = new Random();
int id = rdno.Next(1000);
string dt = DateTime.Now.ToString("h:mm:ss tt");
CrossLocalNotifications.Current.Show("G7CR", "G7CR Notification-" + dt, id);
}, null, 0, 5000);
}
);
t.Start();
return StartCommandResult.Sticky;
}
public override void OnDestroy()
{
base.OnDestroy();
_check_Timer_Data.Dispose();
}
public override void OnCreate()
{
base.OnCreate();
}
}
Hello, I want to build an app, in which you can start a service, which runs intependenly and creates a notification, and this service should constantly proof, if the DateTime.Now.Date is bigger than a spezific Date.
When I execute the code below, the notification gets displayed, but when I am closing the app, a few secondes later I get two times an information that the app crashed and I dont know why.
I cant even debug the code because this anly happens when the application is closed....
I hope you can help me thanks!
Here is my code:
namespace App
{
[Activity(Label = "App", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Click += delegate {
button.Text = string.Format("{0} clicks!", count++);
StartService(new Intent(this, typeof(backgroudservice)));
};
}
}
public class backgroudservice : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
newnotification("Title", "Text: ", 0);
new Task(() => {
DoWork();
Thread.Sleep(1000);
}).Start();
return StartCommandResult.Sticky;
}
public void DoWork()
{
if (DateTime.Now.Date > Convert.ToDateTime("2016-03-29").Date)
{
cancelnotification(0);
StopSelf();
}
}
public override void OnDestroy()
{
base.OnDestroy();
cancelnotification(0);
}
private void newnotification(string titel, string text, int id)
{
Notification.Builder builder = new Notification.Builder(this)
.SetContentTitle(titel)
.SetContentText(text)
.SetSmallIcon(Resource.Drawable.droidlogo_small)
.SetAutoCancel(false)
.SetVisibility(NotificationVisibility.Public)
.SetContentIntent(PendingIntent.GetActivity(this, 0, new Intent(this, typeof(MainActivity)), PendingIntentFlags.OneShot));
// Build the notification:
Notification notification = builder.Build();
notification.Flags = NotificationFlags.NoClear;
//notification.ContentIntent = new Intent(this,typeof(login));
// Get the notification manager:
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
// Publish the notification:
notificationManager.Notify(id, notification);
}
private void cancelnotification(int id)
{
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
notificationManager.Cancel(id);
}
}
}
I solved it, I forgot the [Service] above my class, now it works!
[Service]
public class backgroudservice : Service
{
...
}
You might try moving the call to cancelnotification in your service's OnDestroy to before the call to the base method, i.e.:
public override void OnDestroy()
{
cancelnotification(0);
base.OnDestroy();
}
I have problem. My service worked, but when i close application service stoped.
How i can leave my service is running?
service
[Service]
public class NotificationService : Service
{
public NotificationService () { }
public override StartCommandResult OnStartCommand (Android.Content.Intent intent, StartCommandFlags flags, int startId)
{
new Task(() =>
{
DoWork();
} ).Start();
return StartCommandResult.Sticky;
}
public override void OnDestroy ()
{
base.OnDestroy ();
}
public override IBinder OnBind (Intent intent)
{
throw new NotImplementedException ();
}
void DoWork()
{
new Task(() =>
{
for (int i = 0; i < 100; i ++)
{
System.Threading.Thread.Sleep(1000);
var context = Android.App.Application.Context;
var builder = new Android.App.Notification.Builder(context)
.SetSmallIcon(Resource.Drawable.icon)
.SetContentTitle("My application")
.SetDefaults(NotificationDefaults.Sound)
.SetContentText(i.ToString())
.SetOngoing(true);
var not = builder.Build();
var notManager = context.GetSystemService(NotificationService) as NotificationManager;
notManager.Notify(1, not);
}
}).Start();
}
}
MainActivity.cs
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.Init (this, bundle);
LoadApplication (new App ());
new Task(() =>
{
var notificationIntent = new Intent(this, typeof(NotificationService));
StartService(notificationIntent);
}).Start();
Android.Widget.Toast.MakeText(this, "run", Android.Widget.ToastLength.Short).Show();
}
When we start application from VS and stop an application. VS automatically close all services. Need build the app to the device and than start application from device.
Sorry for the late answer but I think it can help others. I would like to make something clear for you that you are writing this service as a background service. Background service can not run for long. There are some limitation of background services in Android after Android 8.0 onwards. Android automatically kills background service of an app after some time.
See this https://developer.android.com/about/versions/oreo/background
If you want to run a service for a long time then make the service Foreground Service. Please follow https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services for detailed knowledge of foreground service in Xamarin Forms.
In a Xamarin Android application, I have an Activity that calls an async method (a network operation) in a RetainInstance fragment so that the operation doesn't stop on configuration changes. After the operation is complete, the UI is changed, a progress dialog is dismissed, a new fragment is inserted into the layout, etc.
It works correctly, even if the activity is destroyed and re-created on configuration changes. However, if the activity is paused when the async method completes, UI operations throw IllegalStateException: Can not perform this action after onSaveInstanceState exception. This happens if the user turns off the screen or switches to another application while the network operation is running.
Is there a way to make the async method continue normally if the activity is not paused. But if the activity is paused, wait until the activity is resumed before continuing?
Alternatively, what is the proper way to handle async operations that complete while the activity is paused?
The code:
using System;
using System.Threading.Tasks;
using Android.App;
using Android.OS;
using Android.Widget;
namespace AsyncDemo {
[Activity(Label = "AsyncDemo", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity {
const string fragmentTag = "RetainedFragmentTag";
const string customFragmentTag = "CustomFragmentTag";
const string dialogTag = "DialogFragmentTag";
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
var retainedFragment = FragmentManager.FindFragmentByTag(fragmentTag) as RetainedFragment;
if (retainedFragment == null) {
retainedFragment = new RetainedFragment();
FragmentManager.BeginTransaction()
.Add(retainedFragment, fragmentTag)
.Commit();
}
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
button.Text = "Please wait...";
var dialogFragment = new DialogFragment(); // Substitute for a progress dialog fragment
FragmentManager.BeginTransaction()
.Add(dialogFragment, dialogTag)
.Commit();
Console.WriteLine("Starting task");
retainedFragment.doIt();
};
}
void taskFinished() {
Console.WriteLine("Task finished, updating the UI...");
var button = FindViewById<Button>(Resource.Id.myButton);
button.Text = "Task finished";
var dialogFragment = FragmentManager.FindFragmentByTag(dialogTag) as DialogFragment;
dialogFragment.Dismiss(); // This throws IllegalStateException
var customFragment = new CustomFragment();
FragmentManager.BeginTransaction()
.Replace(Resource.Id.container, customFragment, customFragmentTag)
.Commit(); // This also throws IllegalStateException
}
class RetainedFragment : Fragment {
public override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
RetainInstance = true;
}
public void doIt() {
doItAsync();
}
public async Task doItAsync() {
try {
await Task.Delay(3000); // substitute for the real operation
(Activity as MainActivity).taskFinished();
} catch (Exception e) {
Console.WriteLine(e);
}
}
}
}
}
The log:
Starting task
Task finished, updating the UI...
Java.Lang.IllegalStateException: Exception of type 'Java.Lang.IllegalStateException' was thrown.
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000b] in /Users/builder/data/lanes/1978/f98871a9/source/mono/mcs/class/corlib/System.Runtime.ExceptionServices/ExceptionDispatchInfo.cs:61
at Android.Runtime.JNIEnv.CallVoidMethod (IntPtr jobject, IntPtr jmethod) [0x00062] in /Users/builder/data/lanes/1978/f98871a9/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:554
at Android.App.DialogFragment.Dismiss () [0x00043] in /Users/builder/data/lanes/1978/f98871a9/source/monodroid/src/Mono.Android/platforms/android-22/src/generated/Android.App.DialogFragment.cs:284
at AsyncDemo.MainActivity.taskFinished () [0x00039] in /Users/csdvirg/workspaces/xamarin/AsyncDemo/AsyncDemo/MainActivity.cs:52
at AsyncDemo.MainActivity+RetainedFragment+<doItAsync>c__async0.MoveNext () [0x00094] in /Users/csdvirg/workspaces/xamarin/AsyncDemo/AsyncDemo/MainActivity.cs:73
--- End of managed exception stack trace ---
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1323)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1341)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:597)
at android.app.BackStackRecord.commit(BackStackRecord.java:575)
at android.app.DialogFragment.dismissInternal(DialogFragment.java:292)
at android.app.DialogFragment.dismiss(DialogFragment.java:258)
at mono.java.lang.RunnableImplementor.n_run(Native Method)
at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:29)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5756)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(Native Method)
Based on #choper and #xakz comments, I used PauseTokenSource and it works perfectly now.
I modified RetainedFragment:
class RetainedFragment : Fragment {
readonly PauseTokenSource pts = new PauseTokenSource();
public override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
RetainInstance = true;
}
public override void OnPause() {
base.OnPause();
pts.IsPaused = true;
}
public override void OnResume() {
base.OnResume();
pts.IsPaused = false;
}
public void doIt() {
doItAsync();
}
public async Task doItAsync() {
try {
await Task.Delay(3000); // substitute for the real operation
await pts.Token.WaitWhilePausedAsync();
(Activity as MainActivity).taskFinished();
} catch (Exception e) {
Console.WriteLine(e);
}
}
}
PauseTokenSource implementation (pieced together from the blog post):
public class PauseTokenSource {
internal static readonly Task s_completedTask = Task.FromResult(true);
volatile TaskCompletionSource<bool> m_paused;
#pragma warning disable 420
public bool IsPaused {
get { return m_paused != null; }
set {
if (value) {
Interlocked.CompareExchange(
ref m_paused, new TaskCompletionSource<bool>(), null);
} else {
while (true) {
var tcs = m_paused;
if (tcs == null)
return;
if (Interlocked.CompareExchange(ref m_paused, null, tcs) == tcs) {
tcs.SetResult(true);
break;
}
}
}
}
}
#pragma warning restore 420
public PauseToken Token { get { return new PauseToken(this); } }
internal Task WaitWhilePausedAsync() {
var cur = m_paused;
return cur != null ? cur.Task : s_completedTask;
}
}
public struct PauseToken {
readonly PauseTokenSource m_source;
internal PauseToken(PauseTokenSource source) {
m_source = source;
}
public bool IsPaused { get { return m_source != null && m_source.IsPaused; } }
public Task WaitWhilePausedAsync() {
return IsPaused ?
m_source.WaitWhilePausedAsync() :
PauseTokenSource.s_completedTask;
}
}
Using Async as Sync is a wrong way. Use an event(an activity) and thread(a network operation) if you need hard control.