안드로이드 8.0: java.lang.잘못된 상태 예외:서비스를 시작할 수 없습니다.
애플리케이션 시작 시 앱은 일부 네트워크 작업을 수행해야 하는 서비스를 시작합니다.API 레벨 26을 대상으로 한 후 백그라운드에서 안드로이드 8.0에서 애플리케이션이 서비스를 시작하지 못합니다.
원인: java.lang.잘못된 상태 예외:서비스를 시작할 수 없음 Intent {cmp=my.app.tt/com.my.service }: 앱이 백그라운드 uidUidRecord{90372b1 u0a} CEM 유휴 프로시저:1 seq(0,0,0)}에 있습니다.
이제 startService() 메서드는 백그라운드 서비스를 만들 수 없는 상황에서 Android 8.0을 대상으로 하는 앱이 이 메서드를 사용하려고 하면 불법 상태 예외를 발생시킵니다.
"그것이 허용되지 않는 상황에서" - 그것은 실제로 무엇을 의미합니까?그리고 그것을 고치는 방법.서비스를 "포그라운드"로 설정하지 않음
해결책이 있습니다.장치의 경우 8.0 이장의경다음사합니다야용해을우치전▁8▁use,합다니▁for를 사용해야 .startService()
7장치의 에는 7.0을 사용해야 .startForgroundService()
서비스를 시작하기 위한 코드 샘플입니다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, ServedService.class));
} else {
context.startService(new Intent(context, ServedService.class));
}
그리고 서비스 클래스에서 알림을 위해 아래 코드를 추가하십시오.
@Override
public void onCreate() {
super.onCreate();
startForeground(1,new Notification());
}
여기서 O는 Android 버전 26입니다.
Forground에서 서비스를 실행하지 않고 대신 백그라운드에서 실행하려면 Android O를 게시하여 다음과 같은 연결에 서비스를 바인딩해야 합니다.
Intent serviceIntent = new Intent(context, ServedService.class);
context.startService(serviceIntent);
context.bindService(serviceIntent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//retrieve an instance of the service here from the IBinder returned
//from the onBind method to communicate with
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
허용되는 상황은 Android O 이전과 동일하게 백그라운드 서비스가 작동하는 임시 화이트리스트입니다.
특정 상황에서 백그라운드 앱은 몇 분 동안 임시 화이트리스트에 저장됩니다.앱이 화이트리스트에 있는 동안 제한 없이 서비스를 시작할 수 있으며 백그라운드 서비스가 실행될 수 있습니다.응용프로그램은 다음과 같은 사용자가 볼 수 있는 작업을 처리할 때 화이트리스트에 배치됩니다.
- 높은 우선 순위의 FCM(Firebase Cloud Messaging) 메시지 처리
- SMS/MMS 메시지와 같은 브로드캐스트 수신
- 보류 중 실행알림의 의도입니다.
- VPN 앱이 자신을 포그라운드로 승격하기 전에 VPN 서비스를 시작합니다.
출처: https://developer.android.com/about/versions/oreo/background.html
즉, 백그라운드 서비스가 화이트리스트 요구 사항을 충족하지 못하면 새 작업 스케줄러를 사용해야 합니다.기본적으로 백그라운드 서비스와 동일하지만 백그라운드에서 계속 실행하는 대신 주기적으로 호출됩니다.
IntentService를 사용하는 경우 JobIntentService로 변경할 수 있습니다.아래 @kosev의 답변을 참조하십시오.
가장 좋은 방법은 새 JobScheduler for Oreo 또는 이전 서비스(사용할 수 없는 경우)를 사용하는 JobIntentService를 사용하는 것입니다.
매니페스트에 다음을 선언합니다.
<service android:name=".YourService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
또한 서비스에서 onHandleIntent를 onHandleWork로 교체해야 합니다.
public class YourService extends JobIntentService {
public static final int JOB_ID = 1;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, YourService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// your code
}
}
그런 다음 다음 서비스를 시작합니다.
YourService.enqueueWork(context, new Intent());
를 하여 백그라운드 IntentService
대체할 수 있습니다.IntentService
와 함께JobIntentService
지원 됩니다.
를 사용하는 JobIntentService
즉, 그은행니다동합으로 합니다.IntentService
O로 합니다.
JobScheduler
정기/온디맨드 작업에도 사용할 수 있습니다.그러나 은 그나하호다같이합처니다리로 처리해야 JobScheduler
21 API 21 API 할 수 .
네, API 26에서는 더 이상 백그라운드에서 서비스를 시작할 수 없기 때문입니다.따라서 API 26 위에서 Forground Service를 시작할 수 있습니다.
를 사용해야 합니다.
ContextCompat.startForegroundService(...)
누출 처리 중에 알림을 게시합니다.
@kosev가 답변에서 말했듯이 JobIntentService를 사용할 수 있습니다.하지만 저는 대안적인 해결책을 사용합니다 - 저는 불법적인 상태 예외를 포착하고 서비스를 전경으로 시작합니다.예를 들어 이 함수는 서비스를 시작합니다.
@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
val context = App.context
val intent = Intent(context, serviceType)
intent.action = intentAction
intentExtraSetup(intent)
intent.putExtra(NEED_FOREGROUND_KEY, false)
try {
context.startService(intent)
}
catch (ex: IllegalStateException) {
intent.putExtra(NEED_FOREGROUND_KEY, true)
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
}
else {
context.startService(intent)
}
}
}
그리고 내가 인텐트를 처리할 때 나는 다음과 같은 일을 합니다.
override fun onHandleIntent(intent: Intent?) {
val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
if(needToMoveToForeground) {
val notification = notificationService.createSyncServiceNotification()
startForeground(notification.second, notification.first)
isInForeground = true
}
intent?.let {
getTask(it)?.process()
}
}
Forground Service를 사용할 것을 권장하는 응답이 많습니다.Forground 서비스를 사용하려면 관련 알림이 있어야 합니다.사용자는 이 알림을 볼 수 있습니다.상황에 따라, 그들은 당신의 앱에 짜증을 내고 그것을 제거할 수 있습니다.
가장 쉬운 해결책은 WorkManager라는 새로운 아키텍처 구성요소를 사용하는 것입니다.설명서는 https://developer.android.com/topic/libraries/architecture/workmanager/ 에서 확인할 수 있습니다.
작업자를 확장하는 작업자 클래스를 정의하기만 하면 됩니다.
public class CompressWorker extends Worker {
public CompressWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Worker.Result doWork() {
// Do the work here--in this case, compress the stored images.
// In this example no parameters are passed; the task is
// assumed to be "compress the whole library."
myCompress();
// Indicate success or failure with your return value:
return Result.SUCCESS;
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
그런 다음 실행할 시간을 예약합니다.
OneTimeWorkRequest compressionWork =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.build();
WorkManager.getInstance().enqueue(compressionWork);
쉬워요! 작업자를 구성할 수 있는 방법은 여러 가지가 있습니다.반복 작업을 지원하며 필요한 경우 체인과 같은 복잡한 작업도 수행할 수 있습니다.이게 도움이 되길 바랍니다.
JobScheduler를 사용하는 대체 솔루션은 정기적으로 백그라운드에서 서비스를 시작할 수 있습니다.
먼저 Util.java라는 이름의 클래스를 만듭니다.
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
public class Util {
// schedule the start of the service every 10 - 30 seconds
public static void schedulerJob(Context context) {
ComponentName serviceComponent = new ComponentName(context,TestJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(0,serviceComponent);
builder.setMinimumLatency(1*1000); // wait at least
builder.setOverrideDeadline(3*1000); //delay time
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); // require unmetered network
builder.setRequiresCharging(false); // we don't care if the device is charging or not
builder.setRequiresDeviceIdle(true); // device should be idle
System.out.println("(scheduler Job");
JobScheduler jobScheduler = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
jobScheduler = context.getSystemService(JobScheduler.class);
}
jobScheduler.schedule(builder.build());
}
}
그런 다음 JobService 클래스를 TestJobService로 만듭니다.자바
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.widget.Toast;
/**
* JobService to be scheduled by the JobScheduler.
* start another service
*/
public class TestJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Util.schedulerJob(getApplicationContext()); // reschedule the job
Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
이름이 ServiceReceiver.java인 BroadcastReceiver 클래스 뒤에 지정됩니다.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class ServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Util.schedulerJob(context);
}
}
서비스 및 수신자 클래스 코드로 매니페스트 파일 업데이트
<receiver android:name=".ServiceReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".TestJobService"
android:label="Word service"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
main_intent 런처를 기본적으로 생성되는 mainActivity.java 파일로 남겼고, MainActivity.java 파일의 변경 사항은 다음과 같습니다.
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Util.schedulerJob(getApplicationContext());
}
}
와아아아!Foreground 서비스를 사용하지 않고 백그라운드 서비스
[편집]: Android에서 모든 종류의 백그라운드 작업에 대해 Work Manager를 사용할 수 있습니다.
Firebase 릴리스 노트에 따르면 Android O에 대한 지원은 10.2.1에서 처음 릴리스되었다고 합니다(최신 버전을 사용하는 것이 좋습니다만).
Android O에 대한 새로운 Firebase 메시징 종속성을 추가하십시오.
compile 'com.google.firebase:firebase-messaging:11.6.2'
필요한 경우 Google Play 서비스 및 Google 저장소를 업그레이드합니다.
앱이 백그라운드에 있을 때 이전에 잘 작동하는 의도가 있었다면, 안드로이드 8 이상에서는 더 이상 그렇지 않을 것입니다.앱이 백그라운드에 있을 때 일부 처리를 해야 하는 의도만 언급합니다.
다음 단계를 따라야 합니다.
- 는 위서언급의다사합용니다야해음을도는에한▁be합▁should다를 사용하는 것이어야 .
JobIntentService
에IntentService
. 를 .
JobIntentService
- 다구을합니다야를 해야 합니다.onHandleWork(@NonNull Intent intent)
method는 method 아래에 . 는 드메소호하며메소, 합다니출를드야어있및을 합니다.onHandleWork
방법:public static void enqueueWork(Context context, Intent work) { enqueueWork(context, xyz.class, 123, work); }
러
enqueueWork(Context, intent)
사용자의 의도가 정의된 클래스에서.샘플 코드:
Public class A { ... ... Intent intent = new Intent(Context, B.class); //startService(intent); B.enqueueWork(Context, intent); }
아래 클래스는 이전에 서비스 클래스를 확장하고 있었습니다.
Public Class B extends JobIntentService{
...
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, B.class, JobId, work);
}
protected void onHandleWork(@NonNull Intent intent) {
...
...
}
}
com.android.support:support-compat
는 에 필요합니다.JobIntentService
사용합니다26.1.0 V
.가장 중요한 것은 Firebase 라이브러리 버전이 적어도 켜져 있는지 확인하는 것입니다.
10.2.1
는 문가있니다습었제와 가 있었습니다.10.2.0
만약 있다면요!매니페스트에는 서비스 클래스에 대한 다음 권한이 있어야 합니다.
service android:name=".B" android:exported="false" android:permission="android.permission.BIND_JOB_SERVICE"
이게 도움이 되길 바랍니다.
8.0에서 코드를 실행 중인 경우 응용 프로그램이 충돌합니다.그래서 서비스를 전면에서 시작합니다.8.0 미만인 경우 다음을 사용합니다.
Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);
위 또는 8.0인 경우 다음을 사용합니다.
Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );
이 답변(+4/-4 편집 기준)에 대한 논란이 많은 투표이므로 다른 답변을 먼저 보고 마지막 수단으로만 사용하십시오.저는 루트로 실행되는 네트워킹 앱에 이것을 한 번 사용했을 뿐이며 일반적인 상황에서 이 솔루션을 사용해서는 안 된다는 일반적인 의견에 동의합니다.
아래의 원답:
다른 답변은 모두 맞지만, 이 문제를 해결하는 또 다른 방법은 사용자에게 앱에 대한 배터리 최적화를 비활성화하도록 요청하는 것입니다(앱이 시스템과 관련이 없는 경우에는 일반적으로 좋은 생각이 아닙니다).Google Play에서 앱을 금지하지 않고 배터리 최적화를 해제하도록 요청하는 방법은 다음 답변을 참조하십시오.
또한 다음을 통해 충돌을 방지하기 위해 수신기에서 배터리 최적화가 꺼져 있는지 확인해야 합니다.
if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
?.isIgnoringBatteryOptimizations(packageName) != false) {
startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash
통합된 Firebase 메시징 푸시 알림이 있는 경우,
백그라운드 실행 제한으로 인해 Android O(Android 8.0)에 대한 Firebase 메시징 종속성을 새로 추가하거나 업데이트합니다.
compile 'com.google.firebase:firebase-messaging:11.4.0'
필요한 경우 Google Play 서비스 및 Google 저장소를 업그레이드합니다.
업데이트:
compile 'com.google.firebase:firebase-messaging:11.4.2'
사용하다startForegroundService()
대신에startService()
그리고 만드는 것을 잊지 마세요.startForeground(1,new Notification());
서비스를 시작한 후 5초 이내에 서비스를 제공합니다.
전화기가 화면 밖에 있거나 서비스를 시작하는 동안 전원 버튼을 눌렀기 때문에 실제로 발생하고 있습니다.이것에 대한 해결책은 활동을 시작하는 것이고 언제 그것이 재개될 것인지 그리고 서비스를 시작하는 것입니다.저의 경우, 그것은 부팅하고 서비스를 시작하는 것이었습니다.
저는 여기에 있는 답변들에 매우 불만입니다.포그라운드 서비스나 WorkManager가 사용 사례에 적합하다면 어떨까요?프로세스 범위를 사용하고 로깅 로직에 범위 취소 예외를 포함하지 않도록 하는 해결책을 찾았습니다.이와 같은 경우:
with(ProcessLifecycleOwner.get()) {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
try {
context.startService(context, Service::class.java)
} catch (ex: CancellationException) {
// app minimized, scope cancelled, do not log as error
} catch (ex: IllegalStateException) {
logToFirebase(ex)
}
}
}
}
이 기사에서 더 자세한 내용은 https://medium.com/ @lepicekmichal/sship-background-service-without-sshipcup-501e4479110f에서 확인하십시오.
충돌을 방지하기 위해 이 코드를 사용해 보십시오.구글 개발자들이 이슈 트래커에서 말했듯이.
private val activityManager by lazy { getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }
//due to https://issuetracker.google.com/issues/113122354
private fun isInForegroundByImportance(): Boolean {
val importanceState = activityManager.runningAppProcesses.find {
it.pid == android.os.Process.myPid()
}?.importance ?: return false
return importanceState >= RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
및 용법
override fun onResume() {
super.onResume()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || isInForegroundByImportance()) {
val intent = Intent(this, BluetoothScannerService::class.java)
this.startService(intent)
}
}
저도 이 문제가 있었습니다.
이 라이브러리를 추가했습니다.
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
그리고 앱을 다시 설치하여 이것을 해결했습니다.
언급URL : https://stackoverflow.com/questions/46445265/android-8-0-java-lang-illegalstateexception-not-allowed-to-start-service-inten
'itsource' 카테고리의 다른 글
문 실행 또는 스크립트 실행? (0) | 2023.06.11 |
---|---|
vuex 알 수 없는 작업 유형: 로그인 (0) | 2023.06.11 |
게시물을 게시된 순서대로 내림차순으로 표시 (0) | 2023.06.11 |
Python 3.3의 해시 함수가 세션 간에 서로 다른 결과를 반환함 (0) | 2023.06.11 |
기본 앱을 생성할 때 FirebaseOptions는 null일 수 없습니다. (0) | 2023.06.06 |