为什么在Android Oreo上,当屏幕关闭时前台服务会停止?

3

当我关闭屏幕时,Android Oreo会停止我的前台服务,特别是在设备未连接电源时。我在华为MediaPad T5上测试我的应用程序。我使用Handler.postDelayed每30秒发送测试请求。

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

我不能使用Firebase Cloud Messaging,因为我的应用程序需要离线完成部分工作。

我还使用了绑定服务,因为它不应该受到后台限制。但不幸的是,我的应用程序仍然无法正常工作。

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

我通过Retrofit库连接到测试服务器,测试了在后台运行的简单post请求。

MainActivity

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个回答

2
你可以通过这个代码库解决你的问题。虽然它是用于位置相关的,但我相信它会对你有很大帮助。"Original Answer"翻译成"最初的回答"。

谢谢。它通常运行良好。我注意到当屏幕关闭时,无法从基站获取位置。我尝试添加 Wakelock,但它没有起作用。是否可能获取此类型的位置? - ali
我的设备(平板华为MediaPad T5)无法正常工作。我关闭了GPS、Wi-Fi、Wi-Fi扫描和屏幕,应该只从基站获取位置信息。我还将忽略优化选项设置为true。只有当我按下电源按钮时,应用程序才能正常工作。你知道如何解决这个问题吗? - ali
这是一个链接,你可以从中获取基站的位置信息。将其添加到该代码库中。 - BlackBlind
我还有一个问题。我注意到当我打开设置或相机应用程序时,系统会杀死这个应用程序。我尝试在onStartCommand中更改返回值为START_STICKY,但它没有起作用。有可能让系统不杀死应用程序吗?我了解到可以更改应用程序的优先级,但这需要root设备。 - ali
我不知道那个,但你能更详细地描述一下你的问题吗? - BlackBlind
我打开并点击了Android的设置中的一些选项。例如,我进入关于应用程序屏幕,点击卸载按钮,然后点击否,我点击清除应用程序存储,然后点击否。有时,系统会杀死本地应用程序。如果我在onStartCommand中更改返回值为START_STICKY,则系统会在几秒钟后重新启动应用程序的前台服务。看起来像是内存不足的问题。我检查了RAM使用情况,但Android只有平均700 MB的内存。唯一的问题是,当Android杀死服务并且用户关闭屏幕时,前台服务不会重新启动。 - ali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接