from: http://blog.csdn.net/thl789/article/details/7887968

本文从开发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的onReceiver()实现中已经对接收到的ActionAppWidgetManager.ACTION_APPWIDGET_UPDATE / AppWidgetManager.ACTION_APPWIDGET_DELETED/ AppWidgetManager.ACTION_APPWIDGET_ENABLED以及AppWidgetManager.ACTION_APPWIDGET_DISABLED做了处理,分别执行onUpdate()/ onDeleted() / onEnabled() / onDisabled()。

所以,AppWidgetProvider的实现类,要override onReceive(),以及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()的实现:

    void addProvidersForPackageLocked(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>中。


图二、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提供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中选取并绑定AppWidget
  • 下一篇:Android中的广播也定向
 

Android中AppWidget的分析与应用:AppWidgetProvider .相关推荐

  1. Android中Parcel的分析以及使用

    简单点来说:Parcel就是一个存放读取数据的容器, android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel ...

  2. android中SELINUX规则分析和语法简介

    点击打开链接 1. SELINUX是可以理解为一种Android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统 我们可以通过配置SELINUX的相关policy,来 ...

  3. Android中图片压缩分析(上)

    此文章首发:https://mp.weixin.qq.com/s/QZ-XTsO7WnNvpnbr3DWQmg 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种: ...

  4. Android中Bitmap的分析与使用

    下面总结一下Bitmap的分析与使用 Bitmap的创建 创建Bitmap的时候,Java不提供new Bitmap()的形式去创建,而是通过BitmapFactory中的静态方法去创建,如:Bitm ...

  5. ANDROID中的VOLD分析

    现在可能很少有人会用mknod这个命令了,也很少有使用它的机会,但就在几年前,这还是一项linux工程师的必备技能,在制作文件系统前或加载新的驱动前,我们必须小心翼翼的创建设备节点. 不需要使用mkn ...

  6. android中hprof文件分析

    Hprof基本概念 hprof最初是由J2SE支持的一种二进制堆转储格式,hprof文件保存了当前java堆上所有的内存使用信息,能够完整的反映虚拟机当前的内存状态. 格式 Hprof文件由Fixed ...

  7. Android中Touch事件分析--解决HorizontalScrollView滑动和按钮事件触发问题

    1).按下按钮,不滑动,触发按钮功能 (2).按下按钮,滑动触发滑动事件 这里的按下包含长按和短按情况 首先要解决这个问题需要明白Android中的Touch事件是如何进行处理的,这里有一篇文章:An ...

  8. Android中应用安装分析

    #1 安装方式 1 安装系统APK和预制APK时,通过PMS的构造函数中安装,即第一次开机时安装应用,没有安装界面. 2 网络下载安装,通过应用商店等,即调用PackageManager.instal ...

  9. Android中Parcel的分析和使用

    http://www.360doc.com/content/13/0419/12/9171956_279433672.shtml

最新文章

  1. Robocopy.exe使用详例
  2. 新手关于import/export的理解
  3. babel import语法 js_搭建开发JS库的运行环境
  4. 初等代数(3):代数方程、指数与对数、复数
  5. redis的五种数据结构
  6. byte比较_byte[]与String互转不一致引起的bug
  7. JAVA面试常考系列八
  8. 关于@SuppressWarnings(unchecked)注解
  9. 仿生尺蠖机器人_【小小科学家】六脚爬行机器人
  10. 登录日志怎么实现_运维必看:日志标准化必须面对的 4 类问题
  11. mac卸载python3.8_如何使用Homebrew在Mac上默认设置Python3.8?
  12. 软件测试 接口测试 入门Jmeter 接口关联 提取器 断言 与fiddler配合使用 使Jmeter录制和创建脚本 操作数据库 持续集成测试
  13. 免费屏幕录像软件哪个好?简单几步教你快速录制无水印的高清视频!
  14. 瑞芯微Rockchips RK3368对比晶晨Amlogic S905
  15. Android实战技巧之三十四:用TableLayout伪装表格显示数据
  16. ring buffer示例
  17. 阿里测开岗定级P7全流程加面试真题
  18. 视频怎么转文字?快把这些方法收好
  19. Lemon tree 柠檬树
  20. 如何实现年会主分会场视频直播?

热门文章

  1. lucene全文检索的概念
  2. locate: command not found updatedb: command not found
  3. 支付宝回调验签失败解决办法
  4. Java国际化概念和使用介绍
  5. 谱比法计算岩石的品质因子的c语言程序,地层品质因子计算方法及系统与流程...
  6. 大前端算法入门之二分查找
  7. 前端学成什么样就能找工作了?
  8. 前端系统化学习【JS篇】:(四-1)基本数据类型之Number篇
  9. web前端开发:JavaScript 基本语法,
  10. localstorage存mysql_存储localstorage