Thread {
HOPO(this)
}.start()
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
// Вызов функции HOPO в фоновом режиме
launch(Dispatchers.IO) {
HOPO(this@YourActivity)
}
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import android.app.Notification
import android.content.Context
import android.os.Handler
import android.os.Looper
import okhttp3.*
import java.io.IOException
class ForegroundService : Service() {
private val CHANNEL_ID = "1"
private val client = OkHttpClient()
private val handler = Handler(Looper.getMainLooper())
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val input = intent?.getStringExtra("inputExtra")
createNotificationChannel()
val notificationIntent = Intent(this, PostingVKActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service Kotlin Example")
.setContentText(input)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
// Вызываем функцию HOPO
HOPO(this)
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val serviceChannel = NotificationChannel(CHANNEL_ID, "1", NotificationManager.IMPORTANCE_DEFAULT)
val manager = getSystemService(NotificationManager::class.java)
manager!!.createNotificationChannel(serviceChannel)
}
companion object {
fun startService(context: Context, message: String) {
val startIntent = Intent(context, ForegroundService::class.java)
startIntent.putExtra("inputExtra", message)
context.startService(startIntent)
}
}
}
fun HOPO(context: Context) {
var g = 0
while (g < 10) {
val api = "https://test.ru/g=$g"
val request = Request.Builder()
.url(api)
.post(postBody.toRequestBody(MEDIA_TYPE_MARKDOWN))
.header("X-Get-Processing-Time", "1")
.removeHeader("User-Agent")
.addHeader("User-Agent", UserAgent)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
handler.post {
gEditText.text = "$g"
}
}
})
g++
}
}
val intentService = Intent(this, ForegroundService::class.java)
intentService.putExtra("inputExtra", "Your message")
ForegroundService.startService(this, "Your message")
<service
android:name=".ForegroundService"
android:enabled="true"
android:exported="false" />
fun HOPO(context: Context) {
// Ваш код функции HOPO
// ...
}
val intentService = Intent(this, ForegroundService::class.java)
intentService.putExtra("inputExtra", "Your message")
ForegroundService.startService(this, "Your message")
HOPO(this)
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
val service = (binder as ForegroundService.MyBinder).getService()
// Теперь у вас есть доступ к сервису и можете выполнять методы, предоставленные сервисом.
}
override fun onServiceDisconnected(name: ComponentName?) {
// Обработка отсоединения сервиса, если это необходимо.
}
}
ВЫ ИЗНАЧАЛЬНО ПРЕДОСТАВИЛИ КОД, который вполне рабочий,
однако в нем имеются незначительные ошибки логики, попробуйте их исправить!
<uses-permission android:name="android.permission.INTERNET" />
Это вроде бы все значительно влияющие факторы.
Представил ниже пример улучшения кода, предоставленного изначально.
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.*
import java.io.IOException
class FirstActivity : AppCompatActivity() {
private val client = OkHttpClient()
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
HOPO()
}
fun HOPO() {
runBlocking {
val job = launch(Dispatchers.IO) {
for (g in 0 until 10) {
val api = "https://test.ru/g=$g"
val request = Request.Builder()
.url(api)
.post(postBody.toRequestBody(MEDIA_TYPE_MARKDOWN))
.header("X-Get-Processing-Time", "1")
.removeHeader("User-Agent")
.addHeader("User-Agent", UserAgent)
.build()
try {
val response: Response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseData = response.body?.string()
handler.post {
gEditText.text = "$g"
}
}
response.body?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
job.join()
}
}
}
На данный номент это улучшение является лучшим решением задачи без дополнительных проверок и внедрений, на основе изначально предоставленного в вопросе кода.
Попробуйте для начала его, потом уже все остальные ответы, начиная с последнего Вашего ответа,
которые я предоставил. Там различные реализации и пути решения.
Описанное в этом ответе улучшение, гарантированно работает на Android 12, Android 13.
Смело можно проверить, и если не будет работать идти более сложными путями.
разобрал Ваш код в предоставленном Вами последнем ответе.
class ForegroundService : Service() {
private var myCallback: MyCallback? = null
// Метод для установки объекта MyCallback
fun setCallback(callback: MyCallback) {
myCallback = callback
}
}
class FirstActivity : AppCompatActivity(), MyCallback {
private lateinit var gEditText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
// Инициализация gEditText
gEditText = findViewById(R.id.your_edit_text_id)
// Создайте запрос на выполнение MyWorker
val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
// Устанавливаем MyCallback для ForegroundService
val serviceIntent = Intent(this, ForegroundService::class.java)
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
WorkManager.getInstance(this).enqueue(workRequest)
}
override fun updateUI(data: String) {
runOnUiThread {
gEditText.text = data
}
}
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as ForegroundService.MyBinder
val service = binder.getService()
service.setCallback(this@FirstActivity)
}
override fun onServiceDisconnected(name: ComponentName?) {
// Handle disconnection, if needed
}
}
}
С этими изменениями, ваш фоновый сервис должен обновлять UI в gEditText без проблем.
И всё таки попробуйте все представленные мной ответы, попробуйте разобраться в них,
используйте разные реализации - не получится одна, исправит другая.
Пробуйте разные решения, спрашивайте, задайте дополнительно вопрос на StakOverFlow в русской и английской локализациях проекта, возможно, продвинутые кодеры знают готовый ответ.
Не бойтесь предоставить кодеру публично код из проекта, а не тестовые переменные для публичного пользования - это поможет в быстрой реализации решения.
Ваши собственные знания Kotlin и JS помогут в перекомпановке кода, проверке, правильной интерпретации и написании дополнительного кода. Пользуйтесь Debug в студио, чтобы проверить методы.
Вот полный код для вашей ситуации на основе предыдущего Вашего ответа,
с учетом обновлений UI из фонового сервиса:
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params), MyCallback {
override fun doWork(): Result {
var g = 0
while (g < 10) {
// Выполняйте запрос и обновляйте UI здесь
// ...
// Обновление UI через callback
updateUI("Новое значение для UI")
g++
Thread.sleep(2000) // Подождите 2 секунды перед следующим запросом
}
return Result.success()
}
override fun updateUI(data: String) {
// Обновление UI, например, через LiveData, который прослушивается в вашей активности
// или каким-либо другим способом, который вы используете для обновления UI.
}
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
class FirstActivity : AppCompatActivity(), MyCallback {
private lateinit var gEditText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
// Инициализация gEditText
gEditText = findViewById(R.id.your_edit_text_id)
// Создайте запрос на выполнение MyWorker
val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
// Запустите задачу с использованием WorkManager
WorkManager.getInstance(this).enqueue(workRequest)
}
override fun updateUI(data: String) {
runOnUiThread {
gEditText.text = data
}
}
}
class ForegroundService : Service() {
private var myCallback: MyCallback? = null
// Метод для установки объекта MyCallback
fun setCallback(callback: MyCallback) {
myCallback = callback
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// ...
if (myCallback != null) {
myCallback?.updateUI("TEST DATA")
}
// ...
}
}
import android.app.Notification
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
class ForegroundService : Service() {
// ...
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// ...
// Создание уведомления для сервиса
val notification = createNotification(input)
// Запуск сервиса в режиме Foreground
startForeground(1, notification)
return START_NOT_STICKY
}
// ...
// Метод для создания уведомления
private fun createNotification(input: String?): Notification {
val notificationIntent = Intent(this, FirstActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service Kotlin Example")
.setContentText(input)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build()
}
}
С этими изменениями ваш фоновый сервис будет продолжать работать, даже если приложение свернуто. Убедитесь также, что у вас правильно настроена работа с уведомлениями и разрешениями на фоновую работу,, если это необходимо.
Предоставил возможные варианты, исключите в представленных вариантах излишние комментарии, код,
который возможно повторяется. Ваши знания Kotlin помогут Вам проверить значения переменных,
и скомпоновать несколько вариантов решения задачи облегчив решение сути вопроса.
Некоторый код также я опустил, просто закомментировав, проверьте, что ничего не упустили.
Также посмотрите Ваши разрешения Manifest (включая Gradle, Kotlin, Google) просмотрите все активности и разрешения, чтобы исключить серивисы влияющие на разрешения работы в фоне..
Если задача так и не решится, попробуйте предоставить не тестовый код, а боевой из проекта (именно тот кусок, который отвечает за логику фоновой обработки процесса), я попробую исправить код, предоставив вариант решения. Возможно, при энприментации кода из боевого в тестовый и обратно Вы допускаете ошибку логики или простановки знаков. Проверьте Ваш код через BagTracker в AndroidStudio, в Run & Bug
class ForegroundService : Service() {
private var myCallback: MyCallback? = null
// Метод для установки объекта MyCallback
fun setCallback(callback: MyCallback) {
myCallback = callback
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// ...
if (myCallback != null) {
myCallback?.updateUI("TEST DATA")
}
// ...
}
}
class FirstActivity : AppCompatActivity(), MyCallback {
private lateinit var gEditText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
// Инициализация gEditText
gEditText = findViewById(R.id.your_edit_text_id)
// ...
}
override fun updateUI(data: String) {
gEditText.text = data
}
}
Это должно решить проблему с инициализацией myCallback и gEditText
и обеспечить корректную передачу данных и обновление UI.
Прежде чем реализовать, попробуйте понять, что и как решается.
Действительно на стороне Google многое меняется за последнее время.
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params), MyCallback {
override fun doWork(): Result {
var g = 0
while (g < 10) {
// Выполняйте запрос и обновляйте UI здесь
// ...
// Обновление UI через callback
updateUI("Новое значение для UI")
g++
Thread.sleep(2000) // Подождите 2 секунды перед следующим запросом
}
return Result.success()
}
override fun updateUI(data: String) {
// Обновление UI, например, через LiveData, который прослушивается в вашей активности
// или каким-либо другим способом, который вы используете для обновления UI.
}
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Создайте запрос на выполнение MyWorker
val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
// Запустите задачу с использованием WorkManager
WorkManager.getInstance(this).enqueue(workRequest)
}
}
interface MyCallback {
fun updateUI(data: String)
}
class FirstActivity : AppCompatActivity(), MyCallback {
// ... остальной код активити
override fun updateUI(data: String) {
// Здесь вы обновляете ваш интерфейс, например, ваш TextView
gEditText.text = data
}
}
class Activity {
private var myCallback: MyCallback? = null
// Метод для установки объекта MyCallback
fun setCallback(callback: MyCallback) {
myCallback = callback
}
// Где-то в вашем коде, когда нужно обновить UI, вызывайте метод updateUI
private fun someMethod() {
// ...
myCallback?.updateUI("Новое значение для UI")
}
}
val firstActivity = FirstActivity()
val activity = Activity()
// Свяжите Activity и FirstActivity
activity.setCallback(firstActivity)
// Создайте рабочего (Worker) для выполнения запросов
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
var g = 0
while (g < 10) {
// Выполняйте запрос и обновляйте UI здесь
// ...
g++
Thread.sleep(2000) // Подождите 2 секунды перед следующим запросом
}
return Result.success()
}
}
class MyService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Ваш код выполнения запросов с задержкой
// ...
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
import android.app.IntentService
import android.content.Intent
import android.os.Handler
import android.os.Looper
class MyService : IntentService("MyService") {
private val handler = Handler(Looper.getMainLooper())
private var g = 0
override fun onHandleIntent(intent: Intent?) {
while (g < 10) {
val api = "https://test.ru/g=$g"
// Создайте и отправьте запрос
// Обновите UI через главный поток
handler.post {
// Обновите ваш UI здесь
gEditText.text = "$g"
}
g++
// Подождите 2 секунды перед следующим запросом
try {
Thread.sleep(2000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
MyCallback - Ваш код ссылается на MyCallback, но его определение отсутствует. Вы должны импортировать или определить этот интерфейс.
В методе setCallback вы используете myCallback!!, что означает, что вы уверены, что myCallback не равен null. Если это не всегда гарантировано, лучше использовать безопасный вызов ?..
input - Вы получаете input из intent?.getStringExtra("inputExtra"), но не проверяете, что input не равен null. Было бы хорошо добавить проверку на null и, возможно, установить значение по умолчанию, если input равен null.
PostingVKActivity и ForegroundService - В коде создается новый экземпляр PostingVKActivity и ForegroundService, что, возможно, не то, что вам требуется. Вы можете работать с уже существующими экземплярами классов, а не создавать новые.
NotificationCompat.Builder - Здесь используется метод setSmallIcon, который ожидает идентификатор ресурса. Убедитесь, что у вас есть соответствующий ресурс R.mipmap.ic_launcher.
В методе onStartCommand, когда устанавливается startForeground, вы указываете 2 в качестве первого аргумента. Это идентификатор уровня уведомлений, и вам может потребоваться уникальное значение, чтобы отличать разные службы.
В методе createNotificationChannel, вы устанавливаете имя канала "2", это, возможно, не самое информативное имя для канала уведомлений.
код требует проверки на наличие null и уточнения деталей о MyCallback.
Это всё о первом коде в вопросе.
Второй код правильный, однако есть значимая опечатка, которая мешает компиляции кода
button = binding.Button
константа button указана с большой буквы, а надо с маленькой ОБЯЗАТЕЛЬНО