第一部分:Launcher APP 组成分析(一)

本文是对MIUI系统中Launcher的深入分析,基于MIUI PatchROM-ICS版本,探讨了Android四大组件在MIUI系统中的应用,特别是Activity、Content Provider和Launcher布局。作者分享了分析源码的GitHub链接,并介绍了Launcher的整体布局结构,包括DragLayer、Workspace、Hotseat和DeleteZone。此外,还提到了Launcher Provider中用于存储桌面布局和应用位置的Favorites表。博客末尾提供了作者的微博链接,供读者交流讨论。
摘要由CSDN通过智能技术生成

为何会分析小米系统?
我算是个米粉吧,从10年开始就用HTC G7刷MIUI系统。从此就一直使用MIUI,对于小米系统MIUI,我还是给予很高的评价。好了,这里不是广告。
继续回答问题:第一,感觉MIUI系统从更新V5版本以后没有再大的更新,似我这种对新鲜事物和技术的渴求,让我挺失望。第二,MIUI系统号称是开源了PatchROM, 实质这是开放了一些APK。对于我,远远不能满足。虽说,在学习Android的几年过程中也对MIUI有过分析,但是毕竟公司的事情也是非常多,对MIUI系统的分析总是不够完整和系统。

近期,努力的抽出时间来对MIUI做一个系统的分析。

废话不多说,开始我们的MIUI系统之旅。

话在开头:本博客仅对MIUI系统做学习交流使用,不存在商业行为。如若本博客中的文字及图片对MIUI系统有侵权行为还请及时通知与我,我会根据相关规定做出处理。谢谢大家的支持!


简介:

1. MIUI系统分析的内容基于MIUI PatchROM- ICS的版本,不能完全适应于最新的V5系统;

2. 分析过程中会针对Android原生的源代码进行相互的比较,分析出各自的优势及其缺点;


第一章: MIUI系统之Launcher分析

首先,读者最好对Launcher的源码有过简单的阅读和了解。

分析的源码会在http://github.com/mimijava/MKHome逐步上传。


第一部分:Launcher APP 组成分析

一、从Android四大组件开始介绍,我们都知道Android四大组件: Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器;

1. Activity:MIUI系统有哪些主要的Activity呢?

        <!-- 主界面 -->
        <activity 
            android:name="cn.minking.launcher.Launcher"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/Theme"
            android:launchMode="singleTask"
            android:screenOrientation="sensorPortrait"
            android:configChanges="mcc|mnc|keyboard|keyboardHidden|navigation|orientation|uiMode|screenSize">
            <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"/>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
		
        <!-- 设置界面 -->
        <activity 
            android:label="@string/preferences_title" 
            android:name="cn.minking.launcher.LauncherPreferenceActivity" >
        </activity>


2. Service 服务,MIUI Launcher中是有一个服务的,但是不是特别重要,在此不做介绍。

3.BroadcastReceiver广播接收器:
        <!-- 安装APP -->
        <receiver 
            android:name="cn.minking.launcher.InstallShortcutReceiver"
            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
            <intent-filter >
                <action android:name="com.android.launcher.action.INSTALL_SHORTCUT"/>
            </intent-filter>
        </receiver>
	
        <!-- 删除APP -->
        <receiver 
            android:name="cn.minking.launcher.UninstallShortcutReceiver"
            android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">
            <intent-filter >
                <action android:name="com.android.launcher.action.UNINSTALL_SHORTCUT"/>
            </intent-filter>
        </receiver>
	
        <!-- 安装Widget -->
        <receiver 
            android:name="cn.minking.launcher.InstallWidgetReceiver" 
            android:permission="com.android.launcher.permission.INSTALL_WIDGET">
            <intent-filter>
                <action android:name="com.android.launcher.action.INSTALL_WIDGET" />
            </intent-filter>
        </receiver>
        
        <!-- 存储信息 -->
        <receiver android:name="cn.minking.launcher.RestoreFinishedReceiver"> 
		<intent-filter> 
			<action android:name="android.intent.action.RESTORE_FINISH" /> 
		</intent-filter> 
	</receiver>


4. Content Provider内容提供者:

    <!-- 数据存储 -->
    <provider 
        android:name="cn.minking.launcher.LauncherProvider"
        android:readPermission="com.android.launcher.permission.READ_SETTINGS"
        android:writePermission="com.android.launcher.permission.WRITE_SETTINGS" 
        android:authorities="cn.minking.launcher.settings"/>


二、Launcher Activity的布局分析;
首先看onCreate函数, 下面这个部分是根据MIUI系统及Android原生代码写出的几个重要方法入口及变量的初始化,
    /******* 数据 ********/
    private LauncherModel mModel;
    private IconCache mIconCache;
    /******* 桌面内容  ********/
    private DragLayer mDragLayer;
    private DragController mDragController;
    private Workspace mWorkspace;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        Window localWindow = getWindow();
        localWindow.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
        // 是否全屏显示,不带状态栏
        //localWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
            //WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //mIsHardwareAccelerated = ((Window)(localWindow)).getWindowManager().isHardwareAccelerated();
        LauncherApplication launcherApplication = (LauncherApplication)getApplication();
        
        // MODEL与 LAUNCHER APP 绑定
        mModel = launcherApplication.setLauncher(this);
        mIconCache = launcherApplication.getIconCache();

        // 分配拖动控制器 
        mDragController = new DragController(this);
        registerContentObservers();
        
        // 应用消息
        mApplicationsMessage = new ApplicationsMessage(this);
        
        setWallpaperDimension();
        
        // 设置Launcher布局
        setContentView(R.layout.launcher);
        setupViews();
        
        if (!mRestoring) {
            /// M: 如果本地信息变更了,设置为重新装载所有信息
            if (sLocaleChanged) {
                mModel.resetLoadedState(true, true);
                sLocaleChanged = false;
            }
            mIsLoadingWorkspace = true;
            if (sPausedFromUserAction) {
                // 如果用户离开了launcher, 只需要在回到launcher的时候异步的完成装载
            	mModel.startLoader(getApplicationContext(), true);
            } else {
                // 如果用户旋转屏幕或更改配置,则同步装载
            	mModel.startLoader(getApplicationContext(), true);
            }
        }
    }


此处关注一个桌面最重要的三处,mModelstartLoader、 setupViews();、mDragController, 第一个是桌面上的数据,第二个是桌面的布局,第三个是对桌面的操作及控制。虽然这样说是很简单,一个桌面不外乎这几点,但是慢慢分析才会知道其中的复杂,三者之间的互相联系及依赖的关系往往让阅读代码的人头疼。大道至简,我们就慢慢的分析,一丝一丝的剥离出来。


(2). Launcher的布局,MIUI的布局由Launcher.xml来做为整体的布局,其文件在res/layout/launcher.xml中,可以在我的Github中找到。其组成如下:

<?xml version="1.0" encoding="utf-8"?>
<cn.minking.launcher.DragLayer 
    android:id="@id/drag_layer" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <!-- 整个桌面的显示,包括背景,不包括小图标状态栏 -->
    <FrameLayout android:id="@id/screen" 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <!-- 桌面背景 -->
        <cn.minking.launcher.Background android:id="@id/drag_layer_background"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
        
        <!-- 所有内容显示区 -->
        <cn.minking.launcher.Workspace android:id="@id/workspace"
            android:paddingTop="@dimen/status_bar_height"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_marginBottom="@dimen/workspace_margin_bottom"
            />
        
        <!-- 缩略图显示 -->
        <cn.minking.launcher.WorkspaceThumbnailView android:id="@id/workspace_preview" 
            android:paddingTop="@dimen/status_bar_height" 
            android:visibility="invisible" 
            android:layout_width="fill_parent" 
            android:layout_height="fill_parent" />
        
        <!-- 桌面HOTSEAT区 -->
        <cn.minking.launcher.HotSeats android:id="@id/hot_seats" 
            android:gravity="center"
            android:layout_gravity="bottom"
            android:background="@drawable/hotseat_background"
            android:paddingLeft="@dimen/hotseats_padding_side"
            android:paddingRight="@dimen/hotseats_padding_side"
            android:paddingTop="@dimen/hotseats_padding_top"
            android:paddingBottom="@dimen/hotseats_padding_bottom"
            android:animationCache="false"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            />
        
        <!-- 删除区 -->
        <cn.minking.launcher.DeleteZone android:id="@id/delete_zone" 
            android:layout_gravity="top"
            android:background="@null"
            android:orientation="horizontal"
            android:visibility="invisible"
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content"
            >
            <!-- 动画显示删除图标 -->
            <ImageView android:id="@id/trash" 
                android:background="@drawable/delete_zone_selector" 
                android:visibility="invisible" 
                android:layout_width="fill_parent" 
                android:layout_height="wrap_content" 
                android:src="@drawable/delete_zone_01" 
                android:scaleType="center" />
            <!-- 删除提示 -->
            <TextView android:id="@id/editing_tips"
                android:layout_gravity="top|center"  
                android:paddingTop="13.0dip" 
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content" 
                android:text="@string/editing_tips" 
                style="@style/WorkspaceIconTitle.notification" />
		</cn.minking.launcher.DeleteZone>
    </FrameLayout>
    
    <include layout="@layout/folder_cling" />
    
    <!-- 小图标状态栏显示区域 -->
    <FrameLayout 
        android:paddingTop="@dimen/status_bar_height" 
        android:layout_width="1.0px" 
        android:layout_height="fill_parent">
        <FrameLayout android:id="@id/default_position" 
            android:layout_width="1.0px" 
            android:layout_height="fill_parent" />
    </FrameLayout>
    
</cn.minking.launcher.DragLayer>

1. DragLayer 实际就是一个FrameLayout;

2. Workspace及WorkspaceThumbnailView实际为一个ViewGroup;

3. Hotseat及DeleteZone

以上这个只是桌面的一个整体布局,桌面上比如图标,WIDGET,文件夹等都有各自的布局格式,这些最后都会作为 Workspace的子控件放入其中,这部分等以后慢慢再道来。


(3). setupViews(); 

    private void setupViews() {
        // Drag 控制器
        DragController dragController = mDragController;
        
        // Drag 层
        mDragLayer = (DragLayer)findViewById(R.id.drag_layer);
        mDragLayerBackground = (Background)findViewById(R.id.drag_layer_background);
        mDragLayer.setDragController(dragController);
        mDragLayer.setLauncher(this);
        
        mScreen = findViewById(R.id.screen);
        
        // 桌面布局
        mWorkspace = (Workspace)mDragLayer.findViewById(R.id.workspace);
        Workspace workspace = mWorkspace;
        
        mWorkspacePreview = (WorkspaceThumbnailView)mDragLayer.findViewById(R.id.workspace_preview);
        workspace.setHapticFeedbackEnabled(false);
        workspace.setOnLongClickListener(this);
        workspace.setDragController(dragController);
        workspace.setLauncher(this);
        workspace.setThumbnailView(mWorkspacePreview);
        
        // 删除区
        mDeleteZone = (DeleteZone)mDragLayer.findViewById(R.id.delete_zone);
        mDeleteZone.setLauncher(this);
        mDeleteZone.setDragController(dragController);
        
        // HOTSEAT
        mHotSeats = (HotSeats)mDragLayer.findViewById(R.id.hot_seats);
        mHotSeats.setLauncher(this);
        mHotSeats.setDragController(dragController);
        
        mFolderCling = (FolderCling)findViewById(R.id.folder_cling);
        mFolderCling.setLauncher(this);
        mFolderCling.setDragController(dragController);
        
        // 设置拖动控制
        dragController.setDragScoller(workspace);
        dragController.addDragListener(mDeleteZone);
        dragController.setScrollView(mDragLayer);
        dragController.setMoveTarget(workspace);
        dragController.addDropTarget(mHotSeats);
        dragController.addDropTarget(workspace);
        dragController.addDropTarget(mDeleteZone);
        
        setupAnimations();
        mPositionSnap = mDragLayer.findViewById(R.id.default_position);
    }




(图1)Launcher布局概略图

上图中其中Workspace为ViewGroup布局,APP、FLODER、WIDGET/GADGET为其子视图,都有各自的视图结构,此处不做介绍,等介绍到时再做分析;


三、Launcher Provider

        @Override
        public void onCreate(SQLiteDatabase db) {
            mMaxId = 1L;
            
            // 创建 favorites 数据库表
            db.execSQL("DROP TABLE IF EXISTS favorites");
            db.execSQL("CREATE TABLE favorites("
                    + "_id INTEGER PRIMARY KEY,"
                    + "title TEXT,"
                    + "intent TEXT,"
                    + "container INTEGER,"
                    + "screen INTEGER,"
                    + "cellX INTEGER,"
                    + "cellY INTEGER,"
                    + "spanX INTEGER,"
                    + "spanY INTEGER,"
                    + "itemType INTEGER,"
                    + "appWidgetId INTEGER NOT NULL DEFAULT -1,"
                    + "isShortcut INTEGER,"
                    + "iconType INTEGER,"
                    + "iconPackage TEXT,"
                    + "iconResource TEXT,"
                    + "icon BLOB,"
                    + "uri TEXT,"
                    + "displayMode INTEGER,"
                    + "launchCount INTEGER NOT NULL DEFAULT 1,"
                    + "sortMode INTEGER,"
                    + "itemFlags INTEGER NOT NULL DEFAULT 0"
                    + ");");
            
            if (mAppWidgetHost != null) {
                mAppWidgetHost.deleteHost();
                sendAppWidgetResetNotify();
            }
            
            // 读取XML中的预设配置
            loadFavorites(db);
            
            // 创建WORKSPACE中的Screen表
            createScreensTable(db);
        }

为了后续读取的桌面布局及手机中的APP、WIDGET摆放位置,首先需要建立一个SQL数据表来保存这些数据,在Provider中创建一个Favorites表。其数据结构如上所示。

    // 创建 screens 数据库表
    db.execSQL("DROP TABLE IF EXISTS screens");
    db.execSQL("CREATE TABLE screens("
        + "_id INTEGER PRIMARY KEY,"
        + "title TEXT,"
        + "screenOrder INTEGER NOT NULL DEFAULT -1);");


PS: 由于以前没有系统完整的写过博客,使用起来比较生疏,第一章节博客布局和格式问题花了点时间在弄好。觉得我分析的不好或者进度太慢的同学请到我的Github下载代码自己研究研究。地址为: http://github.com/mimijava/MKHome/

我的微博: http://weibo.com/ison7 欢迎关注!


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值