Использование Dagger2 и Service вместе. Kotlin

Рейтинг: 1Ответов: 1Опубликовано: 29.06.2023

Есть Service и Fragment. В Service инжектятся параметры. Суть сервиса заключается в том, что он должен раз в день парсить данные с сайта и, если они подходят под условия, то высылается уведомление. При попытке создать Intent для запуска Service во фрагменте возникает ошибка java.lang.RuntimeException: Unable to create service com.example.currencytask.ui.home.NotificationService: java.lang.InstantiationException: java.lang.Class<com.example.currencytask.ui.home.NotificationService> has no zero argument constructor.

Код сервиса:

class NotificationService @Inject constructor(
private val iGetCurrencyListUseCase: IGetCurrencyListUseCase,
private val iSharedPrefsRepository: ISharedPrefsRepository,
private val context: Context
) : Service() {

private lateinit var scheduler: ScheduledExecutorService
private lateinit var notificationManager: NotificationManager
private val serviceScope = CoroutineScope(Dispatchers.Default)
private val currencyId = "R01235"

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

override fun onCreate() {
    super.onCreate()
    notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}

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

override fun onDestroy() {
    scheduler.shutdown()
    super.onDestroy()
}

private fun startScheduler() {
    scheduler = Executors.newSingleThreadScheduledExecutor()

    serviceScope.launch {
        val currentTime = LocalTime.now()
        val initialDelay = currentTime.until(LocalTime.of(12, 0), ChronoUnit.SECONDS)

        delay(initialDelay)

        while (isActive) {
            val defaultCurrency = iSharedPrefsRepository.getDefaultCurrency()
            val dateTo = LocalDate.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy"))
            val dateFrom =
                LocalDate.now().plusMonths(-1).format(DateTimeFormatter.ofPattern("dd/MM/yyyy"))
            val currencyList = iGetCurrencyListUseCase.invoke(dateFrom, dateTo, currencyId)

            if (currencyList.records?.reversed()?.get(0)?.value?.let {
                    compareStringsAsNumbers(
                        it,
                        defaultCurrency
                    )
                } == 1
            ) sendNotification()

            //delay(24 * 60 * 60 * 1000)
            delay(5 * 1000)
        }
    }
}

private fun sendNotification() {
    val intent = Intent(context, MainApplication::class.java)
    intent.apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    val pendingIntent = PendingIntent.getActivity(
        context,
        0,
        intent,
        PendingIntent.FLAG_IMMUTABLE
    )
    val notificationId = 101
    val channelId = "currency_update"
    val channelName = "Currency Update"

    val importance = NotificationManager.IMPORTANCE_DEFAULT
    val channel = NotificationChannel(channelId, channelName, importance)
    notificationManager.createNotificationChannel(channel)

    val builder = NotificationCompat.Builder(context, channelId)
        .setSmallIcon(R.drawable.ic_dollar)
        .setContentTitle("Оповещение")
        .setContentText("Цена поднялась")
        .setAutoCancel(true)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)

    notificationManager.notify(notificationId, builder.build())
}

private fun compareStringsAsNumbers(str1: String, str2: String): Int {
    val number1 = str1.replace(",", ".").toDouble()
    val number2 = str2.replace(",", ".").toDouble()
    Log.e("First number", number1.toString())
    Log.e("Second number", number2.toString())

    return when {
        number1 < number2 -> -1
        number1 > number2 -> 1
        else -> 0
    }
}

}

Часть фрагмента:

class HomeFragment : Fragment() {

    @Inject
    lateinit var notificationService: NotificationService
    //...остальной код

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val serviceIntent = Intent(context, NotificationService::class.java)
        // был еще такой вариант
        // val serviceIntent = Intent(context, notificationService::class.java)
        requireActivity().startForegroundService(serviceIntent)

Я понимаю, что при создании интента нужно передавать данные в конструктор сервиса, но как сделать, чтобы они сами инжектились? Возможно, неправильно написан модуль.

@Module
class NotificationModule {
    @Provides
    fun provideNotificationService(
        iGetCurrencyListUseCase: IGetCurrencyListUseCase,
        iSharedPrefsRepository: ISharedPrefsRepository,
        context: Context
    ): NotificationService {
        return NotificationService(iGetCurrencyListUseCase, iSharedPrefsRepository, context)
    }
}

Возможно, есть другой способ запуска сервиса или его вообще не нужно использовать в DI, тогда как получить доступ к репозиторию?. Не нашел ответа в интернете.

Ответы

▲ 0

Мне помогло убрать конструктор и заменить его полями

class NotificationService : Service() {

@Inject
lateinit var iGetCurrencyListUseCase: IGetCurrencyListUseCase
@Inject
lateinit var iSharedPrefsRepository: ISharedPrefsRepository

Соответственно модуль тоже поменять

@Module
class NotificationModule {
    @Provides
    fun provideNotificationService(): NotificationService {
        return NotificationService()
    }
}