本文介绍了具有隔离进程的 xamarin 形式的后台服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好朋友,你好吗?.我告诉你,我正在做一个在后台运行服务的练习.我想要实现的是,即使应用程序关闭,该服务仍在运行.我已经按照微软的文档说明了以下内容:docs.microsoft.com/en-us/xamarin/android/应用基础知识/服务

Hello friends how are you?.I tell you that I am doing an exercise to run a service in the background. What I want to achieve is that even if the application is closed this service is still running. I have followed the microsoft documentation that says the following:docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services

隔离进程 – 隔离进程是在自己的沙箱中运行的进程,与系统的其余部分隔离,并且没有自己的特殊权限.要在隔离进程中运行服务,ServiceAttribute 的 IndependentProcess 属性设置为 true,如下代码片段所示:

Isolated Process – An isolated process is a process that runs in its own sandbox, isolated from the rest of the system and with no special permissions of its own. To run a service in an isolated process, the IsolatedProcess property of the ServiceAttribute is set to true as shown in this code snippet:

示例代码:

[Service(Name = "com.xamarin.TimestampService",
     IsolatedProcess= true,
     Process="com.xamarin.xample.messengerservice.timestampservice_process",
     Exported=true)]

我按照所有步骤操作,但该服务甚至没有运行.

I followed all the steps, but the service does not even run.

我使用的代码如下:

MainActivity.cs

  public static Intent notificationServiceIntent;
    internal bool isStarting = false;
    // This is the package name of the APK, set in the Android manifest
    const string REMOTE_SERVICE_COMPONENT_NAME = "com.myapp";
    // This is the name of the service, according the value of ServiceAttribute.Name
    const string REMOTE_SERVICE_PACKAGE_NAME = "com.myapp.notificationservice_process";
    protected override void OnCreate(Bundle savedInstanceState)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;
        base.OnCreate(savedInstanceState);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

        LoadApplication(new App());
        CreateNotificationFromIntent(Intent);
        notificationServiceIntent = new Intent(this.BaseContext, typeof(PDANotificationService));
        ComponentName cn = new ComponentName(REMOTE_SERVICE_PACKAGE_NAME, REMOTE_SERVICE_COMPONENT_NAME);
        notificationServiceIntent.SetComponent(cn);
        StartService(notificationServiceIntent);
    }

NotificationService.cs

[Service(Name = "com.myapp.notificationservice", IsolatedProcess = true,
                 Process = "com.myapp.notificationservice_process", Exported = true, 
                 Label = "Isolated Process service that has trouble starting")]
public class NotificationService: Service
{
    System.Threading.Timer _timer;
    INotificationManager notificationManager;
    int notificationNumber;


    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override void OnCreate()
    {
        base.OnCreate();
    }

    [return: GeneratedEnum]
    public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
        if(notificationManager==null)
            notificationManager = DependencyService.Get<INotificationManager>();
        sendNotification();

        base.OnStartCommand(intent, flags, startId);
        return StartCommandResult.Sticky;

    }

    public void sendNotification()
    {
        Device.StartTimer(new TimeSpan(0, 1, 0), () =>
        {
            notificationNumber++;
            string title = $"Local Notification #{notificationNumber}";
            string message = $"You have now received {notificationNumber} notifications!";
            notificationManager.ScheduleNotification(title, message);
            return true;
        });

    }
}

BroadcastReceiver.cs

[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class BroadcastReceiver : BroadcastReceiver
{
    // This is the package name of the APK, set in the Android manifest
    const string REMOTE_SERVICE_COMPONENT_NAME = "com.myapp";
    // This is the name of the service, according the value of ServiceAttribute.Name
    const string REMOTE_SERVICE_PACKAGE_NAME = "com.myapp.notificationservice_process";
    public override void OnReceive(Context context, Intent intent)
    {
        Log.Info("TestApp", "******* Loading Application *******");

        try
        {
            if (intent.Action.Equals(Intent.ActionBootCompleted))
            {
                Intent service = new Intent(context, typeof(PDANotificationService));
                ComponentName cn = new ComponentName(REMOTE_SERVICE_PACKAGE_NAME, REMOTE_SERVICE_COMPONENT_NAME);
                service.SetComponent(cn);
                service.AddFlags(ActivityFlags.NewTask);
                context.StartService(service);
            }
        }
        catch (Exception ex)
        {
            Log.Error("TestApp", "******* Error message *******: " + ex.Message);
        }
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.myapp" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
<application android:label="My app" android:icon="@mipmap/ic_launcher" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.STATUS_BAR" />

当应用程序在后台时,我能够让服务运行.但是现在,即使主应用程序关闭,我也需要保持服务运行.

I was able to get the service to run when the application is in the background. But now, I need to keep the service running even when the main application is closed.

我已经尝试解决它将近 2 周了,但我仍然找不到答案.如果你能帮助我,我真的,真的,将不胜感激.

I've been trying to solve it for almost 2 weeks, but I still can't find the answer. I really, really, would be very grateful if you could help me.

提前,非常感谢.

推荐答案

您可以使用 前台服务 通过启动前台通知以在您的应用程序关闭时保持活动状态.

You could use Foreground Sevice by start a foregroud notification to keep alive when your app is closed.

这里有一个简单的示例,您可以参考并添加到您的项目中.

here is a simple sample you could refer to and add into your projects.

1.创建一个服务MyService.cs:

[Service(Enabled = true)]
public class MyService : Service
{
    private Handler handler;
    private Action runnable;
    private bool isStarted;
    private int DELAY_BETWEEN_LOG_MESSAGES = 5000;
    private int NOTIFICATION_SERVICE_ID = 1001;
    private int NOTIFICATION_AlARM_ID = 1002;
    private string NOTIFICATION_CHANNEL_ID = "1003";
    private string NOTIFICATION_CHANNEL_NAME = "MyChannel";
    public override void OnCreate()
    {
        base.OnCreate();

        handler = new Handler();

        //here is what you want to do always, i just want to push a notification every 5 seconds here
        runnable = new Action(() =>
        {
           if (isStarted)
            {
                DispatchNotificationThatAlarmIsGenerated("I'm running");
                handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
            }
        });
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        if (isStarted)
        {
            // service is already started
        }
        else
        {
            CreateNotificationChannel();
            DispatchNotificationThatServiceIsRunning();

            handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
            isStarted = true;
        }
        return StartCommandResult.Sticky;
    }

    public override void OnTaskRemoved(Intent rootIntent)
    {
        //base.OnTaskRemoved(rootIntent);
    }

    public override IBinder OnBind(Intent intent)
    {
        // Return null because this is a pure started service. A hybrid service would return a binder that would
        // allow access to the GetFormattedStamp() method.
        return null;
    }

    public override void OnDestroy()
    {
        // Stop the handler.
        handler.RemoveCallbacks(runnable);

        // Remove the notification from the status bar.
        var notificationManager = (NotificationManager)GetSystemService(NotificationService);
        notificationManager.Cancel(NOTIFICATION_SERVICE_ID);

        isStarted = false;
        base.OnDestroy();
    }

    private void CreateNotificationChannel()
    {
        //Notification Channel
        NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);
        notificationChannel.EnableLights(true);
        notificationChannel.EnableVibration(true);
        notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });


        NotificationManager notificationManager = (NotificationManager)this.GetSystemService(Context.NotificationService);
        notificationManager.CreateNotificationChannel(notificationChannel);
    }

    //start a foreground notification to keep alive 
    private void DispatchNotificationThatServiceIsRunning()
    {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
               .SetDefaults((int)NotificationDefaults.All)
               .SetSmallIcon(Resource.Drawable.Icon)
               .SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
               .SetSound(null)
               .SetChannelId(NOTIFICATION_CHANNEL_ID)
               .SetPriority(NotificationCompat.PriorityDefault)
               .SetAutoCancel(false)
               .SetContentTitle("Mobile")
               .SetContentText("My service started")
               .SetOngoing(true);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
        StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
    }

    //every 5 seconds push a notificaition
    private void DispatchNotificationThatAlarmIsGenerated(string message)
    {
        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);

        Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
            .SetSmallIcon(Resource.Drawable.Icon)
            .SetContentTitle("Alarm")
            .SetContentText(message)
            .SetAutoCancel(true)
            .SetContentIntent(pendingIntent);

        var notificationManager = (NotificationManager)GetSystemService(NotificationService);
        notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());
    }
}

2.在您的活动中:

protected override void OnResume()
  {
      base.OnResume();
      StartMyRequestService();
  }
public void StartMyRequestService()
  {
      var serviceToStart = new Intent(this, typeof(MyService));
      StartService(serviceToStart);
  }

这篇关于具有隔离进程的 xamarin 形式的后台服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-29 22:29