异步方法在 Xamarin 的服务模式下无法正常运行 [英] Async Method not running properly in service mode in Xamarin

查看:26
本文介绍了异步方法在 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 your Service to be killed when your service receives the Intent.ActionBootCompleted as you do not have a UI and an inited Xamarin.Forms app... Since you have the service returning StartCommandResult.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 服务,无需任何表单调用即可正常工作.

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆