Android Launcher3分析 |
您所在的位置:网站首页 › launcher系统 › Android Launcher3分析 |
Android Launcher3分析——开篇
简介
Launcher就是一个Activity,Launcher的源码中也是继承的Activity。直观体现就是手机的桌面,当我们打开手机的时候,手机的桌面就是Launcher,一个Activity,只是这个Activity做的事情比较多: View方面,可以左右滑动,可以响应长按操作;逻辑方面,可以承载手机中所有应用的快捷方式,是其他程序的入口;总的来说,Launcher就是一个包含了许多自定义控件的复杂Activity。 整体上看Launcher3Android四大组件一应俱全,可见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 如何定义一个LauncherAndroid定义一个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做详细分析。 |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |