如何从 HomeScreen Widget 调用 Activity 中的函数

Posted

技术标签:

【中文标题】如何从 HomeScreen Widget 调用 Activity 中的函数【英文标题】:How to call function in Activity from HomeScreen Widget 【发布时间】:2017-01-20 16:55:59 【问题描述】:

从位于主屏幕上的小部件调用我的应用程序Activity 中的函数的正确方法是什么?对于我的示例,我有一个在我的 Activity 类中初始化的后台线程,并且希望能够从主屏幕按下小部件以调用特定函数来启动/停止线程,而无需进入应用程序。

以下是我正在使用的 Activity 的精简和准系统版本:

public class MainActivity extends AppCompatActivity 
    private Thread thread;
    private Runnable runner;

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

    private void startThread() 
        //start/restart the thread
    

    public void stopThread() 
        //interupt the thread
       

以下是主屏幕小部件的准系统代码:

static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) 

    Intent intent = new Intent(context, MainActivity.class);

    intent.putExtra("Goal","Stop");
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.speed_widget);
    views.setOnClickPendingIntent(R.id.widgetButton, pendingIntent);

    appWidgetManager.updateAppWidget(appWidgetId, views);

这是我目前最好的解决方案:从我目前所做的研究来看,我似乎需要继续使用putExtra(),然后将getExtra() 与从小部件发送的 Intent 一起使用,并且添加一个字符串来说明是否根据线程的状态调用startThread()stopThread()。目前的目标是这样使用它:

    String intentGoal = getIntent().getExtras().getString("Goal");
    if(intentGoal.compareTo("Stop")==0)
        stopThread();
    else if(intentGoal.compareTo("Start")==0)
        startThread();
    

它很可能在onNewIntent() 的重载版本中运行。但是,从小部件发送的意图也会导致对onCreate() 的调用,我不希望发生这种情况,因为它会重新启动线程,并且我拥有的各种其他初始化代码也将被重新运行。我尝试将launchMode 设置为singleTask,但它仍然会调用onCreate() 而不仅仅是onNewIntent()。回顾一下,我希望能够按下主屏幕小部件,然后启动或停止线程,或者更一般地调用现有“MainActivity”中的特定函数。

【问题讨论】:

【参考方案1】:

我不是小部件构建过程方面的专家,但我认为您需要更改:

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

收件人:

PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

由于您需要提供与应用程序的一些交互,因此您无需将小部件附加到特定活动。取而代之的是,您需要使用这么说的消息传递机制。在 android 中是BroadcastReciever。例如,您使用它与您的“启动”服务进行交互。你可以阅读here about simple example。

另外,根据this的回答,我们找到了解决方案,你需要在manifest中注册reciever:

然后在onRecieve 方法中,您可以解析您的意图并做任何您想做的事情。在您的情况下,我建议将背景工作从Activity 传输到Service,因为可以关闭活动(好吧,Service 也可以,但它可以在没有屏幕的情况下恢复和运行)。在MyWidgetReciever.onReceive 中,您需要通过“停止”意图启动服务。

我不确定,但是,也许您可​​以直接在服务中捕获广播,而不是在外部接收器中,但可能是服务将被杀死的情况,因此它不会处理您的广播。

【讨论】:

【参考方案2】:

在 Michael Spitsin 的回答和 the answer here 之间,我能够找到一种在单击主屏幕小部件按钮时执行特定方法的方法。正如迈克尔所说,getBroadcastgetActivity 之间是有区别的。对于这种情况,我能够使用getBroadcast 来广播一个意图,但我没有打算打开MainActivity,而是使用this.class,其中“this”是函数 updateAppWidget 所在的类.

这样做是向自己广播一个意图,并在同一类的onReceive(Context,Intent) 中捕获。确保在您发送的 Intent 上使用 addAction 以使您的意图与onReceive 捕获的其他 Intent 区分开来您只需要使用自己的实现重载该方法,类似于我链接的其他答案去做。下面是我让小部件工作的精简版:

public class MyWidget extends AppWidgetProvider 
    public static String YOUR_ACTION = "YourAction";

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) 

        Intent intent = new Intent(context, SpeedWidget.class);
        intent.setAction(YOUR_ACTION);

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.speed_widget);
        views.setOnClickPendingIntent(R.id.widgetButton, pendingIntent);

        appWidgetManager.updateAppWidget(appWidgetId, views);

    

    @Override
    public void onReceive(Context context, Intent intent) 
        super.onReceive(context, intent);

        if (intent.getAction().equals(YOUR_ACTION)) 
                //THIS IS WHERE YOUR CODE WILL BE EXECUTED
            
        
    

【讨论】:

以上是关于如何从 HomeScreen Widget 调用 Activity 中的函数的主要内容,如果未能解决你的问题,请参考以下文章

iOS 14 widget功能介绍

每个 Activity 中的 NavigationDrawer

禁用后退按钮导航操作

Flutter Bloc 如何从 Widget 本身更新 BlocBuilder 中的 Widget?

提供商无法访问redux商店

OnUpdate 在 Android Widget 中的 onReceive 之前调用