异步方法在 Xamarin 的服务模式下无法正常运行 [英] Async Method not running properly in service mode in Xamarin
问题描述
我定义了以下服务.该服务应该在设备启动时运行.但是,服务已成功启动,但 DoBackgroundJob()
方法中的方法似乎没有被调用.当我从 MainActivity 启动服务并放置断点时,所有方法都正确执行并得到所需的结果.有人可以建议这里有什么问题吗?
I have the following service defined. The service is supposed to run when the device is booted up. However, the service is successfully started, but it seems on of the method within the DoBackgroundJob()
method does not get invoked. When I launch the service from the MainActivity and put breakpoints, the all the methods are executed correctly and I get the desired result. Could someone please advise what is wrong here ?
我已将 DoBackgroundJob()
包装在一个新线程中,以便该服务在手动启动时不会阻塞应用程序的 UI.该应用程序应该在两个独立的进程中工作.
I have wrapped the DoBackgroundJob()
in a new thread so that the service does not block the UI of the application if it is launched manually. The application is supposed to work in two separate processes.
我想知道的是,像 DoBackgroundJob()
这样的异步方法是否应该包含 async 中的所有方法?请帮我解决这个问题,因为我怀疑某些方法不足以正确调用.
What I am wondering, is whether an async method like DoBackgroundJob()
should contain all the methods within it async ? Please help me to solve this issue, as I suspect some methods is not having sufficient to be invoked properly.
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
try
{
Thread t = new Thread(() =>
{
dsa = new DataServiceAccess();
Task.Run(async () =>
{
await DoBackgroundJob();
});
});
t.Start();
}
catch (Exception ex)
{
}
base.OnStartCommand(intent, flags, startId);
return StartCommandResult.Sticky;
}
public void GetGps()
{
Device.BeginInvokeOnMainThread(() =>
{
try
{
LocationService locService = new LocationService();
GpsData = locService.GetLocation();
a = GpsData.Latitude;
b = GpsData.Longitude;
}
catch (Exception ex)
{
}
});
}
public async Task DoBackgroundJob()
{
GetGps();
try
{
ObservableCollection<Trucks> Trucks = new ObservableCollection<Trucks>();
Trucks truck = new Trucks();
TrucksService ds = new TrucksService();
beacon.DetectAvailableTrucks();
await Task.Delay(100000);
Trucks = beacon.GetAvailableTrcuks();
await MatchRequiredTrayBeacon(Truckss);
await Task.Delay(8000);
await DoBackgroundJob();
} catch (Exception e)
{
}
finally
{
}
}
private ObservableCollection<TrucksLoaded> ConstructTrucks(ObservableCollection<Trucks> trucksMatch)
{
string address = "";
List<ConfigDataModel> conf = new List<ConfigDataModel>();
conf = dsa.GetAllConfigData();
foreach (var item in conf)
{
address = item.address;
}
ObservableCollection<TrucksLoaded> TrucksLoadedFound = new ObservableCollection<TrucksLoaded>();
foreach (var item in matchedBeacon)
{
TrucksLoadedFound.Add(new TrucksLoaded(item.Id, item.Name, address, latitude,
longitude, DateTime.Now.ToString(), false, item.BatteryLife));
}
return TrucksLoadedFound;
}
public ObservableCollection<TrucksLoaded> MatchTrucks(ObservableCollection<Trucks> foundTrucks)
{
TrucksLoaded = new ObservableCollection<TrucksLoaded>();
foundTrucks = new ObservableCollection<Trucks>();
foundTrucks = HandleTrucks(foundTrucks);
TrucksLoaded = ConstructTrucks(foundTrucks);
SavetoLocalDB(TrucksLoaded);
return TrucksLoaded;
}
private ObservableCollection<Trucks> CheckMatching(ObservableCollection<Trucks> foundTrucks)
{
MatchedTrucks = new ObservableCollection<Trucks>();
// matching logic implemeted here..
return MatchedTrucks;
}
private void SavetoLocalDB(ObservableCollection<TrucksLoaded> TrucksLoaded)
{
foreach (var item in TrucksLoaded)
{
dsa.AddTrucksLoaded(item).ConfigureAwait(false);
}
}
需要在主线程上运行的定位服务部分代码
public void InitLocation()
{
if (_locationManager.AllProviders.Contains(LocationManager.NetworkProvider) && _locationManager.IsProviderEnabled(LocationManager.NetworkProvider))
{
_locationManager.RequestLocationUpdates(LocationManager.NetworkProvider, 2000, 5, this);
}
推荐答案
您不能在 Service
中使用 Xamarin.Forms
调用.
You can not use Xamarin.Forms
calls within the Service
.
- 调用
Device.BeginInvokeOnMainThread
将导致您的Service
在您的服务收到Intent.ActionBootCompleted
时被终止,因为您没有 UI和一个初始化的 Xamarin.Forms 应用程序...由于您的服务返回StartCommandResult.Sticky
,操作系统将不断尝试重新启动您的服务(以不断减少的时间表).
- Calling
Device.BeginInvokeOnMainThread
will cause yourService
to be killed when your service receives theIntent.ActionBootCompleted
as you do not have a UI and an inited Xamarin.Forms app... Since you have the service returningStartCommandResult.Sticky
the OS will will continually try to restart your service (on an ever decreasing schedule).
OnStartCommand
中不需要双线程,一个就可以了.
You do not need the double thread in the OnStartCommand
, just one will do it.
- 只启动一次您的工作线程,因为
OnStartCommand
可以被多次调用...
- Only start your worker thread once as
OnStartCommand
can get called multiple times...
我不知道您的 LocationService
方法由什么组成,但再次确保您没有在其中使用 Xamarin.Forms 调用.
I do not know what your LocationService
method consists of, but again make sure that you are not using Xamarin.Forms calls in it.
- Android 位置服务,或者最好是 Google Fused Location 服务,无需任何表单调用即可正常工作.
使用 adb
在 ActionBootCompleted
上测试您的服务启动,具体取决于设备/模拟器类型/API/等...您可以使用这些 之一adb
cmds:
Test your Service startup on ActionBootCompleted
by using adb
, depending upon the device/emulator type/API/etc... you can use one of these adb
cmds:
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p UseYourPackageNameHere
adb shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETED
adb 重启
添加您的启动权限(在清单中手动或通过属性)
Add your boot permissions (manually in the manifest or via an attribute)
[assembly: UsesPermission(Manifest.Permission.ReceiveBootCompleted)]
服务代码:
[Service]
public class BootService : Service
{
const string TAG = "SushiHangover";
public const string SERVICE = "com.sushihangover.BootService";
Thread serviceThread;
public IBinder Binder { get; private set; }
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Debug(TAG, $"OnStartCommand : {Thread.CurrentThread.ManagedThreadId}");
serviceThread = serviceThread ?? new Thread(ServiceRun); // Many ways... use a Task with a cancellation token, etc...
if (!serviceThread.IsAlive)
try
{
serviceThread.Start();
}
catch (ThreadStateException ex) when (ex.Message == "Thread has already been started.")
{
// Xamarin.Android bug: isAlive always returns false, so eat the Start() exception if needed
}
base.OnStartCommand(intent, flags, startId);
return StartCommandResult.Sticky;
}
public override void OnTrimMemory(TrimMemory level)
{
// Stop serviceThread? free resources? ...
base.OnTrimMemory(level);
}
public override void OnLowMemory()
{
// Stop serviceThread? free resources? ...
base.OnLowMemory();
}
public override void OnDestroy()
{
serviceThread.Abort(); // Handle ThreadAbortException in your thread, cleanup resources if needed...
base.OnDestroy();
}
public override IBinder OnBind(Intent intent)
{
Log.Debug(TAG, $"OnBind");
Binder = new BootBinder(this);
return Binder;
}
public class BootBinder : Binder
{
public BootBinder(BootService service)
{
Service = service;
}
public BootService Service { get; private set; }
}
async void ServiceRun()
{
int i = 0;
while (true) // Handle cancellations...
{
await Task.Delay(1000);
Log.Debug(TAG, $"{i} : {Thread.CurrentThread.ManagedThreadId}");
i++;
}
}
}
广播接收器代码:
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class BootBroadcastReceiver : BroadcastReceiver
{
const string TAG = "SushiHangover";
public override void OnReceive(Context context, Intent intent)
{
Log.Debug(TAG, $"OnReceive");
var serviceIntent = new Intent(context, typeof(BootService));
context.StartService(serviceIntent);
}
}
测试
通过调试会话安装应用程序就可以了,只要您至少启动了一次应用程序即可.
Test
Install the app, via a debugging session is fine, as long as you have started the app at least once.
现在通过上面的 adb
cmd 触发 ActionBootCompleted
意图.
Now trigger the ActionBootCompleted
intent via the adb
cmd above.
监控logcat
,你应该看到(我发出了多个ActionBootCompleted
):
Monitor logcat
and you should see (I issued multiple ActionBootCompleted
):
D SushiHangover: OnReceive
D SushiHangover: OnStartCommand : 1
D SushiHangover: 0 : 6
D SushiHangover: 1 : 7
D SushiHangover: 2 : 6
D SushiHangover: 3 : 7
D SushiHangover: 4 : 6
D SushiHangover: 5 : 7
D SushiHangover: OnReceive
D SushiHangover: OnStartCommand : 1
D SushiHangover: 6 : 6
D SushiHangover: 7 : 7
D SushiHangover: 8 : 6
D SushiHangover: OnReceive
D SushiHangover: OnStartCommand : 1
D SushiHangover: 9 : 7
D SushiHangover: 10 : 6
D SushiHangover: OnReceive
D SushiHangover: OnStartCommand : 1
D SushiHangover: 11 : 7
D SushiHangover: 12 : 6
D SushiHangover: 13 : 7
D SushiHangover: 14 : 6
D SushiHangover: 15 : 6
D SushiHangover: 16 : 7
D SushiHangover: 17 : 6
D SushiHangover: 18 : 7
这篇关于异步方法在 Xamarin 的服务模式下无法正常运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!