Android: запуск службы независимо от сбоя или работы приложения

#java #android #crash-reports #android-intentservice #foreground-service

#java #Android #отчеты о сбоях #android-intentservice #передний план-сервис

Вопрос:

Я работаю над функциональностью для извлечения журналов приложения и загрузки его на сервер. Мы записываем журналы в файл.

Что мне нужно сделать?

Мы использовали fabric (в настоящее время Firebase crashlystics) для отчетов о сбоях. но некоторое время многие сбои пропущены. таким образом, мы не можем отследить проблему с клиентом. Мы решили внедрить наш собственный инструмент для создания отчетов о сбоях. Мы решили загружать журналы при запуске приложения без каких-либо дальнейших процессов. После сбоя приложения отчет о сбое будет записан в файл, и при следующем запуске приложения этот файл будет загружен на сервер. Я не хочу, чтобы пользователь зависал в таких вещах, поэтому я решил сделать это с помощью сервисов. поэтому мне нужно запускать службу при запуске приложения, и журналы должны быть загружены на сервер внутри службы без каких-либо жестких дисков. Если сбой приложения после следующей строки запуска службы, служба не должна останавливаться

Что я пробовал?

Я использовал службу для загрузки журналов на сервер. Я попытался запустить службу переднего плана в классе приложения onCreate() , служба будет работать в другом процессе, и это работает нормально, как и ожидалось. но проблема в том, что, если при входе в систему происходит сбой (первое действие) onCreate() , служба переднего плана запускается не так, как ожидалось. Процесс обслуживания останавливается после сбоя.

Чего я хочу?

Я хочу запустить службу переднего плана или службу намерений, которые загружают журналы на сервер. и после загрузки журналов он остановится. Между тем, если сбой приложения по какой-либо другой причине, служба не должна останавливаться, и она должна выполнить свою задачу.

Код

Класс приложения

 public void onCreate() {
       startLogService()
       // Other code
    }

    private void startLogService() {
        Intent serviceIntent = new Intent(this, LogWriteService.class);
       /* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // i have tried with this also
            startForegroundService(serviceIntent);
        } else {
            startService(serviceIntent);
        }*/
        bindService(serviceIntent, m_serviceConnection, BIND_AUTO_CREATE);
    }
  

LoginActivity

 public class LoginActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_initial_login);
        int i = 10/0; // just for crash test
    }
}
  

Класс LogSendService (служба намерений)

 class LogSendService : IntentService("LogSendService") {

    private val NOTIFICATION_ID = 12345
    internal var allLogFile: File? = null

    override fun onHandleIntent(intent: Intent?) {
        createAndShowForegroundNotification(this, NOTIFICATION_ID, "We are gathering logs", true)
        writeLogs()
    }

    private fun createAndShowForegroundNotification(mService: Service, notificationId: Int, message: String, isOnGoing: Boolean) {

        val builder = getNotificationBuilder(mService,
                "Log Service", // Channel id
                NotificationManagerCompat.IMPORTANCE_LOW) //Low importance prevent visual appearance for this notification channel on top
        builder.setSmallIcon(R.mipmap.home_icon)
                .setContentTitle(mService.getString(R.string.app_name))
                .setContentText(message)
        if (isOnGoing) {
            builder.setOngoing(true)
        }

        val notification = builder.build()
        mService.startForeground(notificationId, notification)
    }

    private fun writeLogs() {
        try {
            //log write and upload process 
            createAndShowForegroundNotification(this, NOTIFICATION_ID, "success", false)
        } catch (e: Exception) {
            e.printStackTrace()
            createAndShowForegroundNotification(this, NOTIFICATION_ID, e.message!!, false)
        }
        stopForeground(false)
        stopSelf()
    }



    companion object {
        val STATUS_FINISHED = 1

        fun getNotificationBuilder(context: Context, channelId: String, importance: Int): NotificationCompat.Builder {
            val builder: NotificationCompat.Builder
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                prepareChannel(context, channelId, importance)
                builder = NotificationCompat.Builder(context, channelId)
            } else {
                builder = NotificationCompat.Builder(context)
            }
            return builder
        }

        @TargetApi(26)
        private fun prepareChannel(context: Context, id: String, importance: Int) {
            val appName = context.getString(R.string.app_name)
            val description = "Testing Log Service"
            val nm = context.getSystemService(Activity.NOTIFICATION_SERVICE) as NotificationManager

            if (nm != null) {
                var nChannel: NotificationChannel? = nm.getNotificationChannel(id)

                if (nChannel == null) {
                    nChannel = NotificationChannel(id, appName, importance)
                    nChannel.description = description
                    nm.createNotificationChannel(nChannel)
                }
            }
        }
    }
}
  

Класс LogSendService (служба переднего плана)

 class LogWriteService : Service() {
    private val NOTIFICATION_ID = 12345
    internal var allLogFile: File? = null

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    inner class MyBinder : Binder() {
        val service: LogWriteService
            get() = this@LogWriteService
    }

    override fun onCreate() {
        super.onCreate()
        //        startLogService();
        createAndShowForegroundNotification(this, NOTIFICATION_ID, "We are gathering logs", true)
        writeLogs()
    }

    private fun createAndShowForegroundNotification(yourService: Service, notificationId: Int, message: String, isOnGoing: Boolean) {

        val builder = getNotificationBuilder(yourService,
                "Log Service", // Channel id
                NotificationManagerCompat.IMPORTANCE_LOW) //Low importance prevent visual appearance for this notification channel on top
        builder.setSmallIcon(R.mipmap.home_icon)
                .setContentTitle(yourService.getString(R.string.app_name))
                .setContentText(message)
        if (isOnGoing) {
            builder.setOngoing(true)
        }

        val notification = builder.build()
        yourService.startForeground(notificationId, notification)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return Service.START_STICKY
    }

    private fun writeLogs() {
        try {
           //log write and upload
        } catch (e: Exception) {
            e.printStackTrace()
            createAndShowForegroundNotification(this, NOTIFICATION_ID, e.message!!, false)
        }
        stopForeground(false)
        stopSelf()
    }


    companion object {
        val STATUS_FINISHED = 1

        fun getNotificationBuilder(context: Context, channelId: String, importance: Int): NotificationCompat.Builder {
            val builder: NotificationCompat.Builder
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                prepareChannel(context, channelId, importance)
                builder = NotificationCompat.Builder(context, channelId)
            } else {
                builder = NotificationCompat.Builder(context)
            }
            return builder
        }

        @TargetApi(26)
        private fun prepareChannel(context: Context, id: String, importance: Int) {
            val appName = context.getString(R.string.app_name)
            val description = "Testing Log Service"
            val nm = context.getSystemService(Activity.NOTIFICATION_SERVICE) as NotificationManager

            if (nm != null) {
                var nChannel: NotificationChannel? = nm.getNotificationChannel(id)

                if (nChannel == null) {
                    nChannel = NotificationChannel(id, appName, importance)
                    nChannel.description = description
                    nm.createNotificationChannel(nChannel)
                }
            }
        }
    }
}
  

Может ли кто-нибудь предложить решение? или лучший способ сделать это?

Примечание: часть кода находится на Java, а часть — на Kotlin

Комментарии:

1. Чего вы именно хотите? Служба должна работать вечно? И используйте startService() вместо bindService()

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

3. используйте startService(). bindService() имеет область действия до тех пор, пока не будет активирована привязка.