// Lazy-initialize the dynamic grid
DeviceProfile grid = app.initDynamicGrid(this);
// the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
Context.MODE_PRIVATE);// 初始化SharedPreference,文件名为com.android.launcher3.prefs
mIsSafeModeEnabled = getPackageManager().isSafeMode();// 是否是安全模式,关机启动Recovery Mode可以选择打开安全模式
mModel = app.setLauncher(this);// 获取LauncherModel对象,在LauncherAppState已经初始化
mIconCache = app.getIconCache();// 获取IconCache对象,在LauncherAppState已经初始化
mIconCache.flushInvalidIcons(grid);// 清除尺寸不符的icon缓存对象
mDragController = new DragController(this);// 初始化DragController对象,DragController用来处理拖拽操作
mInflater = getLayoutInflater();// 获取LayoutInflater
mStats = new Stats(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);// 获取AppWidgetManager实例
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);// LauncherAppWidgetHost,桌面插件宿主
mAppWidgetHost.startListening();// 开启LauncherAppWidgetHost的监听,以便发生变化时做出响应
// 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;
还是一些初始化的工作,看注释就可以了。
checkForLocaleChange();// 检查Locale的变化
// 检查Locale是否发生变化,获取系统当前的Local和Launcher中保存的相比较,如果Locale、mcc、mnc中有一个发生了变化就认为Locale变化了
private void checkForLocaleChange() {
if (sLocaleConfiguration == null) {// 启动Launcher时sLocaleConfiguration肯定为null,先初始化,然后读取保存的值,赋值给sLocaleConfiguration,再回调checkForLocaleChange
new AsyncTask() {
@Override
protected LocaleConfiguration doInBackground(Void... unused) {
LocaleConfiguration localeConfiguration = new LocaleConfiguration();
readConfiguration(Launcher.this, localeConfiguration);
return localeConfiguration;
}
@Override
protected void onPostExecute(LocaleConfiguration result) {
sLocaleConfiguration = result;
checkForLocaleChange(); // recursive, but now with a locale configuration
}
}.execute();
return;
}
final Configuration configuration = getResources().getConfiguration();// 读取系统当前的Configuration,里面保存了Locale、mcc、mnc
final String previousLocale = sLocaleConfiguration.locale;
final String locale = configuration.locale.toString();
final int previousMcc = sLocaleConfiguration.mcc;
final int mcc = configuration.mcc;
final int previousMnc = sLocaleConfiguration.mnc;
final int mnc = configuration.mnc;
boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;// 比较
if (localeChanged) {// 如果发生了变化,将新的值写入到launcher.preferences文件中
sLocaleConfiguration.locale = locale;
sLocaleConfiguration.mcc = mcc;
sLocaleConfiguration.mnc = mnc;
mIconCache.flush();
final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
new AsyncTask() {
public Void doInBackground(Void ... args) {
writeConfiguration(Launcher.this, localeConfiguration);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
}
private static class LocaleConfiguration {
public String locale;// 语言环境,如zh_CN
public int mcc = -1;// 移动国家码
public int mnc = -1;// 移动网络码
}
// 调运startLoader方法加载app
if (!mRestoring) {// 判断是否正在恢复,如果第一次启动mSavedState为null,restoreState直接返回,mRestoring为false,然后执行大括号代码
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(true, 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);
// On large interfaces, we want the screen to auto-rotate based on the current orientation
// 对大屏幕,希望Launcher可以横竖屏切换
unlockScreenOrientation(true);
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();
}
public void startLoader(boolean isLaunching, int synchronousBindPage) {
startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
}
public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
synchronized (mLock) {// 将该代码块锁住,同时只能有一个线程执行它
if (DEBUG_LOADERS) {
Log.d(TAG, "startLoader isLaunching=" + isLaunching);
}
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
// 清除延迟执行的绑定app的线程
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {// Callbacks在Launcher中实现
// If there is already one running, tell it to stop.
// also, don't downgrade isLaunching if we're already running
isLaunching = isLaunching || stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mAllAppsLoaded && mWorkspaceLoaded) {// launcher不在前台运行 && 所有app已经加载 && workspace已经加载
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}
执行到mLoaderTask执行块,直接看到LoaderTask的run方法,
public void run() {
boolean isUpgrade = false;
synchronized (mLock) {
mIsLoaderTaskRunning = true;
}
// Optimize for end-user experience: if the Launcher is up and // running with the
// All Apps interface in the foreground, load All Apps first. Otherwise, load the
// workspace first (default).
keep_running: {
// Elevate priority when Home launches for the first time to avoid
// starving at boot time. Staring at a blank home is not cool.
synchronized (mLock) {
if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
(mIsLaunching ? "DEFAULT" : "BACKGROUND"));
android.os.Process.setThreadPriority(mIsLaunching
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
}
// 分两步执行:1.loading workspace 2.loading workspace
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
isUpgrade = loadAndBindWorkspace();
if (mStopped) {
break keep_running;
}
// Whew! Hard work done. Slow us down, and wait until the UI thread has
// settled down.
synchronized (mLock) {
if (mIsLaunching) {
if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
waitForIdle();// loading all apps之前等待
// second step
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
// Restore the default thread priority after we are done loading items
synchronized (mLock) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
}
// Update the saved icons if necessary
if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
synchronized (sBgLock) {
for (Object key : sBgDbIconCache.keySet()) {
updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
}
sBgDbIconCache.clear();
}
if (LauncherAppState.isDisableAllApps()) {
// Ensure that all the applications that are in the system are
// represented on the home screen.
if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
verifyApplications();
}
}
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext = null;
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == this) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
}
}
分两大步执行:
第一步:加载和绑定workspace--loadAndBindWorkspace
/** Returns whether this is an upgrade path */
private boolean loadAndBindWorkspace() {
mIsLoadingAndBindingWorkspace = true;
// Load the workspace
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
}
boolean isUpgradePath = false;
if (!mWorkspaceLoaded) {
isUpgradePath = loadWorkspace();
synchronized (LoaderTask.this) {
if (mStopped) {
return isUpgradePath;
}
mWorkspaceLoaded = true;
}
}
// Bind the workspace
bindWorkspace(-1, isUpgradePath);
return isUpgradePath;
}
// Save a copy of all the bg-thread collections
// 不直接操作全局变量,将其赋值给局部变量
ArrayList workspaceItems = new ArrayList();
ArrayList appWidgets = new ArrayList();
HashMap folders = new HashMap();
HashMap itemsIdMap = new HashMap();
ArrayList orderedScreenIds = new ArrayList();
synchronized (sBgLock) {
workspaceItems.addAll(sBgWorkspaceItems);
appWidgets.addAll(sBgAppWidgets);
folders.putAll(sBgFolders);
itemsIdMap.putAll(sBgItemsIdMap);
orderedScreenIds.addAll(sBgWorkspaceScreens);
}
final boolean isLoadingSynchronously = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
int currScreen = isLoadingSynchronously ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
if (currScreen >= orderedScreenIds.size()) {
// There may be no workspace screens (just hotseat items and an empty page).
currScreen = PagedView.INVALID_RESTORE_PAGE;
}
final int currentScreen = currScreen;// 当前screen
final long currentScreenId = currentScreen < 0 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);// 当前screen id
// Load all the items that are on the current page first (and in the process, unbind
// all the existing workspace items before we call startBinding() below.
unbindWorkspaceItemsOnMainThread();// 先解除绑定
2)根据item中的screenID将items分成当前screen和其他screen,并进行排序
// Separate the items that are on the current screen, and all the other remaining items
ArrayList currentWorkspaceItems = new ArrayList();// 存放当前workspace上的items
ArrayList otherWorkspaceItems = new ArrayList();// 存放除当前workspace之外的items
ArrayList currentAppWidgets = new ArrayList();// 存放当前workspace上的appwidgets
ArrayList otherAppWidgets = new ArrayList();// 存放除当前workspace之外的appwidgets
HashMap currentFolders = new HashMap();// 存放当前workspace上的folder
HashMap otherFolders = new HashMap();// 存放除当前workspace之外的folder
filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems);// 过滤items,区分当前screen和其他screen上的items
filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets, otherAppWidgets);// 同上
filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders, otherFolders);// 同上
sortWorkspaceItemsSpatially(currentWorkspaceItems);// 对workspace上的items进行排序,按照从上到下和从左到右的顺序
sortWorkspaceItemsSpatially(otherWorkspaceItems);// 同上
// Tell the workspace that we're about to start binding items
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.startBinding();
}
}
};
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
/**
* Refreshes the shortcuts shown on the workspace.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
setWorkspaceLoading(true);
// If we're starting binding all over again, clear any bind calls we'd postponed in
// the past (see waitUntilResume) -- we don't need them since we're starting binding
// from scratch again
mBindOnResumeCallbacks.clear();
// Clear the workspace because it's going to be rebound
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
mWidgetsToAdvance.clear();
if (mHotseat != null) {
mHotseat.resetLayout();
}
}
@Override
public void bindScreens(ArrayList orderedScreenIds) {
bindAddScreens(orderedScreenIds);
// If there are no screens, we need to have an empty screen
// 如果没有需要添加screen,那我们就添加一个空白的screen
if (orderedScreenIds.size() == 0) {
mWorkspace.addExtraEmptyScreen();
}
// Create the custom content page (this call updates mDefaultScreen which calls
// setCurrentPage() so ensure that all pages are added before calling this).
if (hasCustomContentToLeft()) {
mWorkspace.createCustomContentContainer();
populateCustomContentContainer();
}
}
@Override
public void bindAddScreens(ArrayList orderedScreenIds) {
// Log to disk
Launcher.addDumpLog(TAG, "11683562 - bindAddScreens()", true);
Launcher.addDumpLog(TAG, "11683562 - orderedScreenIds: " +
TextUtils.join(", ", orderedScreenIds), true);
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
}
}
public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
// Find the index to insert this view into. If the empty screen exists, then
// insert it before that.
int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (insertIndex < 0) {
insertIndex = mScreenOrder.size();
}
return insertNewWorkspaceScreen(screenId, insertIndex);
}
// 插入一个新的workspace screen
public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
// Log to disk
Launcher.addDumpLog(TAG, "11683562 - insertNewWorkspaceScreen(): " + screenId + " at index: " + insertIndex, true);
if (mWorkspaceScreens.containsKey(screenId)) {
throw new RuntimeException("Screen id " + screenId + " already exists!");
}
CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
newScreen.setSoundEffectsEnabled(false);
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(insertIndex, screenId);
addView(newScreen, insertIndex);
return screenId;
}
其实就是创建一个CellLayout,然后添加到Workspace中。
5)Workspace绑定完成之后,就是将items、widgets和folders放到上面去
// Load items on the current page
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, null);
if (isLoadingSynchronously) {
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
callbacks.onPageBoundSynchronously(currentScreen);
}
}
};
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
// Load all the remaining pages (if we are loading synchronously, we want to defer this
// work until after the first render)
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
(isLoadingSynchronously ? mDeferredBindRunnables : null));
// Bind the workspace items
int N = workspaceItems.size();
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);// 批量绑定,批量大小为ITEMS_CHUNK,如果一共少于ITEMS_CHUNK,那就一次全部绑定
final Runnable r = new Runnable() {
@Override
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindItems(workspaceItems, start, start+chunkSize,
false);
}
}
};
if (postOnMainThread) {
synchronized (deferredBindRunnables) {
deferredBindRunnables.add(r);
}
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
}
回调到Launcher的bindItems方法,
/**
* Bind the items start-end from the list.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindItems(final ArrayList shortcuts, final int start, final int end,
final boolean forceAnimateIcons) {
Runnable r = new Runnable() {
public void run() {
bindItems(shortcuts, start, end, forceAnimateIcons);
}
};
if (waitUntilResume(r)) {// 当Launcher处于pause状态时,不进行绑定,待resume时再执行
return;
}
// Get the list of added shortcuts and intersect them with the set of shortcuts here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection bounceAnims = new ArrayList();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
long newShortcutsScreenId = -1;
for (int i = start; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
// Short circuit if we are loading dock items for a configuration which has no dock
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
mHotseat == null) {// 如果绑定的item要放置在Hotseat,但是又没有配置Hotseat,直接跳过
continue;
}
// 根据item的类型,区分绑定
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:// 快捷图标
ShortcutInfo info = (ShortcutInfo) item;
View shortcut = createShortcut(info);// 创建快捷图标视图
/*
* TODO: FIX collision case
*/
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {// 判断item将要放置的位置是否被占据了
View v = cl.getChildAt(item.cellX, item.cellY);
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item + ". Collides with " + tag;
if (LauncherAppState.isDogfoodBuild()) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
}
}
}
workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
item.cellY, 1, 1);// 将快捷图标添加到Workspace screen的指定位置,占据一格
if (animateIcons) {
// Animate all the applications up now
shortcut.setAlpha(0f);
shortcut.setScaleX(0f);
shortcut.setScaleY(0f);
bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
newShortcutsScreenId = item.screenId;
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:// 文件夹
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);// 创建文件夹图标
workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
item.cellY, 1, 1);
break;
default:
throw new RuntimeException("Invalid Item Type");
}
}
if (animateIcons) {
// Animate to the correct page
if (newShortcutsScreenId > -1) {
long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId);
final Runnable startBounceAnimRunnable = new Runnable() {
public void run() {
anim.playTogether(bounceAnims);
anim.start();
}
};
if (newShortcutsScreenId != currentScreenId) {
// We post the animation slightly delayed to prevent slowdowns
// when we are loading right after we return to launcher.
mWorkspace.postDelayed(new Runnable() {
public void run() {
if (mWorkspace != null) {
mWorkspace.snapToPage(newScreenIndex);
mWorkspace.postDelayed(startBounceAnimRunnable,
NEW_APPS_ANIMATION_DELAY);
}
}
}, NEW_APPS_PAGE_MOVE_DELAY);
} else {
mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
}
}
}
workspace.requestLayout();
}
// At bind time, we use the rank (screenId) to compute x and y for hotseat items.
// See implementation for parameter definition.
void addInScreenFromBind(View child, long container, long screenId, int x, int y,
int spanX, int spanY) {
addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
}
/**
* Adds the specified child in the specified screen. The position and dimension of
* the child are defined by x, y, spanX and spanY.
*
* @param child The child to add in one of the workspace's screens.
* @param screenId The screen in which to add the child.
* @param x The X position of the child in the screen's grid.
* @param y The Y position of the child in the screen's grid.
* @param spanX The number of cells spanned horizontally by the child.
* @param spanY The number of cells spanned vertically by the child.
* @param insert When true, the child is inserted at the beginning of the children list.
* @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
* the x and y position in which to place hotseat items. Otherwise
* we use the x and y position to compute the rank.
*/
void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
boolean insert, boolean computeXYFromRank) {
if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (getScreenWithId(screenId) == null) {
Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
// DEBUGGING - Print out the stack trace to see where we are adding from
new Throwable().printStackTrace();
return;
}
}
if (screenId == EXTRA_EMPTY_SCREEN_ID) {
// This should never happen
throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
}
final CellLayout layout;// 先要获取快捷图标的父视图
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
layout = mLauncher.getHotseat().getLayout();
child.setOnKeyListener(new HotseatIconKeyEventListener());
// Hide folder title in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(false);
}
if (computeXYFromRank) {
x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
} else {
screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
}
} else {
// Show folder title if not in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(true);
}
layout = getScreenWithId(screenId);
child.setOnKeyListener(new IconKeyEventListener());
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
CellLayout.LayoutParams lp;// 设置布局参数
if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
} else {
lp = (CellLayout.LayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}
if (spanX < 0 && spanY < 0) {
lp.isLockedToGrid = false;
}
// Get the canonical child id to uniquely represent this view in this screen
ItemInfo info = (ItemInfo) child.getTag();
int childId = mLauncher.getViewIdForItem(info);
boolean markCellsAsOccupied = !(child instanceof Folder);// 添加快捷图标的时候,是否需要标记所占据的位置,除了folder外,都需要标记
if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
// TODO: This branch occurs when the workspace is adding views
// outside of the defined grid
// maybe we should be deleting these items from the LauncherModel?
Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
}
if (!(child instanceof Folder)) {
child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(mLongClickListener);
}
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);// 添加拖拽对象
}
}
public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
boolean markCells) {
final LayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {// 如果是在Hotseat上的图标,将文字去除
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(!mIsHotseat);
}
child.setScaleX(getChildrenScale());
child.setScaleY(getChildrenScale());
// Generate an id for each view, this assumes we have at most 256x256 cells
// per workspace screen
if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {// 判断位置是否合法
// If the horizontal or vertical span is set to -1, it is taken to
// mean that it spans the extent of the CellLayout
if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
child.setId(childId);
mShortcutsAndWidgets.addView(child, index, lp);// 最终放入指定位置
if (markCells) markCellsAsOccupiedForView(child);// 标记所占据的位置,除了folder外,都需要标记
return true;
}
return false;
}
最后设置触摸反馈和长安监听以及拖拽对象的添加。
b)加载folder
// Bind the folders
if (!folders.isEmpty()) {
final Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindFolders(folders);
}
}
};
if (postOnMainThread) {
synchronized (deferredBindRunnables) {
deferredBindRunnables.add(r);
}
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
}
也是回调到Launcher的bindFolders方法.
/**
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindFolders(final HashMap folders) {
Runnable r = new Runnable() {
public void run() {
bindFolders(folders);
}
};
if (waitUntilResume(r)) {
return;
}
sFolders.clear();
sFolders.putAll(folders);
}
// Bind the widgets, one at a time
N = appWidgets.size();
for (int i = 0; i < N; i++) {
final LauncherAppWidgetInfo widget = appWidgets.get(i);
final Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAppWidget(widget);
}
}
};
if (postOnMainThread) {
deferredBindRunnables.add(r);
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
}
回调到Launcher的bindAppWidget方法中去了,
/**
* Add the views for a widget to the workspace.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAppWidget(final LauncherAppWidgetInfo item) {
Runnable r = new Runnable() {
public void run() {
bindAppWidget(item);
}
};
if (waitUntilResume(r)) {
return;
}
final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
if (DEBUG_WIDGETS) {
Log.d(TAG, "bindAppWidget: " + item);
}
final Workspace workspace = mWorkspace;
AppWidgetProviderInfo appWidgetInfo;
if (!mIsSafeModeEnabled
&& ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
&& ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);// 获取AppWidgetProviderInfo
if (appWidgetInfo == null) {
if (DEBUG_WIDGETS) {
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ " belongs to component " + item.providerName
+ ", as the povider is null");
}
LauncherModel.deleteItemFromDatabase(this, item);// 如果系统中找不到该AppWidget,将其从数据库中删除
return;
}
// Note: This assumes that the id remap broadcast is received before this step.
// If that is not the case, the id remap will be ignored and user may see the
// click to setup view.
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);// 创建PendingAddWidgetInfo实例
pendingInfo.spanX = item.spanX;
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
Bundle options = AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);// 获取appwidget最大最小宽度、高度
int newWidgetId = mAppWidgetHost.allocateAppWidgetId();// 给AppWidget分配一个id
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId, appWidgetInfo, options);// 用AppWidgetManager来绑定AppWidget
// TODO consider showing a permission dialog when the widget is clicked.
if (!success) {// 如果绑定失败了,删除刚刚分配的id,并将其从数据库中移除,不在继续执行
mAppWidgetHost.deleteAppWidgetId(newWidgetId);
if (DEBUG_WIDGETS) {
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ " belongs to component " + item.providerName
+ ", as the launcher is unable to bing a new widget id");
}
LauncherModel.deleteItemFromDatabase(this, item);
return;
}
item.appWidgetId = newWidgetId;
// If the widget has a configure activity, it is still needs to set it up, otherwise
// the widget is ready to go.
item.restoreStatus = (appWidgetInfo.configure == null)
? LauncherAppWidgetInfo.RESTORE_COMPLETED
: LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
LauncherModel.updateItemInDatabase(this, item);// 更新AppWidget在数据库中的信息
}
if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
final int appWidgetId = item.appWidgetId;
appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
if (DEBUG_WIDGETS) {
Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
}
item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);// 创建AppWidget视图
} else {
appWidgetInfo = null;
PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
mIsSafeModeEnabled);
view.updateIcon(mIconCache);
item.hostView = view;
item.hostView.updateAppWidget(null);
item.hostView.setOnClickListener(this);
}
item.hostView.setTag(item);
item.onBindAppWidget(this);
workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
item.cellY, item.spanX, item.spanY, false);// 将AppWidget添加到Workspace的指定位置
addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
workspace.requestLayout();
if (DEBUG_WIDGETS) {
Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
+ (SystemClock.uptimeMillis()-start) + "ms");
}
}
其做法跟添加items类似的,就不在赘述了。
最后通知items的绑定完成,
// Tell the workspace that we're done binding items
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.finishBindingItems(isUpgradePath);
}
// If we're profiling, ensure this is the last thing in the queue.
if (DEBUG_LOADERS) {
Log.d(TAG, "bound workspace in "
+ (SystemClock.uptimeMillis()-t) + "ms");
}
mIsLoadingAndBindingWorkspace = false;
}
};
if (isLoadingSynchronously) {
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.add(r);
}
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
/**
* Callback saying that there aren't any more items to bind.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void finishBindingItems(final boolean upgradePath) {
Runnable r = new Runnable() {
public void run() {
finishBindingItems(upgradePath);
}
};
if (waitUntilResume(r)) {
return;
}
if (mSavedState != null) {
if (!mWorkspace.hasFocus()) {
mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
}
mSavedState = null;
}
mWorkspace.restoreInstanceStateForRemainingPages();
setWorkspaceLoading(false); // workspace已经加载结束了
sendLoadingCompleteBroadcastIfNecessary();// 第一次加载完成时会发送广播
// If we received the result of any pending adds while the loader was running (e.g. the
// widget configuration forced an orientation change), process them now.
if (sPendingAddItem != null) {
final long screenId = completeAdd(sPendingAddItem);
// TODO: this moves the user to the page where the pending item was added. Ideally,
// the screen would be guaranteed to exist after bind, and the page would be set through
// the workspace restore process.
mWorkspace.post(new Runnable() {
@Override
public void run() {
mWorkspace.snapToScreenId(screenId);// // 滑动指定的screenId的screen上
}
});
sPendingAddItem = null;
}
if (upgradePath) {
mWorkspace.getUniqueComponents(true, null);
mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
}
PackageInstallerCompat.getInstance(this).onFinishBind();
if (mLauncherCallbacks != null) {
mLauncherCallbacks.finishBindingItems(upgradePath);
}
}
private void loadAllApps() {
final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
return;
}
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List profiles = mUserManager.getUserProfiles();
// Clear the list of apps
mBgAllAppsList.clear();// 清除所有app列表
SharedPreferences prefs = mContext.getSharedPreferences(
LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
for (UserHandleCompat user : profiles) {
// Query for the set of apps
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
List apps = mLauncherApps.getActivityList(null, user);// 获取需要显示在Launcher上的activity列表
if (DEBUG_LOADERS) {
Log.d(TAG, "getActivityList took "
+ (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
}
// Fail if we don't have any apps
// TODO: Fix this. Only fail for the current user.
if (apps == null || apps.isEmpty()) {// 没有需要显示的,直接返回
return;
}
// Sort the applications by name
final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Collections.sort(apps, new LauncherModel.ShortcutNameComparator(mLabelCache));// 排序
if (DEBUG_LOADERS) {
Log.d(TAG, "sort took " + (SystemClock.uptimeMillis()-sortTime) + "ms");
}
// Create the ApplicationInfos
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfoCompat app = apps.get(i);
// This builds the icon bitmaps.
mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));// 创建应用图标对象,并添加到所有APP列表中
}
if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) {
// Add shortcuts for packages which were installed while launcher was dead.
String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX + mUserManager.getSerialNumberForUser(user);
Set packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET);
HashSet newPackageSet = new HashSet();
for (LauncherActivityInfoCompat info : apps) {
String packageName = info.getComponentName().getPackageName();
if (!packagesAdded.contains(packageName)
&& !newPackageSet.contains(packageName)) {
InstallShortcutReceiver.queueInstallShortcut(info, mContext);
}
newPackageSet.add(packageName);
}
prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit();
}
}
// Huh? Shouldn't this be inside the Runnable below?
final ArrayList added = mBgAllAppsList.added;// 获取自上次更新(notify()广播)后新增加的应用清单,如果是开机初次启动Launcher,那么added就是mBgAllAppsList
mBgAllAppsList.added = new ArrayList();// 将AllAppsList的added清空,不影响后续新增的app
// Post callback on main thread
mHandler.post(new Runnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(added);
if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
+ (SystemClock.uptimeMillis() - bindTime) + "ms");
}
} else {
Log.i(TAG, "not binding apps: no Launcher activity");
}
}
});
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
+ (SystemClock.uptimeMillis() - loadTime) + "ms");
}
}
public void dumpState() {
synchronized (sBgLock) {
Log.d(TAG, "mLoaderTask.mContext=" + mContext);
Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
}
}
}
1.获取需要显示到Launcher中的app列表,创建app图标
2.绑定app--bindAllApplications
/**
* Add the icons for all apps.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAllApplications(final ArrayList apps) {
if (LauncherAppState.isDisableAllApps()) {// 判断是否禁用所有app,就是所有应用都显示在一级目录
if (mIntentsOnWorkspaceFromUpgradePath != null) {
if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) {
getHotseat().addAllAppsFolder(mIconCache, apps,
mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace);
}
mIntentsOnWorkspaceFromUpgradePath = null;
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.onPackagesUpdated(
LauncherModel.getSortedWidgetsAndShortcuts(this));
}
} else {
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setApps(apps);
mAppsCustomizeContent.onPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(this));
}
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
/**
* We should call thise method whenever the core data changes (mApps, mWidgets) so that we can
* appropriately determine when to invalidate the PagedView page data. In cases where the data
* has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the
* next onMeasure() pass, which will trigger an invalidatePageData() itself.
*/
private void invalidateOnDataChange() {
if (!isDataReady()) {
// The next layout pass will trigger data-ready if both widgets and apps are set, so
// request a layout to trigger the page data when ready.
requestLayout();
} else {
cancelAllTasks();
invalidatePageData();
}
}
protected void invalidatePageData(int currentPage, boolean immediateAndOnly) {
if (!mIsDataReady) {
return;
}
if (mContentIsRefreshable) {
// Force all scrolling-related behavior to end
forceFinishScroller();
// Update all the pages
syncPages();
// We must force a measure after we've loaded the pages to update the content width and
// to determine the full scroll width
measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
// Set a new page as the current page if necessary
// 设置当前显示页
if (currentPage > -1) {
setCurrentPage(Math.min(getPageCount() - 1, currentPage));
}
// Mark each of the pages as dirty
final int count = getChildCount();
mDirtyPageContent.clear();
for (int i = 0; i < count; ++i) {
mDirtyPageContent.add(true);
}
// Load any pages that are necessary for the current window of views
loadAssociatedPages(mCurrentPage, immediateAndOnly);
requestLayout();
}
if (isPageMoving()) {
// If the page is moving, then snap it to the final position to ensure we don't get
// stuck between pages
snapToDestination();
}
}
@Override
public void syncPages() {
disablePagedViewAnimations();
// 移除所有视图和任务
removeAllViews();
cancelAllTasks();
Context context = getContext();
if (mContentType == ContentType.Applications) {
for (int i = 0; i < mNumAppsPages; ++i) {
AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(context);
setupPage(layout);
addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
}
} else if (mContentType == ContentType.Widgets) {
for (int j = 0; j < mNumWidgetPages; ++j) {
PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
mWidgetCountY);
setupPage(layout);
addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
}
} else {
throw new RuntimeException("Invalid ContentType");
}
enablePagedViewAnimations();
}
app和widget来分别添加,我们这里只需了解如何添加app页面的,widget完全类似。
a)生成
AppsCustomizeCellLayout对象
b)
setupPage,设置page
// 设置page的表格、背景色
private void setupPage(AppsCustomizeCellLayout layout) {
layout.setGridSize(mCellCountX, mCellCountY);// 设置页面表格数
// Note: We force a measure here to get around the fact that when we do layout calculations
// immediately after syncing, we don't have a proper width. That said, we already know the
// expected page width, so we can actually optimize by hiding all the TextView-based
// children that are expensive to measure, and let that happen naturally later.
setVisibilityOnChildren(layout, View.GONE);
int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
layout.measure(widthSpec, heightSpec);
// 设置page背景色
Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);
if (bg != null) {
bg.setAlpha(mPageBackgroundsVisible ? 255: 0);
layout.setBackground(bg);
}
setVisibilityOnChildren(layout, View.VISIBLE);
}
c)addView,将CellLayout添加到page中
2)重新测量,设置当前页等
// We must force a measure after we've loaded the pages to update the content width and
// to determine the full scroll width
measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
// Set a new page as the current page if necessary
// 设置当前显示页
if (currentPage > -1) {
setCurrentPage(Math.min(getPageCount() - 1, currentPage));
}
3)loadAssociatedPages
protected void loadAssociatedPages(int page, boolean immediateAndOnly) {
if (mContentIsRefreshable) {
final int count = getChildCount();
if (page < count) {
int lowerPageBound = getAssociatedLowerPageBound(page);
int upperPageBound = getAssociatedUpperPageBound(page);
if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/"
+ upperPageBound);
// First, clear any pages that should no longer be loaded
for (int i = 0; i < count; ++i) {
Page layout = (Page) getPageAt(i);
if ((i < lowerPageBound) || (i > upperPageBound)) {
if (layout.getPageChildCount() > 0) {
layout.removeAllViewsOnPage();
}
mDirtyPageContent.set(i, true);
}
}
// Next, load any new pages
for (int i = 0; i < count; ++i) {
if ((i != page) && immediateAndOnly) {
continue;
}
if (lowerPageBound <= i && i <= upperPageBound) {
if (mDirtyPageContent.get(i)) {
syncPageItems(i, (i == page) && immediateAndOnly);
mDirtyPageContent.set(i, false);
}
}
}
}
}
}
// 添加items到page上
public void syncAppsPageItems(int page, boolean immediate) {
// ensure that we have the right number of items on the pages
final boolean isRtl = isLayoutRtl();// 是否从右向左排列,一般都是从左向右
int numCells = mCellCountX * mCellCountY;// 每页的格数,及可加载的app数量
int startIndex = page * numCells;// 开始位置
int endIndex = Math.min(startIndex + numCells, mApps.size());// 结束位置,如果不满页,就是app数量的总数
AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page);// 根据page号获取AppsCustomizeCellLayout
layout.removeAllViewsOnPage();
ArrayList
继承 extends 多态
继承是面向对象最经常使用的特征之一:继承语法是通过继承发、基类的域和方法 //继承就是从现有的类中生成一个新的类,这个新类拥有现有类的所有extends是使用继承的关键字:
在A类中定义属性和方法;
class A{
//定义属性
int age;
//定义方法
public void go
hive DDL语法汇总
1、对表重命名
hive> ALTER TABLE table_name RENAME TO new_table_name;
2、修改表备注
hive> ALTER TABLE table_name SET TBLPROPERTIES ('comment' = new_comm