Как запустить метод в фоновом режиме, только когда приложение открыто и работает?
Когда приложение открыто и работает, мне бы хотелось, чтобы фоновый процесс проверял базу данных и делал обновление в зависимости от данных в базе данных. Я бы хотел сделать эту проверку каждую минуту. Я просто хочу, чтобы это произошло, когда приложение находится на переднем плане и с учетом пользователя.
Может кто-нибудь дать мне несколько предложений относительно того, как я это делаю? Я предполагаю, что могу вызвать метод здесь, но я не уверен, как это сделать. Также я не знаю, как остановиться или даже если мне нужно вручную отменить/остановить процесс. Отменит ли он себя, когда приложение не находится на переднем плане и перезапустится, когда приложение вернется на передний план?
public partial class App : Application
{
protected override void OnStart()
{
App.DB.InitData();
MainPage = new Japanese.MainPage();
}
Но мне нужно сделать этот запуск в другом потоке, и если да, то как я могу это сделать.
Извините, если мой вопрос не ясен. Пожалуйста, спросите, и я могу обновить, если это не имеет смысла.
Ответы
Ответ 1
В нашем приложении для форм мы использовали Device.Timer и класс Stopwatch, доступные в System.Diagnostics и Xamarin.Forms, для создания очень общего управляемого таймера, с которым мы могли бы взаимодействовать с помощью onStart, onSleep и onResume. методы в Xamarin.Forms.
Это конкретное решение не требует специальной логики для конкретной платформы, а таймер и секундомер устройства не блокируются пользовательским интерфейсом.
using Xamarin.Forms;
using System;
using System.Linq;
using System.Diagnostics;
namespace YourNamespace
{
public partial class App : Application
{
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 1;
protected override void OnStart()
{
// On start runs when your application launches from a closed state,
if (!stopWatch.IsRunning)
{
stopWatch.Start();
}
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
// Logic for logging out if the device is inactive for a period of time.
if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
//prepare to perform your data pull here as we have hit the 1 minute mark
// Perform your long running operations here.
Device.InvokeOnMainThread(()=>{
// If you need to do anything with your UI, you need to wrap it in this.
});
stopwatch.Restart();
}
// Always return true as to keep our device timer running.
return true;
});
}
protected override void OnSleep()
{
// Ensure our stopwatch is reset so the elapsed time is 0.
stopWatch.Reset();
}
protected override void OnResume()
{
// App enters the foreground so start our stopwatch again.
stopWatch.Start();
}
}
}
Редактировать:
Чтобы дать некоторый контекст относительно того, как вышеприведенное решение работает шаг за шагом:
Приложение запускается из закрытого состояния, а метод OnStart() создает наш Device.Timer, который тикает каждую секунду. Он также запускает наш секундомер, который отсчитывает до минуты.
Когда приложение переходит в фоновый режим, в этот момент оно запускает метод OnSleep, если мы передадим значение false в наше действие Device.StartTimer(), оно не запустится снова. Поэтому вместо этого мы просто сбрасываем наш секундомер, готовый к тому моменту, когда приложение снова открывается.
Когда приложение возвращается на передний план, оно запускает метод "OnResume", который просто запускает существующий секундомер.
2018 Изменить:
Этот ответ по-прежнему имеет некоторые достоинства даже в 2018 году, но в основном для очень специфических ситуаций. Существуют лучшие способы репликации этой функциональности для конкретных платформ даже в Xamarin.Forms. Вышеуказанное все еще остается независимым от платформы способом выполнения задачи по истечении определенного периода времени, принимая во внимание активность/неактивность пользователя.
Ответ 2
Вы можете использовать это,
System.Threading.Tasks.Task.Run(() =>
{
//Add your code here.
}).ConfigureAwait(false);
Ответ 3
Для запуска фоновой задачи используйте Сервис. Обычно классифицирует задачи как длинные задачи, так и периодические задачи.
Код для службы в android выглядит следующим образом
[Service]
public class PeriodicService : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// From shared code or in your PCL
return StartCommandResult.NotSticky;
}
}
И для вызова службы в фоновом режиме
var intent = new Intent (this, typeof(PeriodicService));
StartService(intent);
В случае необходимости вызывать и проверять каждую минуту
private void StartBackgroundDataRefreshService ()
{
var pt = new PeriodicTask.Builder ()
.SetPeriod (1800) // in seconds; minimum is 30 seconds
.SetService (Java.Lang.Class.FromType (typeof(BackgroundService)))
.SetRequiredNetwork (0)
.SetTag (your package name) // package name
.Build ();
GcmNetworkManager.GetInstance (this).Schedule (pt);
}
Чтобы узнать, какой тип сервиса подходит для вас, прочитайте этот учебник
Типы услуг
Блог Xamarin для периодического фонового обслуживания
Блог службы Xamarin
Другой пример
public class PeriodicService : Service
{
private static Timer timer = new Timer();
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
timer.scheduleAtFixedRate(new mainTask(), 0, 5000);
return StartCommandResult.NotSticky;
}
private class mainTask extends TimerTask
{
public void run()
{
//your code
}
}
}
Вот пример кода службы Android XAMARIN, который будет выполнять задачу каждые 10 секунд
using System;
using System.Threading;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Util;
namespace SimpleService
{
[Service]
public class SimpleStartedService : Service
{
static readonly string TAG = "X:" + typeof(SimpleStartedService).Name;
static readonly int TimerWait = 10000;
Timer timer;
DateTime startTime;
bool isStarted = false;
public override void OnCreate()
{
base.OnCreate();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Debug(TAG, $"OnStartCommand called at {startTime}, flags={flags}, startid={startId}");
if (isStarted)
{
TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"This service was already started, it been running for {runtime:c}.");
}
else
{
startTime = DateTime.UtcNow;
Log.Debug(TAG, $"Starting the service, at {startTime}.");
timer = new Timer(HandleTimerCallback, startTime, 0, TimerWait);
isStarted = true;
}
return StartCommandResult.NotSticky;
}
public override IBinder OnBind(Intent intent)
{
// This is a started service, not a bound service, so we just return null.
return null;
}
public override void OnDestroy()
{
timer.Dispose();
timer = null;
isStarted = false;
TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"Simple Service destroyed at {DateTime.UtcNow} after running for {runtime:c}.");
base.OnDestroy();
}
void HandleTimerCallback(object state)
{
TimeSpan runTime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"This service has been running for {runTime:c} (since ${state})." );
}
}
}
Ответ 4
Есть несколько способов сделать это как в iOS, так и в Android. В Xamarin Forms большая часть этой функциональности попадает под прозвище Backgrounding. Там много учебников. Это достаточно тщательно и определенно стоит проверить:
http://arteksoftware.com/backgrounding-with-xamarin-forms/
В Android большая часть этой работы выполняется в Фоновой службе. Для iOS просмотрите Долгое выполнение или Задачи конечной длины. Как вы можете сказать из этого, нет способа создания Xamarin Forms. Вам нужно будет написать специальный код Xamarin.Android и Xamarin.iOS.
Ответ 5
Ты можешь использовать
Device.StartTimer(TimeSpan.FromMinutes(1), () =>
{
var shouldTimerContinueWork = true;
/*your code*/
return shouldTimerContinueWork;
});
Этот таймер работает в фоновом потоке, использует часы устройства и безопасный вход.
Для того, чтобы остановить этот таймер, когда приложение находится в фоновом режиме вы можете использовать методы Xamarin.Forms.Application OnSleep и OnResume, как описано здесь
Ответ 6
Я делаю что-то вроде своих приложений Xamarin Forms.
public void execute()
{
var thread = new Thread(new ThreadStart(startAuthenticationProcess))
{
IsBackground = true
};
thread.Start();
}
private void startAuthenticationProcess()
{
Thread.Sleep(2000);
if (!Utils.isNetworkAvailable(splashActivity))
{
splashActivity.RunOnUiThread(() => Utils.showToast(splashActivity, splashActivity.GetString(Resource.String.r30025)));
splashActivity.FinishAffinity();
}
else
{
try
{
if (StringUtils.isBlank(strIPAdd) || (StringUtils.isNotBlank(strIPAdd) && (StringUtils.isBlank(strDbName) || "site".Equals(strDbName,StringComparison.OrdinalIgnoreCase))))
{
splashActivity.RunOnUiThread(() => DependencyService.Get<IAuthenticationDialog>().showAuthenticationDialog(new Command(() =>
{
var intent = new Intent(splashActivity, typeof(MainActivity));
intent.PutExtra("startLoginActivity", false);
splashActivity.StartActivity(intent);
splashActivity.Finish();
})));
}
else
{
gotoLoginScreen();
}
}
catch (Exception e)
{
Log.Error(TAG, e.Message);
}
}
}
Ответ 7
Легко, попробуйте что-то вроде этого и реализуйте свою логику в этих методах:
public partial class App : Application
{
protected override void OnStart()
{
// Your App On start code should be here...
// and then:
Task.Run(() =>
{
//Add your code here, it might looks like:
CheckDatabase();
MakeAnUpdateDependingOnDatabase();
});
}
Надеюсь, это поможет.