- 浏览: 21525014 次
- 性别:
- 来自: 杭州
最新评论
-
ZY199266:
配置文件还需要额外的配置ma
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
ZY199266:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我又一个问题就是 如果像你的这种形式写。配置文件还需要额外的 ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
y1210251848:
你的那个错误应该是项目所使用的目标框架不支持吧
log4net配置(web中使用log4net,把web.config放在单独的文件中)
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的代码结构。
首先,是一些权限的声明。例如:
|
这部分可以略过;
其次,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>
上面这段代码就标志着它是开机启动后Home的Activity。通过Launcher.java中onCreat()的分析我们可以大致把握屏幕的主要活动:
|
方法onActivityResult():完成在workspace上增加shortcut,appwidge和Livefolder;
方法onSaveInstantceState()和onRestoreInstanceState():为了防止Sensor、Land和Port布局自动切换时数据被置空,通过onSaveInstanceState方法可以保存当前窗口的状态,在即将布局切换前将当前的Activity压入历史堆栈。如果我们的Activity在后台没有因为运行内存吃紧被清理,则切换时回触发onRestoreIntanceState()。
(2)WallpaperChooser:设置墙纸。
同理我们从onCreat()作为入口来分析这个活动的主要功能。
|
(3)default_searchable
对于home中任意的Acitivty,使能系统缺省Search模式,这样就可以使用android系统默认的searchUI。
(4)InstallShortcutReceiver:
继承自BroadcastReceiver,重写onReceier()方法,对于发送来的Broadcast(这里指Intent)进行过滤(IntentFilt)并且响应(这里是InstallShortcut())。这里分析下onReceive():
|
其中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整个系统时放进去(就是系统软件)才起作用,手动安装是没有权限的。
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文件中:- publicclassSystemServer
- {
- ......
- nativepublicstaticvoidinit1(String[]args);
- ......
- publicstaticvoidmain(String[]args){
- ......
- init1(args);
- ......
- }
- ......
- }
Step 2. SystemServer.init1
这个函数是一个JNI方法,实现在frameworks/base/services/jni/com_android_server_SystemServer.cpp文件中:
- 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文件中:
- 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文件中:
- /*
- *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文件中:
- 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文件中:- 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文件中:
- 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);
- }
- }
- ......
- }
这个函数首先是将这个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文件中:
- publicfinalclassActivityManagerServiceextendsActivityManagerNative
- implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
- ......
- publicvoidsystemReady(finalRunnablegoingCallback){
- ......
- 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();
- }
- }
- ......
- }
- ......
- }
这里调用函数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文件中:
- 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;
- }
- ......
- }
函数首先创建一个CATEGORY_HOME类型的Intent,然后通过Intent.resolveActivityInfo函数向PackageManagerService查询Category类型为HOME的Activity,这里我们假设只有系统自带的Launcher应用程序注册了HOME类型的Activity(见packages/apps/Launcher2/AndroidManifest.xml文件):
- <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>
因此,这里就返回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文件中:
- publicfinalclassLauncherextendsActivity
- implementsView.OnClickListener,OnLongClickListener,LauncherModel.Callbacks,AllAppsView.Watcher{
- ......
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- ......
- if(!mRestoring){
- mModel.startLoader(this,true);
- }
- ......
- }
- ......
- }
这里的mModel是一个LauncherModel类型的成员变量,这里通过调用它的startLoader成员函数来执行加应用程序的操作。
Step 15.LauncherModel.startLoader
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- 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);
- }
- }
- }
- ......
- }
这里不是直接加载应用程序,而是把加载应用程序的操作作为一个消息来处理。这里的sWorker是一个Handler,通过它的post方式把一个消息放在消息队列中去,然后系统就会调用传进去的参数mLoaderTask的run函数来处理这个消息,这个mLoaderTask是LoaderTask类型的实例,于是,下面就会执行LoaderTask类的run函数了。
Step 16. LoaderTask.run
这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- publicclassLauncherModelextendsBroadcastReceiver{
- ......
- privateclassLoaderTaskimplementsRunnable{
- ......
- publicvoidrun(){
- ......
- keep_running:{
- ......
- //secondstep
- 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();
- }
- }
- ......
- }
- ......
- }
由于还没有加载过应用程序,这里的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{
- ......
- }
- }
- });
- ......
- }
- ......
- }
- ......
- }
- ......
- }
函数首先构造一个CATEGORY_LAUNCHER类型的Intent:
- finalIntentmainIntent=newIntent(Intent.ACTION_MAIN,null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
接着从mContext变量中获得PackageManagerService的接口:
- finalPackageManagerpackageManager=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文件中:
- classPackageManagerServiceextendsIPackageManager.Stub{
- ......
- publicList<ResolveInfo>queryIntentActivities(Intentintent,
- StringresolvedType,intflags){
- ......
- synchronized(mPackages){
- StringpkgName=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文件中:
- publicfinalclassLauncherextendsActivity
- implementsView.OnClickListener,OnLongClickListener,LauncherModel.Callbacks,AllAppsView.Watcher{
- ......
- privateAllAppsViewmAllAppsGrid;
- ......
- publicvoidbindAllApplications(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();
- }
- ......
- }
函数setApps首先清空mAllAppsList列表,然后调用addApps函数来为上一步得到的每一个应用程序创建一个ApplicationInfo实例了,有了这些ApplicationInfo实例之后,就可以在桌面上展示系统中所有的应用程序了。
到了这里,系统默认的Home应用程序Launcher就把PackageManagerService中的应用程序加载进来了,当我们在屏幕上点击下面这个图标时,就会把刚才加载好的应用程序以图标的形式展示出来了:
点击这个按钮时,便会响应Launcher.onClick函数:
- 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);
- }
- }
- }
- ......
- }
接着就会调用showAllApps函数显示应用程序图标:
- 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);
- }
- ......
- }
这样我们就可以看到系统中的应用程序了:
当点击上面的这些应用程序图标时,便会响应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);
- }
- ......
- }
这里的成员变量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的实例
首先在Launcher.java中定义了如下两个变量
在onCreate函数中初始化,
以上代码是设计模式中标准的单例模式
frameworks/base/core/java/android/appwidget/AppWidgetHost.java
可以看到AppWidgetHost有自己的HostId,Handler,和sService
93mHandler=newUpdateHandler(context.getMainLooper());
这是啥用法呢?
参数为Looper,即消息处理放到此Looper的MessageQueue中,有哪些消息呢?
通过以上可以看到主要有两中类型的消息,HANDLE_UPDATE和HANDLE_PROVIDER_CHANGED
处理即通过自身定义的方法
那么此消息是何时由谁发送的呢?
从以上的代码中看到AppWidgetHost定义了内部类Callback,扩展了类IAppWidgetHost.Stub,类Callback中负责发送以上消息
Launcher中会调用本类中的如下方法,
当用户在Dialog中选择AddAdapter.ITEM_APPWIDGET时,首先会通过AppWidgethost分配一个appWidgetId,并最终调到AppWidgetService中去
同时发送Intent,其中保存有刚刚分配的appWidgetId,AppWidgetManager.EXTRA_APPWIDGET_ID
待用户选择完要添加的widget之后,将会回到Launcher.java中的函数onActivityResult中
上述addAppWidget中做了哪些事情呢?
首先获取appWidgetId,再通过AppWidgetManager获取AppWidgetProviderInfo,最后判断此Widget是否存在ConfigActivity,如果存在则启动ConfigActivity,否则直接调用函数onActivityResult
通过函数completeAddAppWidget把此widget的信息插入到数据库中,并添加到桌面上
Launcher中删除widget
长按一个widget,并拖入到DeleteZone中可实现删除
具体代码在DeleteZone中
删除时,判断删除的类型是否是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有关的
相关推荐
前18大旋转修整器企业占据全球87%的市场份额
SKYSAT IMAGERY PRODUCT SPECIFICATION PLANET.COM VIDEO Full motion videos are collected between 30 and 120 seconds by a single camera from any of the active SkySats. Videos are collected using only the Panchromatic half of the camera, hence all videos are PAN only. Videos are packaged and delivered with a video mpeg-4 file, plus all image frames with accompanying video metadata and a frame index file (reference Product Types below)
Screenshot_20240506_133458_com.netease.yhtj.vivo.jpg
大学生,数学建模,美国大学生数学建模竞赛,MCM/ICM,历年美赛特等奖O奖论文
雷达物位变送器安装和操作手册
Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。
快速排序是一种基于分治策略的排序算法,通过选择一个基准元素,将待排序的数组划分为两个子数组,一个包含所有小于基准的元素,另一个包含所有大于基准的元素,然后递归地对这两个子数组进行快速排序。快速排序在平均情况下具有O(n log n)的时间复杂度,是一种非常高效的排序算法。然而,在最坏情况下,当输入数据已经有序或接近有序时,快速排序的性能会退化为O(n^2)。此外,快速排序是不稳定的排序算法,即相等的元素可能在排序过程中改变相对位置。尽管如此,快速排序仍然因其高效的平均性能而在实际应用中广泛使用。在Python3中,可以通过递归或迭代的方式实现快速排序算法,但为了避免额外的空间开销,通常会采用原地排序的方式来实现。
【作品名称】:基于51单片机的出租车计价器(昼夜) 含(程序、仿真图、流程图、原理图) 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 出租车计价器: 1、不同情况具有不同的收费标准,具有白天和夜晚不同的计价能力 2、能进行手动修改单价 3、具有数据的复位功能(起步价,起步公里数,里程单价,白天晚上不一样) 4、能够在掉电的情况下存储单价等数据 5、步进电机模拟里程,一圈表示一里路
2024年中国API 11P往复式气体压缩机行业研究报告
附件是Windows 10系统上安装和配置Tomcat的步骤,文件绿色安全,请大家放心下载,仅供交流学习使用,无任何商业目的!
此试题是考试后回忆版本,你会发现是惊喜。恭喜你考个好成绩。
数据库+人大金仓+Linux系统安装
大学生,数学建模,美国大学生数学建模竞赛,MCM/ICM,2023年美赛特等奖O奖论文
opencv-python-4.5.4.60-cp36-cp36m-win-amd64.whl
减肥管理,全球前10强生产商排名及市场份额
内容概要:《上海大学大学生创新创业训练计划申请书(创新训练项目)》是用于申请参加上海大学的大学生创新创业训练计划的申请书,旨在帮助学生提出创新项目计划,获得培训和支持,促进学生创新创业能力的提升。 适用人群:适合上海大学的在校大学生,特别是对创新创业感兴趣、有创新想法和创业计划的学生,希望通过该计划获得指导和资源支持,实现自己的创业梦想。 使用场景及目标:申请书的使用场景是为了参加上海大学的大学生创新创业训练计划,目标是通过提交详细的创新项目计划,获得评审通过并获得培训、指导和资金支持,从而推动学生的创新创业实践和能力提升。 其他说明:申请书应包括清晰的创新项目描述、项目可行性分析、预期目标和计划、团队介绍等内容,以展现学生的创新能力和项目潜力。申请书的撰写需要认真准备,体现出学生对创新创业的热情和才华,以提高申请成功的机会。
IEC 60364-7-716-2023 低压电气装置.第7-716部分:特殊装置或场所的要求.信息和通信技术(ICT)电缆基础设施上的ELV直流配电.pdf
IEC PAS 61851-1-1 2023 电动汽车导电充电系统.第1-1部分:使用4型车辆耦合器的电动汽车导电带电系统的特殊要求.pdf
前11大客运渡轮服务企业占据全球30.3%的市场份额
wsl+MCgpu安装记录