Android Launcher3分析

您所在的位置:网站首页 launcher系统 Android Launcher3分析

Android Launcher3分析

2024-07-16 18:23:12| 来源: 网络整理| 查看: 265

Android Launcher3分析——开篇 简介

Launcher就是一个Activity,Launcher的源码中也是继承的Activity。直观体现就是手机的桌面,当我们打开手机的时候,手机的桌面就是Launcher,一个Activity,只是这个Activity做的事情比较多:

View方面,可以左右滑动,可以响应长按操作;逻辑方面,可以承载手机中所有应用的快捷方式,是其他程序的入口;

总的来说,Launcher就是一个包含了许多自定义控件的复杂Activity。

整体上看Launcher3

Android四大组件一应俱全,可见Launcher3是一个综合性较强的项目

Activity(6个) com.android.launcher3.Launcher(主要的Activity)com.android.launcher3.ToggleWeightWatchercom.android.launcher3.WallpaperPickerActivitycom.android.launcher3.WallpaperCropActivitycom.android.launcher3.SettingsActivitycom.android.launcher3.MemoryDumpActivity Service(1个) com.android.launcher3.MemoryTracker BroadcastReceiver(4个) com.android.launcher3.WallpaperChangedReceivercom.android.launcher3.InstallShortcutReceivercom.android.launcher3.AppWidgetsRestoredReceivercom.android.launcher3.StartupReceiver ContentProvider(1个) com.android.launcher3.LauncherProvider 如何定义一个Launcher

Android定义一个Launcher很简单,要定义成Launcher的Activity代码如下:

注意,上面intent-filter的具体含义:

android.intent.action.MAIN决定应用程序最先启动的Activity;android.intent.category.HOME决定设备启动后第一个启动的Activity(通常要更改framework层的设置才能使之生效,因为国内系统定制化严重,各家手机厂商的ROM都提供了自己的Launcher)android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里;android.intent.category.DEFAULT,决定可以接受隐式intent的Activity在没有传递intent-filter时是否能匹配成功,添加了该项的能匹配成功,反之匹配失败(当然,如果Activity时应用最先启动的Activity就不需要这个了)android.intent.category.MONKEY,决定Activity是否能被monkey或其他自动化测试工具进行访问测试

先看Launcher对应的布局文件launcher.xml,如下:

launcher.xml

根布局层次结构:

LauncherRootView extends InsettableFrameLayout |---DragLayer |---FocusIndicatorView |---Workspace |---hotset.xml(层级如下): |---Hotseat extends FramLayout |---CellLayout extends ViewGroup |---search_drop_target_bar.xml |---SearchDropTargetBar extends FrameLayout |---LinearLayout(orientation:horizontal) |---FrameLayout |---DeleteDropTarget extends ButtonDropTarget(删除应用快捷方式) FrameLayout |---InfoDropTarget extends ButtonDropTarget(查看应用信息) FrameLayout |---UninstallDropTarget extends ButtonDropTarget(卸载应用) |---overview_panel.xml(长按屏幕显示,默认隐藏) |---LinearLayout |---TextView(壁纸按钮):点击可设置壁纸 |---TextView(小部件按钮):点击可添加小部件(widget) |---TextView(设置按钮):点击进入设置 |---widgets_view.xml(显示小部件的视图) |---WidgetsContainerView extends BaseContainerView extends LinearLayout |---FrameLayout |---FrameLayout |---WidgetsRecyclerView extends BaseRecyclerView extends RecyclerView |---all_apps.xml(显示所有App的视图,里面的子控件都是通过AppAppsContainerView的实例获取的) |---AllAppsContainerView extends BaseContainerView extends LinearLayout |---FrameLayout(所有App时图顶部的搜索框) |---FrameLayout |---FrameLayout |---all_apps_container.xml(承载所有App的视图(集列出所有安装的app的容器) |---AllAppsRecyclerViewContainerView |---AllAppsRecyclerView |---ViewStub Launcher中核心类的简单说明

具体说明请点击相应连接

Launcher:主界面Activity,最核心且唯一的Activity;LauncherAppState:单例对象,主要有如下作用: 初始化InvariantDeviceProfile、IconCache、WidgetPreviewLoader、LauncherModel等;注册广播,用来处理本地配置变化、搜索数据库(global search provider)变化、应用的安装与卸载等; InvariantDeviceProfile:一些不变的设备相关参数管理类,包含横竖屏的两种DeviceProfile; IconCache:应用程序图标缓存类,里面用数据库存储了应用的icon及title等的缓存信息;WidgetPreviewLoader:加载Widget信息数据库,里面用数据库存储了Widget的信息LauncherModel:在内存中保存Launcher的状态,提供读写数据库的API,其内部类LoaderTask用来加载Launcher的内容(包括workspace icons、widgets和all apps icons)LauncherAppsCompat:兼容抽象基类,用来获取已安装的App列表;UserManagerCompat:兼容抽象基类,用来处理不通版本下的用户管理;DragController:拖拽事件控制类,拖拽事件的处理逻辑在这里实现LauncherStateTransitionAnimation:Launcher的动画导演,负责安排不同状态切换之间的动画处理AppWidgetManagerCompat:兼容抽象基类,负责处理不通版本下应用和Widget管理LauncherAppWidgetHost:继承子AppWidgetHost,顾名思义,AppWidgetHost是桌面app、widget等的宿主,之所以继承是为了LauncherAppWidgetHostView能更好的处理长按事件;FocusIndicatorView:一个实现了View.OnFocusChangeListener的View(具体作用上不清楚)DragLayer:一个用来协调子View拖拽事件的ViewGroup,实际上事件的分发拦截等是在DragController,因为DragLayer持有DragController的实例,并调用了setup方法初始化了它;Workspace:一个包含了壁纸和有限数量的页面的较大空间 Launcher主流程 先分析声明周期函数 onCreate()

主要流程都在onCreate中,如下(采用代码中添加注释的方法说明,省略次要代码):

@Override protected void onCreate(Bundle savedInstanceState) { //省略掉严格模式相关代码... if (mLauncherCallbacks != null) { mLauncherCallbacks.preOnCreate(); } super.onCreate(savedInstanceState); // 各种变量初始化 LauncherAppState.setApplicationContext(getApplicationContext()); // 初始化LauncherAppState对象 LauncherAppState app = LauncherAppState.getInstance(); // 根据配置的orientation来初始化DeviceProfile对象 mDeviceProfile = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? app.getInvariantDeviceProfile().landscapeProfile : app.getInvariantDeviceProfile().portraitProfile; // 初始化SharedPreference对象 mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); mIsSafeModeEnabled = getPackageManager().isSafeMode(); mModel = app.setLauncher(this); mIconCache = app.getIconCache(); mDragController = new DragController(this); mInflater = getLayoutInflater(); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this); mStats = new Stats(this); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); mAppWidgetHost.startListening(); // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here, // this also ensures that any synchronous binding below doesn't re-trigger another // LauncherModel load. mPaused = false; if (PROFILE_STARTUP) { android.os.Debug.startMethodTracing( Environment.getExternalStorageDirectory() + "/launcher"); } // 设置布局(注意,此时的布局是不包含布局参数的) setContentView(R.layout.launcher); // 初始化View,进行各种View的初始化,事件绑定 setupViews(); // 为布局中的View添加上布局参数 mDeviceProfile.layout(this); lockAllApps(); // 恢复保存的状态 mSavedState = savedInstanceState; restoreState(mSavedState); if (PROFILE_STARTUP) { android.os.Debug.stopMethodTracing(); } // 数据加载核心部分,主要有LauncherModel的内部类LoaderTask来完成 if (!mRestoring) { if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) { // If the user leaves launcher, then we should just load items asynchronously when // they return. mModel.startLoader(PagedView.INVALID_RESTORE_PAGE); } else { // 只有当launcher处于前台,且用户旋转屏幕活着触发方向配置上的改变时我们才同步加载数据 mModel.startLoader(mWorkspace.getRestorePage()); } } // For handling default keys mDefaultKeySsb = new SpannableStringBuilder(); Selection.setSelection(mDefaultKeySsb, 0); IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); registerReceiver(mCloseSystemDialogsReceiver, filter); mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext()); // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false); } // On large interfaces, or on devices that a user has specifically enabled screen rotation, // we want the screen to auto-rotate based on the current orientation setOrientation(); if (mLauncherCallbacks != null) { mLauncherCallbacks.onCreate(savedInstanceState); if (mLauncherCallbacks.hasLauncherOverlay()) { ViewStub stub = (ViewStub) findViewById(R.id.launcher_overlay_stub); mLauncherOverlayContainer = (InsettableFrameLayout) stub.inflate(); mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView( mLauncherOverlayContainer, mLauncherOverlayCallbacks); mWorkspace.setLauncherOverlay(mLauncherOverlay); } } // 是否显示欢迎说明 if (shouldShowIntroScreen()) { showIntroScreen(); } else { showFirstRunActivity(); showFirstRunClings(); } }

可以看到,在设置布局之后,进行了View的初始化、View的事件绑定等,然后根据DeviceProfile(设备描述类,定义了Launcher在不同设备、不公状态下的以下常量等)的layout方法为初始化的View添加上布局参数。参数设置完成后,就进入了数据加载阶段,数据加载是通过LauncherModel的内部类LoaderTask来完成的(根据当前的配置,来选择时同步加载数据还是异步加载数据)。接下来就是控制Launcher Intro Screen的显示与否了,显示的话,就显示Intro Screen,不显示就进入else部分,显示Launcher Clings(其实就是首次运行Launcher时的一些关于Launcher用途的说明)

Launcher 代码中关于mLauncherCallbacks部分,由于mLauncherCallbacks的赋值操作必须调用setLauncherCallbacks来完成,但该函数只在LauncherExtension中才调用,所以如果要对Launcher做扩展,需要了解这部分代码,否则,可以忽略。

onResume()

onResume()中主要进行的是视图显示状态的恢复、依次执行Runnable任务(包括BindAllApplicationRunnable、BindPackagesUpdatesRunnable以及UpdateOrientationRunnable)、恢复App或App Shortcut的状态,必要的时候还会重新生成workspace上的Widget和QSB等。

Launcher中有一个waitUntilResume函数,字面意思“直到onResume执行才。。。。”,先看看其具体代码:

@Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { if (mPaused) { if (LOGD) Log.d(TAG, "Deferring update until onResume"); if (deletePreviousRunnables) { while (mBindOnResumeCallbacks.remove(run)) { } } mBindOnResumeCallbacks.add(run); return true; } else { return false; } }

还有一个重载方法:

private boolean waitUntilResume(Runnable run) { return waitUntilResume(run, false); }

在如下几个地方调用了:

Launcher(直接调用的地方) bindAllApplications(final ArrayList apps)bindAllPackages(final WidgetsModel model)onSettingsChanged(String settings, boolean value)Launcher(间接调用的地方,调用的是重载的方法) bindAppsAddedbindAppsUpdatedbindAppWidgetbindComponentsRemovedbindFoldersbindItemsbindRestoreItemsChangebindShortcutsChangedbindWidgetsRestoredfinishBindingItems

可见,与绑定有关的runnable都是在onResume的时候执行的,那么在这些runnable到底都做了什么,有什么功能,如何实现这些功能的呢?这里先提如下几个问题,我们带着问题读代码,马上就能得到答案:

在这些runnable执行之前,又做了什么了?我们都要绑定写什么对象?我们要绑定的这些对象怎么得到的?我们要把这些对象绑定到哪去?

现在依次解答上面的个问题,其实在onResume之前就已经被LauncherModel(数据处理的核心类)安排好了,现在来答上面的问题:

在这些runnable执行之前,做了些什么?

onCreate里面调用了Launcher的startLoader方法,开方法会开启其内部类LoaderTask的run方法来进行如下操作:

loadAndBindWorkspace,即加载并绑定workspace; loadWrokspacebindWorkspaceloadAndBindAllApps,即加载并绑定All apps;

我们都要绑定写什么对象?

想想Launcher上都有什么,显然App、AppShortcut、widget、Folder等都可能有,所以要绑定的当然是这些对象。

我们要绑定的这些对象怎么得到的?

通过LauncherModel的内部类LoaderTask来得到这些对象

我们要把这些对象绑定到哪去?

该放哪儿去放哪儿去。由于Launcher中的对象(App、AppShortcut、Widget、Folder等)无论是在Desktop(即桌面)或Hotseat(即底部的图标栏)都有相应的坐标点(cellX,cellY)及占地面积(spanX、spanY),我们只要将其按对应坐标对号入座即可。当然特需情况(如图标遮挡什么的)需要特殊处理。

其实,2、3、4里面要做的任务都是在1里面完成的,可见数据处理核心还是LauncherModel里面。接下来的文章会对LauncherModel做详细分析。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭