Фоновая служба с изолированным процессом в формах xamarin - PullRequest
0 голосов
/ 02 февраля 2020

Привет друзья, как ты ?. Я говорю вам, что я делаю упражнение для запуска службы в фоновом режиме. Чего я хочу добиться, так это того, что даже если приложение закрыто, эта служба все еще работает. Я следовал документации Microsoft, которая гласит следующее: docs.microsoft.com / en-us / xamarin / android / основы приложения / сервисы

изолированный процесс - Изолированный процесс - это процесс, который выполняется в собственной изолированной программной среде, изолированной от остальной системы и не имеющей собственных специальных разрешений. Чтобы запустить службу в изолированном процессе, свойству IsolatedProcess ServiceAttribute задается значение true, как показано в следующем фрагменте кода:

Пример кода:

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

Я выполнил все шаги, но служба даже не запускается.

Я использую следующий код:

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" />

Мне удалось запустить службу, когда приложение работает в фоновом режиме. Но теперь мне нужно, чтобы служба работала, даже когда основное приложение закрыто.

Я пытался решить эту проблему почти 2 недели, но до сих пор не могу найти ответ. Я действительно, очень, был бы очень благодарен, если бы вы могли помочь мне.

Заранее, большое спасибо.

1 Ответ

0 голосов
/ 03 февраля 2020

Вы можете использовать Foreground Sevice , запустив уведомление на заднем плане, чтобы сохранить работоспособность при закрытии приложения.

Вот простой пример, который вы можете использовать и добавлять в свои проекты.

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);
  }
...