转自:http://www.cnblogs.com/skywang12345/p/3158310.html

Android 之窗口小部件详解--App Widget

版本号 说明 作者 日期
1.0 添加App Widge介绍和示例 Sky Wang 2013/06/27

1 App Widget简介

App Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget Provider来发布一个Widget。

本文参考Android官方文本,先介绍App Widget的主要组件,然后再以示例来详细说明。

2 App Widget主要的相关类介绍

2.1 AppWidgetProvider

AppWidgetProvider继承自 BroadcastReceiver,它能接收 widget 相关的广播,例如 widget 的更新、删除、开启和禁用等。

AppWidgetProvider中的广播处理函数如下:

onUpdate()
当 widget 更新时被执行。
同样,当用户首次添加 widget 时,onUpdate() 也会被调用,这样 widget 就能进行必要的设置工作(如果需要的话) 。但是,如果定义了 widget 的 configure属性(即android:config,后面会介绍),那么当用户首次添加 widget 时,onUpdate()不会被调用;之后更新 widget 时,onUpdate才会被调用。

onAppWidgetOptionsChanged()
当 widget 被初次添加 或者 当 widget 的大小被改变时,执行onAppWidgetOptionsChanged()。你可以在该函数中,根据 widget 的大小来显示/隐藏某些内容。可以通过 getAppWidgetOptions() 来返回 Bundle 对象以读取 widget 的大小信息,Bundle中包括以下信息:
OPTION_APPWIDGET_MIN_WIDTH-- 包含 widget 当前宽度的下限,以dp为单位。
OPTION_APPWIDGET_MIN_HEIGHT-- 包含 widget 当前高度的下限,以dp为单位。
OPTION_APPWIDGET_MAX_WIDTH-- 包含 widget 当前宽度的上限,以dp为单位。
OPTION_APPWIDGET_MAX_HEIGHT-- 包含 widget 当前高度的上限,以dp为单位。

onAppWidgetOptionsChanged() 是 Android 4.1 引入的。

onDeleted(Context, int[])
当 widget 被删除时被触发。

onEnabled(Context)
当第1个 widget 的实例被创建时触发。也就是说,如果用户对同一个 widget 增加了两次(两个实例),那么onEnabled()只会在第一次增加widget时触发。

onDisabled(Context)
当最后1个 widget 的实例被删除时触发。

onReceive(Context, Intent)
接收到任意广播时触发,并且会在上述的方法之前被调用。

总结,AppWidgetProvider 继承于 BroadcastReceiver。实际上,App Widge中的onUpdate()、onEnabled()、onDisabled()等方法都是在 onReceive()中调用的;是onReceive()对特定事情的响应函数。参考android源码frameworks/base/core/java/android/appwidget/AppWidgetProvider.java中onReceive()的定义:

View Code

2.2 AppWidgetProviderInfo

AppWidgetProviderInfo描述一个App Widget元数据,比如App Widget的布局,更新频率,以及AppWidgetProvider 类。这个应该在XML里定义。下面以XML示例来对AppWidgetProviderInfo中常用的类型进行说明。

示例XML
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  android:minWidth="40dp"
  android:minHeight="40dp"
  android:updatePeriodMillis="86400000"
  android:previewImage="@drawable/preview"
  android:initialLayout="@layout/example_appwidget"
  android:configure="com.example.android.ExampleAppWidgetConfigure"
  android:resizeMode="horizontal|vertical"
  android:widgetCategory="home_screen|keyguard"
  android:initialKeyguardLayout="@layout/example_keyguard">
</appwidget-provider>


示例说明
minWidth 和minHeight
它们指定了App Widget布局需要的最小区域。
缺省的App Widgets所在窗口的桌面位置基于有确定高度和宽度的单元网格中。如果App Widget的最小长度或宽度和这些网格单元的尺寸不匹配,那么这个App Widget将上舍入(上舍入即取比该值大的最接近的整数——译者注)到最接近的单元尺寸。
注意:app widget的最小尺寸,不建议比 “4x4” 个单元格要大。关于app widget的尺寸,后面在详细说明。

minResizeWidth 和 minResizeHeight
它们属性指定了 widget 的最小绝对尺寸。也就是说,如果 widget 小于该尺寸,便会因为变得模糊、看不清或不可用。 使用这两个属性,可以允许用户重新调整 widget 的大小,使 widget 的大小可以小于 minWidth 和 minHeight。
注意:(01) 当 minResizeWidth 的值比 minWidth 大时,minResizeWidth 无效;当 resizeMode 的取值不包括 horizontal 时,minResizeWidth 无效。
(02) 当 minResizeHeight 的值比 minHeight 大时,minResizeHeight 无效;当 resizeMode 的取值不包括 vertical 时,minResizeHeight 无效。

updatePeriodMillis
它定义了 widget 的更新频率。实际的更新时机不一定是精确的按照这个时间发生的。建议更新尽量不要太频繁,最好是低于1小时一次。 或者可以在配置 Activity 里面供用户对更新频率进行配置。实际上,当updatePeriodMillis的值小于30分钟时,系统会自动将更新频率设为30分钟!关于这部分,后面会详细介绍。
注意: 当更新时机到达时,如果设备正在休眠,那么设备将会被唤醒以执行更新。如果更新频率不超过1小时一次,那么对电池寿命应该不会造成多大的影响。 如果你需要比较频繁的更新,或者你不希望在设备休眠的时候执行更新,那么可以使用基于 alarm 的更新来替代 widget 自身的刷新机制。将 alarm 类型设置为 ELAPSED_REALTIME 或 RTC,将不会唤醒休眠的设备,同时请将 updatePeriodMillis 设为 0。

initialLayout
指向 widget 的布局资源文件

configure
可选属性,定义了 widget 的配置 Activity。如果定义了该项,那么当 widget 创建时,会自动启动该 Activity。

previewImage
指定预览图,该预览图在用户选择 widget 时出现,如果没有提供,则会显示应用的图标。该字段对应在 AndroidManifest.xml 中 receiver 的 android:previewImage 字段。由 Android 3.0 引入。

autoAdvanceViewId
指定一个子view ID,表明该子 view 会自动更新。在 Android 3.0 中引入。

resizeMode
指定了 widget 的调整尺寸的规则。可取的值有: "horizontal", "vertical", "none"。"horizontal"意味着widget可以水平拉伸,“vertical”意味着widget可以竖值拉伸,“none”意味着widget不能拉伸;默认值是"none"。Android 3.1 引入。

widgetCategory
指定了 widget 能显示的地方:能否显示在 home Screen 或 lock screen 或 两者都可以。它的取值包括:"home_screen" 和 "keyguard"。Android 4.2 引入。

initialKeyguardLayout
指向 widget 位于 lockscreen 中的布局资源文件。Android 4.2 引入。

3 App Widget布局说明

3.1 添加 widget 到lock screen中

默认情况下(即不设置android:widgetCategory属性),Android是将widget添加到 home screen 中。
但在Android 4.2中,若用户希望 widget 可以被添加到lock screen中,可以通过设置 widget 的 android:widgetCategory 属性包含keyguard来完成。

当你把 widget 添加到lock screen中时,你可能对它进行定制化操作,以区别于添加到home screen中的情况。 你能够通过 getAppWidgetOptions() 来进行判断 widget 是被添加到lock screen中,还是home screen中。通过 getApplicationOptions() 获取 Bundle对象,然后读取 Bundle 的OPTION_APPWIDGET_HOST_CATEGORY值:若值为 WIDGET_CATEGORY_HOME_SCREEN, 则表示该 widget 被添加到home screen中; 若值为 WIDGET_CATEGORY_KEYGUARD,则表示该 widget 被添加到lock screen中。

另外,你应该为添加到lock screen中的 widget 单独使用一个layout,可以通过 android:initialKeyguardLayout 来指定。而 widget 添加到home screen中的layout则可以通过 android:initialLayout 来指定。


3.2布局

一 Widget窗口组件

如上图所示,典型的App Widget有三个主要组件:一个边界框(A bounding box),一个框架(a Frame),和控件的图形控件(Widget Controls)和其他元素。App Widget并不支持全部的视图窗口,它只是支持视图窗口的一个子集,后面会详细说明支持哪些视图窗口。
要设计美观的App Widget,建议在“边界框和框架之间(Widget Margins)”以及“框架和控件(Widget Padding)”之间填留有空隙。在Android4.0以上的版本,系统为自动的保留这些空隙。

二 Widget窗口大小

在AppWidgetProviderInfo中已经介绍了,minWidth 和minHeight 用来指定了App Widget布局需要的最小区域。缺省的App Widgets所在窗口的桌面位置基于有确定高度和宽度的单元网格中。如果App Widget的最小长度或宽度和这些网格单元的尺寸不匹配,那么这个App Widget将上舍入(上舍入即取比该值大的最接近的整数——译者注)到最接近的单元尺寸。
例如,很多手机提供4x4网格,平板电脑能提供8x7网格。当widget被添加到时,在满足minWidth和minHeight约束的前提下,它将被占领的最小数目的细胞。

粗略计算minWidth和minHeight,可以参考下面表格:

单元格个数
(行 / 列)
对应的设置大小 (dp)
(minWidth/minHeight)
1 40dp
2 110dp
3 180dp
4 250dp
n 70 ×n− 30



详细计算minWidth和minHeight,要计算各个区域的大小。以下图为例:

计算结果
minWidth = 144dp + (2 × 8dp) + (2 × 56dp) = 272dp
minHeight = 48dp + (2 × 4dp) = 56dp


3.3 App Widget支持的布局和控件

Widget并不支持所有的布局和控件,而仅仅只是支持Android布局和控件的一个子集。
(01) App Widget支持的布局:
  FrameLayout
  LinearLayout
  RelativeLayout
  GridLayout
(02) App Widget支持的控件:
  AnalogClock
  Button
  Chronometer
  ImageButton
  ImageView
  ProgressBar
  TextView
  ViewFlipper
  ListView
  GridView
  StackView
  AdapterViewFlipper

4 App Widget应用实例

应用实例需求:建立一个Widget示例,要求Widget能被添加到主屏中,widget包含3个成分:文本、按钮和图片。文本要求:显示提示信息;按钮要求:点击按钮,弹出一个Toast提示框;图片要求:每个5秒随机更新一张图片。

第1步 新建Android工程
新建空白的Android工程,即不需要在建立Activity子类。

第2步 编辑manifest
修改AndroidManifest.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.skywang.widget"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="14"        android:targetSdkVersion="17" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >                <!-- 声明widget对应的AppWidgetProvider -->        <receiver android:name=".ExampleAppWidgetProvider" >            <intent-filter>                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />                <action android:name="com.skywang.widget.UPDATE_ALL"/>            </intent-filter>            <meta-data android:name="android.appwidget.provider"                android:resource="@xml/example_appwidget_info" />        </receiver>                <service android:name=".ExampleAppWidgetService" >            <intent-filter>                <action android:name="android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE" />            </intent-filter>        </service>            </application></manifest>

说明
(01)ExampleAppWidgetProvider是继承于的AppWidgetProvider类,用来响应widget的添加、删除、更新等操作。
(02)android.appwidget.action.APPWIDGET_UPDATE,必须要显示声明的action!因为所有的widget的广播都是通过它来发送的;要接收widget的添加、删除等广播,就必须包含它。
(03)action android:name="com.skywang.widget.UPDATE_ALL,这是我自己添加了,是为了接收服务所发送的更新图片的广播。
(04)<meta-data>指定了 AppWidgetProviderInfo 对应的资源文件
android:name-- 指定metadata名,通过android.appwidget.provider来辨别data。
android:resource-- 指定 AppWidgetProviderInfo 对应的资源路径。即,xml/example_appwidget_info.xml。
(05)ExampleAppWidgetService是用于更新widget中的图片的服务。
(06)android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE用于启动服务的action。


第3步 编辑AppWidgetProviderInfo对应的资源文件
在当前工程下新建xml目录(若xml目录不存在的话);并在xml目录下新建example_appwidget_info.xml文件。xml文件内容如下:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"    android:minWidth="180dp"    android:minHeight="180dp"    android:previewImage="@drawable/preview"    android:initialLayout="@layout/example_appwidget"    android:resizeMode="horizontal|vertical"    android:widgetCategory="home_screen|keyguard">        <!--    android:minWidth : 最小宽度    android:minHeight : 最小高度    android:updatePeriodMillis : 更新widget的时间间隔(ms),"86400000"为1个小时    android:previewImage : 预览图片    android:initialLayout : 加载到桌面时对应的布局文件    android:resizeMode : widget可以被拉伸的方向。horizontal表示可以水平拉伸,vertical表示可以竖直拉伸    android:widgetCategory : widget可以被显示的位置。home_screen表示可以将widget添加到桌面,keyguard表示widget可以被添加到锁屏界面。    android:initialKeyguardLayout : 加载到锁屏界面时对应的布局文件     -->    </appwidget-provider>

说明:
(01)android:previewImage,用于指定预览图片。即搜索到widget时,查看到的图片。若没有设置的话,系统为指定一张默认图片。
(02)android:updatePeriodMillis更新widget的时间间隔(ms)。在实际测试中,发现设置android:updatePeriodMillis的值为5秒时,不管用!跟踪android源代码,发现:
当android:updatePeriodMillis的值小于30分钟时,会被设置为30分钟。也就意味着,即使将它的值设为5秒,实际上也是30分钟之后才更新。因此,我们若向动态的更新widget的某组件,最好通过service、AlarmManager、Timer等方式;本文采用的是service。

android源码中widget服务文件:frameworks/base/services/java/com/android/server/AppWidgetService.java
与 android:updatePeriodMillis相关代码如下:

    ...private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes    ...void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {    ...    // 获取updatePeriodMillis的值    long period = p.info.updatePeriodMillis;    // 当updatePeriodMillis小于30分钟时,设为它为30分钟    if (period < MIN_UPDATE_PERIOD) {        period = MIN_UPDATE_PERIOD;    }        mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,            SystemClock.elapsedRealtime() + period, period, p.broadcast);   ...}


第4步 编辑example_appwidget.xml等资源文件
新建layout/example_appwidget.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >      <LinearLayout         android:layout_width="wrap_content"          android:layout_height="wrap_content"        android:layout_gravity="center"         android:orientation="horizontal" >                <TextView              android:layout_width="wrap_content"              android:layout_height="wrap_content"              android:text="HomeScreen Widget" />                    <Button            android:id="@+id/btn_show"            android:layout_width="wrap_content"              android:layout_height="wrap_content"              android:text="Show" />    </LinearLayout>             <ImageView        android:id="@+id/iv_show"        android:layout_width="wrap_content"          android:layout_height="wrap_content"         android:layout_gravity="center"/>         </LinearLayout>

说明:

(01)example_appwidget布局中,包含了“1个文本,1个按钮和1个图片控件”。

点击下载:本工程中需要用到的图片
将所有的图片放到drawable目录中。这里的drawable广义的,指工程实际用到的图片所在目录;例如,我自己的是放在drawabld-hdmi中。


第5步 编辑ExampleAppWidgetProvider.java
在当前工程下,新建ExampleAppWidgetProvider.java,代码如下:

package com.skywang.widget;import android.app.PendingIntent;import android.appwidget.AppWidgetManager;  import android.appwidget.AppWidgetProvider;  import android.content.Context;  import android.content.Intent;  import android.os.Bundle;import android.net.Uri;import android.widget.RemoteViews;import android.widget.Toast;import android.util.Log;import java.util.Set;import java.util.HashSet;import java.util.Iterator;/* * @author : skywang <[email protected]> * description : 提供App Widget */public class ExampleAppWidgetProvider extends AppWidgetProvider {    private static final String TAG = "ExampleAppWidgetProvider";    private boolean DEBUG = false;     // 启动ExampleAppWidgetService服务对应的action    private final Intent EXAMPLE_SERVICE_INTENT =             new Intent("android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE");    // 更新 widget 的广播对应的action    private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";    // 保存 widget 的id的HashSet,每新建一个 widget 都会为该 widget 分配一个 id。    private static Set idsSet = new HashSet();    // 按钮信息    private static final int BUTTON_SHOW = 1;    // 图片数组    private static final int[] ARR_IMAGES = {         R.drawable.sample_0,         R.drawable.sample_1,         R.drawable.sample_2,         R.drawable.sample_3,         R.drawable.sample_4,         R.drawable.sample_5,        R.drawable.sample_6,        R.drawable.sample_7,    };        // onUpdate() 在更新 widget 时,被执行,    @Override    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {        Log.d(TAG, "onUpdate(): appWidgetIds.length="+appWidgetIds.length);        // 每次 widget 被创建时,对应的将widget的id添加到set中        for (int appWidgetId : appWidgetIds) {            idsSet.add(Integer.valueOf(appWidgetId));        }        prtSet();    }        // 当 widget 被初次添加 或者 当 widget 的大小被改变时,被调用     @Override      public void onAppWidgetOptionsChanged(Context context,              AppWidgetManager appWidgetManager, int appWidgetId,              Bundle newOptions) {        Log.d(TAG, "onAppWidgetOptionsChanged");        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId,                  newOptions);      }          // widget被删除时调用      @Override      public void onDeleted(Context context, int[] appWidgetIds) {          Log.d(TAG, "onDeleted(): appWidgetIds.length="+appWidgetIds.length);        // 当 widget 被删除时,对应的删除set中保存的widget的id        for (int appWidgetId : appWidgetIds) {            idsSet.remove(Integer.valueOf(appWidgetId));        }        prtSet();                super.onDeleted(context, appWidgetIds);      }    // 第一个widget被创建时调用      @Override      public void onEnabled(Context context) {          Log.d(TAG, "onEnabled");        // 在第一个 widget 被创建时,开启服务        context.startService(EXAMPLE_SERVICE_INTENT);                super.onEnabled(context);      }          // 最后一个widget被删除时调用      @Override      public void onDisabled(Context context) {          Log.d(TAG, "onDisabled");        // 在最后一个 widget 被删除时,终止服务        context.stopService(EXAMPLE_SERVICE_INTENT);        super.onDisabled(context);      }            // 接收广播的回调函数    @Override      public void onReceive(Context context, Intent intent) {          final String action = intent.getAction();        Log.d(TAG, "OnReceive:Action: " + action);        if (ACTION_UPDATE_ALL.equals(action)) {            // “更新”广播            updateAllAppWidgets(context, AppWidgetManager.getInstance(context), idsSet);        } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {            // “按钮点击”广播            Uri data = intent.getData();            int buttonId = Integer.parseInt(data.getSchemeSpecificPart());            if (buttonId == BUTTON_SHOW) {                Log.d(TAG, "Button wifi clicked");                Toast.makeText(context, "Button Clicked", Toast.LENGTH_SHORT).show();            }        }                super.onReceive(context, intent);      }      // 更新所有的 widget     private void updateAllAppWidgets(Context context, AppWidgetManager appWidgetManager, Set set) {        Log.d(TAG, "updateAllAppWidgets(): size="+set.size());                // widget 的id        int appID;        // 迭代器,用于遍历所有保存的widget的id        Iterator it = set.iterator();        while (it.hasNext()) {            appID = ((Integer)it.next()).intValue();                // 随机获取一张图片            int index = (new java.util.Random().nextInt(ARR_IMAGES.length));                        if (DEBUG) Log.d(TAG, "onUpdate(): index="+index);                        // 获取 example_appwidget.xml 对应的RemoteViews                        RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);                        // 设置显示图片            remoteView.setImageViewResource(R.id.iv_show, ARR_IMAGES[index]);                        // 设置点击按钮对应的PendingIntent:即点击按钮时,发送广播。            remoteView.setOnClickPendingIntent(R.id.btn_show, getPendingIntent(context,                    BUTTON_SHOW));            // 更新 widget            appWidgetManager.updateAppWidget(appID, remoteView);                }            }    private PendingIntent getPendingIntent(Context context, int buttonId) {        Intent intent = new Intent();        intent.setClass(context, ExampleAppWidgetProvider.class);        intent.addCategory(Intent.CATEGORY_ALTERNATIVE);        intent.setData(Uri.parse("custom:" + buttonId));        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0 );        return pi;    }    // 调试用:遍历set    private void prtSet() {        if (DEBUG) {            int index = 0;            int size = idsSet.size();            Iterator it = idsSet.iterator();            Log.d(TAG, "total:"+size);            while (it.hasNext()) {                Log.d(TAG, index + " -- " + ((Integer)it.next()).intValue());            }        }    }}

说明
(01) 当我们创建第一个widget到桌面时,会执行onEnabled()。在onEnabled()中通过 context.startService(EXAMPLE_SERVICE_INTENT) 启动服务ExampleAppWidgetService。服务的作用就是每隔5秒发送一个ACTION_UPDATE_ALL广播给我们,用于更新widget中的图片。
仅仅当我们创建第一个widget时才会启动服务,因为onEnabled()只会在第一个widget被创建时才执行。
(02) 当我们删除最后一个widget到桌面时,会执行onDisabled()。在onDisabled()中通过 context.stopService(EXAMPLE_SERVICE_INTENT) 终止服务ExampleAppWidgetService。
仅仅当我们删除最后一个widget时才会终止服务,因为onDisabled()只会在最后一个widget被删除时才执行。
(03) 本工程中,每添加一个widget都会执行onUpdate()。例外情况:在定义android:configure的前提下,第一次添加widget时不会执行onUpdate(),而是执行android:configure中定义的activity。
(04) onReceive()中,处理两个广播:更新桌面的widget 以及 响应按钮点击广播。
当收到ACTION_UPDATE_ALL广播时,调用updateAllAppWidgets()来更新所有的widget。
当收到的广播的categery为Intent.CATEGORY_ALTERNATIVE,并且scheme为BUTTON_SHOW时,对应是按钮点击事件。按钮的监听是在updateAllAppWidgets()中注册的。

第6步 编辑ExampleAppWidgetService.java
在当前工程下,新建ExampleAppWidgetService.java,代码如下:

package com.skywang.widget;import android.app.Service;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.os.IBinder;import android.util.Log;/* * @author : skywang <[email protected]> * description : 周期性更新AppWidget的服务 */public class ExampleAppWidgetService extends Service {        private static final String TAG="ExampleAppWidgetService";     // 更新 widget 的广播对应的action    private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";    // 周期性更新 widget 的周期    private static final int UPDATE_TIME = 5000;    // 周期性更新 widget 的线程    private UpdateThread mUpdateThread;    private Context mContext;    // 更新周期的计数    private int count=0;          @Override    public void onCreate() {                // 创建并开启线程UpdateThread        mUpdateThread = new UpdateThread();        mUpdateThread.start();                mContext = this.getApplicationContext();        super.onCreate();    }        @Override    public void onDestroy(){        // 中断线程,即结束线程。        if (mUpdateThread != null) {            mUpdateThread.interrupt();        }                super.onDestroy();    }        @Override    public IBinder onBind(Intent intent) {        return null;    }    /*     * 服务开始时,即调用startService()时,onStartCommand()被执行。     */    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.d(TAG, "onStartCommand");                super.onStartCommand(intent, flags, startId);                return START_STICKY;    }        private class UpdateThread extends Thread {        @Override        public void run() {            super.run();            try {                count = 0;                while (true) {                    Log.d(TAG, "run ... count:"+count);                    count++;                    Intent updateIntent=new Intent(ACTION_UPDATE_ALL);                    mContext.sendBroadcast(updateIntent);                                        Thread.sleep(UPDATE_TIME);                }             } catch (InterruptedException e) {                // 将 InterruptedException 定义在while循环之外,意味着抛出 InterruptedException 异常时,终止线程。                e.printStackTrace();            }        }    }}

说明

(01) onCreate() 在创建服务时被执行。它的作用是创建并启动线程UpdateThread()。
(02) onDestroy() 在销毁服务时被执行。它的作用是注销线程UpdateThread()。
(03) 服务UpdateThread 每隔5秒,发送1个广播ACTION_UPDATE_ALL。广播ACTION_UPDATE_ALL在ExampleAppWidgetProvider被处理:用来更新widget中的图片。

点击下载源代码

点击查看skywang博客索引

widget在添加到桌面前的效果图

widget在添加到桌面后的效果图



更多相关文章

  1. Android(安卓)ImageButton Example 图片按钮
  2. Android(安卓)添加图标
  3. Android中优雅的退出程序
  4. Android采坑记录-自动更新APK出现No Activity found to handle I
  5. 在Linux下adb连接不上android手机的终极解决方案
  6. android 视图结构 呈现给用户的视图
  7. android v13 的新特性
  8. android 使用Sax 读取xml
  9. android SDK manager 以及ADT eclipse更新问题

随机推荐

  1. android多activity下如何退出整个程序
  2. 布局中嵌套布局!
  3. android上的一个网络接口和图片缓存框架e
  4. Android读取手机文件列表
  5. android刷字体教程
  6. Android获取设备已安装的应用
  7. android 判断字符串是否为空与比对["=="
  8. Android软件集合
  9. Android应用使用百度地图API
  10. android 音频开发之混响效果