异步方法在Xamarin的服务模式下无法正常运行

时间:2017-12-01 16:05:08

标签: c# xamarin xamarin.android

我定义了以下服务。该服务应该在设备启动时运行。但是,服务已成功启动,但似乎没有调用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);
        }

1 个答案:

答案 0 :(得分:2)

无法在Xamarin.Forms使用Service次来电。

  • 当您的服务收到Device.BeginInvokeOnMainThread时,调用Service会导致您的Intent.ActionBootCompleted被杀,因为您没有用户界面和未使用的Xamarin.Forms应用...因为您有返回StartCommandResult.Sticky操作系统的服务将不断尝试重新启动您的服务(按照不断减少的时间表)。

你不需要OnStartCommand中的双线程,只需要一个人就可以了。

  • 只需启动您的工作线程一次,因为OnStartCommand可以多次调用...

我不知道你的LocationService方法包含什么,但请再次确保你没有在其中使用Xamarin.Forms调用。

  • Android位置服务,或者最好是Google融合位置服务,无需任何表单调用即可正常工作。

使用ActionBootCompletedadb上测试您的服务启动,具体取决于设备/模拟器类型/ 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代码:

[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