programing

Context.startForegroundService()가 Service.startForeground()를 호출하지 않았습니다.

goodsources 2023. 9. 13. 22:34
반응형

Context.startForegroundService()가 Service.startForeground()를 호출하지 않았습니다.

사용중입니다ServiceAndroid OS의 클래스입니다.

저는 사용할 계획입니다.Service

안드로이드 문서에는 다음과 같이 나와 있습니다.

앱이 API 레벨 26 이상을 목표로 하는 경우, 시스템은 앱 자체가 포그라운드에 있지 않는 한 백그라운드 서비스를 사용하거나 만드는 데 제한을 가합니다.앱이 포그라운드 서비스를 만들어야 할 경우, 앱은 다음을 호출해야 합니다.startForegroundService().

사용하는 경우startForegroundService() , , , , , , , , , , , , , , , , , , .Service는 다음 오류를 던집니다.

Context.startForegroundService() did not then call
Service.startForeground() 

이거 왜 이래요?

Android 8.0의 Google 문서에서 변경된 동작:

이 시스템은 앱이 백그라운드에 있는 동안에도 Context.startForegroundService()를 호출할 수 있습니다.그러나 앱은 서비스가 생성된 후 5초 이내에 해당 서비스의 startForeground() 메서드를 호출해야 합니다.

: 에게 전화하기 : startForeground()인에onCreate()을 위하여Service이 사용하는 ㅇㅇContext.startForegroundService()

참고 항목:Android 8.0(오레오)의 백그라운드 실행 제한

전화해봤어요.ContextCompat.startForegroundService(this, intent)그 때 서비스를 시작합니다.

onCreate

 @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}

이 문제가 발생하는 이유는 Android 프레임워크가 5초 이내에 서비스가 시작되도록 보장할 수는 없지만 프레임워크가 서비스를 시작하려고 했는지 확인하지 않고 5초 이내에 포그라운드 알림을 실행해야 한다는 엄격한 제한이 있기 때문입니다.

이는 분명 프레임워크 문제이지만, 이 문제에 직면한 모든 개발자가 최선을 다하고 있는 것은 아닙니다.

  1. startForeground는에야다 양쪽에 .onCreate그리고.onStartCommand, 서비스가 이미 생성되어 있고 활동이 다시 시작하려고 한다면,onCreate불리지 않을 겁니다

  2. 알림 ID가 0이 되어서는 안 됩니다. 그렇지 않으면 동일한 이유가 아니더라도 동일한 충돌이 발생합니다.

  3. stopSelf에안다는 앞에 할 수 .startForeground.

위의 3을 모두 사용하면 이 문제는 조금 줄어들 수 있지만 여전히 해결책은 아닙니다. 해결 방법은 목표 sdk 버전을 25로 다운그레이드하는 것입니다.

그리고 Google은 이 문제를 해결하지도 않을 것이며, 이 문제가 자신들의 잘못이라고 생각하지도 않기 때문에 Android P는 여전히 이 문제를 안고 있을 가능성이 높습니다. 자세한 내용은 #36과 #56을 참조하십시오.

이미 너무 많은 답변이 게시되어 있습니다. 하지만 사실은, 시작 포그라운드 서비스는 앱 수준에서 수정할 수 없으므로 사용을 중단해야 합니다.Context#startForegroundService()가 호출된 후 5초 이내에 Service#startForeground() API를 사용하라는 Google의 권장 사항은 앱에서 항상 수행할 수 있는 작업이 아닙니다.

안드로이드는 많은 프로세스를 동시에 실행하며 루퍼가 startForeground()를 호출해야 하는 대상 서비스를 5초 이내에 호출한다는 보장은 없습니다.대상 서비스가 5초 이내에 호출을 수신하지 못하면 운이 나빠지고 사용자는 ANR 상황을 경험하게 됩니다.스택 추적에서 다음과 같은 것을 볼 수 있습니다.

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}

main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
  | sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
  | state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
  | stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
  | held mutexes=
  #00  pc 00000000000712e0  /system/lib64/libc.so (__epoll_pwait+8)
  #01  pc 00000000000141c0  /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
  #02  pc 000000000001408c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
  #03  pc 000000000012c0d4  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
  at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
  at android.os.MessageQueue.next (MessageQueue.java:326)
  at android.os.Looper.loop (Looper.java:181)
  at android.app.ActivityThread.main (ActivityThread.java:6981)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)

루퍼가 여기 대기열을 분석한 결과 '학대자'를 발견하고 그냥 죽인 것으로 알고 있습니다.시스템은 현재 행복하고 건강한 반면 개발자들과 사용자들은 그렇지 않지만, 구글은 시스템에 대한 책임을 제한하고 있는데, 왜 그들이 후자의 두 가지에 신경을 써야 합니까?분명히 그들은 그렇지 않습니다.그들이 더 좋게 만들 수 있을까요?물론 사용자에게 앱을 기다리거나 삭제하는 것에 대한 결정을 요청하는 "애플리케이션이 사용 중입니다" 대화상자를 제공할 수도 있었지만, 왜 신경을 쓰는지는 그들의 책임이 아닙니다.가장 중요한 것은 시스템이 현재 건강하다는 것입니다.

제가 관찰한 바에 따르면, 이러한 일은 비교적 드물게 발생합니다. 제 경우 1K 사용자의 경우 한 달에 약 1번의 충돌이 발생합니다.재현이 불가능하고, 재현이 되더라도 영구적으로 고칠 수 있는 방법이 없습니다.

이 스레드에서는 "시작" 대신 "bind"를 사용하고 서비스가 준비되면 ServiceConnected를 사용하는 것이 좋습니다. 하지만 다시 말해 startForegroundService 호출을 전혀 사용하지 않는 것을 의미합니다.

구글 측의 적절하고 정직한 조치는 스타트업 포건드 서비스에 결함이 있으므로 사용해서는 안 된다는 것을 모두에게 말하는 것이라고 생각합니다.

그 대신 무엇을 사용할 것인가 라는 질문이 여전히 남아있습니다.다행스럽게도 포그라운드 서비스를 위한 더 나은 대안인 JobScheduler와 JobService가 있습니다.이 때문에 더 나은 선택입니다.

작업이 실행되는 동안 시스템은 앱을 대신하여 웨이크락을 유지합니다.이러한 이유로 장치가 작업 기간 동안 깨어 있도록 보장하기 위한 조치를 취할 필요가 없습니다.

더 이상 웨이크락 처리에 신경 쓸 필요가 없다는 뜻으로 전경 서비스와 다르지 않습니다.구현의 관점에서 JobScheduler는 당신의 서비스가 아니며, 시스템의 서비스이며, 아마도 대기열을 제대로 처리할 것이며, Google은 자신의 자녀를 절대 종료하지 않을 것입니다. :)

삼성은 삼성 액세서리 프로토콜(SAP)에서 시작 포그라운드 서비스(startForeground Service)에서 작업 스케줄러(Job Scheduler) 및 작업 서비스(JobService)로 전환했습니다.스마트워치와 같은 장치가 전화와 같은 호스트와 대화해야 할 때, 앱의 메인 스레드를 통해 사용자와 상호 작용할 필요가 있을 때 매우 유용합니다.스케줄러가 기본 스레드에 작업을 게시하므로 작업이 가능합니다.그러나 작업이 기본 스레드에서 실행되고 있으며 모든 무거운 작업을 다른 스레드 및 비동기 작업으로 오프로드해야 합니다.

이 서비스는 응용프로그램의 기본 스레드에서 실행되는 처리기에서 수신되는 각 작업을 실행합니다.실행 로직을 선택한 다른 스레드/핸들러/AsyncTask에 오프로드해야 합니다.

JobScheduler/JobService로 전환할 때 발생하는 유일한 문제점은 이전 코드를 재분류해야 한다는 것이며, 이는 재미가 없다는 것입니다.저는 지난 이틀 동안 삼성의 새 SAP 구현을 사용하기 위해 이 작업을 수행했습니다.제 충돌 보고서를 보고 충돌을 다시 보게 되면 알려드리겠습니다.이론적으로는 그런 일이 있어서는 안 되지만, 항상 우리가 모르는 세부사항들이 있습니다.

업데이트 더 이상 플레이 스토어에서 보고된 충돌은 없습니다.즉, JobScheduler/JobService는 그러한 문제가 없으며, 이 모델로 전환하는 것이 startForegroundService 문제를 영원히 제거할 수 있는 올바른 방법임을 의미합니다.Google/Android가 이를 읽고 최종적으로 모두에게 공식적인 지침을 제공하기를 바랍니다.

업데이트 2

SAP를 사용하고 SAP V2가 JobService를 어떻게 활용하는지 묻는 분들을 위해 아래와 같이 설명합니다.

맞춤형 코드에서 SAP를 초기화해야 합니다(Kotlin입니다).

SAAgentV2.requestAgent(App.app?.applicationContext, 
   MessageJobs::class.java!!.getName(), mAgentCallback)

이제 삼성의 코드를 해독해야 안에서 무슨 일이 일어나고 있는지 알 수 있습니다.SAAgentV2에서 requestAgent 구현과 다음 행을 살펴봅니다.

SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);

where d defined as below

private SAAdapter d;

지금 SAAAdapter 클래스로 이동하여 다음 호출을 사용하여 작업을 예약하는 ServiceConnectionRequested 기능을 찾습니다.

SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12); 

SAJobService는 Android'd JobService의 구현일 뿐이며 작업 스케줄링을 수행합니다.

private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
    ComponentName var7 = new ComponentName(var0, SAJobService.class);
    Builder var10;
    (var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
    PersistableBundle var8;
    (var8 = new PersistableBundle()).putString("action", var1);
    var8.putString("agentImplclass", var2);
    var8.putLong("transactionId", var3);
    var8.putString("agentId", var5);
    if (var6 == null) {
        var8.putStringArray("peerAgent", (String[])null);
    } else {
        List var9;
        String[] var11 = new String[(var9 = var6.d()).size()];
        var11 = (String[])var9.toArray(var11);
        var8.putStringArray("peerAgent", var11);
    }

    var10.setExtras(var8);
    ((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
}

보시는 것처럼 여기 마지막 줄은 Android'd Job Scheduler를 사용하여 시스템 서비스를 받고 작업을 예약합니다.

요청 에이전트 호출에서 중요한 이벤트가 발생했을 때 제어를 받는 콜백 기능인 mAgentCallback을 전달했습니다.내 앱에서 콜백이 정의되는 방법은 다음과 같습니다.

private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
    override fun onAgentAvailable(agent: SAAgentV2) {
        mMessageService = agent as? MessageJobs
        App.d(Accounts.TAG, "Agent " + agent)
    }

    override fun onError(errorCode: Int, message: String) {
        App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
    }
}

여기 있는 MessageJobs는 삼성 스마트워치에서 오는 모든 요청을 처리하기 위해 구현한 클래스입니다.완전한 코드가 아니라 뼈대일 뿐입니다.

class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {


    public fun release () {

    }


    override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
        super.onServiceConnectionResponse(p0, p1, p2)
        App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)


    }

    override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
        super.onAuthenticationResponse(p0, p1, p2)
        App.d(TAG, "Auth " + p1.toString())

    }


    override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {


        }
    }

    override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
    }

    override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
        super.onError(peerAgent, errorMessage, errorCode)
    }

    override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {

    }

}

보시다시피, MessageJobs에는 MessageSocket 클래스도 필요합니다. 이 클래스는 장치에서 전송되는 모든 메시지를 처리하고 구현해야 합니다.

결론적으로, 이 작업은 그리 간단하지 않고 내부를 파고들어 코딩을 해야 하지만 효과가 있으며 가장 중요한 것은 충돌이 일어나지 않는다는 것입니다.

이 ㅇㅇㅇㅇ을 부르면 의 앱이 입니다.Context.startForegroundService(...)그 다음에 전화해요.Context.stopService(...)에 앞에Service.startForeground(...)라고 합니다.

여기 전경 서비스 A를 선명하게 재현해 놓았습니다.PI26

다음 위치에서 버그를 열었습니다: Google 문제 추적기

이에 대한 몇 가지 버그가 열렸다 닫혔습니다. Won't Fix.

선명한 재생 단계의 나의 것이 잘 어울리기를 바랍니다.

Google팀에서 제공하는 정보

구글 발행 추적기 댓글 36

이것은 프레임워크 버그가 아니라 의도적인 것입니다.우로 시작하는 경우startForegroundService(), 해당 서비스 인스턴스를 포그라운드 상태로 전환하고 알림을 표시해야 합니다.서비스 인스턴스가 이전에 중지된 경우startForeground()호출되고 약속이 이행되지 않습니다. 이것은 앱의 버그입니다.

Re#31, 다른 앱이 직접 시작할 수 있는 서비스를 게시하는 것은 근본적으로 안전하지 않습니다.해당 서비스의 모든 시작 작업을 필요한 것으로 간주하여 이를 조금 완화할 수 있습니다.startForeground()도 있지만, 그것은 분명히 당신이 생각했던 것이 아닙니다

구글 발행 추적기 댓글 56

동일한 결과를 초래하는 몇 가지 시나리오가 있습니다.

명백한 의미론적인 문제, 그것은 단순히 무엇인가를 시작하는 것이 오류라는 것입니다.startForegroundService()하지만 그것을 실제로는 다음을 통해 전경으로 전환하는 것을 무시합니다.startForeground()입니다. 문제입니다. , 은 인 입니다 입니다 인 은 .그것은 의도적으로 앱 버그로 취급됩니다.서비스를 포그라운드로 전환하기 전에 중지하는 것은 앱 오류입니다.그것이 OP의 핵심이었고, 이것이 이 문제가 "의도대로 작동"하는 것으로 표시된 이유입니다.

그러나 이 문제를 허위로 탐지하는 것에 대해서도 의문이 제기되고 있습니다.그것은 진짜 문제로 취급되고 있지만, 이 특정 버그 추적 문제와는 별개로 추적되고 있습니다.우리는 그 불평에 귀를 기울이지 않습니다.

이곳을 방문하는 모든 사람들이 같은 고통을 받고 있기 때문에, 저는 (어쨌든 이 질문에서는) 다른 사람들이 시도하지 않았던 저의 해결책을 공유하고 싶습니다.이 방법을 확인하는 정지된 중단점에서도 작동하고 있음을 확신할 수 있습니다.

문제는 전화하는 것입니다.Service.startForeground(id, notification)서비스 자체에서 말이죠? Framework는도 Android Framework는걸는이다수를다이o는dlet도k수dsynkService.startForeground(id, notification)에 이내에Service.onCreate()던지니까초만에 예외를 두게 돼서 이렇게 생각해봤습니다.

  1. 를 호출하기 에 바인딩합니다.Context.startForegroundService()
  2. 하면 이 하면 을 합니다 합니다 을 하면 이 Context.startForegroundService() 서비스 연결을 통해 즉시 전화를 바랍니다.Service.startForeground() 서비스 연결 내부에 있습니다.
  3. 중요 참고: 전화 문의Context.bindService()호출이 예외를 발생시킬 수 있기 때문에 시도해 볼 수 있는 방법입니다. 이 경우 호출에 의존해야 합니다.Context.startForegroundService()직접적으로 실패하지 않기를 바랍니다.예를 들어 브로드캐스트 수신기 컨텍스트를 들 수 있지만, 애플리케이션 컨텍스트를 얻는 것이 그러한 경우에는 예외가 발생하지 않지만 컨텍스트를 직접 사용하는 것이 가능합니다.

서비스를 바인딩한 후 "시작 포그라운드" 호출을 시작하기 전에 중단점에서 대기하고 있을 때도 작동합니다.3-4초 사이의 대기는 예외를 트리거하지 않으며 5초 후에는 예외를 트리거합니다.(장치가 5초 안에 두 줄의 코드를 실행할 수 없다면, 그것을 쓰레기통에 버릴 때입니다.)

먼저 서비스 연결을 만드는 것부터 시작합니다.

// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        // The binder of the service that returns the instance that is created.
        MyService.LocalBinder binder = (MyService.LocalBinder) service;

        // The getter method to acquire the service.
        MyService myService = binder.getService();

        // getServiceIntent(context) returns the relative service intent 
        context.startForegroundService(getServiceIntent(context));

        // This is the key: Without waiting Android Framework to call this method
        // inside Service.onCreate(), immediately call here to post the notification.
        myService.startForeground(myNotificationId, MyService.getNotification());

        // Release the connection to prevent leaks.
        context.unbindService(this);
    }

    @Override
    public void onBindingDied(ComponentName name)
    {
        Log.w(TAG, "Binding has dead.");
    }

    @Override
    public void onNullBinding(ComponentName name)
    {
        Log.w(TAG, "Bind was null.");
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        Log.w(TAG, "Service is disconnected..");
    }
};

서비스 내부에 서비스 인스턴스를 반환하는 바인더를 작성합니다.

public class MyService extends Service
{
    public class LocalBinder extends Binder
    {
        public MyService getService()
        {
            return MyService.this;
        }
    }

    // Create the instance on the service.
    private final LocalBinder binder = new LocalBinder();

    // Return this instance from onBind method.
    // You may also return new LocalBinder() which is
    // basically the same thing.
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return binder;
    }
}

그런 다음 해당 컨텍스트에서 서비스를 바인딩해 보십시오.성공하면 다음이 나옵니다.ServiceConnection.onServiceConnected()사용 중인 서비스 연결을 통한 메소드입니다.그런 다음 위에 나와 있는 코드에 있는 논리를 처리합니다.예제 코드는 다음과 같습니다.

// Try to bind the service
try
{
     context.bindService(getServiceIntent(context), connection,
                    Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
     // This is probably a broadcast receiver context even though we are calling getApplicationContext().
     // Just call startForegroundService instead since we cannot bind a service to a
     // broadcast receiver context. The service also have to call startForeground in
     // this case.
     context.startForegroundService(getServiceIntent(context));
}

제가 개발하는 어플리케이션에서 작동하는 것 같으니, 시도해보시면 작동할 것입니다.

이제 Android O에서는 아래와 같이 배경 제한을 설정할 수 있습니다.

서비스 클래스를 호출하는 서비스

Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    SettingActivity.this.startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

그리고 서비스 클래스는 다음과 같습니다.

public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}

장치가 깨어있을 때 비교적 자주 업데이트되는 위젯이 있는데 며칠 만에 수천 번의 충돌이 발생했습니다.

이슈 트리거

심지어 픽셀 3 XL에서도 장치에 부하가 많이 걸릴 것이라고 생각하지 않았을 때 그 문제를 알아차렸습니다.고든드은두두은hdd드e든고ysedlstartForeground(). 하지만 저는 많은 경우에 제 서비스가 일을 정말 빨리 끝낸다는 것을 깨달았습니다.제 앱의 계기는 시스템이 알림을 표시하기 전에 서비스가 완료되고 있었기 때문이라고 생각합니다.

해결 방법/해결 방법

모든 사고를 없앨 수 있었습니다. 제가 일은 에 대한 호출을 제거하는 것이었습니다. (저는 알림이 표시되는 것이 확실할 때까지 정지를 연기할까 생각 중이었는데, 필요하지 않으면 사용자가 알림을 보지 않았으면 합니다.)서비스가 1분간 유휴 상태이거나 시스템이 예외 없이 정상적으로 서비스를 파괴하는 경우.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    stopForeground(true);
} else {
    stopSelf();
}

저는 이 문제를 해결해야 할 일이 있습니다.저는 이러한 종류의 충돌을 최소 95% 줄일 수 있는 제 앱(300K+ DAU)에서 이 수정 사항을 확인했지만, 여전히 이 문제를 100% 피할 수는 없습니다.

이 문제는 Google이 문서화한 대로 서비스가 시작된 직후에 startForeground()를 호출하는 경우에도 발생합니다.많은 시나리오에서 서비스 생성 및 초기화 과정에 이미 5초 이상의 비용이 소요되었기 때문일 수도 있습니다. 그러면 언제 어디서 startForeground() method를 호출하든 이러한 충돌은 피할 수 없습니다.

제 솔루션은 서비스를 생성하고 초기화해야 하는 시간에 상관없이 startForeground()가 startForeground Service() 메서드 후 5초 이내에 실행되도록 하는 것입니다.자세한 해결 방법은 다음과 같습니다.

  1. 처음부터 startForegroundService를 사용하지 말고 bindService()를 auto_create 플래그와 함께 사용합니다.서비스 초기화를 기다립니다.여기 코드가 있습니다. 제 샘플 서비스는 뮤직 서비스입니다.

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
                }
            }
            applicationContext.unbindService(this);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }, Context.BIND_AUTO_CREATE);
    
  2. 다음은 MusicBinder 구현입니다.

    /**
     * Use weak reference to avoid binder service leak.
     */
     public class MusicBinder extends Binder {
    
         private WeakReference<MusicService> weakService;
    
         /**
          * Inject service instance to weak reference.
          */
         public void onBind(MusicService service) {
             this.weakService = new WeakReference<>(service);
         }
    
         public MusicService getService() {
             return weakService == null ? null : weakService.get();
         }
     }
    
  3. 가장 중요한 부분인 MusicService 구현, forceForeground() 메서드는 startForeground() 메서드가 서비스 시작 직후에 호출되도록 보장합니다.

    public class MusicService extends MediaBrowserServiceCompat {
    ...
        private final MusicBinder musicBind = new MusicBinder();
    ...
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        }
    ...
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after service start.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
    
  4. 앱을 열지 않고 위젯(위젯 버튼 클릭)에서 전경 서비스를 시작하려는 경우와 같이 보류 중인 의도에서 1단계 코드 조각을 실행하려면 코드 조각을 방송 수신기에 래핑하고 시작 서비스 명령 대신 방송 이벤트를 발생시킬 수 있습니다.

그게 다야.도움이 되길 바랍니다.행운을 빌어요.

제가 너무 많은 시간을 낭비했기 때문에 미리 말씀드립니다.제가 전화를 했는데도 이 예외가 계속 발생했습니다.startForeground(..)제일 먼저onCreate(..)는 . 는 으로 한다는 을 가 을 한다는 는 가 으로 NOTIFICATION_ID = 0하면 이문제가 되는 것 . 다른 값을 사용하면 이 문제가 해결되는 것 같습니다.

target sdk 28 이상을 사용할 경우 안드로이드 9 디바이스에 대해 아래와 같이 권한을 추가해야 합니다. 그렇지 않으면 항상 예외가 발생합니다.

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

내 경우엔 답이 너무 많은데 하나도 안 통했어요.저는 이런 서비스를 시작했습니다.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent);
} else {
    startService(intent);
}

제가 근무할 때는 onStartCommand에서

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
      .setContentTitle(getString(R.string.app_name))
      .setContentText("SmartTracker Running")
      .setAutoCancel(true);
   Notification notification = builder.build();
   startForeground(NOTIFICATION_ID, notification);
} else {
   NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
      .setContentTitle(getString(R.string.app_name))
      .setContentText("SmartTracker is Running...")
      .setPriority(NotificationCompat.PRIORITY_DEFAULT)
      .setAutoCancel(true);
   Notification notification = builder.build();
   startForeground(NOTIFICATION_ID, notification);
}

그리고 NOTIFY_ID를 0이 아닌 것으로 설정하는 것도 잊지 마세요.

private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;

그래서 모든 것이 완벽했지만 여전히 8.1에서 충돌하고 있어 원인은 아래와 같습니다.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   stopForeground(true);
} else {
   stopForeground(true);
}

제거 알림과 함께 정지 포그라운드를 호출했는데 알림이 제거되면 백그라운드 서비스가 되고 푸시 수신 후 시작된 안드로이드 O에서 백그라운드 서비스를 실행할 수 없습니다.

그래서 마법같은 단어는

stopSelf();

지금까지 어떤 이유로든 귀하의 서비스는 중단되고 있습니다. 위의 모든 단계를 수행하고 즐기십시오.

이 오류는 Android 8+에서도 ID0으로 설정된 상태에서 Service.startForeground(intid, Notification notification)가 호출될 때 발생합니다.

idint: NotificationManager.notify(int, Notification)에 따라 이 알림에 대한 식별자. 0이 되어서는 안 됩니다.

onCreate() 메서드에 StartForgroundServices를 호출하지 마십시오. 작업자 스레드를 만든 후 onStartCommand()에서 StartForgroundServices를 호출해야 합니다. 그렇지 않으면 항상 ANR이 발생하므로 onStartCommand()의 메인 스레드에 복잡한 로그인을 쓰지 마십시오.

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(1, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(1, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

편집: 주의! startForeground 함수는 0을 첫 번째 인수로 사용할 없습니다. 예외가 발생합니다! 이 예제에는 잘못된 함수 호출이 포함되어 있습니다. 0을 0이거나 Max(Int32)보다 클 수 없는 자신의 상수로 변경하십시오.

약 10명의 사용자들이 우리 애플리케이션의 충돌 분석에서 이 오류를 겪고 있습니다.

enter image description here

기미추가 대답한 것처럼, 이 문제의 가장 큰 원인은 전경으로 승격되기 전에 서비스가 중단되었기 때문입니다.하지만 그 주장은 서비스가 파괴된 후에도 멈추지 않았습니다.startForegroundService-Kimi Chiu를 호출한 후 StopService를 추가하여 재현을 시도할 수 있습니다.

그래서 테스트를 해봤는데 재현이 가능했습니다.

제가 적용한 한 가지 해결책은 서비스가 전면적으로 홍보될 수 있도록 최소 5초 이상 서비스를 유지하는 것입니다.그리고 지금은 테스트 중에 문제를 재현할 수 없습니다.

private fun stopService() {

        lifecycleScope.launch {
            delay(5000L)
            
            try {
                stopForeground(true)
                isForeGroundService = false
                stopSelf()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

다음 빌드에서 문제가 재현되는지 확인해 보겠습니다.

업데이트 :)-> 이번에는 Context.startForegroundService()가 Service.startForeground()를 호출하지 않았습니다.

enter image description here

비교전/후->

전->enter image description here

After-> enter image description here

저는 이 문제를 조사해왔고 이것이 제가 지금까지 발견한 것입니다.이와 유사한 코드를 가진 경우 이 충돌이 발생할 수 있습니다.

My Foreground Service.java

public class MyForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
    }
}

주 활동.java

Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);

예외는 코드의 다음 블록에 던져집니다.

ActiveServices.java

private final void bringDownServiceLocked(ServiceRecord r) {
    ...
    if (r.fgRequired) {
        Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                  + r);
        r.fgRequired = false;
        r.fgWaiting = false;
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            msg.obj = r.app;
            msg.getData().putCharSequence(
                ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
            mAm.mHandler.sendMessage(msg);
         }
    }
    ...
}

은 는 에 됩니다 됩니다 전에 됩니다.onCreate()MyForegroundService안드로이드는 메인 쓰레드 핸들러에 서비스 생성 일정을 잡지만bringDownServiceLocked부름을 받습니다.BinderThread 조건입니다 , , 은 입니다 입니다 은 .은 ㅇㅇ을 합니다.MyForegroundServicestartForeground추락의 원인이 될 겁니다

이 문제를 해결하기 위해서는bringDownServiceLocked지음 앞에 .onCreate()MyForegroundService.

public class MyForegroundService extends Service {

    private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";

    private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.removeStickyBroadcast(intent);
            stopForeground(true);
            stopSelf();
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
        registerReceiver(
            stopReceiver, new IntentFilter(ACTION_STOP));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(stopReceiver);
    }

    public static void stop(Context context) {
        context.sendStickyBroadcast(new Intent(ACTION_STOP));
    }
}

끈적끈적한 방송을 통해서 저희가 방송을 잃어버리지 않도록 하고 또.stopReceiver에자를다다를에 등록되는 .onCreate()MyForegroundService. 지금쯤 우리는 이미 전화를 했습니다.startForeground(...)을 받지 . 수신기가 다음에 알려지는 것을 막기 위해 끈적거리는 방송도 없애야 합니다.

방법은 주의하시기 바랍니다.sendStickyBroadcast사용하지 않습니다. 이 문제를 해결하기 위한 임시 해결책으로만 사용합니다.

를 부른 에도.startForeground인에Service, 우리가 전화를 걸면 몇몇 기기에서 충돌이 발생합니다.stopServiceonCreate라고 합니다.그래서 추가 플래그를 사용하여 서비스를 시작함으로써 이 문제를 해결했습니다.

Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);

StartCommand가 중지되기 시작했는지 확인하기 위해 StartCommand에 체크인을 추가했습니다.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    //call startForeground first
    if (intent != null) {
        boolean stopService = intent.getBooleanExtra("request_stop", false);
        if (stopService) {
            stopSelf();
        }
    }

    //Continue with the background task
    return START_STICKY;
}

P.S. 서비스가 실행되고 있지 않으면 오버헤드인 서비스를 먼저 시작할 것입니다.

Android 12에 대한 Google 문서의 동작 변경 내용:

To provide a streamlined experience for short-running foreground services on Android 12, the system can delay the display of foreground service notifications by 10 seconds for certain foreground services. This change gives short-lived tasks a chance to complete before their notifications appear.

솔루션: Context.startForegroundService()를 사용하는 서비스에 대해 onCreate()의 startForeground()를 호출합니다.

https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)

startService(Intent)와 유사하지만, 서비스가 startForeground(int, Android.app)를 호출한다는 암묵적인 약속과 함께.알림) 실행을 시작하면 실행됩니다.서비스는 ANR 간격에 상응하는 시간이 주어지며, 그렇지 않으면 시스템이 자동으로 서비스를 중지하고 앱 ANR을 선언합니다.

이 방식은 일반적인 시작 서비스(Intent)와 달리 서비스를 호스팅하는 앱이 포그라운드 상태인지 여부와 관계없이 언제든지 사용할 수 있습니다.

꼭 전화해 보세요.Service.startForeground(int, android.app.Notification)호출할 수 있도록 에 설정합니다 만약 당신그렇게 하는 있다면 상태보통사용하는어떤 있는 좋습니다 당신 방해 the 사용하는 좋습니다 off create보통 using better that ) if on 있도록 ( 호출설정합니다 condition you you any that 만약 have , prevent from doing ' may then d 당신당신 you normal 그렇게 하는 , 있다면 상태방해어떤Context.startService(Intent)전화를 걸어요Service.startForeground(int, android.app.Notification)네 자신.

는 것 .Context.startForegroundService() 기능을 가 " " " " " " " " " 를 호출했는지 확인합니다.Service.startForeground(int, android.app.Notification)파괴되기 전에...

저도 같은 문제에 직면해 있고 시간을 들여 해결책을 찾은 후 아래 코드로 시도해 볼 수 있습니다.이 ㅇㅇ을 사용한다면Service using이 Create you using합니다 using에 using를에 입력합니다.Intent ServiceHandleIntent에 이 코드를 입력합니다.

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }

Android O API 26의 문제

서비스를 즉시 중지해도(따라서 실제로 서비스가 실행되지 않고(문장/이해력) ANR 간격보다 훨씬 아래에 있는 경우에도 Self를 중지하기 전에 startForeground를 호출해야 합니다.

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

이 접근 방식을 시도했지만 여전히 오류가 발생합니다.

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

오류가 해결될 때까지 이 기능을 사용합니다.

mContext.startService(playIntent);

의 데이터 업데이트 onStartCommand(...)

Bind(...)에

onBind(...)하기에더은클다다클을 시작하기에 더 나은 입니다.startForegroundonCreate(...)onBind(...)Intent그것은 중요한 데이터를 포함할 수 있습니다.Bundle에를 하기 위해 Service단, . 은, 하지 , 은은 .onStartCommand(...)를 부를 때 호출됩니다.Service를 처음 생성하거나 그 이후에 호출합니다.

시작 명령(...)에

startForeground인에onStartCommand(...)를는기다합니다를 합니다.Service이미 생성된 상태에서

ContextCompat.startForegroundService(...)는 a을서름의a 뒤에 부릅니다.Service되었습니다onBind(...)그리고.onCreate(...)불리지 않습니다.따라서 업데이트된 데이터를 다음과 같이 전달할 수 있습니다.onStartCommand(...)로를 Intent BundleService.

견본

이 하여 Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ ΔPlayerNotificationManager코인버스 암호화폐 뉴스 앱에서.

활동 / Fragment.kt

context?.bindService(
        Intent(context, AudioService::class.java),
        serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
        context!!,
        Intent(context, AudioService::class.java).apply {
            action = CONTENT_SELECTED_ACTION
            putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                audioUrl = uri.toString()
            })
        })

오디오 서비스.kt

private var uri: Uri = Uri.parse("")

override fun onBind(intent: Intent?) =
        AudioServiceBinder().apply {
            player = ExoPlayerFactory.newSimpleInstance(
                    applicationContext,
                    AudioOnlyRenderersFactory(applicationContext),
                    DefaultTrackSelector())
        }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    intent?.let {
        when (intent.action) {
            CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                val intentUri = Uri.parse(content.audioUrl)
                // Checks whether to update Uri passed in Intent Bundle.
                if (!intentUri.equals(uri)) {
                    uri = intentUri
                    player?.prepare(ProgressiveMediaSource.Factory(
                            DefaultDataSourceFactory(
                                    this,
                                    Util.getUserAgent(this, getString(app_name))))
                            .createMediaSource(uri))
                    player?.playWhenReady = true
                    // Calling 'startForeground' in 'buildNotification(...)'.          
                    buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
                }
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
    playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            content.title,
            app_name,
            if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
            object : PlayerNotificationManager.MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player?) = ...
                override fun getCurrentContentText(player: Player?) = ...
                override fun getCurrentContentTitle(player: Player?) = ...
                override fun getCurrentLargeIcon(player: Player?,
                                                 callback: PlayerNotificationManager.BitmapCallback?) = ...
            },
            object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                    startForeground(notificationId, notification)
                }
                override fun onNotificationCancelled(notificationId: Int) {
                    stopForeground(true)
                    stopSelf()
                }
            })
    return playerNotificationManager.setPlayer(player)
}

다른 사람들에게도 도움이 될만한 무언가를 발견했습니다이것은 제가 보고 있는 문제를 해결할 수 있는 방법을 찾을 수 있는지 테스트하는 것과는 완전히 다릅니다.간단히 하기 위해 발표자로부터 이것을 부르는 방법이 있다고 가정해 보겠습니다.

context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}       

이는 동일한 오류와 함께 충돌합니다.메소드가 완료될 때까지 서비스가 시작되지 않습니다. 따라서 아니오onCreate()

따라서 메인 스레드에서 UI를 업데이트해도 해당 방법 이후에 해당 방법을 유지할 수 있는 것이 있으면 제 시간에 시작되지 않고 무서운 전경 오류를 제공됩니다.제 경우엔 대기열에 물건을 싣고 각자 전화를 걸었습니다startForegroundService , 은 의 에 되어 되어 에 의 은 .이 방법을 완료하는 데 논리적으로 너무 오랜 시간이 걸렸을 경우에는 충돌 시간이 필요합니다.startService그냥 그냥 무시하고 그대로 가고 매번 불렀으니까 다음 라운드는 끝날 거예요.

이로 인해 백그라운드에서 서비스를 호출하면 시작 시 완전히 바인딩되지 않고 바로 실행될 수 없을까 하는 의문이 들어 실험을 시작했습니다.즉시 시작되지 않더라도 충돌하지는 않습니다.

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

메인 스레드가 적시에 처리할 수 있을 때까지 기다릴 수밖에 없다고 생각하지만, 왜 충돌하지 않는지 아는 척하지 않을 것입니다.메인 스레드에 묶는 것이 이상적이지 않다는 것을 알고 있지만, 사용법이 백그라운드에서 호출하기 때문에 충돌보다는 완료될 때까지 기다려도 상관이 없습니다.

@humazed 답변에 코드를 추가합니다.그래서 최초 통보는 없었습니다.그것은 해결책일 수도 있지만 저에게는 효과가 있습니다.

@Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("")
                    .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                    .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();

            startForeground(1, notification);
        }
}

작은 아이콘에 투명 컬러와 알림에 컬러를 추가합니다.그건 작동할 것이다.

Android Manifest 파일에서 서비스 클래스가 활성화되어 있지 않다는 문제가 있을 수 있습니다.확인도 부탁드립니다.

<service
        android:name=".AudioRecorderService"
        android:enabled="true"
        android:exported="false"
        android:foregroundServiceType="microphone" />

픽셀 3, 안드로이드 11에서 서비스가 매우 짧을 때 전경 알림이 해제되지 않는 문제가 있었습니다.

정지하기 전에 100ms 지연을 추가하면 포그라운드() 정지 Self()가 도움이 될 것 같습니다.

사람들은 여기에 foreground()를 호출한 후 self()를 중지해야 한다고 적습니다.확인할 수는 없지만, 굳이 그렇게 할 필요는 없겠군요.

public class AService extends Service {

@Override
public void onCreate() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        startForeground(
            getForegroundNotificationId(),
            channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText()),
            ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
    } else {
        startForeground(getForegroundNotificationId(),
            channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText())
        );
    }

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    startForeground();

    if (hasQueueMoreItems()) {
        startWorkerThreads();
    } else {
        stopForeground(true);
        stopSelf();
    }
    return START_STICKY;
}

private class WorkerRunnable implements Runnable {

    @Override
    public void run() {

        while (getItem() != null && !isLoopInterrupted) {
            doSomething(getItem())   
        }

        waitALittle();
        stopForeground(true);
        stopSelf();
    }

    private void waitALittle() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

저는 그냥 확인합니다.PendingIntent 를 에 null 또는 notcontext.startForegroundService(service_intent)기능.

이것은 나에게 알맞습니다.

PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
            context.startForegroundService(service_intent);
        }
        else
        {
            context.startService(service_intent);
        }
}

Service 또는 IntentService가 생성된 후 즉시 startForeground 메서드를 호출합니다.다음과 같이:

import android.app.Notification;
public class AuthenticationService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1,new Notification());
    }
}

서비스를 시작할 때의 문제를 해결했습니다.startService(intent)에 대신에Context.startForeground() ㅇㅇ에게 전화를 .startForegound()super.OnCreate()시할 수 . 또한 부팅 시 서비스를 시작하는 경우 부팅 브로드캐스트에서 서비스를 시작하는 Activity를 시작할 수 있습니다.비록 이것이 영구적인 해결책은 아니지만, 효과는 있습니다.

저는 이에 대한 제 후기를 공유할 뿐입니다.위 코드가 저와 다른 남자들에게도 작동하지 않는다는 것을 확신할 수 없지만(100% 알려드립니다) 가끔 이 문제가 발생했습니다.내가 앱을 10번 실행하고 나서문제를 2~3번 받을있다고 가정합니다.

저는 무엇보다도 그 문제를 해결하려고 노력했지만 여전히 해결하지 못하고 있습니다.저는 무엇보다도 코드를 구현하고 다른 API 레벨(API 레벨 26, 28, 29)과 Different Mobile(삼성, 샤오미, MIUI, 비보, 모토, 원플러스, 화웨이 등)에서 테스트를 해보았고, 아래 이슈에서 동일하게 되었습니다.

Context.startForegroundService() did not then call Service.startForeground();

구글 개발자 웹사이트의 서비스, 다른 블로그와 스택 오버플로 질문을 읽었고, 우리가 전화를 걸었을 때 이 문제가 발생할 것이라는 생각을 들었습니다.startForgroundSerivce()method 하지만 그 때 서비스가 시작되지 않았습니다.

제 경우에는 서비스를 중단하고 즉시 서비스를 시작해야 합니다.아래 힌트입니다.

....//some other code
...// API level and other device auto star service condition is already set
stopService();
startService();
.....//some other code

이 및 가 시작되지 Δ RAM Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ,startForegroundService()method가 호출되고 예외가 발생합니다.

Work for me:

new Handler().postDelayed(()->ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);

코드를 변경하고 500밀리초 지연을 call startService() method로 설정하면 문제가 해결됩니다.이 방법으로 앱의 성능이 저하되기 때문에 완벽한 해결책은 아닙니다.

Note:이것은 전경 및 배경 서비스 전용입니다.Bind 서비스를 사용할 때는 테스트하지 마십시오.제가 이 문제를 해결한 방법이 이것이기 때문에 공유합니다.

언급URL : https://stackoverflow.com/questions/44425584/context-startforegroundservice-did-not-then-call-service-startforeground

반응형