首页 > 移动平台 > 详细

Android Launcher2源码分析

时间:2015-03-18 18:14:20      阅读:575      评论:0      收藏:0      [点我收藏+]

Android   Launcher2源码分析

Android源码程序程序中有一个应用程序入口,官方给出的中文翻译为“启动器”。我们一下统称Launcher.
Launcher源码分析,我们还是从AndroidManifest.xml开始:
<application 
    android:name="com.android.launcher2.LauncherApplication" 
    android:label="@string/application_name" 
    android:icon="@drawable/ic_launcher_home" 
    android:hardwareAccelerated="@bool/config_hardwareAccelerated" 
    android:largeHeap="@bool/config_largeHeap">
    <activity
        android:name="com.android.launcher2.Launcher"
        ...
        >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.HOME" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.MONKEY"/>
        </intent-filter>
    </activity>
    ...
</application>

其他,我们姑且也不管,有三点我们必须说一下:
一、
android:hardwareAccelerated="@bool/config_hardwareAccelerated"
指定了整个应用程序是启用硬件加速的,这样整个应用程序的运行速度会更快。
二、
android:largeHeap="@bool/config_largeHeap"
指定了应用程序使用了大的堆内存,能在一定程度上避免,对内存out of memory错误的出现。
三、
<category android:name="android.intent.category.HOME" />
这个申明,相当于告诉系统这是桌面Activity。如果你希望开发自己的桌面应用。这个申明是必须的
通过这三点的设置,我们大概知道了桌面的核心。
那么,接下来我们从LauncherApplication(com/android/launcher2/LauncherApplication.java)应用程序开始分析
public void onCreate() {
        super.onCreate();
        // 在创建icon cache之前,我们需要判断屏幕的大小和屏幕的像素密度,以便创建合适大小的icon
        final int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
        sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||
            screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
        sScreenDensity = getResources().getDisplayMetrics().density;

        mIconCache = new IconCache(this);  //用来<span style="color: rgb(51, 51, 51); font-family: 宋体; font-size: 14px; line-height: 28px; text-indent: 28px; background-color: rgb(248, 248, 248);">设置了应用程序的图标的cache</span>
        mModel = new LauncherModel(this, mIconCache);  //<span style="color: rgb(51, 51, 51); font-family: 宋体; font-size: 14px; line-height: 28px; text-indent: 28px; background-color: rgb(248, 248, 248);">LauncherModel主要用于加载桌面的图标、插件和文件夹,同时LaucherModel是一个广播接收器,在程序包发生改变、区域、或者配置文件发生改变时,都会发送广播给LaucherModel,LaucherModel会根据不同的广播来做相应加载操作</span>

        // 注册广播接收器
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        ......
        registerReceiver(mModel, filter);


        //注册ContentObserver,监听LauncherSettings.Favorites.CONTENT_URI数据的变化
        ContentResolver resolver = getContentResolver();
        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
                mFavoritesObserver);
    }
查看IconCache(com.android.launcher2.IconCache.java)我们很容易发现,这是一个应用程序图标(icon)缓冲生成器。
正如我们刚才所说的,我们知道LauncherModel主要用于加载桌面的图标(icon)、插件(AppWidge)和文件夹(Floder) 和Shortcut。
LauncherModel(com.android.launcher2.LauncherModel.java)中
public class LauncherModel extends BroadcastReceiver {
        public interface Callbacks {  
        public boolean setLoadOnResume();  
        public int getCurrentWorkspaceScreen();  
        public void startBinding();  
        public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);  
        public void bindFolders(HashMap<Long,FolderInfo> folders);  
        public void finishBindingItems();  
        public void bindAppWidget(LauncherAppWidgetInfo info);  
        public void bindAllApplications(ArrayList<ApplicationInfo> apps);  
        public void bindAppsAdded(ArrayList<ApplicationInfo> apps);  
        public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);  
        public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);  
        public void bindPackagesUpdated();  
        public boolean isAllAppsVisible();  
        public void bindSearchablesChanged();  
    }  
}
很明显

LauncherModel.startLoader(),开始加载的工作。launcherModel中加载好的内容会通过

LauncherModel.Callbacks接口的回调函数将数据传给需要的组件


setLoadOnResume()     由于Launcher继承自Activity,因此Launcher可能会处于paused状态(onPause()被调用),

则有可能在这段时间内资源可能发生了改变,如应用被删除或新应用安装,因此需要在onResume()中调用此方法进行重新加载。

getCurrentWorkspace()    获取当前屏幕的序号

startBinding()     通知Launcher加载开始,并更新Workspace上的shortcuts

bindItems(ArrayList<ItemInfo> shortcuts, int start, int end)     加载一批内容项到Workspace,加载的内容项包括,

Application、shortcut、folder。

bindFolders(HashMap<Long, FolderInfo> folders)    加载folder的内容

finishBindingItems()    通知Launcher加载结束。

bindAppWidget(LauncherAppWidgetInfo item)    加载AppWidget到Workspace

bindAllApplications(final ArrayList<ApplicationInfo> apps)   在All Apps页加载所有应用的Icon

bindAppsAdded(ArrayList<ApplicationInfo> apps)   通知Launcher一个新的应用被安装,并加载这个应用

bindAppsUpdated(ArrayList<ApplicationInfo> apps)  通知Launcher一个应用发生了更新

bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent)    通知Launcher一个应用被删除了

bindPackagesUpdated()   通知Launcher多个应用发生了更新

isAllAppsVisible()用于在加载的过程中记录当前Launcher的状态,返回true则当前显示的All Apps

bindSearchablesChanged()当搜索/删除框状态发生改变时调用

了解了每个方法的作用之后,就可以开始进一步的分析了。


了解了大概过程,下一步。我们走到Launcher(com.android.launcher2.Launcher.java)
public final class Launcher extends Activity
implements View.OnClickListener,LauncherModel.Callbacks{
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    mModel = app.setLauncher(this);
    mIconCache = app.getIconCache();
    ...
    mAppWidgetManager = AppWidgetManager.getInstance(this);
    mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    mAppWidgetHost.startListening();
    ...
    //检查本地保存的配置是否需要更新
    checkForLocaleChange();
    setContentView(R.layout.launcher);
    //对UI控件进行初始化和配置
    setupViews();
    //向用户展示指导的页面
    showFirstRunWorkspaceCling();
    registerContentObservers();
    ...
    if (!mRestoring) {
	//为Launcher加载数据
        mModel.startLoader(this, true);
    }
    ...
}
别的,我们先不管。
我们先来看看Launcher的启动
implements  LauncherModel.Callbacks{
mModel = app.setLauncher(this);
我们再来看看LauncherApplication.java中的
LauncherModel setLauncher(Launcher launcher) {
      mModel.initialize(launcher);
      return mModel;
}
从mModel.startLoader(true, mWorkspace.getCurrentPage());我们大概知道加载流程。但那时具体加载过程我们稍后分析
我们先来看一下整个Launcher的布局
launcher.xml
<com.android.launcher2.DragLayer 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher" 
 
    android:id="@+id/drag_layer" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
 
    <!-- Keep these behind the workspace so that they are not visible when 
         we go into AllApps --> 
    <include 
        android:id="@+id/dock_divider" 
        layout="@layout/workspace_divider" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:layout_marginBottom="@dimen/button_bar_height" 
        android:layout_gravity="bottom"  /> 
    <include 
        android:id="@+id/paged_view_indicator" 
        layout="@layout/scroll_indicator" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom" 
        android:layout_marginBottom="@dimen/button_bar_height"  /> 
 
    <!-- The workspace contains 5 screens of cells --> 
    <com.android.launcher2.Workspace 
        android:id="@+id/workspace" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:paddingTop="@dimen/qsb_bar_height_inset" 
        android:paddingBottom="@dimen/button_bar_height" 
        launcher:defaultScreen="2" 
        launcher:cellCountX="4" 
        launcher:cellCountY="4" 
        launcher:pageSpacing="@dimen/workspace_page_spacing" 
        launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left" 
        launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right"> 
 
        <include android:id="@+id/cell1" layout="@layout/workspace_screen"  /> 
        <include android:id="@+id/cell2" layout="@layout/workspace_screen"  /> 
        <include android:id="@+id/cell3" layout="@layout/workspace_screen"  /> 
        <include android:id="@+id/cell4" layout="@layout/workspace_screen"  /> 
        <include android:id="@+id/cell5" layout="@layout/workspace_screen"  /> 
    </com.android.launcher2.Workspace> 
 
    <include layout="@layout/hotseat" 
        android:id="@+id/hotseat" 
        android:layout_width="match_parent" 
        android:layout_height="@dimen/button_bar_height_plus_padding" 
        android:layout_gravity="bottom"  /> 
 
    <include 
        android:id="@+id/qsb_bar" 
        layout="@layout/qsb_bar"  /> 
 
    <include layout="@layout/apps_customize_pane" 
        android:id="@+id/apps_customize_pane" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:visibility="invisible"  /> 
 
    <include layout="@layout/workspace_cling" 
        android:id="@+id/workspace_cling" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:visibility="gone"  /> 
 
    <include layout="@layout/folder_cling" 
        android:id="@+id/folder_cling" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:visibility="gone"  /> 
</com.android.launcher2.DragLayer> 

一、最外层的DragLayer,是一个继承自FramLayout的View控件,显示的就是整个桌面根容器。桌面的所有控件都是位于DragLayer中。
二、id/dock_divider  它通过include关键字包含了另外一个布局文件workspace_divider.xml ,而这个workspace_divider.xml包含了一ImageView,其实dock_divider就是dock区域上面的那条直线。
技术分享
三、id/paged_view_indicator  同样它包含了scroll_indicator.xml,其中包含了一个ImageView,显示的是一个.9的png文件。实际上就是当Launcher滚动翻页的时候,那个淡蓝色的页面指示条。
技术分享

四、id/workspace 然后桌面的核心容器WorkSpace,如下图所示,当然你看到的只是Workspace的一部分,其实是一个workspace_screen,通过 Launcher.xml可以看到,整个workspace由5个workspace_screen组成,每个workspace_screen其实就是对应桌面一页。而每个workspace_screen包含了一个CellLayout,这是一个自定义控件,继承自ViewGroup,所以它算是一个用来布局的控件,在这里主要用来承载我们每页的桌面图标、widget和文件夹。
技术分享

五 、id/hotseat  其实就是这块dock区域了
技术分享
六、id/qsb_bar  就是屏幕最顶端的Google搜索框。这个搜索框是独立于图标界面的,所以当我们对桌面进行翻页的时候,
这个搜索框会巍然不动滴固定在最顶端。当然他可不止这么简单。他同样是删除框,用于应用图标移动到这里提示
技术分享
七、id/apps_customize_pane    点击dock中显示所有应用程序的按钮后才会从隐藏状态转换为显示状态,如下图所示,
显示了所有应用程序和所有插件的界面。
技术分享
八 、id/workspace_cling当第一次运行Launcher2时,会显示的用于指导的动画,以后不再显示
技术分享
id/folder_cling,第一次使用Folder时,展示给用户的指导画面。
技术分享


看完布局,我们对于Launcher整体用了清醒地认识

首先让我们回顾一下整个加载过程的流程是怎样的

技术分享


Android Launcher2源码分析

原文:http://blog.csdn.net/jaysong2012/article/details/44410385

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!