本文来自http://blog.csdn.net/chenshaoyang0011转载请申明文章出处!

文中如有纰漏之处,望不吝指教~~~欢迎讨论,共同学习~~~

Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:

[html] view plain copy
  1. <manifest
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.android.launcher">
  4. <original-packageandroid:name="com.android.launcher2"/>
  5. ...
  6. <application
  7. android:name="com.android.launcher2.LauncherApplication"
  8. ...
  9. >
  10. <activity
  11. android:name="com.android.launcher2.Launcher"
  12. ...
  13. >
  14. <intent-filter>
  15. <actionandroid:name="android.intent.action.MAIN"/>
  16. <categoryandroid:name="android.intent.category.HOME"/>
  17. <categoryandroid:name="android.intent.category.DEFAULT"/>
  18. <categoryandroid:name="android.intent.category.MONKEY"/>
  19. </intent-filter>
  20. </activity>
  21. ...
  22. </application>
  23. </manifest>

从中我们可以知道启动过程需要先后初始化LauncherApplication和Launcher的对象。更加简洁的说,启动过程可以分成两步,第一步在

LauncherApplication.onCreate()方法中,第二部在Launcher.onCreate()方法中。

先看第一步,代码片段如下:

[java] view plain copy
  1. publicvoidonCreate(){
  2. super.onCreate();
  3. //在创建iconcache之前,我们需要判断屏幕的大小和屏幕的像素密度,以便创建合适大小的icon
  4. finalintscreenSize=getResources().getConfiguration().screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK;
  5. sIsScreenLarge=screenSize==Configuration.SCREENLAYOUT_SIZE_LARGE||
  6. screenSize==Configuration.SCREENLAYOUT_SIZE_XLARGE;
  7. sScreenDensity=getResources().getDisplayMetrics().density;
  8. mIconCache=newIconCache(this);
  9. mModel=newLauncherModel(this,mIconCache);
  10. //注册广播接收器
  11. IntentFilterfilter=newIntentFilter(Intent.ACTION_PACKAGE_ADDED);
  12. ......
  13. registerReceiver(mModel,filter);
  14. //注册ContentObserver,监听LauncherSettings.Favorites.CONTENT_URI数据的变化
  15. ContentResolverresolver=getContentResolver();
  16. resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI,true,
  17. mFavoritesObserver);
  18. }

LauncherApplication是Application的子类,是整个程序的入口。因此,一些全局信息的初始化和保存工作就放到这里执行。包括屏幕大小,像素密度信息的获取,以及

BroadcastReceiver和ContentObserver的注册都在整个程序的开始就完成。LauncherApplication的工作结束之后,下面就开始初始化Launcher了。Launcher是一个Activity,

而Activity的生命周期中,有几个重要的回调方法,而onCreate()方法是最先被执行的用于进行初始化操作的。那下面就来看看Launcher.onCreate()中具体做了哪些操作:

[java] view plain copy
  1. protectedvoidonCreate(BundlesavedInstanceState){
  2. ...
  3. mModel=app.setLauncher(this);
  4. mIconCache=app.getIconCache();
  5. ...
  6. mAppWidgetManager=AppWidgetManager.getInstance(this);
  7. mAppWidgetHost=newLauncherAppWidgetHost(this,APPWIDGET_HOST_ID);
  8. mAppWidgetHost.startListening();
  9. ...
  10. //检查本地保存的配置是否需要更新
  11. checkForLocaleChange();
  12. setContentView(R.layout.launcher);
  13. //对UI控件进行初始化和配置
  14. setupViews();
  15. //向用户展示指导的页面
  16. showFirstRunWorkspaceCling();
  17. registerContentObservers();
  18. ...
  19. if(!mRestoring){
  20. //为Launcher加载数据
  21. mModel.startLoader(this,true);
  22. }
  23. ...
  24. }

可以通过时序图,直观的认识下,onCreate()中主要进行了哪些操作:


可以将Launcher.onCreate()所执行的操作大概分为七步:

1、LauncherAppliaction.setLauncher()。

2、AppWidgetHost.startListening(),对widget事件进行监听

3、checkForLocaleChange(),检查更新本地保存的配置文件

4、setupViews(),配置UI控件

5、showFirstRunWorkspaceCling(),第一次启动时显示的指导画面

6、registerContentObservers(),设置内容监听器

7、LauncherModel.startLoader(),为Launcher加载Workspace和AllApps中的内容

那么,下面就一步一步的顺着执行的过程来看Launcher启动过程中都做了些什么。

Step1:LauncherApplication.setLauncher()

调用LauncherAppliction对象的setLauncher()方法,得到一个LauncherModel对象的引用,setLauncher内容如下:

[java] view plain copy
  1. LauncherModelsetLauncher(Launcherlauncher){
  2. mModel.initialize(launcher);
  3. returnmModel;
  4. }
在setLauncher中继续执行了mModel对象的initialize方法,在initialize中只有小段代码: [java] view plain copy
  1. publicvoidinitialize(Callbackscallbacks){
  2. synchronized(mLock){
  3. mCallbacks=newWeakReference<Callbacks>(callbacks);
  4. }
  5. }

由于Launcher实现了Callback接口。在mModel中,将传入的Launcher对象向下转型为Callback赋值给mCallbacks变量。并在LauncherModel中获得了一个Callbacks的软引

通过这一过程,将Launcher对象作为Callback与mModel进行绑定,当mModel后续进行操作时,Launcher可以通过回调得到结果。

Step2:mAppWidgetHost.startListening()

LauncherAppWidgetHost继承自AppWidgetHost,它的作用就是帮助Launcher管理AppWidget,并且能够捕获长按事件,使得应用可以正常的删除、添加

AppWidget。通过调用mAppWidgetHost.startListening()方法,开启监听。

Step3:checkForLocaleChange()

接下来执行checkForLocaleChange(),方法内容如下:

[java] view plain copy
  1. privatevoidcheckForLocaleChange(){
  2. if(sLocaleConfiguration==null){
  3. //从本地存储文件中加载配置信息,包括locale地理位置、mcc移动国家代码
  4. //mnc移动网络代码
  5. newAsyncTask<Void,Void,LocaleConfiguration>(){
  6. @Override
  7. protectedLocaleConfigurationdoInBackground(Void...unused){
  8. LocaleConfigurationlocaleConfiguration=newLocaleConfiguration();
  9. readConfiguration(Launcher.this,localeConfiguration);
  10. returnlocaleConfiguration;
  11. }
  12. @Override
  13. protectedvoidonPostExecute(LocaleConfigurationresult){
  14. sLocaleConfiguration=result;
  15. //从本地取出信息后,再次调用
  16. checkForLocaleChange();
  17. }
  18. }.execute();
  19. return;
  20. }
  21. //得到设备当前的配置信息
  22. finalConfigurationconfiguration=getResources().getConfiguration();
  23. finalStringpreviousLocale=sLocaleConfiguration.locale;
  24. finalStringlocale=configuration.locale.toString();
  25. finalintpreviousMcc=sLocaleConfiguration.mcc;
  26. finalintmcc=configuration.mcc;
  27. finalintpreviousMnc=sLocaleConfiguration.mnc;
  28. finalintmnc=configuration.mnc;
  29. booleanlocaleChanged=!locale.equals(previousLocale)||mcc!=previousMcc||mnc!=previousMnc;
  30. if(localeChanged){
  31. sLocaleConfiguration.locale=locale;
  32. sLocaleConfiguration.mcc=mcc;
  33. sLocaleConfiguration.mnc=mnc;
  34. //清空Icon
  35. mIconCache.flush();
  36. finalLocaleConfigurationlocaleConfiguration=sLocaleConfiguration;
  37. //将更新后的数据重新写入本地文件保存
  38. newThread("WriteLocaleConfiguration"){
  39. @Override
  40. publicvoidrun(){
  41. writeConfiguration(Launcher.this,localeConfiguration);
  42. }
  43. }.start();
  44. }
  45. }

在这个方法中,先是检查了本地文件的配置与当前设备的配置是否一致,如果不一致,则更新配置,并且清空IconCache,因为配置的改变可能会改变语言环境,

所以需要清空IconCache中的内容重新加载。

Step4:setupViews()

setupViews()方法调用,在这个方法中简单的对所有的UI控件进行加载和配置:

[java] view plain copy
  1. /**
  2. *Findsalltheviewsweneedandconfigurethemproperly.
  3. */
  4. privatevoidsetupViews(){
  5. finalDragControllerdragController=mDragController;
  6. ...
  7. //Setupthedraglayer
  8. mDragLayer.setup(this,dragController);
  9. //Setupthehotseat
  10. mHotseat=(Hotseat)findViewById(R.id.hotseat);
  11. if(mHotseat!=null){
  12. mHotseat.setup(this);
  13. }
  14. //Setuptheworkspace
  15. mWorkspace.setHapticFeedbackEnabled(false);
  16. mWorkspace.setOnLongClickListener(this);
  17. mWorkspace.setup(dragController);
  18. dragController.addDragListener(mWorkspace);
  19. //Getthesearch/deletebar
  20. mSearchDropTargetBar=(SearchDropTargetBar)mDragLayer.findViewById(R.id.qsb_bar);
  21. //SetupAppsCustomize
  22. mAppsCustomizeTabHost=(AppsCustomizeTabHost)
  23. findViewById(R.id.apps_customize_pane);
  24. mAppsCustomizeContent=(AppsCustomizePagedView)
  25. mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
  26. mAppsCustomizeContent.setup(this,dragController);
  27. //Gettheallappsbutton
  28. mAllAppsButton=findViewById(R.id.all_apps_button);
  29. if(mAllAppsButton!=null){
  30. mAllAppsButton.setOnTouchListener(newView.OnTouchListener(){
  31. @Override
  32. publicbooleanonTouch(Viewv,MotionEventevent){
  33. if((event.getAction()&MotionEvent.ACTION_MASK)==MotionEvent.ACTION_DOWN){
  34. onTouchDownAllAppsButton(v);
  35. }
  36. returnfalse;
  37. }
  38. });
  39. }
  40. //Setupthedragcontroller(droptargetshavetobeaddedinreverseorderinpriority)
  41. dragController.setDragScoller(mWorkspace);
  42. dragController.setScrollView(mDragLayer);
  43. dragController.setMoveTarget(mWorkspace);
  44. dragController.addDropTarget(mWorkspace);
  45. if(mSearchDropTargetBar!=null){
  46. mSearchDropTargetBar.setup(this,dragController);
  47. }
  48. }
由于UI组件较多,setupViews中所进行的操作也比较繁琐,先通过时序图来简单的理一下吧:


这里一共包括5个UI组件和一个DragController,那就一步一步地看都进行了哪些操作吧。

1、DragLayer

首先我们简单的认识下Draglayer。DragLayer继承自FrameLayout,是整个Launcher的根容器。当快捷图标或者AppWidget被拖拽时,事件的处理就在DragLayer

行操作的,DragLayer.setup()方法的内容如下:

[java] view plain copy
  1. publicvoidsetup(Launcherlauncher,DragControllercontroller){
  2. mLauncher=launcher;
  3. mDragController=controller;
  4. }
只是简单的做了赋值操作,使DragLayer持有Launcher和DragController对象的引用。DragController可以帮助其实现拖拽操作。

2、Hotseat

Hotseat也是FrameLayout的直接子类,代表主屏幕下方的dock栏,可以放置4个快捷图标和一个进入AllApps的按钮。代码如下:

[java] view plain copy
  1. publicvoidsetup(Launcherlauncher){
  2. mLauncher=launcher;
  3. setOnKeyListener(newHotseatIconKeyEventListener());
  4. }
方法调用之后,Hotseat持有Launcher对象的引用,并且用HotseatIconKeyEvenListener对自身的按键进行监听,进入HotseatIconKeyEvenListener可以看到:

[java] view plain copy
  1. classHotseatIconKeyEventListenerimplementsView.OnKeyListener{
  2. publicbooleanonKey(Viewv,intkeyCode,KeyEventevent){
  3. finalConfigurationconfiguration=v.getResources().getConfiguration();
  4. returnFocusHelper.handleHotseatButtonKeyEvent(v,keyCode,event,configuration.orientation);
  5. }
  6. }

调用方法handleHotseatButtonKeyEvent()来处理相应的事件:

[java] view plain copy
  1. staticbooleanhandleHotseatButtonKeyEvent(Viewv,intkeyCode,KeyEvente,intorientation){
  2. ...
  3. switch(keyCode){
  4. caseKeyEvent.KEYCODE_DPAD_LEFT:
  5. ...
  6. break;
  7. caseKeyEvent.KEYCODE_DPAD_RIGHT:
  8. ...
  9. break;
  10. caseKeyEvent.KEYCODE_DPAD_UP:
  11. ...
  12. break;
  13. caseKeyEvent.KEYCODE_DPAD_DOWN:
  14. ...
  15. break;
  16. default:break;
  17. }
  18. returnwasHandled;
  19. }

handleHotseatButtonKeyEvent()方法中根据当前的方向,对KeyEvent.KEYCODE_DPAD_LEFT、KeyEvent.KEYCODE_DPAD_RIGHT、

KeyEvent.KEYCODE_DPAD_UPKeyEvent.KEYCODE_DPAD_DOWN即可能存在(如果手机有实体按键)的导航按钮上、下、左、右进行响应。这样Hotseat

的初始化工作就完成了。

3、Workspace的初始化

先调用setHapticFeedbackEnabled(false),使其在触摸的时候没有触感反馈。接着设置长按事件的监听setOnLongClickListener(this),Launcher实现了

OnLongClickListener接口,看看Launcher中是如何进行响应的:

[java] view plain copy
  1. publicbooleanonLongClick(Viewv){
  2. ...
  3. if(!(vinstanceofCellLayout)){
  4. v=(View)v.getParent().getParent();
  5. }
  6. ...
  7. CellLayout.CellInfolongClickCellInfo=(CellLayout.CellInfo)v.getTag();
  8. ..
  9. //ThehotseattouchhandlingdoesnotgothroughWorkspace,andwealwaysallowlongpress
  10. //onhotseatitems.
  11. finalViewitemUnderLongClick=longClickCellInfo.cell;
  12. booleanallowLongPress=isHotseatLayout(v)||mWorkspace.allowLongPress();
  13. if(allowLongPress&&!mDragController.isDragging()){
  14. if(itemUnderLongClick==null){
  15. //在空的空间上长按时
  16. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  17. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  18. startWallpaper();
  19. }else{
  20. ...
  21. }
  22. }
  23. returntrue;
  24. }
这里我们只关心与Workspace的长按事件相关的内容,当Workspace发生长按事件时,产生触感反馈,同时调用startWallpaper进行壁纸的设置: [java] view plain copy
  1. privatevoidstartWallpaper(){
  2. showWorkspace(true);
  3. finalIntentpickWallpaper=newIntent(Intent.ACTION_SET_WALLPAPER);
  4. Intentchooser=Intent.createChooser(pickWallpaper,
  5. getText(R.string.chooser_wallpaper));
  6. startActivityForResult(chooser,REQUEST_PICK_WALLPAPER);
  7. }

showWorkspace(true)的作用是不管当前的Launcher处于什么状态,都跳转到显示Workspace的状态,并且带有动画过渡。而后面几段代码的作用就是弹出

Dialog,包含了所有能够响应ACTOIN_SET_WALLPAPER的action的Activity。然后我们就可以选择一个来设置比壁纸了。接着就是调用Workspace.setup():

[java] view plain copy
  1. voidsetup(DragControllerdragController){
  2. mSpringLoadedDragController=newSpringLoadedDragController(mLauncher);
  3. mDragController=dragController;
  4. //hardwarelayersonchildrenareenabledonstartup,butshouldbedisableduntil
  5. //needed
  6. updateChildrenLayersEnabled();
  7. setWallpaperDimension();
  8. }

代码中先创建了一个SpringLoadedDragController的对象,这个类的作用控制当Launcher处于State.APPS_CUSTOMIZE_SPRING_LOADED状态时,即处于缩小状

态时,提供控制Launcher进行滑动、放置item的操作。接着Workspace的成员变量mDragController获取了DragController对象的引用。随后,调用

updateChildrenLayersEnabled (),注释中的意思是当子view在创建的时候会开启硬件层,其它时候关闭。其中调用了内部的API这里就不过多追究了。最后,调用

setWallpaperDimension()设置Wallpaper的尺寸。下面还有一步操作调用dragController.addDragListener(mWorkspace)方法,Workspace实现了DragListener:

[java] view plain copy
  1. interfaceDragListener{
  2. voidonDragStart(DragSourcesource,Objectinfo,intdragAction);
  3. voidonDragEnd();
  4. }
这样mWorkspace就能够响应拖拽事件了,具体响应内容将在后面的文章中进行分析。

这样mWorkspace的初始化就算完成了,主要完成了两件事情:1、设置了对长按事件的处理,2、对拖拽事件的处理

4、AppsCustomizeTabHost、AppsCustomizePagedView

AppsCustomizePagedView是内嵌在AppsCustomizeTabHost中的组件,在当点击AllApp按钮是,会跳转到AppsCustomizeTabHost中,而在

AppsCustomizePagedView装载Icon。初始化时调用AppCustomizedPagedView.setup()方法:

[java] view plain copy
  1. publicvoidsetup(Launcherlauncher,DragControllerdragController){
  2. mLauncher=launcher;
  3. mDragController=dragController;
  4. }
获取Launcher与DragController对象的引用。

5、DragController

DragController类主要的工作就是处理拖拽事件,对其进行初始化时分别调用了四个方法dragController.setDragScoller(mWorkspace);dragController.setScrollView(mDragLayer);

dragController.setMoveTarget(mWorkspace);dragController.addDropTarget(mWorkspace);那分别看看这四个方法中具体都做了什么:

[java] view plain copy
  1. publicvoidsetDragScoller(DragScrollerscroller){
  2. mDragScroller=scroller;
  3. }

首先我想吐槽下,方法名应该时在敲代码的时候拼错了,正常情况应该是setDragScroller()~~~~~。Workspace实现了DragScroller接口,代表了Workspace

可以进行滑动操作。通过此方法获取到了DragScroller对象。接着又调用了DragController.setScrollView()

[java] view plain copy
  1. /**
  2. *Setwhichviewscrollsfortoucheventsneartheedgeofthescreen.
  3. */
  4. publicvoidsetScrollView(Viewv){
  5. mScrollView=v;
  6. }

从提供的代码注释理解,这个方法设置了当屏幕的边缘触摸滑动时,所滚动的View。(目前还不清楚具体所指的对象) [java] view plain copy
  1. /**
  2. *Setstheviewthatshouldhandlemoveevents.
  3. */
  4. voidsetMoveTarget(Viewview){
  5. mMoveTarget=view;
  6. }
设置应该处理移动事件的View,传入的对象是Workspace。 [java] view plain copy
  1. /**
  2. *AddaDropTargettothelistofpotentialplacestoreceivedropevents.
  3. */
  4. publicvoidaddDropTarget(DropTargettarget){
  5. mDropTargets.add(target);
  6. }

将Workspace对象作为DropTarget对象添加到mDropTargets中。其中DropTarget接口的定义了一个能够接收拖曳对象的类。当桌面的item被拖拽后,需要找到下一

个容纳它的容器,而这个容器就一个DropTarget。

6、SearchDropTargetBar

SearchDropTargetBar管理着搜索框和删除框的转换,正常情况下它是一个searchBar,当图标被拖拽时,它就变成了deleteDropTargetBar,将图标拖放到上面松手就可以将其从Workspace中删除。

[java] view plain copy
  1. publicvoidsetup(Launcherlauncher,DragControllerdragController){
  2. dragController.addDragListener(this);
  3. dragController.addDragListener(mInfoDropTarget);
  4. dragController.addDragListener(mDeleteDropTarget);
  5. dragController.addDropTarget(mInfoDropTarget);
  6. dragController.addDropTarget(mDeleteDropTarget);
  7. mInfoDropTarget.setLauncher(launcher);
  8. mDeleteDropTarget.setLauncher(launcher);
  9. }
setup中执行的内容比较繁杂,这里不作详细的分析。

这样setupViews()执行完毕。继续回到onCreate()方法中分析。

Step5:showFirstRunWorkspaceCling()

showFirstRunWorkspaceCling()方法调用,在应用第一次被启动的时候,此方法会被调用,用于向用户展示一个指导界面。以后都不会再出现。

Step6:registerContentObservers()

registerContentObservers()注册对指定URI所指定的数据的监听,及时对数据变化做出反应。

Step7:LauncherModel.startLoader()

在应用启动的时候需要加载数据,LauncherModel.startLoader()就完成了这个任务。加载过程的基本流程如下:


通过上面的时序图,对加载的流程基本有了认识。调用LauncherModel.startLoader()开始加载内容,内容加载完之后,通过LauncherModel.Callbacks接口定义的回

调方法,将数据返回给需要的对象。而Launcher实现了这个接口,数据将回传给Launcher。了解了基本过程之后,开始进入加载过程。

[java] view plain copy
  1. publicvoidstartLoader(Contextcontext,booleanisLaunching){
  2. synchronized(mLock){
  3. ......
  4. //Don'tbothertostartthethreadifweknowit'snotgoingtodoanything
  5. if(mCallbacks!=null&&mCallbacks.get()!=null){
  6. ......
  7. mLoaderTask=newLoaderTask(context,isLaunching);
  8. sWorkerThread.setPriority(Thread.NORM_PRIORITY);
  9. sWorker.post(mLoaderTask);
  10. }
  11. }
  12. }

方法中,创建了一个实现了Runnable接口的LoaderTask类的对象mLoaderTask,mWork是一个Handler,调用mWork.post()将mLoaderTask添加到消息队列中。最

后mLoaderTask中的run方法就会得到执行

[java] view plain copy
  1. publicvoidrun(){
  2. ......
  3. keep_running:{
  4. ......
  5. if(loadWorkspaceFirst){
  6. ......
  7. loadAndBindWorkspace();
  8. }else{
  9. ......
  10. }
  11. if(mStopped){
  12. breakkeep_running;
  13. }
  14. ......
  15. waitForIdle();
  16. //secondstep
  17. if(loadWorkspaceFirst){
  18. ......
  19. loadAndBindAllApps();
  20. }else{
  21. ......
  22. }
  23. ......
  24. }
  25. ......
  26. }

如果是初次启动,则loadWorkspaceFirst=true,loadAndBindWorkspace被调用,此时Workspace中的内容项将被加载并且绑定显示到Workspace中。当

Workspace中的内容加载之后,调用waitForIdle方法,以等待加载结束。确认完成之后紧接着loadAndBindAllApps()方法执行,在这个方法中将加载AllApps页面的

内容。这样加载过程就分成了两个部分:1、loadAndBindWorkspace()加载Workspace内容。2、loadAndBindAllApps()加载AllApps中的内容。这部分内容本文暂

不作深入的分析。

随着startLoader()的过程执行完毕,Launcher的初始化过程就基本上结束了。启动过程是很繁琐的,因为所有应用中需要使用到的组件都可能在启动的时候

进行配置,等到从具体的功能入手的时候,就能够更加清楚启动过程所做的操作的意义。

更多相关文章

  1. 关于手机启动的方向,和屏幕的单,双击事件
  2. Android(安卓)WebView加载H5音视频自动播放、关闭Activity停止播
  3. Android(安卓)复习资料
  4. Android(安卓)Studio:resource android:attr/dialogCornerRadius
  5. Android(安卓)启动过程
  6. android中HttpURLConnection调用getResponseCode()时崩溃 解决方
  7. Android读取xxx.properties配置文件中文出现乱码解决方法
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. 我的android 第23天 - UriMatcher类使用
  2. 改变Android按钮背景颜色的高效方法
  3. Android aapt实现资源分区(补充携程aapt源
  4. 自己看的,随便写,贴。关于Android里面的Sty
  5. iOS 7 需要再和 Android 比比什么?
  6. 从零学Android(四)、适配不同的Android设备
  7. 从底层看android5.0系统的启动过程
  8. 客觀評 Android、iOS、WP7
  9. Android系统触摸屏的校正——http://carv
  10. Android Studio com.android.dex.DexExce