Context.startForegroundService() 仅在 Android 11 Samsung 设备上没有调用 Service.startForeground()

Posted

技术标签:

【中文标题】Context.startForegroundService() 仅在 Android 11 Samsung 设备上没有调用 Service.startForeground()【英文标题】:Context.startForegroundService() did not then call Service.startForeground() only on Android 11 Samsung devices 【发布时间】:2021-05-31 18:47:00 【问题描述】:

我只在运行 android 11 的三星设备上遇到此崩溃。显然该应用程序正在调用 startForegroundService(intent) 并要求我发布通知让用户知道我正在运行前台服务,但此调用是对 @987654322 @从未在应用程序源代码中制作,三星是否有可能制作了android 11的自定义实现并在我调用startService(intent)时自动调用startForegroundService(intent)

堆栈跟踪

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord4ab4323 u0 app package name/library package name.player.PlayerService
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2240)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loop (Looper.java:246)
android.app.ActivityThread.main (ActivityThread.java:8506)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)

我使用context.startService(intent)启动服务,服务在应用的OnResume中启动,上下文就是应用上下文。

这也是服务在清单中的声明方式

 <service
            android:name=".player.PlayerService"
            android:exported="false"
            android:foregroundServiceType="mediaPlayback"
            android:stopWithTask="false">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
  </service>

更新:我找到了调用 startForegroundService(Intent) 的原因,我正在使用来自 android 的以下接收器来帮助处理诸如耳机按钮之类的操作控制设备,所以自从我将应用程序转换为 androidx 后,它开始使用新的MediaButtonReceiver

 <receiver android:name="androidx.media.session.MediaButtonReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
 </receiver>

这是Receiver收到事件时执行的代码

@Override
    public void onReceive(Context context, Intent intent) 
        if (intent == null
                || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) 
            Log.d(TAG, "Ignore unsupported intent: " + intent);
            return;
        
        ComponentName mediaButtonServiceComponentName =
                getServiceComponentByAction(context, Intent.ACTION_MEDIA_BUTTON);
        if (mediaButtonServiceComponentName != null) 
            intent.setComponent(mediaButtonServiceComponentName);
            startForegroundService(context, intent);
            return;
        
        ComponentName mediaBrowserServiceComponentName = getServiceComponentByAction(context,
                MediaBrowserServiceCompat.SERVICE_INTERFACE);
        if (mediaBrowserServiceComponentName != null) 
            PendingResult pendingResult = goAsync();
            Context applicationContext = context.getApplicationContext();
            MediaButtonConnectionCallback connectionCallback =
                    new MediaButtonConnectionCallback(applicationContext, intent, pendingResult);
            MediaBrowserCompat mediaBrowser = new MediaBrowserCompat(applicationContext,
                    mediaBrowserServiceComponentName, connectionCallback, null);
            connectionCallback.setMediaBrowser(mediaBrowser);
            mediaBrowser.connect();
            return;
        
        throw new IllegalStateException("Could not find any Service that handles "
                + Intent.ACTION_MEDIA_BUTTON + " or implements a media browser service.");
    

你可以看到它实际上启动了一个前台服务,我还在调查崩溃但至少我知道应用程序中启动了一个前台服务。

此外,这次崩溃不仅像我想的那样发生在三星,crashlytics 报告分组三星一起崩溃是因为它是来自 android 平台的崩溃,同一崩溃的其他实例发生的频率较低,因此它们在崩溃列表中的排名靠后在火力基地上。

【问题讨论】:

嘿,我也面临同样的问题。你有解决办法吗? @vishusrivastava 您正在构建媒体播放器应用程序吗? 是的,应用中有播客功能。 您只需要确保您的应用程序在前台服务启动时发布通知。就像大多数答案所说的那样。 我的情况有点不同,因为我没有在应用程序中启动前台服务,但是当单击耳机的播放按钮并且我的应用程序是 androidx.media.session.MediaButtonReceiver 调用前台时最后一个播放音频的应用。 【参考方案1】:

更新

可能的原因

在您提供更多代码后,您是否在PlayerService 中调用startForeground() 仍不清楚。看起来你没有,这就是你看到崩溃的原因。另一方面,如果此崩溃仅发生在 Android 11 上,那么您可能还会遇到不同的问题。

示例代码

如果没有调用 startForeground(),我刚刚制作了一个简单的应用程序来重现 PlayerService 的崩溃。或许对你有帮助。

app/build.gradle

添加依赖以使用 androidx MediaButtonReceiver

...
implementation "androidx.media:media:1.2.1" 
...

AndroidManifest.xml

...
    <service
        android:name=".PlayerService"
        android:exported="false"
        android:foregroundServiceType="mediaPlayback"
        android:stopWithTask="false">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </service>

    <receiver android:name="androidx.media.session.MediaButtonReceiver">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </receiver>   
...

MainActivity.java

如下模拟媒体按钮事件

...
@Override
protected void onResume() 
    super.onResume();

    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    intent.setClass(this, MediaButtonReceiver.class);
    intent.putExtra(Intent.EXTRA_KEY_EVENT, "test");
    sendBroadcast(intent);

... 

PlayerService.java

public class PlayerService extends Service 

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        MediaButtonReceiver.handleIntent(null, intent);

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return null;
    

修复

通过将startForeground() 添加到onCreate(),问题得到解决。请注意,我准备参数和使用方法的方式只是草稿。详细信息请查看官方文档here。

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

        Notification testNotification = new Notification();
        int testId = 1;
        startForeground(testId, testNotification);
    
...

【讨论】:

【参考方案2】:

对于一些需要做工作的前台服务需要访问locationmicrophonemediaProjectionphoneCallcameradataSyncconnectedDevicemediaPlayback,必须在清单中指定"foregroundServiceType",并在服务中调用startForeground 时为服务设置ServiceInfo。我认为这可以解决您的问题。

编辑

您应该在您的服务的onStartCommand 中像这样开始您的service

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q)
            startForeground(NOTIFICATION_ID, notification)
        
        else
            startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
        

你做到了吗?

【讨论】:

以上是关于Context.startForegroundService() 仅在 Android 11 Samsung 设备上没有调用 Service.startForeground()的主要内容,如果未能解决你的问题,请参考以下文章