在Android中从服务启动活动

173

安卓:

public class LocationService extends Service {

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        startActivity(new Intent(this, activity.class));
    }
}

我从 Activity 启动了这个服务

如果条件满足,在 Activity 中启动

startService(new Intent(WozzonActivity.this, LocationService.class));

我之前提到的LocationService无法启动Activity,我该如何在服务类中获取当前正在运行的Activity的上下文?

从我之前提到的LocationService无法启动Activity,请问如何在服务类中获取当前正在运行Activity的上下文?

11个回答

379

从 Service 类内部:

Intent dialogIntent = new Intent(this, MyActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);

但是,由于电池优化限制,这在Android 10+上不起作用。


22
只需要使用FLAG_ACTIVITY_NEW_TASK,因为只有活动可以在没有它的情况下启动其他活动。由于服务具有自己的上下文,因此可以使用“this”代替getBaseContext和getApplication。 - Sojurn
1
如何以编程方式从最近的屏幕列表中删除该活动? - Prashanth Debbadwar
3
如果服务在后台运行,则无法正常工作。有解决方法吗? - Rick Royd Aban
4
请帮我实现从后台服务打开应用程序的功能。我使用了 Firebase (FCM) 服务。我想从我的应用程序中打开 "My Calling UI" 屏幕。请帮忙实现这个功能。 - Dipanki Jadav
3
我在我的服务中编写了您的代码,当应用处于后台时,我发现该服务在小米Mi A2上工作,但在小米Redmi Note 7上无法工作。您能帮我解决为什么它在一个设备上可以工作而在另一个设备上不能工作吗? - Amjad Omari
显示剩余6条评论

26

2
@RahulBhobe 我发现这个答案很有帮助,因为在Android 10上添加SYSTEM_ALERT_WINDOW(并在设置中启用它)解决了我的问题。 - Daniel F
1
在Go版设备中无法授予SYSTEM_ALERT_WINDOW权限。 - Shrdi
@Shrdi,那么,你有关于AndroidGo的任何解决方案吗? - AlexS
@AlexS 我认为这在Android 10+Go上无法实现,因为这是系统的限制。关于此功能的任何特性都应该提供另一种替代方式或提示,用户可能也不同意此权限。 - Shrdi

20

我曾经遇到同样的问题,想让你知道上面的方法都对我没用。对我有效的是:

 Intent dialogIntent = new Intent(this, myActivity.class);
 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 this.startActivity(dialogIntent);

而在我的一个子类中,存储在另一个文件中,我不得不:

public static Service myService;

myService = this;

new SubService(myService);

Intent dialogIntent = new Intent(myService, myActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
myService.startActivity(dialogIntent);

所有其他回答都给了我一个nullpointerexception


7
你正在将Service存储在静态字段中,这可能会导致内存泄漏。 - Murmel
3
startActivity和this.startActivity是相同的!在您的服务中创建一个无用的实例与普通的startActivity相等,为什么? - Amir Hossein Ghasemi

10

还有一件值得一提的事情:尽管上面的答案在我们的任务在后台时可以正常工作,但我只能通过以下方式使其在我们的任务(由服务和一些活动组成)在前台(即我们的某个活动对用户可见)时正常工作:

    Intent intent = new Intent(storedActivity, MyActivity.class);
    intent.setAction(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    storedActivity.startActivity(intent);

我不知道 ACTION_VIEW 或 FLAG_ACTIVITY_NEW_TASK 在这里是否有任何实际用途。成功的关键是

storedActivity.startActivity(intent);

当然,FLAG_ACTIVITY_REORDER_TO_FRONT可以避免再次实例化活动。祝你好运!


1
这个不起作用。所有它给我的只是一个错误:“从Activity上下文之外调用startActivity()需要FLAG_ACTIVITY_NEW_TASK标志。这真的是你想要的吗?” - Cerin
5
这是因为第二次调用setFlags覆盖了第一次的设置。解决方法应该使用addFlags,或者一次性调用setFlags并通过Intent.FLAG_ACTIVITY_NEW_TASK && Intent.FLAG_ACTIVITY_REORDER_TO_FRONT传递参数。 - Fergusmac
8
不要使用 &&(逻辑与符号)来组合标志,应该使用 |(位或符号)来代替。 - Jon Watte
Intent intent = new Intent(storedActivity, MyActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); storedActivity.startActivity(intent); - Prakhar Kulshreshtha

4
新的方式从Android 10及以上版本开始是使用SYSTEM_ALERT_WINDOW权限。以下是操作步骤: 在AndroidManifest.xml文件中声明此权限。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

在 onCreate 方法中,当按钮被点击时,调用一个辅助方法来显示一个对话框,这样如果用户选择允许权限,设备设置中的覆盖权限将会打开。请注意,此点击按钮仅供您了解如何调用权限。根据您的应用程序要求,您可以以任何方式调用它。
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);      
    Button button = (Button) findViewById(R.id.button1);

    button.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            showMessageForFloatingPermission("To use this feature requires over lay permission");
        }
    });
}

下面是已声明的辅助方法吗?

//Helper method to show a dialog window
private void showMessageForFloatingPermission(String message) {
    new android.app.AlertDialog.Builder(MainActivity.this)
            .setMessage(message)
            .setPositiveButton("OK",  new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {

                    checkFloatingPermission();

                }
            })
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {

                    //User opted not to use this feature
                    //finish();

                }
            })
            .create()
            .show();
}





//Helper method for checking over lay floating permission
public void checkFloatingPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            startActivityFloatingPermission.launch(intent);//this will open device settings for over lay permission window

        }
    }
}





//Initialize ActivityResultLauncher. Note here that no need custom request code
ActivityResultLauncher<Intent> startActivityFloatingPermission = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    //Permission granted
                }else{
                    //If there is no permission allowed yet, still a dialog window will open unless a user opted not to use the feature. 
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (!Settings.canDrawOverlays(MainActivity.this)) {
                            // You don't have permission yet, show a dialog reasoning
                            showMessageForFloatingPermission("To use this feature requires over lay permission");

                        }
                    }
                }
            }
        });

一旦您按照上述代码进行了实现,您就可以从服务类启动任何活动。您的活动将以编程方式启动。

Intent intent = new Intent(this, MyActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);

基本上,这就是你可以做到的。它运行良好。顺便说一句,您可以根据自己的需求随意操纵此代码。希望这能帮到您!


没有一个答案有效,但这个有效!谢谢你的发布! - Tejas Sharma

1

您还可以在您的服务中使用getApplicationContext()方法来运行下面所写的startActivity()方法:

Intent myIntent = new Intent();
myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(myIntent);

1

无法使用 ServiceContext;能够获取类似于 (package) Context

Intent intent = new Intent(getApplicationContext(), SomeActivity.class);

2
我认为这仅适用于使用FLAG_ACTIVITY_NEW_TASK标志。 - Cerlin

0

或者,

您可以使用自己的Application类,并从任何地方调用(特别是非活动状态)。

public class App extends Application {

    protected static Context context = null;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    public static Context getContext() {
        return context;
    }

}

并注册您的Application类:

<application android:name="yourpackage.App" ...

然后调用:

App.getContext();

2
注意使用上下文的静态实例,否则可能会导致多个内存泄漏问题。 - crgarridos
1
@crgarridos 不是的。这是应用程序上下文,它天生就是单例的,并不会导致内存泄漏。 - GV_FiQst
1
我会将代码行 context = getApplicationContext(); 修改为 context = this; - GV_FiQst

0
在Android 10+版本中,如果您的应用程序需要使用SYSTEM_ALERT_WINDOW权限,并且需要在清单文件中声明该权限。此外,您还需要手动从移动设备的设置中启用该权限。

您的回答可以通过添加更多支持信息来改进。请[编辑]以添加进一步的细节,例如引用或文档,以便其他人可以确认您的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

0

谢谢!对于红米4A,Android 7,MIUI 10,这是唯一帮助我的方法。 - Anton Samokat

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