Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色

本文深入探讨了Android Launcher在处理AppWidget时的关键步骤,包括选取AppWidgetProvider后的处理流程,初始化过程中的AppWidget信息加载,以及数据库初始化时对初始AppWidget的加载策略。详细分析了Launcher与AppWidget之间的交互过程,特别是如何通过AppWidgetHost创建本地的AppWidgetHostView,以及数据模型的构建和更新机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

田海立@优快云

2012-8-21

Launcher在Android的AppWidget整个体系中扮演AppWidgetHost的角色,本文分析Launcher对于AppWidget的处理,主要包括:选取AppWidgetProvider之后的处理;Launcher初始化过程中加载(包括第一次加载和之后正常的加载)AppWidget信息的处理,等。

由《Android中选取并绑定AppWidget》中知道,Launcher发起选取操作;Settings中的AppWidgetPickActivity获取所有已经安装的AppWidgetProvider,让用户选择,用户选择之后,回到启动它的Activity的onActivityResult()。

一、Launcher获取AppWidget之后的处理

先看Launcher中定义的用来处理AppWidget的相关的类:

Launcher AppWidgetHost Class Diagram

图一、Launcher中AppWidget的相关类

  • Launcher是一个Activity;
  • 继承AppWidgetHost的LauncherAppWidgetHost用来操作AppWidgetHost功能,overrideonCreateView()用于创建自己的AppWidgetHostView– LauncherAppWidgetHostView;
  • LauncherAppWidgetHostView用来改变点击操作行为习惯;
  • Launcher把UserFolder/ LiveFolder / AppWidget等做成一定的数据模型,用ItemInfo来抽象,对应AppWidget用LauncherAppWidgetInfo来表达。

图二的时序图描述了,从AppWidgetPickActivity返回之后,Launcher如何处理AppWidget的。

Launcher AddAppWidget Sequence Diagram

图二、Picked之后Launcher对AppWidget的处理

执行过程:

1.onActivityResult()中,从requestCode以及resultCode里知道,选取AppWidget成功,可以从返回的data:Intent中获得appWidgetId;[Seq#1]

2.通过AppWidgetId获得info: AppWidgetProviderInfo;[Seq#5~ #6]

3.创建LauncherAppWidgetInfo的实例,并加入到数据模型LauncherModel中;[Seq#7]

4.通过LauncherAppWidgetHost.createView()创建AppHostView;[Seq#8~ #15]

  • 由于override里onCreateView(),onCreateView()被执行。在onCreateView()中创建LauncherAppWidgetHostView;[Seq#8~ #10]
  • AppWidgetHost.createView()中,把AppWidgetProviderInfo设置到appWidgetHostView里;[Seq#11]
  • AppWidgetHost.createView()中,通过AppWidgetService获得AppWidgetProvider提供的RemoteViews【AppWidgetHost、AppWidgetProvider、AppWidgetService运行在不同的进程中,此时不能保证RemoteViews有内容,亦即不能保证AppWidgetProvider.onUpdate()已经被执行】;[Seq#12~ #13]
  • AppWidgetHost.createView()中,用RemoteViews更新appWidgetHostView;[Seq#14]
  • 返回已创建AppWidgetHostView的实例;[Seq#15]

5.向AppWidgetHostView里设置TAG – LauncherAppWidgetInfo的实例。[Seq#17]

最后,LauncherAppWidgetHostView被加入到当前屏,让相应的显示部分来完成显示。因为此时RemoteViews里可能还没有内容,这里只是用一定的占空在Workspace中先占一定的空间。

当AppWidgetProvider获得更新的广播,并执行onUpdate(),onUpdate()中创建了RemoteViews并通过AppWidgetManager.updateAppWidget()更新到AppWidgetService之后,AppWidgetService会通过注册的IAppWidgetHost的回调,执行AppWidgetHost的更新。

Launcher updateAppWidget Sequence Diagram

图三、AppWidgetHost被更新

Android中RemoteViews的实现》中的Section#3讲述了RemoteViews后续的处理。

Launcher在初始化过程中,还会根据配置在第一次创建Database时把AppWidget加载进来;不是第一次创建时,把数据库中的AppWidget的内容Load到数据模型中。


二、Launcher第一次创建Database时,处理AppWidget

Launcher的数据库操作的相关的类

Launcher LauncherProvider Class Diagram

图四、Launcher的数据库操作LauncherProvider

  • Launcher在LauncherProvider中操作数据库;AppWidget相关项是在TABLE_FAVORITIES表单中;LauncherProvider.AUTHORITY定义操作数据库的入口,组合了LauncherSettings.Favorites.CONTENT_URI这个Uri来具体操作。
  • 用SQLite具体存储,所有用SQLiteOpenHelper的子类LauncherProvider.DatabaseHelper来具体操作SQLite数据库。
  • 数据库TABLE_FAVORITIES中的具体FiledLauncherSettings.Favorites中定义。

Launcher第一次创建数据库时,LauncherProvider.DatabaseHelper.onCreate()会被执行,对AppWidget的处理如下:

Launcher LauncherProvider loadAppWidget Sequence Diagram

图五、Launcher第一次创建数据库时,对AppWidget的处理

执行过程:

1.移除掉Launcher作为AppWidgetHost相关的内容;[Seq#4]

2.解析default_workspace.xml中的内容,如果是appwidget相关的:

a)申请AppWidgetId;[Seq#8 ~ #9]

b)把解析出的内容插入TABLE_FAVORITES表单;[Seq#10]

c)把AppWidgetId与AppWidgetProvider绑定;[Seq#11]

其实这个过程就浓缩了用户选择AppWidgetProvider,然后再绑定等等一系列的过程。只是这里的要用哪个AppWidgetProvider,放在哪一屏的哪个位置都在配置里确定了,所以可以直接自动完成。

比如,下面是res/xml/default_workspace.xml中,关于“电量控制”这个AppWidget的配置:

<appwidget
       launcher:packageName="com.android.settings"
       launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
       launcher:screen="3"
       launcher:x="0"
       launcher:y="0"
       launcher:spanX="4"
       launcher:spanY="1" />

而要解析default_workspace.xml中AppWidget的哪些属性是由res/values/attrs.xml中的Favorite指定的:

   <!-- XML attributes used by default_workspace.xml -->
   <declare-styleable name="Favorite">
       <attr name="className" format="string" />
       <attr name="packageName" format="string" />
       <attr name="screen" format="string" />
       <attr name="x" format="string" />
       <attr name="y" format="string" />
       <attr name="spanX" format="string" />
       <attr name="spanY" format="string" />
       <attr name="icon" format="reference" /> 
       <attr name="title" format="reference" />
       <attr name="uri" format="string" />
   </declare-styleable>

三、Launcher正常启动加载数据库中的AppWidget

3.1 Launcher中的数据模型

Launcher LauncherModel Class Diagram

图六、Launcher中的简要数据模型

  • LauncherModel是一个BroadcastReceiver;用mCallbacks记录Model变化时,要通知的对象;mAppWidgets中记录加入的AppWidget的信息。
  • Launcher实现LauncherModel.Callbacks,注册进LauncherModel,当Model变化时,做相应的处理。

3.2 Launcher数据模型的初始化

Launcher LauncherModel constructor Sequence Diagram

图七、Launcher数据模型的初始化

执行顺序:

1.Launcher被创建时,Launcher.onCreate()被执行;

2.通过getApplication()获得LauncherApplication;LauncherApplication被创建(launcherApplication.onCreate())时:

a)实例化LauncherModel,并把LauncherApplication自身传进去;

b)为LauncherModel注册广播;

3.通过LauncherApplication的setLauncher()把Launcher自身传进去;

LauncherApplication. setLauncher()调用LauncherModel的initialize()把Launcher这个launcherModel.Callbacks的实例传进去;

4.实例化LauncherAppWidgetHost这个AppWidgetHost,并通过startListening(),把IAppWidgetHost注册进AppWidgetSerivce。

3.3 加载并绑定Workspace

在需要加载数据模型的时,LauncherModel的startLoader()会被执行。LauncherModel开启一个LoaderTask线程,具体执行load和bind的工作。

Launcher LauncherModel LoadWorkspace BindWorkspace Sequence Diagram

图八、LauncherModel加载并绑定Workspace

执行加载过程:

1.用LauncherSettings.Favorites.CONTENT_URI查询所有的数据;[Seq#1~ #3]

2.从LauncherSettings.Favorites.ITEM_TYPE字段获取当前记录的类型;[Seq#4~ #7]。

3.对于AppWidget类型(type为LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET),获得AppWidget关注的其他字段,并赋值给LauncherAppWidgetInfo;[Seq#8~ #9]

4.把LauncherAppWidgetInfo的实例加入mAppWidgets;[Seq#10]

执行绑定过程:

通过LauncherModel.Callbacks的实现,也就是Launcher,执行:

  • startBinding();
  • 对所有的mAppWidgets中的Widget,执行bindAppWidget()。

执行LauncherModel.Callbacks.bindAppWidget()在Launcher中执行。

3.4 Launcher绑定AppWidget

Launcher LauncherModel.Callbacks bindAppWidget Sequence Diagram

图九、Launcher bindAppwidget

这个过程同图二的执行,可参考研读。

总结

本文讲述了:

  • Launcher在选择了一个AppWidgetProvider之后,通过AppWidgetHost创建本地的AppWidgetHostView,用来呈现AppWidgetProvider通过RemoteViews提供的的提供内容。相应的LauncherAppWidgetInfo加入到LauncherModel的数据模型中。
  • Launcher(AppWidgetHost) / AppWidgetService /AppWidgetProvider由于运行于不同的进程中,执行的次序不确定使得RemoteViews的内容时效性不定,但是只要RemoteViews有更新,AppWidgetHost就会得到通知而更新。
  • 在系统第一次执行(刚烧机或恢复出厂设置之后)时,数据库第一次被初始化,会从default_workspace.xml中加载初始的AppWidget信息,并加入到LauncherModel的数据模型中。
  • 在正常开机过程(刚烧机或恢复出厂设置之后)中,AppWidget的信息被从数据库中读取出来,并加入到LauncherModel的数据模型中。

可进一步参考的文章

通过这一系列的其他文章,可获得与本文关联的信息:

Android AppWidget框架

AppWidget系统框架。

Android中选取并绑定AppWidget

Launcher发起选取过程,此文中描述选取并绑定的过程,可结合本文看完整的选取/绑定/加入显示系统的完整过程。

Android中AppWidget的分析与应用:AppWidgetProvider

本文所描述的信息,是此文所描述的AppWodgetProvider所提供的。

Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色

本文

Android中RemoteViews的实现

RemoteViews的内部如何实现,看如何具体用RemoteViewsupdate AppWidgetHostView。

<think>好的,我现在需要回答用户关于如何在Android Launcher中集成小部件的问题。首先,我得回忆一下Android小部件开发的基本知识。小部件,也就是App Widget,是Android应用程序的一部分,可以嵌入到主屏幕(Launcher)上,显示实时信息或提供快捷操作。用户可能想知道从开发到集成的整个流程,所以我需要分步骤讲解。 首先,确定用户的需求是集成小部件到Launcher,这可能包括创建小部件和让Launcher支持小部件。但通常来说,Launcher本身需要支持小部件,比如像默认的Pixel Launcher那样。所以可能需要分两部分:开发小部件和配置Launcher以支持小部件。 接下来,我需要回忆小部件开发的关键步骤。开发小部件需要以下几个步骤:创建AppWidgetProvider子类,定义小部件的元数据XML,配置AndroidManifest.xml,以及设计小部件的布局和可能的配置界面。同时,Launcher需要声明支持小部件,这通常在Launcher应用的manifest中添加相关的intent-filter和metadata。 不过,用户的问题可能更偏向于如何在现有的Launcher应用中添加对小部件的支持,或者如何在自己的Launcher中允许用户添加小部件。假设用户指的是后者,即开发一个自定义Launcher并支持小部件,那么需要了解Launcher如何管理小部件的添加和显示。 在Android中,Launcher应用需要实现AppWidgetHost,管理小部件的生命周期。例如,使用AppWidgetHost类来分配小部件ID,绑定服务,以及创建小部件的视图。这可能涉及到几个关键类:AppWidgetProvider、AppWidgetManager、AppWidgetHostAppWidgetHostView等。 然后,我需要整理步骤。例如,在自定义Launcher中,首先初始化AppWidgetHost,然后在用户添加小部件时,启动选择小部件的Activity,获取小部件的信息,分配ID,创建视图并添加到主屏幕布局中。同时,需要处理小部件的更新、删除等操作。 还要注意权限和清单文件的配置,比如需要BIND_APPWIDGET权限,以及声明AppWidget相关的组件。可能还需要处理不同Android版本的兼容性问题,例如从Android 12开始,某些小部件的行为可能有所变化。 另外,用户可能需要示例代码,比如如何初始化AppWidgetHost,如何启动小部件选择器,如何处理返回的小部件信息等。这需要具体的代码示例,比如使用startActivityForResult来启动选择器,然后在onActivityResult中处理结果,使用AppWidgetManager来绑定小部件实例,创建视图并添加到布局中。 最后,需要验证这些步骤的正确性,确保没有遗漏关键点,比如安全地处理小部件的绑定和更新,确保Launcher应用有正确的权限,以及布局的动态添加和管理。可能还需要提醒用户关于小部件尺寸的适配,以及如何响应用户的交互事件。</think>在 Android Launcher 中集成小部件(Widget开发需要以下关键步骤: ### 一、小部件开发基础 1. **定义 AppWidgetProvider 类** 继承 `AppWidgetProvider` 并覆写生命周期方法: ```java public class MyWidget extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // 更新小部件逻辑 } } ``` 2. **配置元数据 XML** 在 `res/xml` 目录下创建 `widget_info.xml`,定义尺寸、更新频率等: ```xml <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="150dp" android:minHeight="150dp" android:updatePeriodMillis="86400000" android:initialLayout="@layout/widget_layout"/> ``` 3. **声明组件** 在 `AndroidManifest.xml` 中添加: ```xml <receiver android:name=".MyWidget" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info" /> </receiver> ``` --- ### 二、Launcher 集成核心逻辑 1. **初始化 AppWidgetHost** 在 Launcher 的 `onCreate` 中初始化: ```java AppWidgetHost mAppWidgetHost = new AppWidgetHost(context, HOST_ID); mAppWidgetHost.startListening(); // 激活小部件监听 ``` 2. **启动小部件选择器** 通过隐式 Intent 触发系统选择器: ```java int requestCode = 100; Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetHost.allocateAppWidgetId()); startActivityForResult(pickIntent, requestCode); ``` 3. **处理选择结果** 在 `onActivityResult` 中绑定小部件: ```java int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); AppWidgetManager manager = AppWidgetManager.getInstance(context); // 检查小部件是否可绑定(针对 Android 12+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (!manager.isRequestPinAppWidgetSupported()) { // 处理不支持的情况 } } // 创建视图并添加到布局 AppWidgetHostView hostView = mAppWidgetHost.createView(context, appWidgetId, appWidgetInfo); parentLayout.addView(hostView); // 将小部件加入主屏幕布局 ``` --- ### 三、关键权限与配置 ```xml <uses-permission android:name="android.permission.BIND_APPWIDGET" /> <application> <!-- 声明 Launcher 入口 --> <activity android:name=".LauncherActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> </application> ``` --- ### 四、优化建议 1. **动态尺寸适配** 使用 `AppWidgetProviderInfo.getMinWidth/Height()` 根据网格单元调整布局[^1] 2. **跨版本兼容** - Android 12 引入 `requestPinAppWidget()` 实现无权限绑定[^2] - 使用 `RemoteViews` 的适配 API 避免版本兼容问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值