技术开发 频道

AppWidget开发简介

  1.概要

  本文主要介绍了OPhone 1.5平台上的AppWidget框架结构,并用一个“按需更新”的例子详细讲解了AppWidget的开发过程及其特点。 (作者:吴博)

  2.AppWidget框架

  AppWidget是OPhone 1.5平台推出的一种崭新的应用程序框架。基于该框架,开发者可以在OPhone及模拟器上开发“外形”类似传统Widget的小应用程序,并将其嵌入到其他应用中。一个最典型的应用场景就是在主屏上灵活的添加,拖动和删除AppWidget应用。

  和传统的Widget,如Yahoo的Dashboard和中移动的BAE Widget等相比,AppWidget和它们“形似而神不似”,有着完全不同的技术路线。前者的核心基于Web相关技术,有专门的Widget引擎运行环境,而AppWidget则完全基于OPhone平台的上层应用框架,根据特定的UI控件来展示内容。

  AppWidget应用框架中,常用的几个类如下:

  AppWidgetProvider: 继承自BroadcastReceiver,在AppWidget应用update, enable, disable和deleted时接受通知。其中,onUpdate,onReceive是最常用到的方法,它们接受更新通知。

  AppWidgetProviderInfo: 描述AppWidget的大小,更新频率和初始界面邓信息。以XML文件形式存在于应用的res/xml/目录下。

  AppWidgetManager: 负责管理AppWidget, 向AppWidgetProvider发送通知

  RemoteViews: 一个可以在其他应用进程中运行的类,是构造AppWidget的核心。目前,OPhone平台上的RemoteViews支持的布局(Layout)类暂时只有FrameLayout, LinearLayout和RelativeLayout,并且不支持自定义类。

  

  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属性。

<?xml version="1.0" encoding="utf-8"?>
<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()方法的示例代码如下:

    @Override
    
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()方法的示例代码如下:

    @Override
        
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耗用信息。

  3.4 AndroidManifest.xml

  第四步,在AndroidManifest.xml文件中注册名为MyWidgetProvider 的AppWidgetProvider类对象和自定义的Service:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package
="com.MyAppWidget"
      android:versionCode
="1"
      android:versionName
="1.0">
    
<application android:icon="@drawable/icon" android:label="@string/app_name">
        
<receiver android:name="MyWidgetProvider">
            
<meta-data android:name="android.appwidget.provider"
                android:resource
="@xml/mywidget_info" />
            
<intent-filter>
                
<action
            android:name
="android.appwidget.action.APPWIDGET_UPDATE" />
                
<action
                    android:name
="com.OPhone.update"/>
            
</intent-filter>
        
</receiver>
        
<service android:name=".MyWidgetProvider$UpdateService" >
        
</service>
    
</application>
</manifest>    

   其中,android:name属性值“MyWidgetProvider”是AppWidgetProvider的名字,meta-data标签的android:name属性指定了数据类型是“android.appwidget.provider”,而android:resource属性指定了AppWidgetProviderInfo信息的存储资源是mywidget_info.xml.在intent-fliter标签中,我们指定该 AppWidgetProvider接受的通知的Action类型。

  3.5运行

  运行该工程后,我们通过主屏上的长按响应,选择“外部工具”,添加“AppWidget示例”。在主屏上得到的AppWidget如下图所示:

  图下方的蓝色图标及旁边显示的数字及为我们在主屏添加的AppWidget。用户可以随意的拖动,并可以添加多个该类对象。在用户添加多个对象时,每个对象的显示内容都一致,并且只有在用户点击蓝色图标时,才会更新显示的CPU耗用值。

  观察程序的log,我们可以发现AppWidget虽然在主屏上显示,但它和主屏却运行在两个不同的进程中。

  4.结束语

  AppWidget为开发者提供了一种让一个应用显示在另一个应用中的方法。因为主屏使用了AppWidgetHost这个类,因而,在默认情况下,我们编写的AppWidget都会添加到主屏上。如果开发者想在自己编写的应用中添加AppWidget,则还需要实现自定义的 AppWidgetHostView对象,有兴趣的读者可以参考主屏源代码中的实现过程。

0
相关文章