2014-01-26 67 views
2

我讀過http://developer.android.com/guide/topics/appwidgets/index.htmlhttp://www.vogella.com/tutorials/AndroidWidgets/article.html數據,尤其是第8節教程:通過服務設計模式加載耗時

更新組件,但是,仍然無法找到一個合適的答案。

1.執行耗時的操作

在vogella教程,似乎執行耗時的操作,AppWidgetProvideronUpdate推出的一項服務。但是,我做了一個快速測試。推出的ServiceonUpdate正在同一個線程中運行。因此,如果ServiceonStart正在執行耗時的操作,則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不會有機會完成整個循環,直到i達到10.大多數的時候,它就會停止時i是2。所以,我想該服務被OS所殺,因爲當我比較ServiceonStart線程ID和AppWidgetProvideronUpdate線程ID時。他們是一樣的。

2.只有從磁盤加載數據一旦

我只想一次加載從磁盤上的數據。但onUpdate將被重複觸發。那麼,對於我來說,放置「從磁盤加載數據一次」代碼的更合適的地方是什麼?

回答

3
  1. 我認爲你的服務被終止,因爲它是AppWidgetProvider類的父節點中的BroadcastReceiver的10秒限制。

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

    有10秒的超時,該系統允許考慮到被阻擋的接收器和一個候選人之前被殺死

  2. 服務類並沒有真正啓動一個新的線程,它在當前線程內運行。

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

    需要注意的是服務,如其他應用程序對象,在其宿主進程的主線程中運行。這意味着,如果你的服務要做任何CPU密集型(如MP3播放)或阻塞(如網絡)操作,它應該產生自己的線程來完成這項工作。

因此您服務於廣播接收器線程,10秒鐘後殺開始。也許你可以考慮使用IntentService,或者產生一個新線程來運行服務。

+0

是的。我的確和你有同樣的想法。但是,到目前爲止,我還沒有看到在小部件中使用「IntentService」的真實代碼示例。我想知道爲什麼... –

+0

順便說一句,你有什麼想法'2。只從磁盤加載數據一次? –

+0

我想問題是,你想從磁盤加載?如果您只想永久加載它,那麼只需禁用小部件自動刷新(將updatePeriodMillis設置爲0)。我相信onUpdate只會在放置小部件時調用一次。 –

1

要「只從磁盤加載數據一次」,您可能需要考慮覆蓋AppWidgetProvider的onEnabled()方法。這是第一次將您的應用程序窗口小部件添加到主屏幕。

爲了一個的onUpdate(區分)和onEnabled()調用,您可以在一些額外的數據傳遞到您用來啓動服務的意圖:

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密集型工作可能會在後臺線程上,而不是在用戶界面上,所以我希望這在實踐中不會成爲問題。