首次放置小部件的设计模式,用于加载耗时的数据

时间:2014-01-26 14:42:37

标签: android

我已阅读http://developer.android.com/guide/topics/appwidgets/index.htmlhttp://www.vogella.com/tutorials/AndroidWidgets/article.html,尤其是第8节。教程:通过服务更新小部件

但是,仍然找不到合适的答案。

1。执行耗时的操作

在vogella教程中,似乎要执行耗时的操作,AppWidgetProvider将在onUpdate中启动服务。但是,我做了一个快速测试。已启动的ServiceonUpdate正在同一个线程中运行。因此,如果Service的{​​{1}}执行耗时的操作,onStart似乎在耗尽的操作完成之前被杀死。这是我的测试代码。

Service

public class MyAppWidgetProvider extends AppWidgetProvider {    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

        Log.i("CHEOK", Thread.currentThread().getId() + " start LoadWidgetService");
        // Build the intent to call the service
        Intent intent = new Intent(context.getApplicationContext(), LoadWidgetService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
        // Update the widgets via the service
        context.startService(intent);
        ...

对于上面的代码,public class LoadWidgetService extends Service { @Override public void onStart(Intent intent, int startId) { for (int i = 0; i < 10; i++) { Log.i("CHEOK", Thread.currentThread().getId() + " " + i + " : try to sleep 10 seconds..."); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("CHEOK", Thread.currentThread().getId() + " " + i + " : try to sleep 10 seconds done!"); } } 将无法完成整个循环,直到LoadWidgetService达到10.大多数情况下,当i为2时,它将停止。所以,我猜这个服务被操作系统杀死了,就像我将i的{​​{1}}线程ID与Service的{​​{1}}线程ID进行比较一样。他们是一样的。

2。仅从磁盘加载数据

我只想从磁盘加载一次数据。但是onStart将被重复触发。那么,对于我来说,放置“从磁盘加载一次数据”代码的更合适的地方是什么?

2 个答案:

答案 0 :(得分:3)

  1. 我认为您的服务因为BroadcastReceiver中的10秒限制而被终止,而BroadcastReceiver是AppWidgetProvider类的父级。

    http://developer.android.com/reference/android/content/BroadcastReceiver.html

      

    在考虑阻止接收器和被杀死的候选人之前,系统允许超时10秒

  2. 服务类并没有真正启动新线程,它在当前线程内运行。

    http://developer.android.com/reference/android/app/Service.html

      

    请注意,服务与其他应用程序对象一样,在其托管进程的主线程中运行。这意味着,如果您的服务要进行任何CPU密集型(例如MP3播放)或阻止(例如网络)操作,它应该生成自己的线程来执行该工作。

  3. 因此,您的服务在broadcastreceiver线程中启动,该线程在10秒后被终止。也许您可以考虑使用IntentService,或者生成一个新线程来运行该服务。

答案 1 :(得分:1)

要“仅从磁盘加载一次数据”,您可能需要考虑重写AppWidgetProvider的onEnabled()方法。首次将任何应用小部件添加到主屏幕时,都会调用此方法。

为了区分onUpdate()和onEnabled()调用,您可以将一些额外数据传递给用于启动服务的intent:

public class MyAppWidgetProvider extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        Intent intent = new Intent(context.getApplicationContext(), LoadWidgetService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
        intent.putExtra("WidgetState", "Update");
        context.startService(intent);
    }

    @Override
    public void onEnabled(Context context) {
        Intent intent = new Intent(context.getApplicationContext(), LoadWidgetService.class).putExtra("WidgetState",
                                                                                                      "Enabled");
        context.startService(intent);
    }
}

然后在您的服务中,您需要检查额外的参数以查看要执行的操作:

public class LoadWidgetService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String widgetState = intent.getStringExtra("WidgetState");
        if ("Enabled".equals(widgetState)) {
            // Perform one-time disk load here...
            for (int i = 0; i < 50; i++) {
                try {
                    Log.d(getClass().getName(), "In onStartCommand()...Enabled");
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else if ("Update".equals(widgetState)) {
            for (int i = 0; i < 50; i++) {
                try {
                    Log.d(getClass().getName(), "In onStartCommand()...Update");
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

首次使用上面的代码将小部件添加到主屏幕时,您应该会看到一堆“已启用”的logcat消息。您添加到主屏幕的任何后续窗口小部件都将生成一系列“更新”行。

就服务未完成而言,当我在Nexus 7上尝试超时10秒时,这似乎与您发布的代码成功完成。我不确定,但我想知道你的服务是否没有完成,因为它将UI线程挂起太长时间(这可能取决于设备)。在我的例子中,我在预感中减少了每次循环迭代的Thread.sleep时间,但这只是我的一个猜测。一般来说,你在服务中进行的CPU密集型工作很可能是在后台线程而不是在UI上,所以我希望这在实践中不会成为问题。