一,项目初看
(事先要说明的一点就是,我使用的是android4.1的源代码哦,虚拟机也是Jelly Bean的哦。
(1)项目主要由com.android.deskclock及com.android.alarmclock组成。总共
26个源文件
其中最简单的一个要数Log.java类了,相信只要有基本的Java基础的人都能够看懂。
从AndroidManifest.xml文件中可以看出,应用的入口是DeskClock.java文件的DeskClock类.
二,进入应用,Jelly Bean的虚拟机的话,应用的主界面,就有一个时钟。
显然这个时钟是一个Widget。我们就先来看看这个吧。widget在清单文件中上的声明吧:
<receiver
android:name="com.android.alarmclock.AnalogAppWidgetProvider"
android:icon="@mipmap/ic_widget_analog_clock"
android:label="@string/analog_gadget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.oldName"
android:value="com.android.deskclock.AnalogAppWidgetProvider" />
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/analog_appwidget" />
</receiver>
我们知道一个Widget是一个BroadcastReceiver的子类组件。
上面我们只关注它的intent-filter的action,这个声明,表明这个广播接收者只接收intent的action
值为"android.appwidget.action.APPWIDGET_UPDATE"的广播。
事实是Widget本身也对这个做了判断。
AnalogAppWidgetProvider类代码如下:
/**
* Simple widget to show analog clock.
*/
public class AnalogAppWidgetProvider extends BroadcastReceiver {
static final String TAG = "AnalogAppWidgetProvider";
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.analog_appwidget);
views.setOnClickPendingIntent(R.id.analog_appwidget,
PendingIntent.getActivity(context, 0,
new Intent(context, AlarmClock.class), 0));
int[] appWidgetIds = intent.getIntArrayExtra(
AppWidgetManager.EXTRA_APPWIDGET_IDS);
AppWidgetManager gm = AppWidgetManager.getInstance(context);
gm.updateAppWidget(appWidgetIds, views);
}
}
}
上面的代码比较简短,但是有几个比较重要的地方:
(1) RemoteViews
文档的解释是:RemoteViews是一个描述了可以在其它进程显示View层级的类。View层级通过一个布局资源文件构造得来,RemoteViews类同时提供了些基本的用于修改构造的布局层级的操作。
关于RemoteViews呢,我发现RemoteViews概述 这一篇博客讲得很不错,推荐去看。
(2)关于PendingIntent
为也好说明,将上面的其中的两行代码改写如下:
Intent intent = new Intent(cotext,AlarmClock.class);
PendingIntent pi = PendingIntent.getActivity(context,0,intent,0)
views.setOnClickPendingIntent(R.id.analog_appwidget,pi)
上面的三行代码,第一行自不必说了。
第二行呢,需要对PendingIntent有一些了解。
顾名思义,PendingIntent即一个捎带了Intent的类。
我们知道Intent用于启动某一个组件。所以PendingIntent的静态工厂方法getActivity()
即是返回一个用于启用Activity的Intent.
因此显然PendingIntent如你所想像中那样还有下面的这些方法:
public static PendingIntent getBroadcast(Context context, int requestCode,
Intent intent, int flags)
public static PendingIntent getService(Context context, int requestCode,
Intent intent, int flags)
第三行,views.setOnClickPendingIntent(R.id.analog_appwidget,pi)
即是当点击桌面的时钟widget时(布局文件是analog_appwidget)时,启动pi所捎带的Intent.于是就会启动AlarmClock类,它是一个Activity.
关于PendingIntent的更多说明,请查阅文档,或者问下Google大人。
其它的三行代码就比较好理解了。
int[] appWidgetIds = intent.getIntArrayExtra(
AppWidgetManager.EXTRA_APPWIDGET_IDS);
AppWidgetManager gm = AppWidgetManager.getInstance(context);
gm.updateAppWidget(appWidgetIds, views);
(1)获得些widget的ID,注意是数组。
(2)获得AppWidgetManager
(3)使用AppWidgetManager的单例,及appWidgetIds,及RemoteViews的views更新此widget.
对于些widget类还有一个地方没有提及的就是:
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/analog_appwidget" />
这个是android中widget必须提供一些信息。
看看@xml/analog_appwiget便知,
推荐完全参数android中关于app widget的文档。