一直想来分析下launcher,今晚终于开始了。

1.Launcher的启动过程

从网络上找了一段关于Launcher的启动过程的文章,作为学习Launcher的背景知识:

Linuxkernel启动以后会通过app_main进程来初始化androidRuntimeJava运行环境,而zygoteAndroid的第一个进程。所有的android的应用以及大部分系统服务都是通过zygotefork出来的子进程(我现在看到的只有native的servicemanager不是zygotefork出来的)。在systemserver中启动的若干系统服务中与我们启动进程相关的就是AcitivityManager
当systermserver启动好所有服务以后,系统就进入”systemready”状态,这个时候ActivityManager就登场了。ActivityManager光看代码行就知道是一个重量级的服务,它主要管理Activity之间的跳转,以及进程的生命周期。当ActivityManager发现系统已经启动好以后它就会发出一个intent:

view plain
  1. Intentintent=newIntent(mTopAction,mTopData!=null?Uri.parse(mTopData):null);
  2. intent.setComponent(mTopComponent);
  3. if(mFactoryTest!=SystemServer.FACTORY_TEST_LOW_LEVEL){
  4. intent.addCategory(Intent.CATEGORY_HOME);
  5. }

通过这个category类型为home的intent,ActivityManager就会通过:
view plain
  1. startActivityLocked(null,intent,null,null,0,aInfo,
  2. null,null,0,0,0,false,false);

启动 Home 进程了。而这个启动Home进程的过程实际上还是去通过zygotefork出的一个子进程。

因此只要在manifest中具备这样的intent-filter都可以在开机的时候作为Home启动:

view plain
  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.MAIN"/>
  3. <categoryandroid:name="android.intent.category.HOME"/>
  4. <categoryandroid:name="android.intent.category.DEFAULT"/>
  5. </intent-filter>

多个home之间的switch会在开始的时候有个选择,至于这个选择好像是packagemanager来实现的,没有仔细研究过。

2.UI结构

通过launcher/Res/Layout-land/launcher.xml分析可以得到主屏幕的UI结构:

整个homescreen是一个包含三个childview的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的代码结构。

首先,是一些权限的声明。例如:

view plain
  1. <uses-permissionandroid:name="android.permission.CALL_PHONE"/>
  2. <uses-permissionandroid:name="android.permission.EXPAND_STATUS_BAR"/>

这部分可以略过;

其次,Application的构成,如上图:

(1)LauncherHomeScreenActivity

view plain
  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.MAIN"/>
  3. <categoryandroid:name="android.intent.category.HOME"/>
  4. <categoryandroid:name="android.intent.category.DEFAULT"/>
  5. <categoryandroid:name="android.intent.category.MONKEY"/></intent-filter>

上面这段代码就标志着它是开机启动后HomeActivity。通过Launcher.javaonCreat()的分析我们可以大致把握屏幕的主要活动:

view plain
  1. protectedvoidonCreate(BundlesavedInstanceState){
  2. super.onCreate(savedInstanceState);
  3. //把xml文件的内容实例化到View中
  4. mInflater=getLayoutInflater();
  5. //监听应用程序控件改变事件
  6. mAppWidgetManager=AppWidgetManager.getInstance(this);
  7. mAppWidgetHost=newLauncherAppWidgetHost(this,APPWIDGET_HOST_ID);
  8. mAppWidgetHost.startListening();
  9. //用于调试?
  10. if(PROFILE_STARTUP){
  11. android.os.Debug.startMethodTracing("/sdcard/launcher");
  12. }
  13. //监听locale,mcc,mnc是否改变,如果改变,则重写新配置
  14. //mcc:mobilecountrycode(国家代码China460);mnc:mobilenetworkcode(网络代码)
  15. checkForLocaleChange();
  16. /*Thisallowssuchapplicationstohaveavirtualwallpaperthatislargerthanthephysicalscreen,matchingthesizeoftheirworkspace.*/
  17. setWallpaperDimension();
  18. //显示主屏幕UI元素,workspace,slidingdrawer(handleviewandappgridview),deletezone
  19. setContentView(R.layout.launcher);
  20. //Findsalltheviewsweneedandconfigurethemproperly.
  21. //完成workspace,slidingdrawer,deletezone的各种事件操作和监听
  22. setupViews();
  23. //Registersvariousintentreceivers.
  24. //允许其他应用对本应用的操作
  25. registerIntentReceivers();
  26. //Registersvariouscontentobservers.
  27. //例如,注册一个内容观察者跟踪喜爱的应用程序
  28. registerContentObservers();
  29. //重新保存前一个状态(目的??)
  30. mSavedState=savedInstanceState;
  31. restoreState(mSavedState);
  32. //调试?
  33. if(PROFILE_STARTUP){
  34. android.os.Debug.stopMethodTracing();
  35. }
  36. //LoadsthelistofinstalledapplicationsinmApplications.
  37. if(!mRestoring){
  38. startLoaders();
  39. }
  40. //Forhandlingdefaultkeys??
  41. mDefaultKeySsb=newSpannableStringBuilder();
  42. Selection.setSelection(mDefaultKeySsb,0);
  43. }

方法onActivityResult():完成在workspace上增加shortcutappwidgeLivefolder

方法onSaveInstantceState()onRestoreInstanceState():为了防止SensorLandPort布局自动切换时数据被置空,通过onSaveInstanceState方法可以保存当前窗口的状态,在即将布局切换前将当前的Activity压入历史堆栈。如果我们的Activity在后台没有因为运行内存吃紧被清理,则切换时回触发onRestoreIntanceState()


(2)WallpaperChooser:设置墙纸。

同理我们从onCreat()作为入口来分析这个活动的主要功能。

view plain
  1. publicvoidonCreate(Bundleicicle){
  2. super.onCreate(icicle);
  3. //设置允许改变的窗口状态,需在setContentView之前调用
  4. requestWindowFeature(Window.FEATURE_NO_TITLE);
  5. //添加墙纸资源,将资源标识符加入到动态数组中
  6. findWallpapers();
  7. //显示墙纸设置屏幕的UI元素,Imageview,GalleryandButton(LinearLayout)
  8. setContentView(R.layout.wallpaper_chooser);
  9. //图片查看功能的实现
  10. mGallery=(Gallery)findViewById(R.id.gallery);
  11. mGallery.setAdapter(newImageAdapter(this));
  12. mGallery.setOnItemSelectedListener(this);
  13. mGallery.setCallbackDuringFling(false);
  14. //Button事件监听,点击选择setWallpaper(Resid)
  15. findViewById(R.id.set).setOnClickListener(this);
  16. mImageView=(ImageView)findViewById(R.id.wallpaper);
  17. }

(3)default_searchable

对于home中任意的Acitivty,使能系统缺省Search模式,这样就可以使用android系统默认的searchUI


(4)InstallShortcutReceiver

继承自BroadcastReceiver,重写onReceier()方法,对于发送来的Broadcast(这里指Intent)进行过滤(IntentFilt)并且响应(这里是InstallShortcut())。这里分析下onReceive():

view plain
  1. <!--Enablesystem-defaultsearchmodeforanyactivityinHome-->
  2. <!--Intentreceivedusedtoinstallshortcutsfromotherapplications-->
  3. publicvoidonReceive(Contextcontext,Intentdata){
  4. //接受并过滤Intent
  5. if(!ACTION_INSTALL_SHORTCUT.equals(data.getAction())){
  6. return;
  7. }
  8. //获取屏幕
  9. intscreen=Launcher.getScreen();
  10. //安装快捷方式
  11. if(!installShortcut(context,data,screen)){
  12. //如果屏幕已满,搜寻其他屏幕
  13. for(inti=0;i<Launcher.SCREEN_COUNT;i++){
  14. if(i!=screen&&installShortcut(context,data,i))break;
  15. }
  16. }
  17. }

其中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装载的。

参考文章:http://blog.csdn.net/fengkehuan/article/details/6205980

这篇文章写得很详细,在eclipse中,结合launcher源码,大体了解launcher的结构。


Launcher是JRE中用于启动程序入口main()的类。分析这个类有助于理解JRE系统类的加载机制。

该类主要功能是:创建ExtClassLoader和AppClassLoader,还根据配置创建SercurityManager,设置进程上下文类加载器。

(一)Launcher初始化代码

private static Launcher launcher = new Launcher();
public static Launcher getLauncher() {
return launcher;
}

public Launcher() {
// 1. 创建扩展类加载器:ExtClassLoader
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
“Could not create extension class loader”);
}

// 2. 创建用于启动应用程序的类加载器:AppClassLoader
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
“Could not create application class loader”);
}

// 3. 设置当前线程的上下文类加载器为前一步创建的AppClassLoader实例
Thread.currentThread().setContextClassLoader(loader);

//4. 根据需求创建安全管理器:SecurityManager实例
String s = System.getProperty(“java.security.manager”);
if (s != null) {
SecurityManager sm = null;
if (“”.equals(s) || “default”.equals(s)) {
sm = new java.lang.SecurityManager();
} else {
try {
sm = (SecurityManager)loader.loadClass(s).newInstance();
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
} catch (ClassNotFoundException e) {
} catch (ClassCastException e) {
}
}
if (sm != null) {
System.setSecurityManager(sm);
} else {
throw new InternalError(
“Could not create SecurityManager: ” + s);
}
}
}

创建Launcher几乎是用了一个Singleton模式,但令人疑惑的是其构造方法访问修饰符为public,这样就破坏了Singleton模式,可能是需要在必要的时候创建另一个Launcher实例。

(二)ExtClassLoader

关键代码如下:
String s = System.getProperty(“java.ext.dirs”);
File[] dirs = analyze(s);
URLClassLoader(dirs, null, factory);

因此,ExtClassLoader将加载变量“java.ext.dirs”的值指示的路径下的类,默认是jre安装目录/lib/ext,可以通过Djava.ext.dirs=…,来修改,这个指定目录是一些JDK或JRE的可选择功能扩展包。

(三)AppClassLoader

关键代码是:
final String s = System.getProperty(“java.class.path”);
final File[] path = (s == null) ? new File[0] : getClassPath(s);
return (AppClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL[] urls =
(s == null) ? new URL[0] : pathToURLs(path);
return new AppClassLoader(urls, extcl);
}
});

可见,AppClassLoader取的是环境变量java.class.path中设定的路径作为类加载的搜索路径。
可以通过对该变量的设定来修改默认配置,实际上我们也常常这样做——虽然我们很少修改java.ext.dirs的值。

Launcher是Android系统的桌面系统,是比较重要也比较复杂的程序,这里对其代码做一个分析,希望起到抛砖引玉的作用。

1.Launcher有什么?live folder, widget , shortcut , wallpaper,见onActivityResult

2. UI分成3部分:workspace,slibingdrawer,deletezone

3. Menu:见onCreateOptionsMenu in launcher.java

4. launcher类是个activity,遵循activity的生命周期。

5.资源文件比较多,这里只关注Layout相关的文件

代码分析的主线:

1.了解类

2.了解类的关系

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的实现)。

2) 如果开始就深入各个实现细节则会发现千头万绪,很难有个清醒的方向。

<manifestxmlns:android=”http://schemas.android.com/apk/res/android”

package=”com.android.settings”

android:sharedUserId=”android.uid.system”>

<uses-permissionandroid:name=”com.google.android.providers.gmail.permission.WRITE_GMAIL”/>

<uses-permissionandroid:name=”com.google.android.providers.gmail.permission.READ_GMAIL”/>

<uses-permissionandroid:name=”android.permission.WRITE_SETTINGS”/>

<uses-permissionandroid:name=”android.permission.WRITE_SECURE_SETTINGS”/>

<uses-permissionandroid:name=”android.permission.DEVICE_POWER”/>

<uses-permissionandroid:name=”android.permission.CHANGE_CONFIGURATION”/>

<uses-permissionandroid:name=”android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>

<uses-permissionandroid:name=”android.permission.VIBRATE”/>

<uses-permissionandroid:name=”android.permission.BLUETOOTH”/>

…….

<activityandroid:name=”Settings”android:label=”@string/settings_label”

android:taskAffinity=”com.android.settings”

android:clearTaskOnLaunch=”true”

android:launchMode=”singleTop”>

<intent-filter>

<actionandroid:name=”android.intent.action.MAIN”/>

<actionandroid:name=”android.settings.SETTINGS”/>

<categoryandroid:name=”android.intent.category.DEFAULT”/>

<categoryandroid:name=”android.intent.category.LAUNCHER”/>

</intent-filter>

</activity>

我们来看这段,Intent-filter这个是说预备接受哪些intent,android.intent.action.MAIN指的是这个Activity是此应用程序的进口。那么这个intent是谁发出来的呢?

我们可以看看code.

我们知道android再init最后面启动home服务,也就是我们这里所说的launcher.假如从homescreen这里点击touchpad事件,这个event是如何传递给homescreen呢?

通常在home启动前,我们的各种服务就已经建立好了,包括了ActivityManagerService,home也是一个Activity。

publicvoidonItemClick(AdapterViewparent,Viewv,intposition,longid){

ApplicationInfoapp=(ApplicationInfo)parent.getItemAtPosition(position);

mLauncher.startActivitySafely(app.intent);

}

这一段就在launch里面的,这里我不得不说的是launcher的抽屉类,也就是按一下打开,在按一下关掉。

SlidingDrawer

<SlidingDrawer

android:id=”@+id/drawer”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

android:orientation=”horizontal”

android:bottomOffset=”4dip”

android:handle=”@+id/all_apps”

android:content=”@+id/content”>

<com.android.launcher.HandleView

android:id=”@id/all_apps”

android:layout_width=”56dip”

android:layout_height=”fill_parent”

android:background=”@drawable/handle”

android:focusable=”true”

android:clickable=”true”

android:scaleType=”center”

android:src=”@drawable/handle_icon”

launcher:direction=”vertical”/>

<com.android.launcher.AllAppsGridView

android:id=”@id/content”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

launcher:texture=”@drawable/pattern_carbon_fiber_dark”

android:scrollbarStyle=”outsideInset”

android:drawSelectorOnTop=”false”

android:listSelector=”@drawable/grid_selector”

android:nextFocusLeft=”@id/all_apps”

android:nextFocusDown=”@id/content”

android:nextFocusUp=”@id/content”

android:nextFocusRight=”@id/content”

android:verticalSpacing=”10dip”

android:numColumns=”5″/>

</SlidingDrawer>

SlidingDrawer有两个必须的元素,一个就是handle,一个是content,handle就是当你点击它的时候,content要么抽抽屉要么关抽屉,这里得handler

是一个自己创建的类叫做handleView.content是AllAppsGridView

我们往看看hanleView的代码

这里我们需要夸大的是用xml的方式天生的对象如何写构造函数,由于假如用findViewById这样的方式,没有构造函数的话,轻易返回null.

publicHandleView(Contextcontext){

super(context);

}

publicHandleView(Contextcontext,AttributeSetattrs){

this(context,attrs,0);

}

publicHandleView(Contextcontext,AttributeSetattrs,intdefStyle){

super(context,attrs,defStyle);

TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.HandleView,defStyle,0);

mOrientation=a.getInt(R.styleable.HandleView_direction,ORIENTATION_HORIZONTAL);

a.recycle();

}

我们在使用xml天生布局的时候,系统会自动调用view的publicView(Contextcontext,AttributeSetattrs)这个构造器,attrs这个参数是有系统帮我们自动实现的。

handleView是在那里被调用的呢?实在launch.java的setupView函数中被调用的,context应该就是应用的地方调进的。我们看看launch.java

(Context字面意思上下文,位于frameworkpackage的android.content.Context中,实在该类为LONG型,类似Win32中的Handle句柄,很多方法需要通过Context才能识别调用者的实例,

比如说Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的实例为Activity,

而到了一个button的onClick(Viewview)等方法时,我们用this时就会报错,

所以我们可能使用ActivityName.this来解决,主要原因是由于实现Context的类主要有Android特有的几个模型,Activity、Service以及BroadcastReceiver.)

setContentView(R.layout.Launcher).LauncherextendsActivity

在调用setConentView的时候往读些R.layout下面的xml文档,完成初始化UI的过程,slidingDraw就是在这个时候初始化的,那构造函数为什么要3个呢?

实在在这里第二个已经够了,

第一个是我们用new的时候需要调用的。第二个就是上面说的,第三个呢?SetContentView这个函数还有不同的参数调用,例如publicvoidsetContentView(Viewview,ViewGroup.LayoutParamsparams)这个就是会往调用第三个

Settheactivitycontenttoanexplicitview.Thisviewisplaceddirectlyintotheactivity’sviewhierarchy.Itcanitselfbeacomplexviewhierarhcy.

Parameters

重叠性的activity

对于drawer我们需要实现的是SlidingDrawer.OnDrawerOpenListener,onDrawerCloseListener,onDrawerScollListener的事件

具体我们可以往查看code

我们下来具体看看handleView都作了些什么

里面有focusSearch,onKeyDown,onKeyUp

focusSearch

这个函数主要是用来判定那些地方可以focus,根据不同的direction

这个函数主要是imageView需要

mDrawer=(SlidingDrawer)dragLayer.findViewById(R.id.drawer);

finalSlidingDrawerdrawer=mDrawer;

drawer.lock();

finalDrawerManagerdrawerManager=newDrawerManager();

drawer.setOnDrawerOpenListener(drawerManager);

drawer.setOnDrawerCloseListener(drawerManager);

drawer.setOnDrawerScrollListener(drawerManager);

/**

*Representsalaunchableapplication.Anapplicationismadeofaname(ortitle),

*anintentandanicon.

*/

是AllAppsGridView里面的元素,如何把这些content都找出来的呢?

/**

*LoadsthelistofinstalledapplicationsinmApplications.

*

*@returntrueiftheapplicationsloadermustbestarted

*(seestartApplicationsLoader()),falseotherwise.

*/

synchronizedbooleanloadApplications(booleanisLaunching,Launcherlauncher,

booleanlocaleChanged){

if(DEBUG_LOADERS)d(LOG_TAG,”loadapplications”);

if(isLaunching&&mApplicationsLoaded&&!localeChanged){

mApplicationsAdapter=newApplicationsAdapter(launcher,mApplications);

if(DEBUG_LOADERS)d(LOG_TAG,”–>applicationsloaded,return”);

returnfalse;

}

stopAndWaitForApplicationsLoader();

if(localeChanged){

dropApplicationCache();

}

if(mApplicationsAdapter==null||isLaunching||localeChanged){

mApplications=newArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);

mApplicationsAdapter=newApplicationsAdapter(launcher,mApplications);

}

mApplicationsLoaded=false;

if(!isLaunching){

startApplicationsLoaderLocked(launcher,false);

returnfalse;

}

returntrue;

}

PackageManagerandroid的安装包治理

finalIntentmainIntent=newIntent(Intent.ACTION_MAIN,null);

mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

finalLauncherlauncher=mLauncher.get();

finalPackageManagermanager=launcher.getPackageManager();

finalList<ResolveInfo>apps=manager.queryIntentActivities(mainIntent,0);

这段code告诉我们如何用一个packageManager往查找那些包的治理,得到的相应的activityinfo,这个是从manifest.xml里面<activity>里面分离出来的。

finalHashMap<ComponentName,ApplicationInfo>appInfoCache=mAppInfoCache;

ComponentName的结构定义在content下面

ApplicationInfo继续ItemInfo

使用ArrayAdapter(数组适配器)顾名思义,需要把数据放进一个数组以便显示。

android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字

SimpleAdapter能定义各种各样的布局出来,可以放上ImageView(图片),还可以放上Button(按钮),CheckBox(复选框)

privatevoidbindDrawer(Launcher.DesktopBinderbinder,

ApplicationsAdapterdrawerAdapter){

mAllAppsGrid.setAdapter(drawerAdapter);

binder.startBindingAppWidgetsWhenIdle();

}

通过这个函数把LaunchermAllAppsGrid和LauncherModel的mApplicationsAdapter结合起来。

整个流程就是在DesktopItemLoaded结束后,就调用bindDrawer把GridView和content绑定,然后再GridView里面的getView函数往绘制

Launcher就分析到这里。

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,墙纸。

类Favorites(LauncherSettings.java)和类LauncherProvider定义了一个content provider,用来存储桌面上可以放置的几个对象,包括shortcut, search和clock等。

类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。

参考地址:http://artskill.cn/blog/?p=324



更多相关文章

  1. Android中shape定义控件的使用
  2. Android(安卓)Handler 用法
  3. Android的ListView控件滚动时背景问题
  4. android XMl 解析神奇xstream 四: 将复杂的xml文件解析为对象
  5. Android应用程序与SurfaceFlinger服务的连接过程分析
  6. android:gravity与android:layout_gravity
  7. Android(安卓)HAL模块实现
  8. android控件的对齐方式
  9. Android应用程序键盘(Keyboard)消息处理机制分析

随机推荐

  1. 2020年5A PMP 经验分享
  2. 静态链表
  3. 栈和队列就是这么简单
  4. 解读容器的 2020:寻找云原生的下一站
  5. JVM中一个小知识点:深堆和浅堆的认识
  6. Linux网络基础概念
  7. 十道简单算法题
  8. HashMap的负载因子初始值为什么是0.75?这
  9. 格式化Curl返回的Json工具
  10. 大数据开发工程师完结