无法在Android 10 [Android Q] 后台启动活动

20
我使用安卓10 [安卓Q,Galaxy 10]操作系统, 使用安卓Studio 3.3开发工具, 利用AVD创建了一个API 29 [安卓10]虚拟手机。 在虚拟机上, 我先运行我的应用程序,然后启动其他应用程序如日历、计算器等。 这时我的应用程序活动进入了后台模式。 当我在广播接收器中接收到消息时, 我调用startActivity方法。 代码如下: public class myReceiver extends BroadcastReceiver {}
public void onReceive(Context context, Intent intent)
{
        Intent intentRun = new Intent(context, LoginSuccess.class);
        intentRun.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intentRun);
}

但是,在我的应用处于后台模式时,LoginSuccess活动没有显示出来。
使用相同的代码,在我的应用处于前台模式时,LoginSuccess活动非常好地显示出来。 调用堆栈捕获图像 上面的图片显示了在广播接收器中调用startActivity之前的调用堆栈。
我已经阅读了Android 10后台活动问题的指南。[developer.android.com/~~一些位置]
在指南中,
我知道如果活动存在于调用堆栈中,即使在后台模式下也可以启动它。
以上代码尝试启动最近调用堆栈中存在的活动。
为什么在后台模式下无法启动startActivity?[可能不失败,但无论如何都没有激活到前台]

1
Android 10(API级别29)及更高版本限制应用在后台运行时启动活动的时间。欲了解更多详细信息,请查看此博客。https://developer.android.com/guide/components/activities/background-starts - Kaushal Panchal
看看这篇文章,其中使用了EventBus https://stackoverflow.com/a/64670128/12805923 - Ahmet B.
4个回答

30

使用Android Q,如果您的应用程序不包括以下链接中列出的那些异常情况,则无法自动从后台启动活动。

https://developer.android.com/guide/components/activities/background-starts

可能的解决方案:

1- 可以选择只显示一个服务通知,并在单击时启动挂起的意图

2- 您可以使用全屏意图立即显示您的意图,如其他答案所示并由Google建议。

对于全屏意图解决方案,如官方文档所述

系统UI可能会选择在用户使用设备时显示悬浮通知,而不是启动此意图。

3- 要在后台自动启动活动,在我的意见中最可能的解决方案是将“SYSTEM_ALERT_WINDOW”添加到清单文件中。并在应用程序第一次打开时请求用户权限。(用户可以手动授予此权限 -(设置-应用程序-您的应用程序-高级-绘制其他应用程序))

请求权限的示例代码:

在清单中:

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

在应用程序的某个地方:

 public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 2323;

//if the user already granted the permission or the API is below Android 10 no need to ask for permission

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
 !Settings.canDrawOverlays(getContext()))
                    {RequestPermission()}

 private void RequestPermission() {
            // Check if Android M or higher
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // Show alert dialog to the user saying a separate permission is needed
                // Launch the settings activity if the user prefers
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getActivity().getPackageName()));
                startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
            }
        }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (!Settings.canDrawOverlays(getContext())) {
                    PermissionDenied();
                }
                else
                {
                 // Permission Granted-System will work
            }

        }
    }
 }

我测试了你的代码。 它运行良好。但是,它不具备运行时权限。 因此,用户必须在切换应用程序后返回我的应用程序, 完成悬浮窗口设置。谢谢! - jongdae lee
是的,他们必须这样做。Android 限制了在后台启动活动的操作。 - Eren Tüfekçi
1
使用共享首选项来检查您是否允许权限, boolean checkPermission = SpUtil.getInstance().getBoolean(AppConstants.CHECK_PERMISSION, false); - Hasnat Hasan
Settings.canDrawOverlays(getContext() 方法返回此权限是否已授予。因此,如果用户授予了该权限,则无需多次请求权限。 - Eren Tüfekçi
我应该在PermissionDenied()方法中写什么?你能把那个方法发给我吗? - Saloni Parikh
在我的情况下,我创建了一个对话框,解释了系统没有这个权限将无法工作,并向用户展示了它。 - Eren Tüfekçi

10

我正在使用以下逻辑打开活动。正如Google和博客所说,如果您想在Android 10或更高版本上使用通知,在后台服务中打开活动。

在清单文件中:

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

例子:

private void startActivity() {

        Uri sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.siren);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            NotificationManager notificationManager =
                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            AudioAttributes attributes = new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ALARM)
                    .build();

            String CHANNEL_ID = BuildConfig.APPLICATION_ID.concat("_notification_id");
            String CHANNEL_NAME = BuildConfig.APPLICATION_ID.concat("_notification_name");
            assert notificationManager != null;

            NotificationChannel mChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
            if (mChannel == null) {
                mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
                mChannel.setSound(sound, attributes);
                notificationManager.createNotificationChannel(mChannel);
            }

            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);

            builder.setSmallIcon(R.drawable.logo)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText(getString(R.string.login))
                    .setPriority(NotificationCompat.PRIORITY_HIGH)
                    .setCategory(NotificationCompat.CATEGORY_CALL)
                    .setFullScreenIntent(openScreen(Constants.NOTIFICATION_ID), true)
                    .setAutoCancel(true)
                    .setOngoing(true);

            Notification notification = builder.build();
            notificationManager.notify(Constants.NOTIFICATION_ID, notification);
        } else {
            startActivity(new Intent(BackgroundService.this, LoginActivity.class)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
        }

    }

    private PendingIntent openScreen(int notificationId) {
        Intent fullScreenIntent = new Intent(this, LoginActivity.class);
        fullScreenIntent.putExtra(Constants.NOTIFICATION_IDS, notificationId);
        return PendingIntent.getActivity(this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

谢谢你的回答。但是,我的应用程序不应该使用通知。因为在我的应用程序中响应时间非常重要。用户应该在几秒钟内做出反应,所以在点击通知消息后,时间已经过去了,所以限制时间已经结束。在用户响应时间方面是否有其他方法? 从后台激活我的活动到前台非常好,但这是不可能的吗? - jongdae lee
阅读此博客 https://developer.android.com/guide/components/activities/background-starts .. 并在您的情况下尝试处理一些特定情况,其中限制不适用于打开活动。 - Kaushal Panchal
我会阅读它。 正如你所说,我认为我必须处理与活动堆栈相关的一些特定情况。 我测试了更多的情况, 有些活动出现了,其他活动没有出现。 我必须知道每种情况的不同之处。 - jongdae lee
非常完美!谢谢。如果不需要通知,您可以在notificationManager.notify之后立即调用notificationManager.cancel(notId);来取消通知。 - Basim Sherif
这在Android 10 Go设备上无法工作。https://developer.android.com/about/versions/10/behavior-changes-all#sysalert-go - Shrdi
1
@Shrdi 我还没有在Go设备上测试过这个代码,但是这段代码几乎适用于所有设备。请给我一些时间,我会检查并更新我的答案,提供一个可行的Go设备代码。 - Kaushal Panchal

0
如果您拥有root权限,您可以在shell中使用am命令来完成此操作:
    public static final void switchAcitivty (final Context context) throws IOException {
        final Runtime runtime = Runtime.getRuntime();
        final String intentCommand =  "su -c am start -n yourpackage/.MainActivity -a android.intent.action.VIEW";
        Log.i("TAG", intentCommand);
        runtime.exec(intentCommand);
    }

没有根权限,它会被静默地阻止,这很烦人。


0

非常奇怪,但从前台服务启动活动在发布版本中可以工作。在调试时(通过Android Studio进行调试)在调试版本中无法工作。


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