By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处
之前分析了下widget添加到laucher的过程,现在我们来分析下widget被添加到laucher之后发生的故事。
AppWidgetProvider
桌面组件实现的组要类,它的父类是一个广播接收器,它主要作用就是接收更新桌面组件的广播消息,然后更新桌面组件
i. public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds):
该方法对应事件:android.appwidget.action.APPWIDGET_UPDATE,当桌面组件被周期更新的时候它被调用,深入父类的onReceive方法就会了解到,当父类的onReceive方法接收到android.appwidget.action.APPWIDGET_UPDATE消息就会调用该update方法。
ii. public void onReceive(Context context, Intent intent):
AppWidgetProvider本身是一个广播接收器,父类已经覆盖了该方法,你重新覆盖该方法的时候,注意要添加super. onReceive到你的方法中,否则onUpdate将不再会被调用
iii. public void onDeleted(Context context, int[] appWidgetIds):
AppWidgetProvider接收消息android.appwidget.action.APPWIDGET_DELETED时候调用该方法。
每个桌面组件就是一个AppWidget,当你需要在代码中实现一个桌面组件,首先你必须得实现AppWidgetProvider类,当你点击AppWidgetProvider类你会发现其实AppWidgetProvider就是一个BroadcastReceiver子类,也就是说它也是一个广播接收器。桌面组件内容显示更新全部是通过RemoteViews对象,刚创建桌面组件的时候系统会绑定AppWidgetProvider到一个AppWidgetId,然后后面通过AppWidgetManager.updateAppWidget方法把RemoteViews对象更新到对应的桌面组件。
回到之前添加widget时的流程,在桌面组件刚被添加的时候,系统通过AppWidgetHost.allocateAppWidgetId函数为桌面组件分配一个新的AppWidgetId。在选择widget的时候,通过点击的列表选项可获取到对应桌面组件对应的ComponentName,一个ComponentName由一个包名和一个AppWidgetProvider类名称唯一指定,然后通过AppWidgetManager.bindAppWidgetId(int appWidgetId, ComponentName provider)把前面生成的AppWidgetId与一个ComponentName绑定,一个ComponentName可以绑定多个AppWidgetId。所以你可以在桌面上放置几个一样的widget。
一个AppWidgetProvider可以由一个ComponentName唯一确定,在一般的widget应用中:
//先获取一个AppWidgetManager对象 AppWidgetManager appWidgetMgr = AppWidgetManager.getInstance(context); //新建一个ComponentName对象,用来获取获取桌面组件widgetIds ComponentName compName = new ComponentName(context, ExampleWidgetProvider.class); //返回的是由 ComponentName对应的 int[] widgetIds int[] widgetIds = appWidgetMgr.getAppWidgetIds(compName);
每一个AppWidgetProvider上可能建立了多个桌面组件,每一个桌面组件对以应一个AppWidgetId,要获取一个AppWidgetProvider上所有的桌面组件的AppWidgetId数据,可以先建一个ComponentName对象,设置ComponentName中的类名和包名为AppWidgetProvider的类名和包名,然后可通过AppWidgetManager. getAppWidgetIds(ComponentName provider)方法获取AppWidgetId列表。
通过AppWidgetId我们便可以操作指定的widget。
widget界面的更新是通过RemoteViews日哦,而RemoteViews并不是一个真正的View,它没有实现View的接口,而只是一个用于描述View的实体。比如:创建View需要的资源ID和各个控件的事件响应方法。RemoteViews会通过进程间通信机制传递给AppWidgetHost。
AppWidgetHost和AppWidgetHostView是在framework中定义的两个基类。应用程序可以利用这两个类来实现自己的Host。Launcher是缺省的桌面,它是一个Host的实现者。
LauncherAppWidgetHostView扩展了AppWidgetHostView,实现了对长按事件的处理。
LauncherAppWidgetHost扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例。
Remoteviews更新桌面组件最终还是要在launcher进程中执行,在launcher中有一个handler: AppWidgetHost.UpdateHandler,该handler就是用来处理Remoteviews更新的,一旦我们调用appWidgetManager.updateAppWidget(appWidgetId, views)这个方法,AppWidgetHost.UpdateHandler. handleMessage事件就会响应,不过要注意是该事件响应是在launcher进程中执行:
switch (msg.what) { case HANDLE_UPDATE: { updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);//执行Remoteviews更新 break; }