为啥在 Android Oreo 上关闭屏幕时前台服务停止?

Posted

技术标签:

【中文标题】为啥在 Android Oreo 上关闭屏幕时前台服务停止?【英文标题】:Why foreground service stops when screen is off on Android Oreo?为什么在 Android Oreo 上关闭屏幕时前台服务停止? 【发布时间】:2019-05-07 12:41:25 【问题描述】:

当我关闭屏幕时,android Oreo 会停止我的前台服务。当设备被拔下时,它会发生。我在华为 MediaPad T5 上测试我的应用程序。我使用 Handler.postDelayed 每 30 秒发送一次测试请求。

我读到了 Android 8 中的后台执行限制。在迁移指南中,该前台服务应该可以工作。我不能使用 JobScheduler、JobIntenService 或 WorkManager,因为它们只能每 15 分钟重复一次。

我无法使用 Firebase Cloud Messaging,因为我的应用会离线完成部分工作。

我也使用绑定服务,因为它不应该有后台限制。不幸的是,我的应用仍然无法正常运行。

我尝试使用 WakeLock,为另一个进程 AlarmManager 提供服务,将我的应用添加到白名单,但仍然无法正常工作。

我通过简单的发布请求测试在后台运行。我通过改造库连接到测试服务器。

主活动

private LocalService mService;
private boolean mBound = false;
private Intent mIntent;

private ServiceConnection connection = new ServiceConnection() 

    @Override
    public void onServiceConnected(ComponentName className, IBinder service) 
        LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    

    @Override
    public void onServiceDisconnected(ComponentName arg0) 
        mBound = false;
        mService = null;
    

;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mIntent = new Intent(this, LocalService.class);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
        ContextCompat.startForegroundService(getApplicationContext(), mIntent);
    
    PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
    cpuLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kb:wl");


@Override
protected void onStart() 
    super.onStart();
    Intent intent = new Intent(this, LocalService.class);
    bindService(intent, connection, Context.BIND_AUTO_CREATE);


@Override
protected void onStop() 
    super.onStop();
    cpuLock.acquire();



public void onButtonClick(View v) 
    if (mBound) 
        bindService(mIntent, connection, BIND_AUTO_CREATE);
        mService.send();
    


@Override
protected void onRestart() 
    super.onRestart();
    cpuLock.release();

本地服务类

public class LocalService extends Service 

    private IBinder mBinder = new LocalBinder();

    Handler mHandler;
    String DEBUG_TAG = "local";

    public class LocalBinder extends Binder 
        LocalService getService() 
            return LocalService.this;
        
    

    public int onStartCommand(Intent intent, int flags, int startId) 
        return START_STICKY;
    

    @Override
    public void onCreate() 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
            NotificationManager notificationManager =
                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            String channelId = "some_channel_id";
            CharSequence channelName = "Channel La Manche";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName, importance);
            notificationManager.createNotificationChannel(notificationChannel);

            Intent notificationIntent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent =
                    PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification =
                    new Notification.Builder(this, channelId)
                            .setContentTitle("post title")
                            .setContentText("post text")
                            .setSmallIcon(R.drawable.ic_launcher_foreground)
                            .setContentIntent(pendingIntent)
                            .setTicker("post ticker")
                            .setOngoing(true)
                            .build();

            startForeground(1, notification);
        
    

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        mHandler = new Handler();
        return mBinder;
    

    public void send() 
        mHandler.postDelayed(test, 1000);
    

    private Runnable test = new Runnable() 

        public void run() 
            Retrofit retrofit = new Retrofit.Builder()
                    .addConverterFactory(ScalarsConverterFactory.create())
                    .baseUrl("https://test.server.url/")
                    .build();
            Post servicePost = retrofit.create(Post.class);
            Call<String> request = servicePost.send("");
            request.enqueue(new Callback<String>() 

                @Override
                public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) 
                    Log.i(DEBUG_TAG, "Code " + response.code());
                

                @Override
                public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) 
                    Log.i(DEBUG_TAG, "Not connected to server. " + t.getMessage());
                

            );

            mHandler.postDelayed(test, 30 * 1000);
        

    ;

发布界面

@Headers("Content-Type: application/json" )
@POST("api/test")
Call<String> send(@Body String empty);

当平板电脑正在充电或不充电且屏幕开启时,应用程序运行良好。即使屏幕关闭,Endomondo 也能正常工作。我哪里错了?

【问题讨论】:

【参考方案1】:

您可以在this repository 的帮助下解决您的问题。此存储库用于定位,但我相信它会对您有很大帮助。

【讨论】:

谢谢。它通常效果很好。我注意到屏幕关闭时我无法从手机信号塔获取位置。我试图添加唤醒锁,但它没有工作。是否有可能获得这种类型的位置? 它在我的设备(平板电脑华为 MediaPad T5)上不起作用。我关闭 GPS、Wi-Fi、Wi-Fi 扫描和屏幕。它应该只从手机信号塔获取位置。我还将忽略优化选项设置为 true。应用程序仅在我按下电源按钮时才有效。你知道怎么解决吗? Here 是您可以从手机信号塔获取位置的链接,将其添加到该存储库中。 我还有一个问题。当我打开设置或相机应用程序时,我注意到系统杀死了这个应用程序。我尝试将 onSTartCommand 中的返回值更改为 START_STICKY,但没有成功。是否有可能,为了系统不会杀死应用程序?我阅读了有关更改应用程序优先级的信息,但这需要根设备。 我不知道,但可以更详细地描述我的问题。

以上是关于为啥在 Android Oreo 上关闭屏幕时前台服务停止?的主要内容,如果未能解决你的问题,请参考以下文章

屏幕关闭时,前台服务未在 Android 7.0+ 中接收位置更新

即使应用程序关闭也运行作业(Android Oreo)

在运行 Oreo 和 Pie 的设备中如何在锁定屏幕上覆盖布局

屏幕打开/关闭时如何更新 Android 小部件?

屏幕关闭时,Android 10 会阻止网络请求和 GPS 呼叫

Android Oreo中的通知被延迟