1.
Launcher的启动过程
从网络上找了一段关于Launcher
的启动过程的文章,作为学习
Launcher
的背景知识:
Linux kernel
启动以后会通过
a
pp_main
进程来初始化android Runtime Java运行环境,而
zygote
是
A
ndroid的第一个进程。所有的android的应用以及大部分系统服务都是通过zygote fork出来的子进程(
我现在看到的只有native的service manager不是zygote fork出来的)
。在system server中启动的若干系统服务中与我们启动进程相关的就是
Acitivity Manager
。
当
systerm server启动好所有服务以后,系统就进入”system ready”状态,这个时候Activity Manager就登场了。
Activity Manager光看代码行就知道是一个重量级的服务,它主要管理Activity之间的跳转,以及进程的生命周期。当
Activity Manager发现系统已经启动好以后它就会发出一个intent:
-
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);
-
}
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);
}
通过这个category类型为home的intent,Activity Manager就会通过:
-
startActivityLocked(
null
, intent,
null
,
null
, 0, aInfo,
-
null
,
null
, 0, 0, 0,
false
,
false
);
startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
启动
Home
进程了。而这个启动Home进程的过程实际上还是去通过zygote fork出的一个子进程。
因此只要在manifest中具备这样的intent-filter都可以在开机的时候作为Home启动:
-
<intent-filter>
-
<action android:name="android.intent.action.MAIN"
/>
-
<category android:name="android.intent.category.HOME"
/>
-
<category android:name="android.intent.category.DEFAULT"
/>
-
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
多个home之间的switch会在开始的时候有个选择,至于这个选择好像是package manager来实现的,没有仔细研究过。
2.UI结构
通过
launcher/Res/Layout-land/launcher.xml
分析可以得到主屏幕的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时,把手位置就会出现一个垃圾桶形状的控件,就是这个控件。
3.应用程序代码分析
由Launcher
中的
AndroidManifest.xml
可以看出整个Launcher
的代码结构。
首先,是一些权限的声明。例如:
-
<uses-permission android:name=
"android.permission.CALL_PHONE"
/>
-
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"
/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
这部分可以略过;
其次,Application
的构成,如上图:
(1)
Launcher
:HomeScreen
的
Activity
。
-
<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>
<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()
的分析我们可以大致把握屏幕的主要活动:
-
protected
void
onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
//把xml文件的内容实例化到View中
-
mInflater = getLayoutInflater();
-
//监听应用程序控件改变事件
-
mAppWidgetManager = AppWidgetManager.getInstance(this
);
-
mAppWidgetHost = new
LauncherAppWidgetHost(
this
, APPWIDGET_HOST_ID);
-
mAppWidgetHost.startListening();
-
// 用于调试?
-
if
(PROFILE_STARTUP) {
-
android.os.Debug.startMethodTracing("/sdcard/launcher"
);
-
}
-
//监听locale,mcc,mnc是否改变,如果改变,则重写新配置
-
//mcc:mobile country code(国家代码China 460); mnc:mobile network code(网络代码)
-
checkForLocaleChange();
-
/*This allows such applications to have a virtual wallpaper that is larger than the physical screen, matching the size of their workspace.*/
-
setWallpaperDimension();
-
//显示主屏幕UI元素,workspace,slidingdrawer(handleview and appgridview),deletezone
-
setContentView(R.layout.launcher);
-
//Finds all the views we need and configure them properly.
-
//完成workspace,slidingdrawer,deletezone的各种事件操作和监听
-
setupViews();
-
//Registers various intent receivers.
-
//允许其他应用对本应用的操作
-
registerIntentReceivers();
-
//Registers various content observers.
-
//例如,注册一个内容观察者跟踪喜爱的应用程序
-
registerContentObservers();
-
//重新保存前一个状态(目的??)
-
mSavedState = savedInstanceState;
-
restoreState(mSavedState);
-
//调试?
-
if
(PROFILE_STARTUP) {
-
android.os.Debug.stopMethodTracing();
-
}
-
//Loads the list of installed applications in mApplications.
-
if
(!mRestoring) {
-
startLoaders();
-
}
-
// For handling default keys??
-
mDefaultKeySsb = new
SpannableStringBuilder();
-
Selection.setSelection(mDefaultKeySsb, 0);
-
}
protected
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//把xml文件的内容实例化到View中
mInflater = getLayoutInflater();
//监听应用程序控件改变事件
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this,
APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
// 用于调试?
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing("/sdcard/launcher");
}
//监听locale,mcc,mnc是否改变,如果改变,则重写新配置
//mcc:mobile country code(国家代码China 460); mnc:mobile network
code(网络代码)
checkForLocaleChange();
/*This allows such applications to have a virtual wallpaper that
is larger than the physical screen, matching the size of their
workspace.*/
setWallpaperDimension();
//显示主屏幕UI元素,workspace,slidingdrawer(handleview and
appgridview),deletezone
setContentView(R.layout.launcher);
//Finds all the views we need and configure them properly.
//完成workspace,slidingdrawer,deletezone的各种事件操作和监听
setupViews();
//Registers various intent receivers.
//允许其他应用对本应用的操作
registerIntentReceivers();
//Registers various content observers.
//例如,注册一个内容观察者跟踪喜爱的应用程序
registerContentObservers();
//重新保存前一个状态(目的??)
mSavedState = savedInstanceState;
restoreState(mSavedState);
//调试?
if (PROFILE_STARTUP) {
android.os.Debug.stopMethodTracing();
}
//Loads the list of installed applications in mApplications.
if (!mRestoring) {
startLoaders();
}
// For handling default keys??
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
}
|
方法
onActivityResult():
完成在workspace
上增加
shortcut
,
appwidge
和
Livefolder
;
方法
onSaveInstantceState()
和
onRestoreInstanceState()
:为了防止
Sensor
、
Land
和
Port
布局自动切换时数据被置空,通过
onSaveInstanceState
方法可以保存当前窗口的状态,在即将布局切换前将当前的
Activity
压入历史堆栈。如果我们的
Activity
在后台没有因为运行内存吃紧被清理,则切换时回触发
onRestoreIntanceState()
。
(2)
WallpaperChooser
:设置墙纸。
同理我们从onCreat()
作为入口来分析这个活动的主要功能。
-
public
void
onCreate(Bundle icicle) {
-
super.onCreate(icicle);
-
//设置允许改变的窗口状态,需在 setContentView 之前调用
-
requestWindowFeature(Window.FEATURE_NO_TITLE);
-
/ /添加墙纸资源,将资源标识符加入到动态数组中
-
findWallpapers();
-
//显示墙纸设置屏幕的UI元素,Imageview,Gallery and Button(LinearLayout)
-
setContentView(R.layout.wallpaper_chooser);
-
//图片查看功能的实现
-
mGallery = (Gallery) findViewById(R.id.gallery);
-
mGallery.setAdapter(new
ImageAdapter(
this
));
-
mGallery.setOnItemSelectedListener(this
);
-
mGallery.setCallbackDuringFling(false
);
-
//Button事件监听,点击选择setWallpaper(Resid)
-
findViewById(R.id.set
).setOnClickListener(
this
);
-
mImageView = (ImageView) findViewById(R.id.wallpaper);
-
}
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
//设置允许改变的窗口状态,需在 setContentView 之前调用
requestWindowFeature(Window.FEATURE_NO_TITLE);
/ /添加墙纸资源,将资源标识符加入到动态数组中
findWallpapers();
//显示墙纸设置屏幕的UI元素,Imageview,Gallery and Button(LinearLayout)
setContentView(R.layout.wallpaper_chooser);
//图片查看功能的实现
mGallery = (Gallery) findViewById(R.id.gallery);
mGallery.setAdapter(new ImageAdapter(this));
mGallery.setOnItemSelectedListener(this);
mGallery.setCallbackDuringFling(false);
//Button事件监听,点击选择setWallpaper(Resid)
findViewById(R.id.set).setOnClickListener(this);
mImageView = (ImageView) findViewById(R.id.wallpaper);
}
|
(3)
default_searchable
对于home
中任意的
Acitivty
,使能系统缺省
Search
模式,这样就可以使用
android
系统默认的
search UI
。
(4)
InstallShortcutReceiver
:
继承自BroadcastReceiver
,重写
onReceier()
方法,对于发送来的
Broadcast
(这里指
Intent
)进行过滤(
IntentFilt
)并且响应(这里是
InstallShortcut()
)。这里分析下
onReceive():
-
<!-- Enable system-
default
search mode
for
any activity
in
Home -->
-
<!-- Intent received used to install shortcuts from other applications -->
-
public
void
onReceive(Context context, Intent data) {
-
//接受并过滤Intent
-
if
(!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
-
return
;
-
}
-
//获取屏幕
-
int
screen = Launcher.getScreen();
-
//安装快捷方式
-
if
(!installShortcut(context, data, screen)) {
-
//如果屏幕已满,搜寻其他屏幕
-
for
(
int
i = 0; i < Launcher.SCREEN_COUNT; i++) {
-
if
(i != screen && installShortcut(context, data, i))
break
;
-
}
-
}
-
}
<!-- Enable system-default search mode for any activity in Home -->
<!-- Intent received used to install shortcuts from other applications -->
public void onReceive(Context context, Intent data) {
//接受并过滤Intent
if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
return;
}
//获取屏幕
int screen = Launcher.getScreen();
//安装快捷方式
if (!installShortcut(context, data, screen)) {
//如果屏幕已满,搜寻其他屏幕
for (int i = 0; i < Launcher.SCREEN_COUNT; i++) {
if (i != screen && installShortcut(context, data, i)) break;
}
}
}
|
其中
IntallShortcut()
方法:首先,对传入的坐标进行判断(findEmptyCell()
)
,
如果是空白位置,则可以放置快捷方式;其次,缺省情况下,我们允许创建重复的快捷方式,具体创建过程(
addShortcut()
)就是把快捷方式的信息传入数据库(
addItemToDatabase()
)。
(5)
UninstallShortcutReceiver
:
同理,UninstallShortcutReceiver()
继承自
BroadcastReceiver()
,实现
onReceiver()
方法。定义一个
ContentResolver
对象完成对数据库的访问和操作(通过
URI
定位),进而删除快捷方式 。
(6)
LauncherProvider
:
继承自ContentProvider()
,主要是建立一个数据库来存放
HomeScreen
中的数据信息,并通过内容提供者来实现其他应用对
launcher
中数据的访问和操作。
重写了ContentProvider()
中的方法
:
getType():返回数据类型。如果有自定义的全新类型,通过此方法完成数据的访问。
query():查询数据。传入
URI
,返回一个
Cursor
对象,通过
Cursor
完成对数据库数据的遍历访问。
I
nsert():插入一条数据。
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
负责将桌面上所有的对象从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
装载的。