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

时间:2016-09-13 07:09:27

标签: android multithreading android-intent widget android-widget

从主屏幕上的小部件调用应用程序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”中的特定功能。

2 个答案:

答案 0 :(得分:1)

我不是Widget-building-process的专家,但我认为你需要改变:

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

要:

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

由于您需要与应用程序进行一些交互,因此您无需将窗口小部件附加到特定活动。而不是你需要使用这样说消息传递机制。在Android中它是BroadcastReciever。例如,您使用它与您的"开始"服务。您可以阅读here about simple example

另外,根据this回答我们解决了,你需要在清单中注册接收者:

                  

然后在onRecieve方法中,您可以解析您的意图并做任何您想做的事情。在您的情况下,我建议您使用ActivityService的后台进行传输,因为Activity可以关闭(okey,Service,但它可以在没有屏幕的情况下恢复和运行) 。在MyWidgetReciever.onReceive中,你需要通过传递"停止"意图。

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

答案 1 :(得分:0)

在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
            }
        }
    }
}