我已阅读http://developer.android.com/guide/topics/appwidgets/index.html和http://www.vogella.com/tutorials/AndroidWidgets/article.html,尤其是第8节。教程:通过服务更新小部件
但是,仍然找不到合适的答案。
1。执行耗时的操作
在vogella教程中,似乎要执行耗时的操作,AppWidgetProvider
将在onUpdate
中启动服务。但是,我做了一个快速测试。已启动的Service
和onUpdate
正在同一个线程中运行。因此,如果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
将被重复触发。那么,对于我来说,放置“从磁盘加载一次数据”代码的更合适的地方是什么?
答案 0 :(得分:3)
我认为您的服务因为BroadcastReceiver中的10秒限制而被终止,而BroadcastReceiver是AppWidgetProvider类的父级。
http://developer.android.com/reference/android/content/BroadcastReceiver.html
在考虑阻止接收器和被杀死的候选人之前,系统允许超时10秒
服务类并没有真正启动新线程,它在当前线程内运行。
http://developer.android.com/reference/android/app/Service.html
请注意,服务与其他应用程序对象一样,在其托管进程的主线程中运行。这意味着,如果您的服务要进行任何CPU密集型(例如MP3播放)或阻止(例如网络)操作,它应该生成自己的线程来执行该工作。
因此,您的服务在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上,所以我希望这在实践中不会成为问题。