3.一个按需更新的AppWidget案例
接下来,我们创建一个监控系统CPU使用率的小应用来具体讲解AppWidget的使用方法。在OPhone平台上,如果使用定时更新策略来更新AppWidget,则会增加电量和CPU资源的开销,因此,在本例中,我们提供了一种按需更新的策略,并且确保该应用不会因时延而造成ANR故障。
3.1 AppWidgetProviderInfo
首先,建立MyAppWidget工程,并在res/xml/目录下添加mywidget_info.xml文件来反应 AppWidgetProviderInfo信息。AppWidget的像素大小取决于它所占的方块多少,其计算公式是(块数 * 74) – 2,因此,我们取高度是72像素,宽度220像素。android:initialLayout设置了AppWidget的布局文件是 mywidget_frame.xml。因为本例采用按需更新的策略,所以没有标注android:updatePeriodMillis属性。
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="220dip" android:minHeight="72dip"
android:initialLayout="@layout/mywidget_frame"
/>
3.2 RemoteView
第二步,建立RemoteViews对应的布局文件。目前, RemoteViews支持的控件暂时有TextView, ImageView, Button,Progressbar等。我们在一个LinearLayout类中添加一个ImageView和一个TextView,并将布局文件命名为mywidget_frame.xml
3.3 AppWidgetProvider
第三步,创建自定义的MyWidgetProvider类,它继承自AppWidgetProvider。在添加AppWidget应用或自动定时更新时,AppWidgetManager会广播动作名字是“android.appwidget.action.APPWIDGET_UPDATE”的 Intent,当onReceive()方法没有被重载时,onUpdate方法会接受到这些广播的Intent。类似普通 BroadcastReceiver类,我们可以重载MyWidgetProvider的onReceive方法,并在其中指定我们想要接受的 Intent。在本例子中,我们自定义的更新动作名为"com.OPhone.update"。在OPhone平台中,对于 BroadcastReceiver类,如果其运行时间过长,有可能会出现ANR(Application Not Response)的故障。因此,我们在onReceive方法中,添加了一个Service来保持应用一直存在。这个Service和 AppWidget应用运行在同一个进程中。通过重载onReceive()方法,我们开辟了直接对AppWidget的更新途径,从而不依赖于 AppWidgetManager发出的更新消息。onReceiver()方法的示例代码如下:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if("android.appwidget.action.APPWIDGET_UPDATE".equals(action)
||"com.OPhone.update".equals(action))
context.startService(new Intent(context,UpdateService.class));
Log.d(TAG,"Action: "+ action);
Log.d(TAG,"onReceive Process Id: "+ Process.myPid());
}
AppWidget依赖RemoteViews来完成内容的更新,我们在Service的onStart()方法中对新的RemoteViews对象赋值。和普通的控件(如Botton, TextView等)不同的是,RemoteView没有OnClickListener方法,去而代之的是 setOnClickPendingIntent方法---在点击RemoteViews对象时,用PendingIntent的方式来启动一个新的 Activity,新的广播或新Service。AppWidgetManager负责把新生成的Remote更新到AppWidget上。在本例中,我们采用PendingIntent的getBroadcast方法来发送广播,其携带的Intent有自定义的接收动作。onStart()方法的示例代码如下:
public void onStart(Intent intent, int startId) {
String info=null;
//创建RemoteViews
RemoteViews views = new RemoteViews(getPackageName(),
R.layout.mywidget_frame);
try{
info = String.valueOf(getCpuInfo()); //获取CPU消耗信息
}catch(Exception e){
e.printStackTrace();
}
//设置控件
views.setTextViewText(R.id.text_1, info);
views.setImageViewResource(R.id.image_1, R.drawable.icon);
Intent intent_b = new Intent();
intent_b.setAction("com.OPhone.update");
pendingIntent = PendingIntent.getBroadcast(this, 0, intent_b, 0);
//给图片设置响应事件
views.setOnClickPendingIntent(R.id.image_1, pendingIntent);
//获得组建的完整名字
ComponentName thisWidget = new ComponentName(this, MyWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
//执行内容更新
manager.updateAppWidget(thisWidget, views);
}
在以上代码中,AppWidgetManager类的updateAppWidget方法完成了将RemoteViews对象推送到显示区的作用。如果没有这句操作,则主屏上会显示小应用加载错误的提示。OPhone平台基于Linux,因此getCpuInfo()方法采用读取平台"/proc /stat/"目录下信息的方法来计算CPU耗用信息。