[置顶] Android(安卓)Launcher全面剖析
Android Launcher全面剖析
首先来说说我为什么写这篇文章,最近公司要我负责搞Launcher,网上一查这方面的资料比较少,并且不全,研究起来相当困难,所以就写了这篇文章,希望对大家有帮助。这篇文章是相当长的,希望读者能耐心读下去,实际上也花了我很长时间来写。好了闲话少说,我们切入正题。
这篇文章我会讲以下Launcher内容:
Launcher UI总体架构
Launcher Res下的Layout
Launcher Res下的Xml文件
Launcher Manifest文件
Launcher 常用类介绍
Launcher 启动过程
Launcher widget添加过程
Launcher celllayout的介绍
一 Launcher UI总体架构
Home screen可以说是一个手机的最重要应用,就像一个门户网站的首页,直接决定了用户的第一印象。下面对home screen做一简要分析。
home screen的代码位于packages/apps/Launcher目录。从文件launcher.xml,workspace_screen.xml可获知home screen的UI结构如下图所示:
整个homescreen是一个包含三个child view的FrameLayout(com.android.launcher.DragLayer)。
第一个child就是桌面com.android.launcher.Workspace。这个桌面又包含三个child。每个child就对应一个桌面。这就是你在Android上看到的三个桌面。每个桌面上可以放置下列对象:应用快捷方式,appwidget和folder。
第二个child是一个SlidingDrawer控件,这个控件由两个子控件组成。一个是com.android.launcher.HandleView,就是Android桌面下方的把手,当点击这个把手时,另一个子控件,com.android.launcher.AllAppsGridView就会弹出,这个子控件列出系统中当前安装的所有类型为category.launcher的Activity。
第三个child是com.android.launcher.DeleteZone。当用户在桌面上长按一个widget时,把手位置就会出现一个垃圾桶形状的控件,就是这个控件。
在虚拟桌面上可以摆放四种类型的对象:
1. ITEM_SHORTCUT,应用快捷方式
2. ITEM_APPWIDGET,app widget
3. ITEM_LIVE_FOLDER,文件夹
4. ITEM_WALLPAPER,墙纸。
类AddAdapter(AddAdapter.java)列出了这四个类型对象。当用户在桌面空白处长按时,下列函数序列被执行:
Launcher::onLongClick -->
Launcher::showAddDialog -->
Launcher::showDialog(DIALOG_CREATE_SHORTCUT); -->
Launcher::onCreateDialog -->
Launcher::CreateShortcut::createDialog:这个函数创建一个弹出式对话框,询问用户是要添加什么(快捷方式,appwidget, 文件夹和墙纸)其内容就来自AddAdapter。
类Favorites(LauncherSettings.java)和类LauncherProvider定义了一个content provider,用来存储桌面上可以放置的几个对象,包括shortcut, search和clock等。
类DesktopItemsLoader负责将桌面上所有的对象从content provider中提取。
线程private ApplicationsLoader mApplicationsLoader负责从包管理器中获取系统中安装的应用列表。(之后显示在AllAppsGridView上)。ApplicationsLoader::run实现:
1)通过包管理器列出系统中所有类型为Launcher,action为MAIN的activity;
2)对每一个Activity,
a) 将Activity相关元数据信息,如title, icon, intent等缓存到appInfoCache;
b) 填充到ApplicationsAdapter 中。填充过程中用到了一些小技巧,每填充4(UI_NOTIFICATION_RATE)个activity更新一下相应view。
在Launcher::onCreate中,函数startLoaders被调用。而该函数接着调用loadApplications和loadUserItems,分别获取系统的应用列表,以及显示在桌面上的对象列表(快捷方式,appwidget,folder等)。
Launcher上排列的所有应用图标由AllAppsGridView对象呈现。这个对象是一个GridView。其对应的Adapter是ApplicationsAdapter,对应的model则是ApplicationInfo数组。数组内容是由ApplicationsLoader装载的。
private class ApplicationsLoader implements Runnable。
由Launcher中的AndroidManifest.xml可以看出整个Launcher的代码结构。
首先,是一些权限的声明。例如:
view plain copy to clipboard print ?
|
这部分可以略过;
其次,Application的构成,如上图:
(1)Launcher:HomeScreen的Activity。
- <intent-filter>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.HOME"/>
- <categoryandroid:name="android.intent.category.DEFAULT"/>
- <categoryandroid:name="android.intent.category.MONKEY"/></intent-filter>
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME"/> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.MONKEY" /> </intent-filter>
上面这段代码就标志着它是开机启动后Home的Activity。通过Launcher.java中onCreat()的分析我们可以大致把握屏幕的主要活动:
view plain copy to clipboard print ?
|
方法onActivityResult():完成在workspace上增加shortcut,appwidge和Livefolder;
方法onSaveInstantceState()和onRestoreInstanceState():为了防止Sensor、Land和Port布局自动切换时数据被置空,通过onSaveInstanceState方法可以保存当前窗口的状态,在即将布局切换前将当前的Activity压入历史堆栈。如果我们的Activity在后台没有因为运行内存吃紧被清理,则切换时回触发onRestoreIntanceState()。
(2)WallpaperChooser:设置墙纸。
同理我们从onCreat()作为入口来分析这个活动的主要功能。
view plain copy to clipboard print ?
|
(3)default_searchable
对于home中任意的Acitivty,使能系统缺省Search模式,这样就可以使用android系统默认的searchUI。
(4)InstallShortcutReceiver:
继承自BroadcastReceiver,重写onReceier()方法,对于发送来的Broadcast(这里指Intent)进行过滤(IntentFilt)并且响应(这里是InstallShortcut())。这里分析下onReceive():
view plain copy to clipboard print ?
|
其中IntallShortcut()方法:首先,对传入的坐标进行判断(findEmptyCell()),如果是空白位置,则可以放置快捷方式;其次,缺省情况下,我们允许创建重复的快捷方式,具体创建过程(addShortcut())就是把快捷方式的信息传入数据库(addItemToDatabase())。
(5)UninstallShortcutReceiver:
同理,UninstallShortcutReceiver()继承自BroadcastReceiver(),实现onReceiver()方法。定义一个ContentResolver对象完成对数据库的访问和操作(通过URI定位),进而删除快捷方式 。
(6)LauncherProvider:
继承自ContentProvider(),主要是建立一个数据库来存放HomeScreen中的数据信息,并通过内容提供者来实现其他应用对launcher中数据的访问和操作。
重写了ContentProvider()中的方法:
getType():返回数据类型。如果有自定义的全新类型,通过此方法完成数据的访问。
query():查询数据。传入URI,返回一个Cursor对象,通过Cursor完成对数据库数据的遍历访问。
Insert():插入一条数据。
bulkInsert():大容量数据的插入。
delete():删除一条数据。
update():更改一条数据。
sendNotify():发送通知。
类DatabaseHelper继承自一个封装类SQLiteOpenHelper(),方便了数据库的管理和维护。
重写的方法:
onCreate():创建一个表。其中db.execSQL()方法执行一条SQL语句,通过一条字符串执行相关的操作。当然,对SQL基本语句应该了解。
onUpgrade():升级数据库。
对HomeScreen数据库操作的一些方法:
addClockWidget(),addSearchWidget,addShortcut,addAppShortcut,
loadFavorites(),launcherAppWidgetBinder(),convertWidget(),updateContactsShortcuts(),
copyFromCursor()
补充:
类AddAdapter(AddAdapter.java)列出了这四个类型对象。当用户在桌面空白处长按时,下列函数序列被执行:
Launcher::onLongClick-->
Launcher::showAddDialog-->
Launcher::showDialog(DIALOG_CREATE_SHORTCUT);-->
Launcher::onCreateDialog-->
Launcher::CreateShortcut::createDialog:这个函数创建一个弹出式对话框,询问用户是要添加什么(快捷方式,appwidget,文件夹和墙纸)其内容就来自AddAdapter。
类DesktopItemsLoader负责将桌面上所有的对象从contentprovider中提取。
线程privateApplicationsLoadermApplicationsLoader负责从包管理器中获取系统中安装的应用列表。(之后显示在AllAppsGridView上)。ApplicationsLoader::run实现:
1)通过包管理器列出系统中所有类型为Launcher,action为MAIN的activity;
2)对每一个Activity,
a)将Activity相关元数据信息,如title,icon,intent等缓存到appInfoCache;
b)填充到ApplicationsAdapter中。填充过程中用到了一些小技巧,每填充4(UI_NOTIFICATION_RATE)个activity更新一下相应view。
在Launcher::onCreate中,函数startLoaders被调用。而该函数接着调用loadApplications和loadUserItems,分别获取系统的应用列表,以及显示在桌面上的对象列表(快捷方式,appwidget,folder等)。
Launcher上排列的所有应用图标由AllAppsGridView对象呈现。这个对象是一个GridView。其对应的Adapter是ApplicationsAdapter,对应的model则是ApplicationInfo数组。数组内容是由ApplicationsLoader装载的。
二 Launcher Res下的Layout
现在我们来看res目录里的布局文件,布局文件都放在layout*目录里。
本以为launcher的layout都放在layout目录里,由于屏幕放置方式的不同会对桌面造成一定的影响,所以google的Android项目组就决定因地制宜。比如当你横着放置屏幕的时候就会使用layout-land目录里的文件来对系统launcher进行布局,竖着屏幕的时候会使用layout-port内的布局文件来对launcher来布局。
横竖屏幕切换之际,会重新进行布局。那我们就以layout-land目录为例来看吧。
layout-land/launcuer.xml
- <?xmlversion="1.0"encoding="utf-8"?>
- <!--
- /*
- **
- **Copyright2008,TheAndroidOpenSourceProject
- **
- **LicensedundertheApacheLicense,Version2.0(the"License");
- **youmaynotusethisfileexceptincompliancewiththeLicense.
- **YoumayobtainacopyoftheLicenseat
- **
- **http://www.apache.org/licenses/LICENSE-2.0
- **
- **Unlessrequiredbyapplicablelaworagreedtoinwriting,software
- **distributedundertheLicenseisdistributedonan"ASIS"BASIS,
- **WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
- **SeetheLicenseforthespecificlanguagegoverningpermissionsand
- **limitationsundertheLicense.
- */
- -->
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.zkx_launcher"
- android:sharedUserId="@string/sharedUserId"
- >
- <!--应用程序的包名-->
- <!--<original-packageandroid:name="com.android.zkx_launcher2"/>-->
- <!--对系统资源的访问权限-->
- <permission
- android:name="com.android.zkx_launcher.permission.INSTALL_SHORTCUT"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_install_shortcut"
- android:description="@string/permdesc_install_shortcut"/>
- <permission
- android:name="com.android.zkx_launcher.permission.UNINSTALL_SHORTCUT"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_uninstall_shortcut"
- android:description="@string/permdesc_uninstall_shortcut"/>
- <permission
- android:name="com.android.zkx_launcher.permission.READ_SETTINGS"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_read_settings"
- android:description="@string/permdesc_read_settings"/>
- <permission
- android:name="com.android.zkx_launcher.permission.WRITE_SETTINGS"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_write_settings"
- android:description="@string/permdesc_write_settings"/>
- <uses-permissionandroid:name="android.permission.CALL_PHONE"/>
- <uses-permissionandroid:name="android.permission.EXPAND_STATUS_BAR"/>
- <uses-permissionandroid:name="android.permission.GET_TASKS"/>
- <uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
- <uses-permissionandroid:name="android.permission.SET_WALLPAPER"/>
- <uses-permissionandroid:name="android.permission.SET_WALLPAPER_HINTS"/>
- <uses-permissionandroid:name="android.permission.VIBRATE"/>
- <uses-permissionandroid:name="android.permission.WRITE_SETTINGS"/>
- <uses-permissionandroid:name="android.permission.BIND_APPWIDGET"/>
- <uses-permissionandroid:name="com.android.launcher.permission.READ_SETTINGS"/>
- <uses-permissionandroid:name="com.android.launcher.permission.WRITE_SETTINGS"/>
- <!--对应用程序的配置-->
- <application
- android:name="LauncherApplication"
- android:process="@string/process"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
- <!--配置应用程序额的名字,进程,标签,和图标
- label的值为values/strings.xml中application_name键值对的值
- icon为drawable目录下名为的ic_launcher_home的图片
- 实际上该图片的位置位于drawable-hdpi(高分辨率)目录下,是个小房子这个主要是为了支持多分辨率的.
- hdpi里面主要放高分辨率的图片,
- 如WVGA(480x800),FWVGA(480x854)mdpi里面主要放中等分辨率的图片,
- 如HVGA(320x480)ldpi里面主要放低分辨率的图片,
- 如QVGA(240x320)系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片
- 所以在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片.
- 只需要在res目录下创建不同的layout文件夹,
- 比如layout-640x360,layout-800x480,
- 所有的layout文件在编译之后都会写入R.java里,
- 而系统会根据屏幕的大小自己选择合适的layout进行使用-->
- <!--设置intent-filter可以先启动该Activity-->
- <activity
- android:name="Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.HOME"/>
- <categoryandroid:name="android.intent.category.DEFAULT"/>
- <categoryandroid:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- <!--设置Wallpapaer的Activity-->
- <activity
- android:name="WallpaperChooser"
- android:label="@string/pick_wallpaper"
- android:icon="@drawable/ic_launcher_wallpaper"
- android:screenOrientation="nosensor"
- android:finishOnCloseSystemDialogs="true">
- <intent-filter>
- <actionandroid:name="android.intent.action.SET_WALLPAPER"/>
- <categoryandroid:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
- <!--安装快捷方式的Intent-->
- <!--Intentreceivedusedtoinstallshortcutsfromotherapplications-->
- <receiver
- android:name="InstallShortcutReceiver"
- android:permission="com.android.zkx_launcher.permission.INSTALL_SHORTCUT">
- <intent-filter>
- <actionandroid:name="com.android.zkx_launcher.action.INSTALL_SHORTCUT"/>
- </intent-filter>
- </receiver>
- <!--卸载快捷方式的Intent-->
- <!--Intentreceivedusedtouninstallshortcutsfromotherapplications-->
- <receiver
- android:name="UninstallShortcutReceiver"
- android:permission="com.android.zkx_launcher.permission.UNINSTALL_SHORTCUT">
- <intent-filter>
- <actionandroid:name="com.android.zkx_launcher.action.UNINSTALL_SHORTCUT"/>
- </intent-filter>
- </receiver>
- <!--供应商信息-->
- <!--ThesettingsprovidercontainsHome'sdata,liketheworkspacefavorites-->
- <provider
- android:name="LauncherProvider"
- android:authorities="com.android.zkx_launcher.settings"
- android:writePermission="com.android.zkx_launcher.permission.WRITE_SETTINGS"
- android:readPermission="com.android.zkx_launcher.permission.READ_SETTINGS"/>
- </application>
- </manifest>
三 Launcher Res下的Xml文件
Res/xml下有两个xml文件,default_workspace.xml&&default_wallpaper.xml
Andorid这个默认壁纸不在launcher里,在源码中frameworks/base/core/res/res /drawable/default_wallpaper.jpg.
另frameworks/base/core/res/res路径下包含很多default资源。如果需要修改默认设置可以尝试到这里来找一找
<favorites xmlns:launcher="http://schemas.android.com/apk/res/com.unique.launcher">
<!-- Far-left screen [0] -->
<!-- Left screen [1] -->
<appwidget
launcher:packageName="com.google.android.apps.genie.geniewidget"
launcher:className="com.google.android.apps.genie.geniewidget.miniwidget.MiniWidgetProvider"
launcher:screen="1"
launcher:x="0"
launcher:y="0"
launcher:spanX="4"
launcher:spanY="1" />
#天气新闻时钟插件
#packageName:widget的packageName
#className :实现 widget的 receiver 类的名称.
#launcher:container 放置的位 置(只能为desktop)
#screen : 在哪一个screen添加
#x,y: 在screen中的位置
#launcher:spanX:在x方向上所占格数
#launcher:spanY:在y方向上所占格数
<!-- Middle screen [2] -->
<search
launcher:screen="2"
launcher:x="0"
launcher:y="0" />
<appwidget
launcher:packageName="com.android.protips"
launcher:className="com.android.protips.ProtipWidget"
launcher:screen="2"
launcher:x="0"
launcher:y="1"
launcher:spanX="4"
launcher:spanY="1 " />
<!-- Right screen [3] -->
<appwidget
launcher:packageName="com.android.music"
launcher:className="com.android.music.MediaAppWidgetProvider"
launcher:screen="3"
launcher:x="0"
launcher:y="0"
launcher:spanX="4"
launcher:spanY="1" />
<appwidget
launcher:packageName="com.android.vending"
launcher:className="com.android.vending.MarketWidgetProvider"
launcher:screen="3"
launcher:x="1"
launcher:y="1"
launcher:spanX="2"
launcher:spanY="2" />
#电子市场Android Market
<!-- Far-right screen [4] -->
</favorites>
代码如下:
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="net.sunniwell.launcher"
android:versionCode="1"android:versionName="1.0.1">
关于自定义权限,这是很好的例子,其他apk程序要想使用Launcher的功能必须添加这些权限,而这些权限都是在这里声明的。
这个是安装快捷方式的权限定义:
<permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"
android:label="@string/permlab_install_shortcut"
android:description="@string/permdesc_install_shortcut"/>
这个是卸载快捷方式的权限定义:
<permission
android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"
android:label="@string/permlab_uninstall_shortcut"
android:description="@string/permdesc_uninstall_shortcut"/>
这个是读取launcher.db内容的权限定义:
<permission
android:name="net.sunniwell.launcher.permission.READ_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"
android:label="@string/permlab_read_settings"
android:description="@string/permdesc_read_settings"/>
这个是修改和删除launcher.db内容的权限定义:
<permission
android:name="net.sunniwell.launcher.permission.WRITE_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"
android:label="@string/permlab_write_settings"
android:description="@string/permdesc_write_settings"/>
这些是Launcher的权限声明,通过这些就能看出launcher的大概功能了:
打电话权限:
<uses-permissionandroid:name="android.permission.CALL_PHONE"/>
使用状态栏权限:
<uses-permissionandroid:name="android.permission.EXPAND_STATUS_BAR"/>
获取当前或最近运行的任务的信息的权限:
<uses-permissionandroid:name="android.permission.GET_TASKS"/>
读取通信录权限:
<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
设置壁纸权限:
<uses-permissionandroid:name="android.permission.SET_WALLPAPER"/>
允许程序设置壁纸hits的权限:
<uses-permissionandroid:name="android.permission.SET_WALLPAPER_HINTS"/>
使用震动功能权限:
<uses-permissionandroid:name="android.permission.VIBRATE"/>
修改删除launcher.db内容权限:
<uses-permissionandroid:name="android.permission.WRITE_SETTINGS"/>
绑定widget权限:
<uses-permissionandroid:name="android.permission.BIND_APPWIDGET"/>
读取launcher.db内容权限:
<uses-permissionandroid:name="net.sunniwell.launcher.permission.READ_SETTINGS"/>
修改删除launcher.db内容权限:
<uses-permissionandroid:name="net.sunniwell.launcher.permission.WRITE_SETTINGS"/>
读写外部存储设备权限:
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<application
android:name="LauncherApplication"
activity应该运行的进程的名字:
android:process="android.process.acore"
android:label="@string/application_name"
android:icon="@drawable/swicon">
<activity
android:name="Launcher"
是否
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
这个activity是否在被杀死或者重启后能恢复原来的状态:
android:stateNotNeeded="true"
android:theme="@style/Theme"
android:screenOrientation="landscape"
android:windowSoftInputMode="stateUnspecified|adjustPan">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>
<categoryandroid:name="android.intent.category.LAUNCHER"/>
桌面应用的标记:
<categoryandroid:name="android.intent.category.HOME"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
自动化测试工具Monkey的标记,待研究…
<categoryandroid:name="android.intent.category.MONKEY"/>
</intent-filter>
</activity>
选择壁纸的activity:
<activity
android:name="WallpaperChooser"
android:label="@string/pick_wallpaper"
android:icon="@drawable/ic_launcher_gallery">
设置壁纸的intent-filter:
<intent-filter>
<actionandroid:name="android.intent.action.SET_WALLPAPER"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
</intent-filter>
搜索的activity:
</activity>
<!-- Enable system-default search mode for any activity in Home -->
<meta-data
android:name="android.app.default_searchable"
android:value="*"/>
安装快捷方式的广播接收器:
<!-- Intent received used to install shortcuts from other applications -->
<receiver
android:name=".InstallShortcutReceiver"
android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
<intent-filter>
<actionandroid:name="com.android.launcher.action.INSTALL_SHORTCUT"/>
</intent-filter>
</receiver>
<!-- Intent received used to uninstall shortcuts from other applications -->
卸载快捷方式的广播接收器:
<receiver
android:name=".UninstallShortcutReceiver"
android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">
<intent-filter>
<actionandroid:name="com.android.launcher.action.UNINSTALL_SHORTCUT"/>
</intent-filter>
</receiver>
声明ContentProvider,用于对launcher.db操作:
<!-- The settings provider contains Home's data, like the workspace favorites -->
<provider
android:name="SWLauncherProvider"
android:authorities="net.sunniwell.launcher.settings"
android:writePermission="net.sunniwell.launcher.permission.WRITE_SETTINGS"
android:readPermission="net.sunniwell.launcher.permission.READ_SETTINGS"/>
</application>
<uses-sdkandroid:minSdkVersion="4"/>
说明:
1.
<manifest标签头部还应声明:
android:sharedUserId="android.uid.shared",作用是获得系统权限,但是这样的程序属性只能在build整个系统时放进去(就是系统软件)才起作用,手动安装是没有权限的。
五 Launcher 常用类介绍
AddAdapter:维护了live fold, widget , shortcut , wallpaper 4个ListItem,长按桌面会显示该列表
AllAppsGridView:显示APP的网格
ApplicationInfo:一个可启动的应用
ApplicationsAdapter:gridview的adapter
BubbleTextView:一个定制了的textview
CellLayout:屏幕网格化
DeleteZone:UI的一部分
DragController,dragscroller, dragsource, droptarget:支持拖拽操作
DragLayer:内部支持拖拽的viewgroup
FastBitmapDrawable:工具
Folder:Icons的集合
FolderIcon:出现在workspace的icon代表了一个folder
FolderInfo: ItemInfo子类
HandleView:一个imageview。
InstallShortcutReceiver,UninstallShortcutReceiver:一个broadcastrecier
ItemInfo:代表Launcher中一个Item(例如folder)
Launcher: Launcher程序的主窗口
LauncherApplication:在VM中设置参数
LauncherAppWidgetHost,LauncherAppWidgetHostView,:Widget相关
LauncherModel:MVC中的M
LauncherProvider:一个contentprovider,为Launcher存储信息
LauncherSettings:设置相关的工具
LiveFolder,LiveFolderAdapter,LiveFolderIcon,LiveFolderInfo:livefolder相关
Search:搜索
UserFolder,UserFolderInfo:文件夹包含applications ,shortcuts
Utilities:小工具
WallpaperChooser:选择wallpaper的activity
Workspace:屏幕上的一块区域
widget :代表启动的widget实例,例如搜索
总结
1) Launcher中实现了MVC模式(M:launchermode , V:draglayer ,C: launcher),以此为主线,可以得到Launcher对各个组件管理的细节(如drag的实现)。
六 Launcher 起动过程
Android系统在启动时会安装应用程序,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了,我将详细分析Launcher应用程序的启动过程。
Android系统的Home应用程序Launcher是由ActivityManagerService启动的,而ActivityManagerService和PackageManagerService一样,都是在开机时由SystemServer组件启动的,SystemServer组件首先是启动ePackageManagerServic,由它来负责安装系统的应用程序,系统中的应用程序安装好了以后,SystemServer组件接下来就要通过ActivityManagerService来启动Home应用程序Launcher了,Launcher在启动的时候便会通过PackageManagerServic把系统中已经安装好的应用程序以快捷图标的形式展示在桌面上,这样用户就可以使用这些应用程序了。
下面详细分析每一个步骤。
Step 1. SystemServer.main
这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中: view plain copy to clipboard print ?- publicclassSystemServer
- {
- ......
- nativepublicstaticvoidinit1(String[]args);
- ......
- publicstaticvoidmain(String[]args){
- ......
- init1(args);
- ......
- }
- ......
- }
Step 2. SystemServer.init1
这个函数是一个JNI方法,实现在frameworks/base/services/jni/com_android_server_SystemServer.cpp文件中:
view plain copy to clipboard print ?- namespaceandroid{
- extern"C"intsystem_init();
- staticvoidandroid_server_SystemServer_init1(JNIEnv*env,jobjectclazz)
- {
- system_init();
- }
- /*
- *JNIregistration.
- */
- staticJNINativeMethodgMethods[]={
- /*name,signature,funcPtr*/
- {"init1","([Ljava/lang/String;)V",(void*)android_server_SystemServer_init1},
- };
- intregister_android_server_SystemServer(JNIEnv*env)
- {
- returnjniRegisterNativeMethods(env,"com/android/server/SystemServer",
- gMethods,NELEM(gMethods));
- }
- };//namespaceandroid
Step 3.libsystem_server.system_init
函数system_init实现在libsystem_server库中,源代码位于frameworks/base/cmds/system_server/library/system_init.cpp文件中:
view plain copy to clipboard print ?- extern"C"status_tsystem_init()
- {
- LOGI("Enteredsystem_init()");
- sp<ProcessState>proc(ProcessState::self());
- sp<IServiceManager>sm=defaultServiceManager();
- LOGI("ServiceManager:%p\n",sm.get());
- sp<GrimReaper>grim=newGrimReaper();
- sm->asBinder()->linkToDeath(grim,grim.get(),0);
- charpropBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsurfaceflinger",propBuf,"1");
- if(strcmp(propBuf,"1")==0){
- //StarttheSurfaceFlinger
- SurfaceFlinger::instantiate();
- }
- //Startthesensorservice
- SensorService::instantiate();
- //Onthesimulator,audioflingeretaldon'tgetstartedthe
- //samewayasonthedevice,andweneedtostartthemhere
- if(!proc->supportsProcesses()){
- //StarttheAudioFlinger
- AudioFlinger::instantiate();
- //Startthemediaplaybackservice
- MediaPlayerService::instantiate();
- //Startthecameraservice
- CameraService::instantiate();
- //Starttheaudiopolicyservice
- AudioPolicyService::instantiate();
- }
- //AndnowstarttheAndroidruntime.Wehavetodothisbit
- //ofnastinessbecausetheAndroidruntimeinitializationrequires
- //someofthecoresystemservicestoalreadybestarted.
- //AllotherserversshouldjuststarttheAndroidruntimeat
- //thebeginningoftheirprocesses'smain(),beforecalling
- //theinitfunction.
- LOGI("Systemserver:startingAndroidruntime.\n");
- AndroidRuntime*runtime=AndroidRuntime::getRuntime();
- LOGI("Systemserver:startingAndroidservices.\n");
- runtime->callStatic("com/android/server/SystemServer","init2");
- //Ifrunninginourownprocess,justgointothethread
- //pool.Otherwise,calltheinitializationfinished
- //functoletthisprocesscontinueitsinitilization.
- if(proc->supportsProcesses()){
- LOGI("Systemserver:enteringthreadpool.\n");
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- LOGI("Systemserver:exitingthreadpool.\n");
- }
- returnNO_ERROR;
- }
Step 4. AndroidRuntime.callStatic
这个函数定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中:
view plain copy to clipboard print ?- /*
- *CallastaticJavaProgrammingLanguagefunctionthattakesnoargumentsandreturnsvoid.
- */
- status_tAndroidRuntime::callStatic(constchar*className,constchar*methodName)
- {
- JNIEnv*env;
- jclassclazz;
- jmethodIDmethodId;
- env=getJNIEnv();
- if(env==NULL)
- returnUNKNOWN_ERROR;
- clazz=findClass(env,className);
- if(clazz==NULL){
- LOGE("ERROR:couldnotfindclass'%s'\n",className);
- returnUNKNOWN_ERROR;
- }
- methodId=env->GetStaticMethodID(clazz,methodName,"()V");
- if(methodId==NULL){
- LOGE("ERROR:couldnotfindmethod%s.%s\n",className,methodName);
- returnUNKNOWN_ERROR;
- }
- env->CallStaticVoidMethod(clazz,methodId);
- returnNO_ERROR;
- }
Step 5.SystemServer.init2
这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:
- publicclassSystemServer
- {
- ......
- publicstaticfinalvoidinit2(){
- Slog.i(TAG,"EnteredtheAndroidsystemserver!");
- Threadthr=newServerThread();
- thr.setName("android.server.ServerThread");
- thr.start();
- }
- }
Step 6.ServerThread.run
这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:
- classServerThreadextendsThread{
- ......
- @Override
- publicvoidrun(){
- ......
- IPackageManagerpm=null;
- ......
- //Criticalservices...
- try{
- ......
- Slog.i(TAG,"PackageManager");
- pm=PackageManagerService.main(context,
- factoryTest!=SystemServer.FACTORY_TEST_OFF);
- ......
- }catch(RuntimeExceptione){
- Slog.e("System","Failurestartingcoreservice",e);
- }
- ......
- }
- ......
- }
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
view plain copy to clipboard print ?- publicfinalclassActivityManagerServiceextendsActivityManagerNative
- implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
- ......
- publicstaticfinalContextmain(intfactoryTest){
- AThreadthr=newAThread();
- thr.start();
- synchronized(thr){
- while(thr.mService==null){
- try{
- thr.wait();
- }catch(InterruptedExceptione){
- }
- }
- }
- ActivityManagerServicem=thr.mService;
- mSelf=m;
- ActivityThreadat=ActivityThread.systemMain();
- mSystemThread=at;
- Contextcontext=at.getSystemContext();
- m.mContext=context;
- m.mFactoryTest=factoryTest;
- m.mMainStack=newActivityStack(m,context,true);
- m.mBatteryStatsService.publish(context);
- m.mUsageStatsService.publish(context);
- synchronized(thr){
- thr.mReady=true;
- thr.notifyAll();
- }
- m.startRunning(null,null,null,null);
- returncontext;
- }
- ......
- }
Step 8.PackageManagerService.main
这个函数定义在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中: view plain copy to clipboard print ?- classPackageManagerServiceextendsIPackageManager.Stub{
- ......
- publicstaticfinalIPackageManagermain(Contextcontext,booleanfactoryTest){
- PackageManagerServicem=newPackageManagerService(context,factoryTest);
- ServiceManager.addService("package",m);
- returnm;
- }
- ......
- }
- classPackageManagerServiceextendsIPackageManager.Stub{
- ......
- publicPackageManagerService(Contextcontext,booleanfactoryTest){
- ......
- synchronized(mInstallLock){
- synchronized(mPackages){
- ......
- FiledataDir=Environment.getDataDirectory();
- mAppDataDir=newFile(dataDir,"data");
- mSecureAppDataDir=newFile(dataDir,"secure/data");
- mDrmAppPrivateInstallDir=newFile(dataDir,"app-private");
- ......
- mFrameworkDir=newFile(Environment.getRootDirectory(),"framework");
- mDalvikCacheDir=newFile(dataDir,"dalvik-cache");
- ......
- //Findbaseframeworks(resourcepackageswithoutcode).
- mFrameworkInstallObserver=newAppDirObserver(
- mFrameworkDir.getPath(),OBSERVER_EVENTS,true);
- mFrameworkInstallObserver.startWatching();
- scanDirLI(mFrameworkDir,PackageParser.PARSE_IS_SYSTEM
- |PackageParser.PARSE_IS_SYSTEM_DIR,
- scanMode|SCAN_NO_DEX,0);
- //Collectallsystempackages.
- mSystemAppDir=newFile(Environment.getRootDirectory(),"app");
- mSystemInstallObserver=newAppDirObserver(
- mSystemAppDir.getPath(),OBSERVER_EVENTS,true);
- mSystemInstallObserver.startWatching();
- scanDirLI(mSystemAppDir,PackageParser.PARSE_IS_SYSTEM
- |PackageParser.PARSE_IS_SYSTEM_DIR,scanMode,0);
- //Collectallvendorpackages.
- mVendorAppDir=newFile("/vendor/app");
- mVendorInstallObserver=newAppDirObserver(
- mVendorAppDir.getPath(),OBSERVER_EVENTS,true);
- mVendorInstallObserver.startWatching();
- scanDirLI(mVendorAppDir,PackageParser.PARSE_IS_SYSTEM
- |PackageParser.PARSE_IS_SYSTEM_DIR,scanMode,0);
- mAppInstallObserver=newAppDirObserver(
- mAppInstallDir.getPath(),OBSERVER_EVENTS,false);
- mAppInstallObserver.startWatching();
- scanDirLI(mAppInstallDir,0,scanMode,0);
- mDrmAppInstallObserver=newAppDirObserver(
- mDrmAppPrivateInstallDir.getPath(),OBSERVER_EVENTS,false);
- mDrmAppInstallObserver.startWatching();
- scanDirLI(mDrmAppPrivateInstallDir,PackageParser.PARSE_FORWARD_LOCK,
- scanMode,0);
- ......
- }
- }
- }
- ......
- }
/system/framework
/system/app
/vendor/app
/data/app
/data/app-private
Step 9.ActivityManagerService.setSystemProcess
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
view plain copy to clipboard print ?- publicfinalclassActivityManagerServiceextendsActivityManagerNative
- implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
- ......
- publicstaticvoidsetSystemProcess(){
- try{
- ActivityManagerServicem=mSelf;
- ServiceManager.addService("activity",m);
- ServiceManager.addService("meminfo",newMemBinder(m));
- if(MONITOR_CPU_USAGE){
- ServiceManager.addService("cpuinfo",newCpuBinder(m));
- }
- ServiceManager.addService("permission",newPermissionController(m));
- ApplicationInfoinfo=
- mSelf.mContext.getPackageManager().getApplicationInfo(
- "android",STOCK_PM_FLAGS);
- mSystemThread.installSystemApplicationInfo(info);
- synchronized(mSelf){
- ProcessRecordapp=mSelf.newProcessRecordLocked(
- mSystemThread.getApplicationThread(),info,
- info.processName);
- app.persistent=true;
- app.pid=MY_PID;
- app.maxAdj=SYSTEM_ADJ;
- mSelf.mProcessNames.put(app.processName,app.info.uid,app);
- synchronized(mSelf.mPidsSelfLocked){
- mSelf.mPidsSelfLocked.put(app.pid,app);
- }
- mSelf.updateLruProcessLocked(app,true,true);
- }
- }catch(PackageManager.NameNotFoundExceptione){
- thrownewRuntimeException(
- "Unabletofindandroidsystempackage",e);
- }
- }
- ......
- }
public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... public static void setSystemProcess() { try { ActivityManagerService m = mSelf; ServiceManager.addService("activity", m); ServiceManager.addService("meminfo", new MemBinder(m)); if (MONITOR_CPU_USAGE) { ServiceManager.addService("cpuinfo", new CpuBinder(m)); } ServiceManager.addService("permission", new PermissionController(m)); ApplicationInfo info = mSelf.mContext.getPackageManager().getApplicationInfo( "android", STOCK_PM_FLAGS); mSystemThread.installSystemApplicationInfo(info); synchronized (mSelf) { ProcessRecord app = mSelf.newProcessRecordLocked( mSystemThread.getApplicationThread(), info, info.processName); app.persistent = true; app.pid = MY_PID; app.maxAdj = SYSTEM_ADJ; mSelf.mProcessNames.put(app.processName, app.info.uid, app); synchronized (mSelf.mPidsSelfLocked) { mSelf.mPidsSelfLocked.put(app.pid, app); } mSelf.updateLruProcessLocked(app, true, true); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException( "Unable to find android system package", e); } } ...... } 这个函数首先是将这个ActivityManagerService实例添加到ServiceManager中去托管,这样其它地方就可以通过ServiceManager.getService接口来访问这个全局唯一的ActivityManagerService实例了,接着又通过调用mSystemThread.installSystemApplicationInfo函数来把应用程序框架层下面的android包加载进来 ,这里的mSystemThread是一个ActivityThread类型的实例变量,它是在上面的Step 7中创建的,后面就是一些其它的初始化工作了。
Step 10. ActivityManagerService.systemReady
这个函数是在上面的Step 6中的ServerThread.run函数在将系统中的一系列服务都初始化完毕之后才调用的,它定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
view plain copy to clipboard print ?- publicfinalclassActivityManagerServiceextendsActivityManagerNative
- implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
- ......
- publicvoidsystemReady(finalRunnablegoingCallback){
- ......
- synchronized(this){
- ......
- mMainStack.resumeTopActivityLocked(null);
- }
- }
- ......
- }
public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... public void systemReady(final Runnable goingCallback) { ...... synchronized (this) { ...... mMainStack.resumeTopActivityLocked(null); } } ...... } 这个函数的内容比较多,这里省去无关的部分,主要关心启动Home应用程序的逻辑,这里就是通过mMainStack.resumeTopActivityLocked函数来启动Home应用程序的了,这里的mMainStack是一个ActivityStack类型的实例变量。
Step 11. ActivityStack.resumeTopActivityLocked
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- publicclassActivityStack{
- ......
- finalbooleanresumeTopActivityLocked(ActivityRecordprev){
- //Findthefirstactivitythatisnotfinishing.
- ActivityRecordnext=topRunningActivityLocked(null);
- ......
- if(next==null){
- //Therearenomoreactivities!Let'sjuststartupthe
- //Launcher...
- if(mMainStack){
- returnmService.startHomeActivityLocked();
- }
- }
- ......
- }
- ......
- }
public class ActivityStack { ...... final boolean resumeTopActivityLocked(ActivityRecord prev) { // Find the first activity that is not finishing. ActivityRecord next = topRunningActivityLocked(null); ...... if (next == null) { // There are no more activities! Let's just start up the // Launcher... if (mMainStack) { return mService.startHomeActivityLocked(); } } ...... } ...... } 这里调用函数topRunningActivityLocked返回的是当前系统Activity堆栈最顶端的Activity,由于此时还没有Activity被启动过,因此,返回值为null,即next变量的值为null,于是就调用mService.startHomeActivityLocked语句,这里的mService就是前面在Step 7中创建的ActivityManagerService实例了。
Step 12.ActivityManagerService.startHomeActivityLocked
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
view plain copy to clipboard print ?- publicfinalclassActivityManagerServiceextendsActivityManagerNative
- implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
- ......
- booleanstartHomeActivityLocked(){
- ......
- Intentintent=newIntent(
- mTopAction,
- mTopData!=null?Uri.parse(mTopData):null);
- intent.setComponent(mTopComponent);
- if(mFactoryTest!=SystemServer.FACTORY_TEST_LOW_LEVEL){
- intent.addCategory(Intent.CATEGORY_HOME);
- }
- ActivityInfoaInfo=
- intent.resolveActivityInfo(mContext.getPackageManager(),
- STOCK_PM_FLAGS);
- if(aInfo!=null){
- intent.setComponent(newComponentName(
- aInfo.applicationInfo.packageName,aInfo.name));
- //Don'tdothisifthehomeappiscurrentlybeing
- //instrumented.
- ProcessRecordapp=getProcessRecordLocked(aInfo.processName,
- aInfo.applicationInfo.uid);
- if(app==null||app.instrumentationClass==null){
- intent.setFlags(intent.getFlags()|Intent.FLAG_ACTIVITY_NEW_TASK);
- mMainStack.startActivityLocked(null,intent,null,null,0,aInfo,
- null,null,0,0,0,false,false);
- }
- }
- returntrue;
- }
- ......
- }
public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... boolean startHomeActivityLocked() { ...... Intent intent = new Intent( mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } ActivityInfo aInfo = intent.resolveActivityInfo(mContext.getPackageManager(), STOCK_PM_FLAGS); if (aInfo != null) { intent.setComponent(new ComponentName( aInfo.applicationInfo.packageName, aInfo.name)); // Don't do this if the home app is currently being // instrumented. ProcessRecord app = getProcessRecordLocked(aInfo.processName, aInfo.applicationInfo.uid); if (app == null || app.instrumentationClass == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo, null, null, 0, 0, 0, false, false); } } return true; } ...... } 函数首先创建一个CATEGORY_HOME类型的Intent,然后通过Intent.resolveActivityInfo函数向PackageManagerService查询Category类型为HOME的Activity,这里我们假设只有系统自带的Launcher应用程序注册了HOME类型的Activity(见packages/apps/Launcher2/AndroidManifest.xml文件):
view plain copy to clipboard print ?- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.launcher"
- android:sharedUserId="@string/sharedUserId"
- >
- ......
- <application
- android:name="com.android.launcher2.LauncherApplication"
- android:process="@string/process"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
- <activity
- android:name="com.android.launcher2.Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.HOME"/>
- <categoryandroid:name="android.intent.category.DEFAULT"/>
- <categoryandroid:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- ......
- </application>
- </manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.launcher" android:sharedUserId="@string/sharedUserId" > ...... <application android:name="com.android.launcher2.LauncherApplication" android:process="@string/process" android:label="@string/application_name" android:icon="@drawable/ic_launcher_home"> <activity android:name="com.android.launcher2.Launcher" android:launchMode="singleTask" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:theme="@style/Theme" android:screenOrientation="nosensor" android:windowSoftInputMode="stateUnspecified|adjustPan"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.MONKEY"/> </intent-filter> </activity> ...... </application> </manifest>
因此,这里就返回com.android.launcher2.Launcher这个Activity了。由于是第一次启动这个Activity,接下来调用函数getProcessRecordLocked返回来的ProcessRecord值为null,于是,就调用mMainStack.startActivityLocked函数启动com.android.launcher2.Launcher这个Activity了,这里的mMainStack是一个ActivityStack类型的成员变量。
Step 13. ActivityStack.startActivityLocked
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中
Step 14.Launcher.onCreate
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/Launcher.java文件中:
view plain copy to clipboard print ?- publicfinalclassLauncherextendsActivity
- implementsView.OnClickListener,OnLongClickListener,LauncherModel.Callbacks,AllAppsView.Watcher{
- ......
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- ......
- if(!mRestoring){
- mModel.startLoader(this,true);
- }
- ......
- }
- ......
- }
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... @Override protected void onCreate(Bundle savedInstanceState) { ...... if (!mRestoring) { mModel.startLoader(this, true); } ...... } ...... } 这里的mModel是一个LauncherModel类型的成员变量,这里通过调用它的startLoader成员函数来执行加应用程序的操作。
Step 15.LauncherModel.startLoader
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
view plain copy to clipboard print ?- publicclassLauncherModelextendsBroadcastReceiver{
- ......
- publicvoidstartLoader(Contextcontext,booleanisLaunching){
- ......
- synchronized(mLock){
- ......
- //Don'tbothertostartthethreadifweknowit'snotgoingtodoanything
- if(mCallbacks!=null&&mCallbacks.get()!=null){
- //Ifthereisalreadyonerunning,tellittostop.
- LoaderTaskoldTask=mLoaderTask;
- if(oldTask!=null){
- if(oldTask.isLaunching()){
- //don'tdowngradeisLaunchingifwe'realreadyrunning
- isLaunching=true;
- }
- oldTask.stopLocked();
- }
- mLoaderTask=newLoaderTask(context,isLaunching);
- sWorker.post(mLoaderTask);
- }
- }
- }
- ......
- }
public class LauncherModel extends BroadcastReceiver { ...... public void startLoader(Context context, boolean isLaunching) { ...... synchronized (mLock) { ...... // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { // If there is already one running, tell it to stop. LoaderTask oldTask = mLoaderTask; if (oldTask != null) { if (oldTask.isLaunching()) { // don't downgrade isLaunching if we're already running isLaunching = true; } oldTask.stopLocked(); } mLoaderTask = new LoaderTask(context, isLaunching); sWorker.post(mLoaderTask); } } } ...... } 这里不是直接加载应用程序,而是把加载应用程序的操作作为一个消息来处理。这里的sWorker是一个Handler,通过它的post方式把一个消息放在消息队列中去,然后系统就会调用传进去的参数mLoaderTask的run函数来处理这个消息,这个mLoaderTask是LoaderTask类型的实例,于是,下面就会执行LoaderTask类的run函数了。
Step 16. LoaderTask.run
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
view plain copy to clipboard print ?- publicclassLauncherModelextendsBroadcastReceiver{
- ......
- privateclassLoaderTaskimplementsRunnable{
- ......
- publicvoidrun(){
- ......
- keep_running:{
- ......
- //secondstep
- if(loadWorkspaceFirst){
- ......
- loadAndBindAllApps();
- }else{
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- ......
- }
public class LauncherModel extends BroadcastReceiver { ...... private class LoaderTask implements Runnable { ...... public void run() { ...... keep_running: { ...... // second step if (loadWorkspaceFirst) { ...... loadAndBindAllApps(); } else { ...... } ...... } ...... } ...... } ...... } 这里调用loadAndBindAllApps成员函数来进一步操作。
Step 17.LoaderTask.loadAndBindAllApps
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- publicclassLauncherModelextendsBroadcastReceiver{
- ......
- privateclassLoaderTaskimplementsRunnable{
- ......
- privatevoidloadAndBindAllApps(){
- ......
- if(!mAllAppsLoaded){
- loadAllAppsByBatch();
- if(mStopped){
- return;
- }
- mAllAppsLoaded=true;
- }else{
- onlyBindAllApps();
- }
- }
- ......
- }
- ......
- }
public class LauncherModel extends BroadcastReceiver { ...... private class LoaderTask implements Runnable { ...... private void loadAndBindAllApps() { ...... if (!mAllAppsLoaded) { loadAllAppsByBatch(); if (mStopped) { return; } mAllAppsLoaded = true; } else { onlyBindAllApps(); } } ...... } ...... } 由于还没有加载过应用程序,这里的mAllAppsLoaded为false,于是就继续调用loadAllAppsByBatch函数来进一步操作了。
Step 18.LoaderTask.loadAllAppsByBatch
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- publicclassLauncherModelextendsBroadcastReceiver{
- ......
- privateclassLoaderTaskimplementsRunnable{
- ......
- privatevoidloadAllAppsByBatch(){
- ......
- finalIntentmainIntent=newIntent(Intent.ACTION_MAIN,null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- finalPackageManagerpackageManager=mContext.getPackageManager();
- List<ResolveInfo>apps=null;
- intN=Integer.MAX_VALUE;
- intstartIndex;
- inti=0;
- intbatchSize=-1;
- while(i<N&&!mStopped){
- if(i==0){
- mAllAppsList.clear();
- ......
- apps=packageManager.queryIntentActivities(mainIntent,0);
- ......
- N=apps.size();
- ......
- if(mBatchSize==0){
- batchSize=N;
- }else{
- batchSize=mBatchSize;
- }
- ......
- Collections.sort(apps,
- newResolveInfo.DisplayNameComparator(packageManager));
- }
- startIndex=i;
- for(intj=0;i<N&&j<batchSize;j++){
- //Thisbuildstheiconbitmaps.
- mAllAppsList.add(newApplicationInfo(apps.get(i),mIconCache));
- i++;
- }
- finalbooleanfirst=i<=batchSize;
- finalCallbackscallbacks=tryGetCallbacks(oldCallbacks);
- finalArrayList<ApplicationInfo>added=mAllAppsList.added;
- mAllAppsList.added=newArrayList<ApplicationInfo>();
- mHandler.post(newRunnable(){
- publicvoidrun(){
- finallongt=SystemClock.uptimeMillis();
- if(callbacks!=null){
- if(first){
- callbacks.bindAllApplications(added);
- }else{
- callbacks.bindAppsAdded(added);
- }
- ......
- }else{
- ......
- }
- }
- });
- ......
- }
- ......
- }
- ......
- }
- ......
- }
public class LauncherModel extends BroadcastReceiver { ...... private class LoaderTask implements Runnable { ...... private void loadAllAppsByBatch() { ...... final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); final PackageManager packageManager = mContext.getPackageManager(); List<ResolveInfo> apps = null; int N = Integer.MAX_VALUE; int startIndex; int i=0; int batchSize = -1; while (i < N && !mStopped) { if (i == 0) { mAllAppsList.clear(); ...... apps = packageManager.queryIntentActivities(mainIntent, 0); ...... N = apps.size(); ...... if (mBatchSize == 0) { batchSize = N; } else { batchSize = mBatchSize; } ...... Collections.sort(apps, new ResolveInfo.DisplayNameComparator(packageManager)); } startIndex = i; for (int j=0; i<N && j<batchSize; j++) { // This builds the icon bitmaps. mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache)); i++; } final boolean first = i <= batchSize; final Callbacks callbacks = tryGetCallbacks(oldCallbacks); final ArrayList<ApplicationInfo> added = mAllAppsList.added; mAllAppsList.added = new ArrayList<ApplicationInfo>(); mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (first) { callbacks.bindAllApplications(added); } else { callbacks.bindAppsAdded(added); } ...... } else { ...... } } }); ...... } ...... } ...... } ...... } 函数首先构造一个CATEGORY_LAUNCHER类型的Intent:
- finalIntentmainIntent=newIntent(Intent.ACTION_MAIN,null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 接着从mContext变量中获得PackageManagerService的接口:
view plain copy to clipboard print ?- finalPackageManagerpackageManager=mContext.getPackageManager();
final PackageManager packageManager = mContext.getPackageManager();
下一步就是通过这个PackageManagerService.queryIntentActivities接口来取回所有Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。
我们先进入到PackageManagerService.queryIntentActivities函数中看看是如何获得这些Activity的,然后再回到这个函数中来看其余操作。
Step 19.PackageManagerService.queryIntentActivities
这个函数定义在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中:
view plain copy to clipboard print ?- classPackageManagerServiceextendsIPackageManager.Stub{
- ......
- publicList<ResolveInfo>queryIntentActivities(Intentintent,
- StringresolvedType,intflags){
- ......
- synchronized(mPackages){
- StringpkgName=intent.getPackage();
- if(pkgName==null){
- return(List<ResolveInfo>)mActivities.queryIntent(intent,
- resolvedType,flags);
- }
- ......
- }
- ......
- }
- ......
- }
class PackageManagerService extends IPackageManager.Stub { ...... public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) { ...... synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return (List<ResolveInfo>)mActivities.queryIntent(intent, resolvedType, flags); } ...... } ...... } ...... }
系统在启动PackageManagerService时,会把系统中的应用程序都解析一遍,然后把解析得到的Activity都保存在mActivities变量中,这里通过这个mActivities变量的queryIntent函数返回符合条件intent的Activity,这里要返回的便是Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。
回到Step 18中的LoaderTask.loadAllAppsByBatch函数中,从queryIntentActivities函数调用处返回所要求的Activity后,便调用函数tryGetCallbacks(oldCallbacks)得到一个返CallBack接口,这个接口是由Launcher类实现的,接着调用这个接口的.bindAllApplications函数来进一步操作。注意,这里又是通过消息来处理加载应用程序的操作的。
Step 20.Launcher.bindAllApplications
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/Launcher.java文件中:
view plain copy to clipboard print ?- publicfinalclassLauncherextendsActivity
- implementsView.OnClickListener,OnLongClickListener,LauncherModel.Callbacks,AllAppsView.Watcher{
- ......
- privateAllAppsViewmAllAppsGrid;
- ......
- publicvoidbindAllApplications(ArrayList<ApplicationInfo>apps){
- mAllAppsGrid.setApps(apps);
- }
- ......
- }
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... private AllAppsView mAllAppsGrid; ...... public void bindAllApplications(ArrayList<ApplicationInfo> apps) { mAllAppsGrid.setApps(apps); } ...... } 这里的mAllAppsGrid是一个AllAppsView类型的变量,它的实际类型一般就是AllApps2D了。
Step 21.AllApps2D.setApps
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/AllApps2D.java文件中:
- publicclassAllApps2D
- extendsRelativeLayout
- implementsAllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource{
- ......
- publicvoidsetApps(ArrayList<ApplicationInfo>list){
- mAllAppsList.clear();
- addApps(list);
- }
- publicvoidaddApps(ArrayList<ApplicationInfo>list){
- finalintN=list.size();
- for(inti=0;i<N;i++){
- finalApplicationInfoitem=list.get(i);
- intindex=Collections.binarySearch(mAllAppsList,item,
- LauncherModel.APP_NAME_COMPARATOR);
- if(index<0){
- index=-(index+1);
- }
- mAllAppsList.add(index,item);
- }
- mAppsAdapter.notifyDataSetChanged();
- }
- ......
- }
public class AllApps2D extends RelativeLayout implements AllAppsView, AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, View.OnKeyListener, DragSource { ...... public void setApps(ArrayList<ApplicationInfo> list) { mAllAppsList.clear(); addApps(list); } public void addApps(ArrayList<ApplicationInfo> list) { final int N = list.size(); for (int i=0; i<N; i++) { final ApplicationInfo item = list.get(i); int index = Collections.binarySearch(mAllAppsList, item, LauncherModel.APP_NAME_COMPARATOR); if (index < 0) { index = -(index+1); } mAllAppsList.add(index, item); } mAppsAdapter.notifyDataSetChanged(); } ...... } 函数setApps首先清空mAllAppsList列表,然后调用addApps函数来为上一步得到的每一个应用程序创建一个ApplicationInfo实例了,有了这些ApplicationInfo实例之后,就可以在桌面上展示系统中所有的应用程序了。
到了这里,系统默认的Home应用程序Launcher就把PackageManagerService中的应用程序加载进来了,当我们在屏幕上点击下面这个图标时,就会把刚才加载好的应用程序以图标的形式展示出来了:
点击这个按钮时,便会响应Launcher.onClick函数:
view plain copy to clipboard print ?- publicfinalclassLauncherextendsActivity
- implementsView.OnClickListener,OnLongClickListener,LauncherModel.Callbacks,AllAppsView.Watcher{
- ......
- publicvoidonClick(Viewv){
- Objecttag=v.getTag();
- if(taginstanceofShortcutInfo){
- ......
- }elseif(taginstanceofFolderInfo){
- ......
- }elseif(v==mHandleView){
- if(isAllAppsVisible()){
- ......
- }else{
- showAllApps(true);
- }
- }
- }
- ......
- }
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... public void onClick(View v) { Object tag = v.getTag(); if (tag instanceof ShortcutInfo) { ...... } else if (tag instanceof FolderInfo) { ...... } else if (v == mHandleView) { if (isAllAppsVisible()) { ...... } else { showAllApps(true); } } } ...... } 接着就会调用showAllApps函数显示应用程序图标:
view plain copy to clipboard print ?- publicfinalclassLauncherextendsActivity
- implementsView.OnClickListener,OnLongClickListener,LauncherModel.Callbacks,AllAppsView.Watcher{
- ......
- voidshowAllApps(booleananimated){
- mAllAppsGrid.zoom(1.0f,animated);
- ((View)mAllAppsGrid).setFocusable(true);
- ((View)mAllAppsGrid).requestFocus();
- //TODO:fadethesetwotoo
- mDeleteZone.setVisibility(View.GONE);
- }
- ......
- }
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... void showAllApps(boolean animated) { mAllAppsGrid.zoom(1.0f, animated); ((View) mAllAppsGrid).setFocusable(true); ((View) mAllAppsGrid).requestFocus(); // TODO: fade these two too mDeleteZone.setVisibility(View.GONE); } ...... } 这样我们就可以看到系统中的应用程序了:
当点击上面的这些应用程序图标时,便会响应AllApps2D.onItemClick函数:
- publicclassAllApps2D
- extendsRelativeLayout
- implementsAllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource{
- ......
- publicvoidonItemClick(AdapterViewparent,Viewv,intposition,longid){
- ApplicationInfoapp=(ApplicationInfo)parent.getItemAtPosition(position);
- mLauncher.startActivitySafely(app.intent,app);
- }
- ......
- }
public class AllApps2D extends RelativeLayout implements AllAppsView, AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, View.OnKeyListener, DragSource { ...... public void onItemClick(AdapterView parent, View v, int position, long id) { ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position); mLauncher.startActivitySafely(app.intent, app); } ...... } 这里的成员变量mLauncher的类型为Launcher,于是就调用Launcher.startActivitySafely函数来启动应用程序了。
七 Launcher widget添加过程
Android中的AppWidget与google widget和中移动的widget并不是一个概念,这里的AppWidget只是把一个进程的控件嵌入到别外一个进程的窗口里的一种方法。View在另外一个进程里显示,但事件的处理方法还是在原来的进程里。这有点像 X Window中的嵌入式窗口。
首先我们需要了解RemoteViews, AppWidgetHost, AppWidgetHostView等概念
RemoteViews:并不是一个真正的View,它没有实现View的接口,而只是一个用于描述View的实体。比如:创建View需要的资源ID和各个控件的事件响应方法。RemoteViews会通过进程间通信机制传递给AppWidgetHost。
AppWidgetHost
AppWidgetHost是真正容纳AppWidget的地方,它的主要功能有两个:
o 监听来自AppWidgetService的事件:
class Callbacks extends IAppWidgetHost.Stub{publicvoidupdateAppWidget(intappWidgetId,RemoteViews views){Message msg=mHandler.obtainMessage(HANDLE_UPDATE);msg.arg1=appWidgetId;msg.obj=views;msg.sendToTarget();} publicvoidproviderChanged(intappWidgetId,AppWidgetProviderInfo info){Message msg=mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED);msg.arg1=appWidgetId;msg.obj=info;msg.sendToTarget();}}
这是主要处理update和provider_changed两个事件,根据这两个事件更新widget。
class UpdateHandler extends Handler{public UpdateHandler(Looper looper){super(looper);} publicvoidhandleMessage(Message msg){switch(msg.what){caseHANDLE_UPDATE:{updateAppWidgetView(msg.arg1,(RemoteViews)msg.obj);break;}caseHANDLE_PROVIDER_CHANGED:{onProviderChanged(msg.arg1,(AppWidgetProviderInfo)msg.obj);break;}}}}
o 另外一个功能就是创建AppWidgetHostView。前面我们说过RemoteViews不是真正的View,只是View的描述,而AppWidgetHostView才是真正的View。这里先创建AppWidgetHostView,然后通过AppWidgetService查询appWidgetId对应的RemoteViews,最后把RemoteViews传递给AppWidgetHostView去updateAppWidget。
public final AppWidgetHostView createView(Context context,intappWidgetId,AppWidgetProviderInfo appWidget){AppWidgetHostView view=onCreateView(context,appWidgetId,appWidget);view.setAppWidget(appWidgetId,appWidget);synchronized(mViews){mViews.put(appWidgetId,view);}RemoteViews views=null;try{views=sService.getAppWidgetViews(appWidgetId);}catch(RemoteException e){throw new RuntimeException("system server dead?",e);}view.updateAppWidget(views);returnview;}
AppWidgetHostView
AppWidgetHostView是真正的View,但它只是一个容器,用来容纳实际的AppWidget的View。这个AppWidget的View是根据RemoteViews的描述来创建。这是在updateAppWidget里做的:
publicvoidupdateAppWidget(RemoteViews remoteViews){...if(content==null&&layoutId==mLayoutId){try{remoteViews.reapply(mContext,mView);content=mView;recycled=true;if(LOGD)Log.d(TAG,"was able to recycled existing layout");}catch(RuntimeException e){exception=e;}}// Try normal RemoteView inflationif(content==null){try{content=remoteViews.apply(mContext,this);if(LOGD)Log.d(TAG,"had to inflate new layout");}catch(RuntimeException e){exception=e;}}...if(!recycled){prepareView(content);addView(content);}if(mView!=content){removeView(mView);mView=content;}...}
remoteViews.apply创建了实际的View,下面代码可以看出:
public View apply(Context context,ViewGroup parent){View result=null; Context c=prepareContext(context); Resources r=c.getResources();LayoutInflater inflater=(LayoutInflater)c .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater=inflater.cloneInContext(c);inflater.setFilter(this); result=inflater.inflate(mLayoutId,parent,false); performApply(result);returnresult;}
Host的实现者
AppWidgetHost和AppWidgetHostView是在框架中定义的两个基类。应用程序可以利用这两个类来实现自己的Host。Launcher是缺省的桌面,它是一个Host的实现者。
LauncherAppWidgetHostView扩展了AppWidgetHostView,实现了对长按事件的处理。
LauncherAppWidgetHost扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例。
AppWidgetService
AppWidgetService存在的目的主要是解开AppWidgetProvider和AppWidgetHost之间的耦合。如果AppWidgetProvider和AppWidgetHost的关系固定死了,AppWidget就无法在任意进程里显示了。而有了AppWidgetService,AppWidgetProvider根本不需要知道自己的AppWidget在哪里显示了。
LauncherAppWidgetHostView: 扩展了AppWidgetHostView,实现了对长按事件的处理
LauncherAppWidgetHost: 扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例
24 /** 25 *Specific{@linkAppWidgetHost}thatcreatesour{@linkLauncherAppWidgetHostView} 26 *whichcorrectlycapturesalllong-pressevents.Thisensuresthatuserscan 27 *alwayspickupandmovewidgets. 28 */ 29 publicclassLauncherAppWidgetHostextendsAppWidgetHost{ 30 publicLauncherAppWidgetHost(Contextcontext,inthostId){ 31 super(context,hostId); 32 } 33 34 @Override 35 protectedAppWidgetHostViewonCreateView(Contextcontext,intappWidgetId, 36 AppWidgetProviderInfoappWidget){ 37 returnnewLauncherAppWidgetHostView(context); 38 } 39 }首先在Launcher.java中定义了如下两个变量
174 privateAppWidgetManagermAppWidgetManager; 175 privateLauncherAppWidgetHostmAppWidgetHost;在onCreate函数中初始化,
224 mAppWidgetManager=AppWidgetManager.getInstance(this); 225 mAppWidgetHost=newLauncherAppWidgetHost(this,APPWIDGET_HOST_ID); 226 mAppWidgetHost.startListening(); 上述代码,获取mAppWidgetManager的实例,并创建LauncherAppWidgetHost,以及监听 AppWidgetManager只是应用程序与底层Service之间的一个桥梁,是Android中标准的aidl实现方式 应用程序通过AppWidgetManager调用Service中的方法 frameworks/base / core / java / android / appwidget / AppWidgetManager.java 35 /** 36 *UpdatesAppWidgetstate;getsinformationaboutinstalledAppWidgetprovidersandother 37 *AppWidgetrelatedstate. 38 */ 39 publicclassAppWidgetManager{ 197staticWeakHashMap<Context,WeakReference<AppWidgetManager>>sManagerCache=newWeakHashMap(); 198 staticIAppWidgetServicesService; 204 /** 205 *GettheAppWidgetManagerinstancetouseforthesupplied{@linkandroid.content.Context 206 *Context}object. 207 */ 208 publicstaticAppWidgetManagergetInstance(Contextcontext){ 209 synchronized(sManagerCache){ 210 if(sService==null){ 211 IBinderb=ServiceManager.getService(Context.APPWIDGET_SERVICE); 212 sService=IAppWidgetService.Stub.asInterface(b); 213 } 214 215 WeakReference<AppWidgetManager>ref=sManagerCache.get(context); 216 AppWidgetManagerresult=null; 217 if(ref!=null){ 218 result=ref.get(); 219 } 220 if(result==null){ 221 result=newAppWidgetManager(context); 222 sManagerCache.put(context,newWeakReference(result)); 223 } 224 returnresult; 225 } 226 } 227 228 privateAppWidgetManager(Contextcontext){ 229 mContext=context; 230 mDisplayMetrics=context.getResources().getDisplayMetrics(); 231 }以上代码是设计模式中标准的单例模式
frameworks/base/core/java/android/appwidget/AppWidgetHost.java
90 publicAppWidgetHost(Contextcontext,inthostId){ 91 mContext=context; 92 mHostId=hostId; 93 mHandler=newUpdateHandler(context.getMainLooper()); 94 synchronized(sServiceLock){ 95 if(sService==null){ 96 IBinderb=ServiceManager.getService(Context.APPWIDGET_SERVICE); 97 sService=IAppWidgetService.Stub.asInterface(b); 98 } 99 } 100 }可以看到AppWidgetHost有自己的HostId,Handler,和sService
93mHandler=newUpdateHandler(context.getMainLooper());
这是啥用法呢?
参数为Looper,即消息处理放到此Looper的MessageQueue中,有哪些消息呢?
40 staticfinalintHANDLE_UPDATE=1; 41 staticfinalintHANDLE_PROVIDER_CHANGED=2; 48 49 classCallbacksextendsIAppWidgetHost.Stub{ 50 publicvoidupdateAppWidget(intappWidgetId,RemoteViewsviews){ 51 Messagemsg=mHandler.obtainMessage(HANDLE_UPDATE); 52 msg.arg1=appWidgetId; 53 msg.obj=views; 54 msg.sendToTarget(); 55 } 56 57 publicvoidproviderChanged(intappWidgetId,AppWidgetProviderInfoinfo){ 58 Messagemsg=mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED); 59 msg.arg1=appWidgetId; 60 msg.obj=info; 61 msg.sendToTarget(); 62 } 63 } 64 65 classUpdateHandlerextendsHandler{ 66 publicUpdateHandler(Looperlooper){ 67 super(looper); 68 } 69 70 publicvoidhandleMessage(Messagemsg){ 71 switch(msg.what){ 72 caseHANDLE_UPDATE:{ 73 updateAppWidgetView(msg.arg1,(RemoteViews)msg.obj); 74 break; 75 } 76 caseHANDLE_PROVIDER_CHANGED:{ 77 onProviderChanged(msg.arg1,(AppWidgetProviderInfo)msg.obj); 78 break; 79 } 80 } 81 } 82 }通过以上可以看到主要有两中类型的消息,HANDLE_UPDATE和HANDLE_PROVIDER_CHANGED
处理即通过自身定义的方法
231 /** 232 *CalledwhentheAppWidgetproviderforaAppWidgethasbeenupgradedtoanewapk. 233 */ 234 protectedvoidonProviderChanged(intappWidgetId,AppWidgetProviderInfoappWidget){ 235 AppWidgetHostViewv; 236 synchronized(mViews){ 237 v=mViews.get(appWidgetId); 238 } 239 if(v!=null){ 240 v.updateAppWidget(null,AppWidgetHostView.UPDATE_FLAGS_RESET); 241 } 242 } 243 244 voidupdateAppWidgetView(intappWidgetId,RemoteViewsviews){ 245 AppWidgetHostViewv; 246 synchronized(mViews){ 247 v=mViews.get(appWidgetId); 248 } 249 if(v!=null){ 250 v.updateAppWidget(views,0); 251 } 252 }那么此消息是何时由谁发送的呢?
从以上的代码中看到AppWidgetHost定义了内部类Callback,扩展了类IAppWidgetHost.Stub,类Callback中负责发送以上消息
Launcher中会调用本类中的如下方法,
102 /** 103 *StartreceivingonAppWidgetChangedcallsforyourAppWidgets.Callthiswhenyouractivity 104 *becomesvisible,i.e.fromonStart()inyourActivity. 105 */ 106 publicvoidstartListening(){ 107 int[]updatedIds; 108 ArrayList<RemoteViews>updatedViews=newArrayList<RemoteViews>(); 109 110 try{ 111 if(mPackageName==null){ 112 mPackageName=mContext.getPackageName(); 113 } 114 updatedIds=sService.startListening(mCallbacks,mPackageName,mHostId,updatedViews); 115 } 116 catch(RemoteExceptione){ 117 thrownewRuntimeException("systemserverdead?",e); 118 } 119 120 finalintN=updatedIds.length; 121 for(inti=0;i<N;i++){ 122 updateAppWidgetView(updatedIds[i],updatedViews.get(i)); 123 } 124 } 最终调用AppWidgetService中的方法startListening方法,并把mCallbacks传过去,由Service负责发送消息 Launcher中添加Widget 在Launcher中添加widget,有两种途径,通过Menu或者长按桌面的空白区域,都会弹出Dialog,让用户选择添加 如下代码是当用户选择 1999 /** 2000 *Handletheactionclickedinthe"Addtohome"dialog. 2001 */ 2002 publicvoidonClick(DialogInterfacedialog,intwhich){ 2003 Resourcesres=getResources(); 2004 cleanup(); 2005 2006 switch(which){ 2007 caseAddAdapter.ITEM_SHORTCUT:{ 2008 //Insertextraitemtohandlepickingapplication 2009 pickShortcut(); 2010 break; 2011 } 2012 2013 caseAddAdapter.ITEM_APPWIDGET:{ 2014 intappWidgetId=Launcher.this.mAppWidgetHost.allocateAppWidgetId(); 2015 2016 IntentpickIntent=newIntent(AppWidgetManager.ACTION_APPWIDGET_PICK); 2017 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId); 2018 //startthepickactivity 2019 startActivityForResult(pickIntent,REQUEST_PICK_APPWIDGET); 2020 break; 2021 }当用户在Dialog中选择AddAdapter.ITEM_APPWIDGET时,首先会通过AppWidgethost分配一个appWidgetId,并最终调到AppWidgetService中去
同时发送Intent,其中保存有刚刚分配的appWidgetId,AppWidgetManager.EXTRA_APPWIDGET_ID
139 /** 140 *GetaappWidgetIdforahostinthecallingprocess. 141 * 142 *@returnaappWidgetId 143 */ 144 publicintallocateAppWidgetId(){ 145 try{ 146 if(mPackageName==null){ 147 mPackageName=mContext.getPackageName(); 148 } 149 returnsService.allocateAppWidgetId(mPackageName,mHostId); 150 } 151 catch(RemoteExceptione){ 152 thrownewRuntimeException("systemserverdead?",e); 153 } 154 } 2016 IntentpickIntent=newIntent(AppWidgetManager.ACTION_APPWIDGET_PICK); 2017 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId); 2018 //startthepickactivity 2019 startActivityForResult(pickIntent,REQUEST_PICK_APPWIDGET); 这段代码之后,代码将会怎么执行呢,根据Log信息,可以看到代码将会执行到Setting应用中 packages/apps/Settings/ src / com / android / settings / AppWidgetPickActivity.java 此类将会通过AppWidgetService获取到当前系统已经安装的Widget,并显示出来 78 /** 79 *Createlistentriesforanycustomwidgetsrequestedthrough 80 *{@linkAppWidgetManager#EXTRA_CUSTOM_INFO}. 81 */ 82 voidputCustomAppWidgets(List<PickAdapter.Item>items){ 83 finalBundleextras=getIntent().getExtras(); 84 85 //getandvalidatetheextrastheygaveus 86 ArrayList<AppWidgetProviderInfo>customInfo=null; 87 ArrayList<Bundle>customExtras=null; 88 try_custom_items:{ 89 customInfo=extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_INFO); 90 if(customInfo==null||customInfo.size()==0){ 91 Log.i(TAG,"EXTRA_CUSTOM_INFOnotpresent."); 92 breaktry_custom_items; 93 } 94 95 intcustomInfoSize=customInfo.size(); 96 for(inti=0;i<customInfoSize;i++){ 97 Parcelablep=customInfo.get(i); 98 if(p==null||!(pinstanceofAppWidgetProviderInfo)){ 99 customInfo=null; 100 Log.e(TAG,"errorusingEXTRA_CUSTOM_INFOindex="+i); 101 breaktry_custom_items; 102 } 103 } 104 105 customExtras=extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_EXTRAS); 106 if(customExtras==null){ 107 customInfo=null; 108 Log.e(TAG,"EXTRA_CUSTOM_INFOwithoutEXTRA_CUSTOM_EXTRAS"); 109 breaktry_custom_items; 110 } 111 112 intcustomExtrasSize=customExtras.size(); 113 if(customInfoSize!=customExtrasSize){ 114 Log.e(TAG,"listsizemismatch:EXTRA_CUSTOM_INFO:"+customInfoSize 115 +"EXTRA_CUSTOM_EXTRAS:"+customExtrasSize); 116 breaktry_custom_items; 117 } 118 119 120 for(inti=0;i<customExtrasSize;i++){ 121 Parcelablep=customExtras.get(i); 122 if(p==null||!(pinstanceofBundle)){ 123 customInfo=null; 124 customExtras=null; 125 Log.e(TAG,"errorusingEXTRA_CUSTOM_EXTRASindex="+i); 126 breaktry_custom_items; 127 } 128 } 129 } 130 131 if(LOGD)Log.d(TAG,"Using"+customInfo.size()+"customitems"); 132 putAppWidgetItems(customInfo,customExtras,items); 133 } 从上述代码中可以看到,可以放置用户自己定义的伪Widget 关于伪widget,个人有如下想法: 早期Android版本中的Google Search Bar就属于伪Widget,其实就是把widget做到Launcher中,但是用户体验与真widget并没有区别,个人猜想HTC的sense就是这样实现的。 优点:是不需要进程间的通信,效率将会更高,并且也可以规避点Widget开发的种种限制 缺点:导致Launcher代码庞大,不易于维护 用户选择完之后,代码如下 135 /** 136 *{@inheritDoc} 137 */ 138 @Override 139 publicvoidonClick(DialogInterfacedialog,intwhich){ 140 Intentintent=getIntentForPosition(which); 141 142 intresult; 143 if(intent.getExtras()!=null){ 144 //Ifthereareanyextras,it'sbecausethisentryiscustom. 145 //Don'ttrytobindit,justpassitbacktotheapp. 146 setResultData(RESULT_OK,intent); 147 }else{ 148 try{ 149 mAppWidgetManager.bindAppWidgetId(mAppWidgetId,intent.getComponent()); 150 result=RESULT_OK; 151 }catch(IllegalArgumentExceptione){ 152 //Thisisthrownifthey'realreadybound,orotherwisesomehow 153 //bogus.Settheresulttocanceled,andexit.Theapp*should* 154 //cleanupatthispoint.Wecouldpasstheerroralong,but 155 //it'snotclearthatthat'suseful--thewidgetwillsimplynot 156 //appear. 157 result=RESULT_CANCELED; 158 } 159 setResultData(result,null); 160 } 161 finish(); 162 } 将会 149 mAppWidgetManager.bindAppWidgetId(mAppWidgetId,intent.getComponent()); 如果此次添加的Widget是intent.getComponent()的第一个实例,将会发送如下广播 171 /** 172 *SentwhenaninstanceofanAppWidgetisaddedtoahostforthefirsttime. 173 *ThisbroadcastissentatboottimeifthereisaAppWidgetHostinstalledwith 174 *aninstanceforthisprovider. 175 * 176 *@seeAppWidgetProvider#onEnabledAppWidgetProvider.onEnabled(Contextcontext) 177 */ 178publicstaticfinalStringACTION_APPWIDGET_ENABLED="android.appwidget.action.APPWIDGET_ENABLED"; 紧接着会发送UPDATE广播 135 /** 136 *SentwhenitistimetoupdateyourAppWidget. 137 * 138 *<p>ThismaybesentinresponsetoanewinstanceforthisAppWidgetproviderhaving 139 *beeninstantiated,therequested{@linkAppWidgetProviderInfo#updatePeriodMillisupdateinterval} 140 *havinglapsed,orthesystembooting. 141 * 142 *<p> 143 *Theintentwillcontainthefollowingextras: 144 *<table> 145 *<tr> 146 *<td>{@link#EXTRA_APPWIDGET_IDS}</td> 147 *<td>TheappWidgetIdstoupdate.ThismaybealloftheAppWidgetscreatedforthis 148 *provider,orjustasubset.ThesystemtriestosendupdatesforasfewAppWidget 149 *instancesaspossible.</td> 150 *</tr> 151 *</table> 152 * 153*@seeAppWidgetProvider#onUpdateAppWidgetProvider.onUpdate(Contextcontext,AppWidgetManagerappWidgetManager,int[]appWidgetIds) 154 */ 155publicstaticfinalStringACTION_APPWIDGET_UPDATE="android.appwidget.action.APPWIDGET_UPDATE";待用户选择完要添加的widget之后,将会回到Launcher.java中的函数onActivityResult中
538 caseREQUEST_PICK_APPWIDGET: 539 addAppWidget(data); 540 break;上述addAppWidget中做了哪些事情呢?
1174 voidaddAppWidget(Intentdata){ 1175 //TODO:catchbadwidgetexceptionwhensent 1176 intappWidgetId=data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,-1); 1177 AppWidgetProviderInfoappWidget=mAppWidgetManager.getAppWidgetInfo(appWidgetId); 1178 1179 if(appWidget.configure!=null){ 1180 //Launchovertoconfigurewidget,ifneeded 1181 Intentintent=newIntent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 1182 intent.setComponent(appWidget.configure); 1183 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId); 1184 1185 startActivityForResultSafely(intent,REQUEST_CREATE_APPWIDGET); 1186 }else{ 1187 //Otherwisejustaddit 1188 onActivityResult(REQUEST_CREATE_APPWIDGET,Activity.RESULT_OK,data); 1189 } 1190 }首先获取appWidgetId,再通过AppWidgetManager获取AppWidgetProviderInfo,最后判断此Widget是否存在ConfigActivity,如果存在则启动ConfigActivity,否则直接调用函数onActivityResult
541 caseREQUEST_CREATE_APPWIDGET: 542 completeAddAppWidget(data,mAddItemCellInfo); 543 break;通过函数completeAddAppWidget把此widget的信息插入到数据库中,并添加到桌面上
873 /** 874 *Addawidgettotheworkspace. 875 * 876 *@paramdataTheintentdescribingtheappWidgetId. 877 *@paramcellInfoThepositiononscreenwheretocreatethewidget. 878 */ 879 privatevoidcompleteAddAppWidget(Intentdata,CellLayout.CellInfocellInfo){ 880 Bundleextras=data.getExtras(); 881 intappWidgetId=extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,-1); 882 883 if(LOGD)Log.d(TAG,"dumpingextrascontent="+extras.toString()); 884 885 AppWidgetProviderInfoappWidgetInfo=mAppWidgetManager.getAppWidgetInfo(appWidgetId); 886 887 //Calculatethegridspansneededtofitthiswidget 888 CellLayoutlayout=(CellLayout)mWorkspace.getChildAt(cellInfo.screen); 889 int[]spans=layout.rectToCell(appWidgetInfo.minWidth,appWidgetInfo.minHeight); 890 891 //TryfindingopenspaceonLauncherscreen 892 finalint[]xy=mCellCoordinates; 893 if(!findSlot(cellInfo,xy,spans[0],spans[1])){ 894 if(appWidgetId!=-1)mAppWidgetHost.deleteAppWidgetId(appWidgetId); 895 return; 896 } 897 898 //BuildLauncher-specificwidgetinfoandsavetodatabase 899 LauncherAppWidgetInfolauncherInfo=newLauncherAppWidgetInfo(appWidgetId); 900 launcherInfo.spanX=spans[0]; 901 launcherInfo.spanY=spans[1]; 902 903 LauncherModel.addItemToDatabase(this,launcherInfo, 904 LauncherSettings.Favorites.CONTAINER_DESKTOP, 905 mWorkspace.getCurrentScreen(),xy[0],xy[1],false); 906 907 if(!mRestoring){ 908 mDesktopItems.add(launcherInfo); 909 910 //Performactualinflationbecausewe'relive 911 launcherInfo.hostView=mAppWidgetHost.createView(this,appWidgetId,appWidgetInfo); 912 913 launcherInfo.hostView.setAppWidget(appWidgetId,appWidgetInfo); 914 launcherInfo.hostView.setTag(launcherInfo); 915 916 mWorkspace.addInCurrentScreen(launcherInfo.hostView,xy[0],xy[1], 917 launcherInfo.spanX,launcherInfo.spanY,isWorkspaceLocked()); 918 } 919 }Launcher中删除widget
长按一个widget,并拖入到DeleteZone中可实现删除
具体代码在DeleteZone中
92 publicvoidonDrop(DragSourcesource,intx,inty,intxOffset,intyOffset, 93 DragViewdragView,ObjectdragInfo){ 94 finalItemInfoitem=(ItemInfo)dragInfo; 95 96 if(item.container==-1)return; 97 98 if(item.container==LauncherSettings.Favorites.CONTAINER_DESKTOP){ 99 if(iteminstanceofLauncherAppWidgetInfo){ 100 mLauncher.removeAppWidget((LauncherAppWidgetInfo)item); 101 } 102 }else{ 103 if(sourceinstanceofUserFolder){ 104 finalUserFolderuserFolder=(UserFolder)source; 105 finalUserFolderInfouserFolderInfo=(UserFolderInfo)userFolder.getInfo(); 106 //ItemmustbeaShortcutInfootherwiseitcouldn'thavebeeninthefolder 107 //inthefirstplace. 108 userFolderInfo.remove((ShortcutInfo)item); 109 } 110 } 111 if(iteminstanceofUserFolderInfo){ 112 finalUserFolderInfouserFolderInfo=(UserFolderInfo)item; 113 LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher,userFolderInfo); 114 mLauncher.removeFolder(userFolderInfo); 115 }elseif(iteminstanceofLauncherAppWidgetInfo){ 116 finalLauncherAppWidgetInfolauncherAppWidgetInfo=(LauncherAppWidgetInfo)item; 117 finalLauncherAppWidgetHostappWidgetHost=mLauncher.getAppWidgetHost(); 118 if(appWidgetHost!=null){ 119 appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId); 120 } 121 } 122 LauncherModel.deleteItemFromDatabase(mLauncher,item); 123 }删除时,判断删除的类型是否是AppWidget,如果是的话,要通过AppWidgetHost,删除AppWidetId,并最终从数据库中删除。
八 Launcher celllayout介绍
(1) 大家都知道workspace是有celllayout组成
Celllayout被划分为了4行4列的表格,用Boolean类型的mOccupied二维数组来标记每个cell是否被占用。在attrs.xml中定义了shortAxisCells和longAxisCells分别存储x轴和y轴方向的cell个数。在Celllayout构造函数中初始化。
(2) 内部类CellInfo为静态类,实现了ContextMenu.ContextMenuInfo接口,其对象用于存储cell的基本信息
VacantCell类用于存储空闲的cell,用到了同步机制用于管理对空闲位置的操作。所有的空cell都存储在vacantCells中。
cellX和cellY用于记录cell的位置,起始位0。如:(0,0) (0,1),每一页从新开始编号。
clearVacantCells作用是将Vacant清空:具体是释放每个cell,将list清空。
findVacantCellsFromOccupied从存放cell的数值中找到空闲的cell。在Launcher.Java中的restoreState方法中调用。
(3) mPortrait用于标记是横屏还是竖屏,FALSE表示竖屏,默认为FALSE。
(4)修改CellLayout页面上cell的布局:
CellLayout页面上默认的cell为4X4=16个,可以通过修改配置文件来达到修改目的。
在CellLayout.Java类的CellLayout(Context context, AttributeSet attrs, int defStyle)构造方法中用变量mShortAxisCells和mLongAxisCells存储行和列。
其值是在自定义配置文件attrs.xml中定义的,并在workspace_screen.xml中赋初值的,初值都为4,即4行、4列。可以在workspace_screen.xml修改对应的值。
注意:CellLayout构造方法中从attrs.xml中获取定义是这样的:mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);当workspace_screen.xml中没有给定义的变量赋值时,上面的4就起作用。
(5)Launcher(主屏/待机) App的BUG: 没有初始化定义CellLayout中屏幕方向的布尔值参数:
- Launcher App:\cupcake\packages\apps\Launcher
待机画面分为多层,桌面Desktop Items在\res\layout-*\workspace_screen.xml中置:
- <com.android.launcher.CellLayout
- ... ...
- launcher:shortAxisCells="4"
- launcher:longAxisCells="4"
- ... ...
- />
以上表示4行4列.
再看看com.android.launcher.CellLayout ,其中有定义屏幕方向的参数:
- private boolean mPortrait;
但是一直没有初始化,也就是mPortrait=false,桌面的单元格设置一直是以非竖屏(横屏)的设置定义进行初始化。
再来看看横屏和竖屏情况下的初始化不同之处,就可以看出BUG了:
- boolean[][] mOccupied;//二元单元格布尔值数组
- if (mPortrait) {
- mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
- } else {
- mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
- }
如果我们满屏显示桌面(横向和纵向的单元格数不一致),而不是默认的只显示4行4列,则mShortAxisCells = 4, mLongAxisCells = 5,数组应该初始化是:new boolean[4][5],但是实际是按照非竖屏处理,初始化成了new boolean[5][4],会产生数组越界异常。
可以在构造函数中,添加通过屏幕方向初始化mPortrait,代码如下:
- public CellLayout(Context context, AttributeSet attrs, int defStyle)
- {
- super(context, attrs, defStyle);
- mPortrait = this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;// 新增代码
- ... ...
好了就写这些,太累了,以后再补上其他跟Launcher有关的
更多相关文章
- C语言函数的递归(上)
- 通过浏览器调用Android或iOS应用
- getevent工具和Android中inputevent的分析
- Android编程实现自定义ProgressBar样式示例(背景色及一级、二级
- 百度Android开发面试题
- 初窥图像处理利器RenderScript
- android面试题整理(2)
- Android(安卓)蓝牙开发(四)OPP传输文件
- Android(安卓)SDK中tools详解