我定义了以下服务。该服务应该在设备启动时运行。但是,服务已成功启动,但似乎没有调用DoBackgroundJob()
方法中的方法。当我从MainActivity启动服务并放置断点时,所有方法都正确执行,我得到了所需的结果。有人可以告诉我这里有什么问题吗?
我已将DoBackgroundJob()
包装在新线程中,以便在手动启动时,服务不会阻止应用程序的UI。应用程序应该在两个单独的进程中工作。
我想知道的是,像DoBackgroundJob()
这样的异步方法是否应该包含其中的所有方法async?请帮我解决这个问题,因为我怀疑某些方法不足以被正确调用。
[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);
}
答案 0 :(得分:2)
您无法在Xamarin.Forms
内使用Service
次来电。
Device.BeginInvokeOnMainThread
时,调用Service
会导致您的Intent.ActionBootCompleted
被杀,因为您没有用户界面和未使用的Xamarin.Forms应用...因为您有返回StartCommandResult.Sticky
操作系统的服务将不断尝试重新启动您的服务(按照不断减少的时间表)。你不需要OnStartCommand
中的双线程,只需要一个人就可以了。
OnStartCommand
可以多次调用... 我不知道你的LocationService
方法包含什么,但请再次确保你没有在其中使用Xamarin.Forms调用。
使用ActionBootCompleted
在adb
上测试您的服务启动,具体取决于设备/模拟器类型/ API /等等...您可以使用其中一个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 reboot
添加启动权限(在清单中或通过属性手动添加)
[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);
}
}
通过调试会话安装应用程序很好,只要您至少启动过一次应用程序。
现在通过上面的ActionBootCompleted
cmd触发adb
意图。
监控logcat
您应该看到(我发布了多个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