封闭的应用程序如何接收定期广播?

How can a closed application receive periodic broadcasts?

我有一个 Android9 的设备,我正在开发一个根据当前日期和时间显示内容的应用程序。我希望它定期检查是否有新内容可用并通知用户。此外,我希望它在用户关闭应用程序时也能正常工作。例如,我的电子邮件应用程序在关闭时也会显示有关新电子邮件的通知。

我的方法是接收一般广播(Intent.ACTION_USER_PRESENTIntent.ACTION_SCREEN_ON),运行 一个接收计时器,然后定期检查新内容。我知道用户必须为此启动我的应用程序一次 (Broadcast receiver not working in ICS if the app is not started atleast once)。该方法成功,如果用户使用多任务按钮并向上滑动我的activity来关闭它。

即使用户关闭了我的application/activity,我如何接收广播?

这里有很多个帖子,问的都是一样的,不过大部分好像都过时了!我已经尝试了很多,但我还没有设法让它发挥作用!因此,我已经为 API 28 设置了一个示例应用程序,我想再次问这个问题,但这次是一个定义的示例。然后应该很容易检查建议的解决方案是否真的有效!

  1. AndroidStudio 中用 File->New->New Project... 创建一个空 activity ->Empty Activity->Language: Java, SDK: API 28.
  2. 添加一个class MyBroadcastReceiver,内容如下:
package org.test.myapplication;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyBroadcastReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Toast.makeText(context, "Hurray! I have received the broadcast!", Toast.LENGTH_LONG).show();
    }
}
  1. 修改classMainActivity内容如下:
package org.test.myapplication;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity
{
    private static BroadcastReceiver broadcastReceiver = null;

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

        if (null == broadcastReceiver)
        {
            // Register broadcast receiver (only a running application my do that)
            IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
            filter.addAction(Intent.ACTION_SCREEN_ON);

            Log.i("MainActivity", "Registered broadcast receiver.");

            broadcastReceiver = new MyBroadcastReceiver();

            registerReceiver(broadcastReceiver, filter);
        }
    }
}

安装和 运行 宁 activity 时,每当我注销并再次登录时,我都会看到 toast。但是,一旦我向上滑动 activity 将其关闭,我就再也看不到吐司了。我该如何改变它?

But as soon as I have swiped the activity up to close it, I don't see the toast anymore.

当然可以。充其量,您的接收器将在您的进程 运行 时接收广播。您的流程将因多种原因终止,包括您描述的手动用户操作。

How can I change that?

对于较旧的 Android 设备,您可以通过 <receiver> 元素在清单中注册您的接收器。这不适用于大多数 implicit broadcasts on Android 8.0 及更高版本,例如您引用的两个。

My email application here for example shows notifications about new emails also when it's closed.

服务器可能正在使用 Firebase 云消息传递 (FCM) 来提醒应用程序有新消息。或者,该应用程序可以使用 WorkManager 或类似的东西进行轮询。请记住,打瞌睡模式、应用待机和各种特定于制造商的 hack 使得应用很难在后台可靠地进行周期性工作,因为这样的工作对电池寿命不利。

如已接受答案的评论中所述,使用 WorkManager 是解决我的问题的简单方法。作为补充,我想在这里为对同一主题感兴趣的任何人展示我的代码。

Class MyWorker:

package org.test.myapplication;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class MyWorker extends Worker
{
    public MyWorker(@NonNull Context context, @NonNull WorkerParameters params)
    {
        super(context, params);
    }

    @Override
    public Result doWork()
    {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                // Run your task here
                Toast.makeText(getApplicationContext(), "Testing", Toast.LENGTH_SHORT).show();
            }
        }, 1000);

        // Indicate whether the work finished successfully with the Result
        return Result.success();
    }
}

Class MainActivity:

package org.test.myapplication;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import androidx.work.WorkRequest;

import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WorkRequest workRequest =
                new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES).build();

        WorkManager.getInstance(this).enqueue(workRequest);
    }
}