Launcher启动流程分析(一)

Launcher分析

当用户点击手机桌面上的shortIcon的时候,系统这个时候是通过Launcher这类启动app的,首先先介绍下什么是Launcher.通过查看源码知道,Launcher是继承Activity,因此Launcher简单理解他就是一个Activity,而这个Activity通常情况是不能被销毁的,相当于系统页面.我们进入onCreate函数看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener {

protected void onCreate(Bundle savedInstanceState) {
<!--省略-->
LauncherApplication app = ((LauncherApplication)getApplication());
mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(),
Context.MODE_PRIVATE);
//这里绑定Launcher到LauncherModel.Callbacks的接口中
mModel = app.setLauncher(this);
<!--省略-->

//加载了launcher的布局文件
setContentView(R.layout.launcher);
//设置布局文件的theme属性,主要是初始化了一些UI组件,如AppWidghts(小部件)、AppTabHost(导航栏)、AppAppsCustomizeContent(这个类就是桌面放置app icon的容器)
setupViews();
<!--省略-->
//这里加载桌面的app图标
if (!mRestoring) {
if (sPausedFromUserAction) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, -1);
} 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.getCurrentPage());
}
}
}
}

布局文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<!-- Full screen view projects under the status bar and contains the background -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"

android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/workspace_bg">

<com.android.launcher2.DragLayer
android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<!-- 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:paddingStart="@dimen/workspace_left_padding"
android:paddingEnd="@dimen/workspace_right_padding"
android:paddingTop="@dimen/workspace_top_padding"
android:paddingBottom="@dimen/workspace_bottom_padding"
launcher:defaultScreen="2"
launcher:cellCountX="@integer/cell_count_x"
launcher:cellCountY="@integer/cell_count_y"
launcher:pageSpacing="@dimen/workspace_page_spacing"
launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">

<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
android:id="@+id/qsb_divider"
layout="@layout/workspace_divider"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/qsb_bar_height"
android:layout_gravity="start" />

<include
android:id="@+id/dock_divider"
layout="@layout/workspace_divider"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/button_bar_height"
android:layout_gravity="end" />

<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" />

<include layout="@layout/hotseat"
android:id="@+id/hotseat"
android:layout_width="@dimen/button_bar_height_plus_padding"
android:layout_height="match_parent"
android:layout_gravity="end" />

<include
android:id="@+id/qsb_bar"
layout="@layout/qsb_bar" />

<!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
that it is still visible during the transition to AllApps and doesn't overlay on
top of that view. -->
<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.DrawableStateProxyView
android:id="@+id/voice_button_proxy"
android:layout_width="@dimen/qsb_bar_height"
android:layout_height="@dimen/app_icon_size"
android:layout_gravity="top|start"
android:layout_marginTop="64dp"
android:clickable="true"
android:onClick="onClickVoiceButton"
android:importantForAccessibility="no"
launcher:sourceViewId="@+id/voice_button" />

<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" />
</com.android.launcher2.DragLayer>
</FrameLayout>

桌面上显示的app icon都是放在apps_customize_pane.xml文件中,看下布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<com.android.launcher2.AppsCustomizeTabHost
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:background="#FF000000">
<LinearLayout
android:id="@+id/apps_customize_content"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<!-- The layout_width of the tab bar gets overriden to align the content
with the text in the tabs in AppsCustomizeTabHost. -->
<FrameLayout
android:id="@+id/tabs_container"
android:layout_width="wrap_content"
android:layout_height="@dimen/apps_customize_tab_bar_height"
android:layout_marginTop="@dimen/apps_customize_tab_bar_margin_top"
android:layout_gravity="center_horizontal">
<com.android.launcher2.FocusOnlyTabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="start"
android:background="@drawable/tab_unselected_holo"
android:tabStripEnabled="false"
android:divider="@null" />
<include
android:id="@+id/market_button"
layout="@layout/market_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end" />
</FrameLayout>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.launcher2.AppsCustomizePagedView
android:id="@+id/apps_customize_pane_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:maxAppCellCountX="@integer/apps_customize_maxCellCountX"
launcher:maxAppCellCountY="@integer/apps_customize_maxCellCountY"
launcher:pageLayoutWidthGap="@dimen/apps_customize_pageLayoutWidthGap"
launcher:pageLayoutHeightGap="@dimen/apps_customize_pageLayoutHeightGap"
launcher:pageLayoutPaddingTop="@dimen/apps_customize_pageLayoutPaddingTop"
launcher:pageLayoutPaddingBottom="@dimen/apps_customize_pageLayoutPaddingBottom"
launcher:pageLayoutPaddingLeft="@dimen/apps_customize_pageLayoutPaddingLeft"
launcher:pageLayoutPaddingRight="@dimen/apps_customize_pageLayoutPaddingRight"
launcher:widgetCellWidthGap="@dimen/apps_customize_widget_cell_width_gap"
launcher:widgetCellHeightGap="@dimen/apps_customize_widget_cell_height_gap"
launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"
launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"
launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"
launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"
launcher:maxGap="@dimen/workspace_max_gap" />
<FrameLayout
android:id="@+id/animation_buffer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF000000"
android:visibility="gone" />

<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" />
</FrameLayout>
</LinearLayout>

<include layout="@layout/all_apps_cling"
android:id="@+id/all_apps_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</com.android.launcher2.AppsCustomizeTabHost>

核心View是AppsCustomizePagedView,它是一个展示applications, widgets, and shortcuts的ViewGroup,后面我再做详细的分析。

解释下mModel,它是一个LauncherModel实例,LauncherModel里面有许多封装的对数据库的操作。包含几个线程,其中最主要的是ApplicationsLoader和DesktopItemsLoader。ApplicationsLoader在加载所有应用程序时使用,DesktopItemsLoader在加载workspace的时候使用。其他的函数就是对数据库的封装,比如在删除,替换,添加程序的时候做更新数据库和UI的工作.这里我们看下startLoader方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void startLoader(boolean isLaunching, int synchronousBindPage) {
synchronized (mLock) {
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
// If there is already one running, tell it to stop.
// also, don't downgrade isLaunching if we're already running
isLaunching = isLaunching || stopLoaderLocked();
//这里开启了一个异步线程load一个workspace的桌面配置文件
mLoaderTask = new LoaderTask(mApp, isLaunching);
if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}

LoaderTask实现了runnable接口,看下它的run方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private class LoaderTask implements Runnable {
public void run() {}
synchronized (mLock) {
mIsLoaderTaskRunning = true;
}
keep_running:
{
// First step. Load workspace first, this is necessary since adding of apps from
// managed profile in all apps is deferred until onResume. See http://b/17336902.
loadAndBindWorkspace();
if (mStopped) {
break keep_running;
}
// Second step. Load all apps.加载桌面的apps
loadAndBindAllApps();
}
// 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();
}
// 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;
}
}
}

代码中已经做了注释,经过处理程序会执行到loadAndBindAllApps,我们直接跳转到loadAndBindAllApps方法中去看下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void loadAndBindAllApps() {
//这里判断是否已经加载过app icon到桌面,如果没有就执行下面的代码
if (!mAllAppsLoaded) {
loadAllAppsByBatch();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
mAllAppsLoaded = true;
}
} else {
onlyBindAllApps();
}
}

接着再进入loadAllAppsByBatch方法看下,首先通过mUserManager对象获取当前设备上的所有用户信息(返回一个链表),然后遍历整个列表,取得一个UserHandler对象。

UserHandler代表一个用户的设备信息,如桌面上有什么文件,哪些app是属于当前用户,哪些文件属于当前用户,当前用户拥有什么权限,都可以通过UserHandler获取.

最后通过LauncherApps对象的getActivityList方法获取属于当前用户的app,也就是AndroidMainfest.xml文件中配置的IntentFilter标签,我们每个app都有这样一个启动程序入口activity,这里不做过多介绍了。

1
2
3
4
5
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  private void loadAllAppsByBatch() {
// Don't use these two variables in any of the callback runnables.
// Otherwise we hold a reference to them.
final Callbacks oldCallbacks = mCallbacks.get();
<!--省略-->
// 获取当前设备上所有的用户配置信息
final List<UserHandle> profiles = mUserManager.getUserProfiles();

mBgAllAppsList.clear();
final int profileCount = profiles.size();
for (int p = 0; p < profileCount; p++) {
UserHandle user = profiles.get(p);
List<LauncherActivityInfo> apps = null;
int N = Integer.MAX_VALUE;

int startIndex;
int i = 0;
int batchSize = -1;
while (i < N && !mStopped) {
if (i == 0) {
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
//这里返回所有IntenFilter标签带有LAUNCHER、MAIN的Activity
apps = mLauncherApps.getActivityList(null, user);
//重新排序
Collections.sort(apps, new LauncherModel.ShortcutNameComparator(mLabelCache));
}

startIndex = i;
for (int j=0; i<N && j<batchSize; j++) {
// This builds the icon bitmaps.生成桌面图标的位图,并存放到mBgAllAppsList中
mBgAllAppsList.add(new ApplicationInfo(apps.get(i), user,
mIconCache, mLabelCache));
i++;
}

//获取LauncherModel.Callback的回调,oldCallbacks是通过mCallbacks获取到的,mCallbacks是一个弱引用
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
final ArrayList<ApplicationInfo> added = mBgAllAppsList.added;
final boolean firstProfile = p == 0;
mBgAllAppsList.added = new ArrayList<ApplicationInfo>();
//这里使用handler post到主线程执行callBack回调,这里的callback其实就是前面说到的Launcher,Launcher本事实现了LauncherModel.Callback回调,这样load完成后就回到launcher中的回调接口中
mHandler.post(new Runnable() {
public void run() {
if (callbacks != null) {
if (firstProfile) {
callbacks.bindAllApplications(added);
} else {
callbacks.bindAppsAdded(added);
}
}
}
});
<!--省略-->
}

}
}

==注意:==这里说明下Launcher是怎么绑定LauncherModel.Callback接口的,我们回到前面Launcher.onCreate函数的开头,进入LauncherApplicationsetLauncher方法中.在LauncherApplicationonCreate方法中实例化了一个LauncherModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LauncherApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
......................................
mIconCache = new IconCache(this);
//实例化一个LauncherModel
mModel = new LauncherModel(this, mIconCache);
LauncherApps launcherApps = (LauncherApps)
getSystemService(Context.LAUNCHER_APPS_SERVICE);
launcherApps.registerCallback(mModel.getLauncherAppsCallback());
}

LauncherModel setLauncher(Launcher launcher) {
//这里通过LauncherModel的initialize方法注入了LauncherModel.Callback接口,这样就实现了接口绑定
mModel.initialize(launcher);
return mModel;
}
}

最后我们回到LauncherbindAllApplications回调函数中看下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Add the icons for all apps.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
Runnable setAllAppsRunnable = new Runnable() {
public void run() {
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setApps(apps);
}
}
};

// Remove the progress bar entirely; we could also make it GONE
// but better to remove it since we know it's not going to be used
View progressBar = mAppsCustomizeTabHost.
findViewById(R.id.apps_customize_progress_bar);
if (progressBar != null) {
((ViewGroup)progressBar.getParent()).removeView(progressBar);

// We just post the call to setApps so the user sees the progress bar
// disappear-- otherwise, it just looks like the progress bar froze
// which doesn't look great
mAppsCustomizeTabHost.post(setAllAppsRunnable);
} else {
// If we did not initialize the spinner in onCreate, then we can directly set the
// list of applications without waiting for any progress bars views to be hidden.
setAllAppsRunnable.run();
}
}

调用了mAppsCustomizeContent.setApps方法,前面我们提到了AppsCustomizePagedView,这里我们再分析下,先看下AppsCustomizePagedView源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
View.OnClickListener, View.OnKeyListener, DragSource,
PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
LauncherTransitionable {

public void setApps(ArrayList<ApplicationInfo> list) {
mApps = list;
Collections.sort(mApps, LauncherModel.getAppNameComparator());
updatePageCountsAndInvalidateData();
}

private void updatePageCountsAndInvalidateData() {
if (mInBulkBind) {
mNeedToUpdatePageCountsAndInvalidateData = true;
} else {
updatePageCounts();
invalidateOnDataChange();
mNeedToUpdatePageCountsAndInvalidateData = false;
}
}

/**
* 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();
}
}
}

首先经过updatePageCountsAndInvalidateData–>>invalidateOnDataChange–>>invalidateOnDataChange,最后会进入invalidatePageData方法,这里会调用父类invalidatePageData方法,我们继续往下看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
   protected void invalidatePageData(int currentPage, boolean immediateAndOnly) {
if (!mIsDataReady) {
return;
}

if (mContentIsRefreshable) {
// Force all scrolling-related behavior to end
mScroller.forceFinished(true);
mNextPage = INVALID_PAGE;

// 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
//关键代码,这里会加载桌面View
loadAssociatedPages(mCurrentPage, immediateAndOnly);
requestLayout();
}
}

//加载桌面程序icons等
protected void loadAssociatedPages(int page, boolean immediateAndOnly) {
if (mContentIsRefreshable) {
final int count = getChildCount();
if (page < count) {
int lowerPageBound = getAssociatedLowerPageBound(page);
int upperPageBound = getAssociatedUpperPageBound(page);
// First, clear any pages that should no longer be loaded
//先做一些清理工作,这里涉及到view重用机制,不再讲解了
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;
}
//找到当前页面对应的Page,然后调用syncPageItems,这个函数里面就是向桌面上添加views组件
if (lowerPageBound <= i && i <= upperPageBound) {
if (mDirtyPageContent.get(i)) {
syncPageItems(i, (i == page) && immediateAndOnly);
mDirtyPageContent.set(i, false);
}
}
}
}
}
}

Ok,上面的代码里面,我已经做了注释,讲的比较清楚了,我们直接跳到syncPageItems方法里面看下它做的操作,这个函数里面就是往桌面上添加view,就像我们平常往ViewGrounp里面addView一样,比较好理解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
   @Override
public void syncPageItems(int page, boolean immediate) {
if (page < mNumAppsPages) {
syncAppsPageItems(page, immediate);
} else {
syncWidgetPageItems(page, immediate);
}
}

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;
int startIndex = page * numCells;
int endIndex = Math.min(startIndex + numCells, mApps.size());
PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(page);

layout.removeAllViewsOnPage();
ArrayList<Object> items = new ArrayList<Object>();
ArrayList<Bitmap> images = new ArrayList<Bitmap>();
for (int i = startIndex; i < endIndex; ++i) {
ApplicationInfo info = mApps.get(i);
PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
R.layout.apps_customize_application, layout, false);
icon.applyFromApplicationInfo(info, true, this);
icon.setOnClickListener(this);
icon.setOnLongClickListener(this);
icon.setOnTouchListener(this);
icon.setOnKeyListener(this);

int index = i - startIndex;
int x = index % mCellCountX;
int y = index / mCellCountX;
if (isRtl) {
x = mCellCountX - x - 1;
}
layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));

items.add(info);
images.add(info.iconBitmap);
}

enableHwLayersOnVisiblePages();
}

最后从mApps对象里面取出所有的app信息,并添加点击事件,将App图标添加到桌面上,这时我们就可以在onClick回掉中找到启动app的代码了,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void onClick(View v) {
// When we have exited all apps or are in transition, disregard clicks
if (!mLauncher.isAllAppsVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return;

if (v instanceof PagedViewIcon) {
// Animate some feedback to the click
final ApplicationInfo appInfo = (ApplicationInfo) v.getTag();

// Lock the drawable state to pressed until we return to Launcher
//这里锁定app点击,防止二次点击启动2个app程序
if (mPressedIcon != null) {
mPressedIcon.lockDrawableState();
}

// NOTE: We want all transitions from launcher to act as if the wallpaper were enabled
// to be consistent. So re-enable the flag here, and we will re-disable it as necessary
// when Launcher resumes and we are still in AllApps.
mLauncher.updateWallpaperVisibility(true);
//最后调用launcher里面的startActivitySafely方法,这里就启动了app
mLauncher.startActivitySafely(v, appInfo.intent, appInfo);

} else if (v instanceof PagedViewWidget) {
// Let the user know that they have to long press to add a widget
}
}

王洋 wechat
我的微信号,欢迎交流~

热评文章