`
runfeel
  • 浏览: 936306 次
文章分类
社区版块
存档分类
最新评论

Android中AppWidget的分析与应用:AppWidgetProvider

 
阅读更多

田海立@CSDN

2012-8-20

本文从开发AppWidgetProvider角度出发,看一个AppWidgetPrvodier在整个AppWidget体系中所扮演的角色。分析了AppWidgetProvider如何被AppWidget系统所识别;AppWidgetProvider何时/如何通过RemoteViews提供并更新数据;如何响应通过RemoteViews提供的PendingIntent的按钮点击操作。

因为一般应用开发者并不关注AppWidget其他部分(比如,AppWidgetHost,或AppWidget内部组件)的开发,所以一般就直接把AppWidgetProvider开发称为“AppWidget开发”。


一、实现一个AppWidgetProvider

要实现一个AppWidgetProvider,需要:

  1. 实现AppWidgetProvider的子类,并至少override onUpdate()方法[非必须,但是如果不这样做,该AppWidgetProvider就没有提供任何内容,也就不是AppWidgetProvider了];
  2. 在AndroidManifest.xml中,声明上述的AppWidgetProvider的子类是一个Receiver,并且:
  • 该Receiver的intent-filter的Action必须包含“android.appwidget.action.APPWIDGET_UPDATE”;
  • 该Receiver的meta-data为“android.appwidget.provider”,并用一个xml文件来描述布局属性。
  • 在2.2的xml文件中描述布局属性的节点名称必须为“appwidget-provider”。
  • 以上几点皆是AppWidget系统判断是否是AppWidgetProvider的标志。后面本文的3.2中详述是如何被检索并加入到系统中的。

    二、AppWidgetProvider类分析

    AppWidgetProvider是一个BroadcastReceiver,必须在AndroidManifest.xml中声明该Receiver,并接收“android.appwidget.action.APPWIDGET_UPDATE”。

    类AppWidgetProvider的实现是一个模板模式:

    AppWidgetProvider

    图一、AppWidgetProvider

    在AppWidgetProvider的onReceiver()实现中已经对接收到的ActionAppWidgetManager.ACTION_APPWIDGET_UPDATE / AppWidgetManager.ACTION_APPWIDGET_DELETED/ AppWidgetManager.ACTION_APPWIDGET_ENABLED以及AppWidgetManager.ACTION_APPWIDGET_DISABLED做了处理,分别执行onUpdate()/ onDeleted() / onEnabled() / onDisabled()。

    所以,AppWidgetProvider的实现类,要overrideonReceive(),以及onXXX()[注:至少要实现onUpdate(),在这里AppWidgetProvider通过RemoteViews提供内容给AppWidgetHost,否则,所谓的AppWidgetProvider什么也没提供]。

    而在onReceive()的开始处就要执行super.onReceive()让AppWidgetProvider来分发AppWidgetProvider所要处理的上述广播消息。

    AppWidgetProvider处理AppWidget中的广播:

    • onUpdate() 处理AppWidgetManager.ACTION_APPWIDGET_UPDATE广播。该广播在需要AppWidgetProvider提供RemoteViews数据时,由AppWidgetService.sendUpdateIntentLocked()发出。
    • onDeleted() 处理AppWidgetManager.ACTION_APPWIDGET_DELETED广播。该广播在有该AppWidgetProvider的实例被删除时,由AppWidgetService.deleteAppWidgetLocked()发出。
    • onEnabled() 处理AppWidgetManager.ACTION_APPWIDGET_ENABLED广播。该广播在该AppWidgetProvider被实例化时,由AppWidgetService.sendEnableIntentLocked()发出。
    • onDisabled() 处理AppWidgetManager.ACTION_APPWIDGET_DISABLED广播。该广播在该AppWidgetProvider的所有实例中的最后一个实例被删除时,由AppWidgetService.deleteAppWidgetLocked()发出。

    一般地,AppWidgetProvider必须处理onUpdate();onEnabled()和onDisabled()最好也要处理;onDeleted()可以不处理。

    三、AppWidgetProvider的如何被系统识别

    3.1 AppWidgetProvider中配置AndroidManifest.xml

    Android中“电量控制”这个AppWidget是由Settings中的SettingsAppWidgetProvider实现的,先来看它的AndroidManifest.xml。

    下面是Settings中关于SettingsAppWidgetProvider这个AppWidgetProvider的描述信息:

           <receiver android:name=".widget.SettingsAppWidgetProvider"
                   android:label="@string/gadget_title"android:exported="true">
               <intent-filter>
                   <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                   <action android:name="android.net.wifi.WIFI_STATE_CHANGED"/>
                   <action android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED" />
                   <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
                   <action android:name="android.location.PROVIDERS_CHANGED"/>
                   <action android:name="com.android.sync.SYNC_CONN_STATUS_CHANGED" />
               </intent-filter>
               <meta-data android:name="android.appwidget.provider"android:resource="@xml/appwidget_info" />
           </receiver>

    这其中满足一中的1&2的要求,另外,这个AppWidget要处理设置Wifi、Bluetooth、GPS、数据同步和亮度,所以要处理这些相应的设置项变化时的广播通知。

    对3这点,还要要看res/xml/appwidget_info.xml文件

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
       android:minWidth="294dip"
       android:minHeight="72dip"
       android:updatePeriodMillis="0"
       android:initialLayout="@layout/widget"
        >
    </appwidget-provider>

    这里定义了最小宽度minWidth、最小高度minHeight和初始layoutinitialLayout,用来在AppWidgetProvider还未通过RemoteViews提供数据之前,AppWidgetHost就能够获知需要为该AppWidget预留大概的位置;

    updatePeriodMillis指示是否需要周期性的更新AppWidget,0是不需要周期更新。

    3.2 AppWidgetProvider的信息被系统所识别

    这部分是由AppWidgetService实现。

    当包含AppWidgetProvider的apk被安装到系统中的时候,AppWidgetService会监听广播,并处理相应的AppWidgetProvider:

    • 监听到有包被加入(Intent.ACTION_PACKAGE_ADDED或Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE)时,执行addProvidersForPackageLocked()/ updateProvidersForPackageLocked() 添加或更新其中的AppWidgetProvider;
    • 监听到有包被移除(Intent.ACTION_PACKAGE_REMOVED或Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)时,执行removeProvidersForPackageLocked()移除其中的AppWidgetProvider。

    下面重点关注如何加入AppWidgetProvider,看addProvidersForPackageLocked()的实现:

        voidaddProvidersForPackageLocked(String pkgName) {
           Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
           intent.setPackage(pkgName);
           List<ResolveInfo> broadcastReceivers =mPackageManager.queryBroadcastReceivers(intent,
                   PackageManager.GET_META_DATA);
     
           final int N = broadcastReceivers == null ? 0 :broadcastReceivers.size();
           for (int i=0; i<N; i++) {
               ResolveInfo ri = broadcastReceivers.get(i);
               ActivityInfo ai = ri.activityInfo;
               if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE)!= 0) {
                   continue;
               }
               if (pkgName.equals(ai.packageName)) {
                   addProviderLocked(ri);
               }
            }
        }

    1. 检索所加入包中的所有AppWidgetManager.ACTION_APPWIDGET_UPDATE的Receiver,并放入broadcastReceivers:List<ResolveInfo>;[Line#2~ 5]
    2. 对每一个这样的Broadcast,满足下列要求的加入已安装AppWidgetProvider列表:

      • 不是安装在外存储器上;
      • Receiver的包名与安装的包名相同;
      • Meta-data的节点名必须是"appwidget-provider"[解析meta-data中android:resource指向的xml内容]

    解析meta-data中android:resource指向的xml内容时,所要解析哪些内容是由frameworks/base/core/res/res/values/attrs.xml中的AppWidgetProviderInfo配置:

       <declare-styleable name="AppWidgetProviderInfo">
           <!-- Minimum width of the AppWidget. -->
           <attr name="minWidth"/>
           <!-- Minimum height of the AppWidget. -->
           <attr name="minHeight"/>
           <!-- Update period in milliseconds, or 0 if the AppWidget will updateitself. -->
           <attr name="updatePeriodMillis" format="integer"/>
           <!-- A resource id of a layout. -->
           <attr name="initialLayout" format="reference"/>
           <!-- A class name in the AppWidget's package to be launched toconfigure.
                If not supplied, then no activity will be launched. -->
           <attr name="configure" format="string" />
       </declare-styleable>

    这些值被解析出来之后,连同label、icon以及由ComponentName(packageName,className)构造的provider被赋值到AppWidgetProviderInfo中,并被记录在AppWidgetService的mInstalledProviders:ArrayList<Provider>中。

    AppWidgetProvider AppWidgetProviderInfo

    图二、AppWidgetProviderInfo

    四、AppWidgetProvider的Enable与Disable

    因为AppWidgetProvider只是提供显示内容,具体显示是显示在AppWidgetHost中的。因为Android机制的关系,后台的AppWidgetProvider很容易被系统杀掉。所以AppWidgetProvider在收到AppWidgetManager.ACTION_APPWIDGET_ENABLED和AppWidgetManager.ACTION_APPWIDGET_DISABLED而执行的onEnbaled()和onDisabled()中是恰当的设置实现AppWidgetProvider的包能不能被移除设置的恰当点。

    在onEnbaled()中,该AppWidgetProvider正在被使用,不让被杀掉:

           PackageManager pm = context.getPackageManager();
           pm.setComponentEnabledSetting(
                   new ComponentName("com.android.settings",".widget.SettingsAppWidgetProvider"),
                   PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                   PackageManager.DONT_KILL_APP);

    在onDisabled()中,该AppWidgetProvider不再被使用,可以被杀掉了:

           Class clazz =com.android.settings.widget.SettingsAppWidgetProvider.class;
           PackageManager pm = context.getPackageManager();
            pm.setComponentEnabledSetting(
                   new ComponentName("com.android.settings",".widget.SettingsAppWidgetProvider"),
                   PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                   PackageManager.DONT_KILL_APP);

    五、AppWidgetProvider通过RemoteViews提供内容

    在需要AppWIdgetProvider提供RemoteViews时,AppWidget系统会发出AppWidgetManager.ACTION_APPWIDGET_UPDATE广播,进而onUpdate()会被执行。

    AppWidgetProvider onUpdate remoteViews

    图三、AppWidgetProvider提供RemoteViews

    • 在onUpdate()中,创建RemoteViews的实例,传入AppWidgteProvider所在的包名和该AppWidget所要用的Layout;[Seq#5]
    • 如果要响应layoutId中某个viewId被点击操作,要创建本地的PendingIntent,并通过setOnClickPendingIntetn设置到RemoteViews中;[Seq#6~ #9]
    • 为layoutId中要显示的控件加上显示元素,比如某个ImageView的ImageResource。[Seq#10]
    • 用AppWidgetManager.updateAppWidget()更新RemoteViews到系统中,AppWidget系统会更新与之绑定的AppWidgetHost。[Seq#11]

    RemoteViews设置与显示详细实现可参考《Android中RemoteViews的实现》;AppWidgetHost如何更新RemoteView可参看《Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色》。

    六、通过PendingIntent设置按钮响应

    上面讲到,可以通过给RemoteViews设置PendingIntent获知感兴趣的View被点击时的响应:

           Intent launchIntent = new Intent();
           launchIntent.setClass(context, SettingsAppWidgetProvider.class);
           launchIntent.addCategory(Intent.CATEGORY_ALTERNATIVE);
           launchIntent.setData(Uri.parse("custom:" + buttonId));
           PendingIntent pi = PendingIntent.getBroadcast(context, 0 /* norequestCode */,
                   launchIntent, 0 /* no flags */);

    buttonId是layout中的各个Button对应的自定义的Id,该Id只要在本程序中用来能够区分出是哪个Button就可以,被指进”custom:”参数。

    AppWidgetProvider本身就是个BroadcastReceiver,在其onReceive()中,就可以判断出是哪个Button被点击了:

            if(intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
               Uri data = intent.getData();
               int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
               if (buttonId == BUTTON_WIFI) {
                   // 切换Wifi状态
               } else if (buttonId == BUTTON_BRIGHTNESS) {
                   // 切换亮度
               } else if (buttonId == BUTTON_SYNC) {
                   // 切换数据同步设置
               } else if (buttonId == BUTTON_GPS) {
                   // 切换GPS打开开关
               } else if (buttonId == BUTTON_BLUETOOTH) {
                   // 切换蓝牙打开状态
               }
            }

    总结

    本文讲述了:

    • 实现一个AppWidgetProvider所需要的配置和实现;
    • AppWidgetProvider如何被AppWidget识别和加入到已安装列表;
    • AppWidgetProvider如何生成RemoteViews对象,并更新到AppWidgetHost;
    • AppWidgetProvider响应按钮操作。

    可进一步参考的文章

    Android AppWidget框架

    AppWidget系统框架。

    Android中选取并绑定AppWidget

    看如何调用本文描述的已经获取的AppWidgetProvider列表的。

    Android中AppWidget的分析与应用:AppWidgetProvider

    本文。

    Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色

    可以看选取并绑定AppWidgetProvider之后,Launcher作为AppWidgetHost如何创建显示RemoteViews里AppWidgetProvider所提供的图形元素。还可以看到数据模型的加载。

    Android中RemoteViews的实现

    RemoteViews的内部如何实现设置进去的OnClickPendingIntent和ViewImageResource的。

    分享到:
    评论

    相关推荐

      android 桌面组件 App widget的使用 AppWidgetProvider

      &lt;action android:name="android.appwidget.action.APPWIDGET_UPDATE" /&gt; android:name="android.appwidget.provider" android:resource="@xml/app_widget_info" /&gt; ``` 其中,`&lt;meta-data&gt;` 标签引用了一个 ...

      appWidget启动Service

      在Android开发中,AppWidget是桌面小部件,它允许用户在主屏幕上与应用程序进行交互,而无需实际打开应用。Service是Android系统中的一个组件,它可以在后台长时间运行,执行一些耗时的操作,如音乐播放、数据同步等...

      android appwidget使用说明及示例程序

      1. **AppWidgetProvider**: 这是Android的BroadcastReceiver子类,用于接收和处理与AppWidget相关的广播事件,如添加、删除或更新小部件。 2. **AppWidgetInfo**: 定义了小部件的元数据,包括其大小、名称和图标等...

      appWidget启动Activity

      在Android开发中,`appWidget` 是一种可以在用户主屏幕上显示的小部件,它提供了一种无需打开应用程序就能与用户交互的方式。本篇文章将深入探讨如何使用`appWidget`来启动一个`Activity`。 首先,我们需要了解`app...

      android之appwidget(一)简单appwidget

      在Android平台上,AppWidget是系统桌面的一个组成部分,它允许开发者创建小型、静态或动态的应用程序组件,用户可以直接在主屏幕上与这些组件互动,无需打开完整的应用程序。本篇将深入探讨如何开发一个简单的App...

      Android AppWidget实例验证

      在Android平台上,AppWidget是Android系统提供的一种小部件功能,允许开发者创建可以在用户主屏幕上显示的小应用程序。AppWidgets虽然小巧,但能为用户提供便捷的功能访问,例如天气预报、音乐播放控制等。本教程将...

      android之appwidget(四)终 appwidget控件更新

      在Android平台上,AppWidget是能够将小部件添加到用户主屏幕上的迷你应用程序,它们提供了一种无需打开完整应用即可展示信息或执行简单操作的方式。本文将深入探讨Android AppWidget的第四部分,主要关注如何更新App...

      Android App Widget 开发

      &lt;action android:name="android.appwidget.action.APPWIDGET_UPDATE" /&gt; android:name="android.appwidget.provider" android:resource="@xml/app_widget_info" /&gt; &lt;service android:name="....

      android 桌面控件appwidget

      2. **AppWidget提供者**(AppWidgetProvider):这是一个特殊的BroadcastReceiver,用于接收并处理与AppWidget相关的广播事件,如添加、删除或更新AppWidget。 3. **服务或BroadcastReceiver**:这通常用于更新App...

      android之appwidget(二)启动新activity

      在Android开发中,AppWidget是桌面小部件,它允许用户在主屏幕上与应用程序进行交互,而无需实际打开应用。在本篇博文中,我们将深入探讨如何在AppWidget中启动一个新的Activity,这通常是为了提供更丰富的用户体验...

      android AppWidget ListView

      在Android开发中,AppWidget是桌面小部件,它允许开发者将应用程序的功能直接集成到用户的主屏幕上,无需打开应用。在本案例中,我们探讨的是如何在AppWidget中使用ListView,这是一个增强用户交互的方式,使小部件...

      Android -- AppWidget源码.zip

      在Android系统中,AppWidget是桌面小部件的实现,它允许开发者将应用程序的功能和信息集成到用户的主屏幕上,用户无需打开应用即可与之交互。这个压缩包“Android -- AppWidget源码.zip”很可能是包含了Android App...

      Android应用源码之Android小部件AppWidget.zip

      通过分析这个源码示例,开发者可以深入了解AppWidget的工作原理和实际应用,从而提高自己在Android开发中的技能,尤其是对于那些希望创建具有个性化和实用性的桌面小部件的开发者来说,这将是一个非常有价值的参考...

      android之appwidget

      AppWidget是Android操作系统提供的一种组件,允许开发者在用户主屏幕上创建小型应用程序,这些小部件无需用户直接启动应用就能显示和交互。AppWidget为用户提供了一种快速访问应用功能的方式,增强了用户体验。本文...

      android appwidget 经典完整代码

      Android AppWidget是Android系统提供的一种桌面小部件功能,允许开发者创建可以放置在用户主屏幕上的小型应用程序。这些小部件不需要用户直接启动,而是持续显示在屏幕上,为用户提供快速访问或控制应用的功能。在这...

      Android应用源码之Android小部件AppWidget-IT计算机-毕业设计.zip

      - **论文撰写**:可以从设计思路、实现技术、用户交互等方面阐述AppWidget的设计与实现,分析其在实际应用中的价值和挑战。 通过这个毕业设计项目,学生可以深入理解Android系统的组件模型,掌握BroadcastReceiver...

      android appwidget测试

      Appwidget就是手机应用中常常放在桌面(即home)上的一些应用程序,比如说闹钟等。这种应用程序的特点是它上面显示的内容能够根据系统内部的数据进行更新,不需要我们进入到程序的内部去,比如说闹钟指针的摆动等。本...

      安卓Android源码——安卓Android小部件AppWidget.rar

      在Android应用开发中,AppWidget的创建涉及以下几个核心知识点: 1. **AppWidgetProvider**: 这是AppWidget的主要组件,类似于BroadcastReceiver,用于监听和响应来自系统的广播事件,比如用户添加、删除或更新小...

      应用创建AppWidget的小demo

      在Android开发中,AppWidget的使用可以提升用户体验,让用户能快速访问或操作应用的核心功能。本教程将通过一个名为"AppWidgetTest"的小型示例项目,详细介绍如何在Android应用中创建和管理AppWidget。 1. **...

    Global site tag (gtag.js) - Google Analytics