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文件来描述布局属性。
  3. 在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的分析与应用:AppWidgetProvider .的更多相关文章

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

    作者: shawnzhao,QQ音乐技术团队一员 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种:其一是质量压缩,其二是下采样压缩. 前者是在不改变图片尺寸的情 ...

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

    本文转载自:https://blog.csdn.net/LoongEmbedded/article/details/62430039 1. SELINUX是可以理解为一种Android上面的安全机制, ...

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

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

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

    之前写过关于HorizontalScrollView滑动和按钮事件触发问题,但是不能所有的情况,最近几天一直在想这个问题,今天有一个比较好的解决思路,最终应用在项目里面效果也很好,首先说明一下功能: ...

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

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

  6. Android中应用安装分析

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

  7. Android中Context样式分析

    目录 1.样式定义以及使用 1.1.默认样式 1.2.样式定义及使用 1.3.当前样式下attr属性的获取 1.4.属性集合的定义与获取 2.Activity中Theme的初始化流程 2.1.系统调用 ...

  8. Android中Adapter和Bridge模式理解和应用

    一 Adapter模式 意图: 将一个类的接口转换成客户希望的另外一个接口. Adapter模式使得原本由于接口不兼容而不能在一起工作的那些类可以在一起工作. 适用性: 使用一个已存在的类,而它的接口 ...

  9. Android中Input型输入设备驱动原理分析(一)

    转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...

随机推荐

  1. Top 10 Java Debugging Tips with Eclipse

    In this tutorial we will see about debugging java applications using Eclipse. Debugging helps us to ...

  2. Magic of David Copperfield II(奇偶性)

    题目大意:这是一个魔术游戏,首先把你的手指放在一个左上角的格子里面,然后魔术师说你可以移动K1步,移动完之后,他会删除一些方格,并且说,你肯定不在这里,删除的方格不可以再去了,然后让你再走K2步,继续 ...

  3. poj 2312 Battle City【bfs+优先队列】

      Battle City Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7579   Accepted: 2544 Des ...

  4. 你能在windows上创建一个叫做AUX的文件夹吗?

    Windows的文件名不能有如下这些特殊符号,这个大家都比较熟悉了. < (less than) > (greater than) : (colon) " (double quo ...

  5. 给考研计划报考“管理学科学与project”方向大学生的建议(大二阶段)

    [来信]丁老师: 你好.在做学习计划前能了解到PDCA循环,着实感到受益匪浅. 这一理念不仅适用于质量管理体系.也适用于一切循序渐进的管理工作. 了解PDCA循环后.对此次学习计划的制定起到一定的导向 ...

  6. uva 11178 Morley&#39;s Theorem(计算几何-点和直线)

    Problem D Morley's Theorem Input: Standard Input Output: Standard Output Morley's theorem states tha ...

  7. 理解 Linux 网络栈(1):Linux 网络协议栈简单总结 图

    http://www.cnblogs.com/sammyliu/p/5225623.html

  8. Mysql重要配置参数的整理2

    http://ssydxa219.iteye.com/category/209848 下面开始优化下my.conf文件(这里的优化只是在mysql本身的优化,之前安装的时候也要有优化) cat /et ...

  9. JMX 与系统管理--转

    前言 在 Java 程序的运行过程中,对 JVM 和系统的监测一直是 Java 开发人员在开发过程所需要的.一直以来,Java 开发人员必须通过一些底层的 JVM API,比如 JVMPI 和 JVM ...

  10. Scope Chain(作用域链)

    本章,我们讨论一下ECMAScript中的作用域链 , 开门见山. 什么是作用域链 i.ECMAScript是允许创建内部函数的,甚至能从父函数中返回这些函数.作用域链正是内部上下文中所有变量对象(及 ...