Android Launcher分析和修改9——Launcher启动APP流程
本来想分析AppsCustomizePagedView类,不过今天突然接到一个临时任务。客户反馈说机器界面的图标很难点击启动程序,经常点击了没有反应,Boss说要优先解决这问题。没办法,只能看看是怎么回事。今天分析一下Launcher启动APP的过程。从用户点击到程序启动的流程,下面针对WorkSpace上的快捷图标点击启动流程进行分析。(如果分不清WorkSpace是什么或者不知道快捷方式和其他图标区别,请看我前面的Launcher分析文章)
PS:新建的QQ群,有兴趣可以加入一起讨论:Android群:322599434
下面我们先看看Launcher启动APP的大概流程:
(鉴于很多转载文章的人把作者信息都删除了,只能在图片上加入水印,不会给大家阅读造成影响)
上面就是手指触摸屏幕开始,到点击响应的流程。Launcher里面因为有滑动、拖曳、点击等手势操作,所以区分了很多流程判断。最后调用Launcher.java里面的onClick()方法响应点击,启动程序。下面我们针对关键流程做分析。
1、WorkSpace触摸
前面我们分析Launcher的配置文件时就说过,Launcher外面的界面主要就是通过WorkSpace来显示的。它是一个ViewGroup的自定义类。下面我们先看看WorkSpace的onInterceptTouchEvent做了什么。
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onInterceptTouchEvent(MotionEvent ev)
{
if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent enter");
//对ACTION_DOWN和ACTION_UP做一些标记处理。
switch (ev.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
mXDown = ev.getX();
mYDown = ev.getY();
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_REST)
{
final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
if (!currentPage.lastDownOnOccupiedCell())
{
onWallpaperTap(ev);
}
}
} if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent call super InterceptTouch");
//调用父类的onInterceptTouchEvent,这里是调用了SmoothPagedView
return super.onInterceptTouchEvent(ev);
}
在WorkSpace里面并没有拦截消息,主要是调用父类的方法,也就是PagedView的onInterceptTouchEvent()方法。因为WorkSpace的直接父类SmoothPagedView也是继承了PagedView类,有关PagedView的onInterceptTouchEvent()方法,我在前面的文章已经分析过。这里不做多说,不了解的朋友可以看看(点这里)。
2、CellLayout的onInterceptTouchEvent()方法
CellLayout也是一个继承了ViewGroup的类,主要用来显示桌面控件。刚开始分析Launcher的时候,我们分析配置文件的时候也说过WorkSpace就是由5个CellLayout组成的。因此我们点击WorkSpace里面图标的时候,自然会调用CellLayout里面的东西。CellLayout里面的onInterceptTouchEvent()做的事情并不多:
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onInterceptTouchEvent(MotionEvent ev)
{
if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent enter"); final int action = ev.getAction(); if (action == MotionEvent.ACTION_DOWN)
{
//清除所有触摸标记
clearTagCellInfo();
}
//mInterceptTouchListener是WorkSpace的onTouch方法回调,下面会分析
//掌握这点很重要,因为onInterceptTouchEvent的返回值直接决定了触摸事件的传递方向 mythou
if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))
{
//截断Touch传输,直接处理 ,返回true会直接调用onTouchEvent处理。 mythou
if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent return true Intercept msg");
return true;
} if (action == MotionEvent.ACTION_DOWN)
{
setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
} if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent return false");
//注意这里返回的是false ,onInterceptTouchEvent的返回值决定了触摸事件的传递方式。mythou
return false;
}
这里需要注意的是onInterceptTouchEvent()的返回值,如果是返回true,触摸消息会直接被CellLayout的onTouchEvent()处理,一般点击启动程序返回的都是false,因为消息最后是TextView处理(workspace上的快捷方式都是TextView的子类)。Android的触摸消息传递机制和消息拦截机制,需要好好理解好才能明白Launcher的触摸事件处理。因为个人觉得Launcher里面事件处理层次还是比较多,如果对Android的事件传递机制理解不深,就很难理解Launcher的事件处理。如果对这方面不了解的朋友,可以查查网上相关资料。后面有空我也会写一篇深入分析Android触摸事件处理的文章。
3、WorkSpace的onTouch()事件
WorkSpace里面还处理了onTouch事件,这里的onTouch事件是因为WorkSpace使用了View.OnTouchListener接口,所以实现了onTouch事件的回调。
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onTouch(View v, MotionEvent event)
{
return (isSmall() || !isFinishedSwitchingState());
}
onTouch里面其实没做什么事情,就是根据两个方法返回值,判断onTouch是返回false还是返回true,返回值是什么决定了触摸事件的传递方向,上面已经说过了。这里的onTouch是给WorkSpace里面的装载的View使用的,也就是CellLayout。我们可以看看每次调用WorkSpace的onChildViewAdded()方法,会设置CellLayout的onTouch监听器。
//Edited by mythou
//http://www.cnblogs.com/mythou/
public void onChildViewAdded(View parent, View child) { if (!(child instanceof CellLayout))
{
throw new IllegalArgumentException("A Workspace can only have CellLayout children."); }
CellLayout cl = ((CellLayout) child);
//设置onTouch的监听器,CellLayout的onInterceptTouchEvent()方法会根据onTouch监听器判断是否需要拦截onTouch事件。
cl.setOnInterceptTouchListener(this);
cl.setClickable(true);
cl.setContentDescription(getContext().getString(
R.string.workspace_description_format, getChildCount()));
}
上面第二点CellLayout的onInterceptTouchEvent()方法的分析里面,return ture的时候,判断的依据mInterceptTouchListener和mInterceptTouchListener.onTouch()方法的返回值就是从这里设置的,他们也就是调用了WorkSpace的onTouch方法。
4、BubbleTextView
BubbleTextView是继承了TextView的子类,在Launcher里面所有的WorkSpace的快捷方式都是使用BubbleTextView绘画的,它的作用相当于一个按钮。在分析BubbleTextView的触摸响应前,我们先看看Launcher里面如何创建快捷方式。前面的文章我分析过Launcher加载界面数据的流程,不过没有仔细分析如何创建相关对象,下面我们先看看WorkSpace的快捷方式如何创建:
//Edited by mythou
//http://www.cnblogs.com/mythou/
View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info)
{
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
favorite.applyFromShortcutInfo(info, mIconCache);
//注意这里设置了onClickListener OWL
favorite.setOnClickListener(this);
return favorite;
}
上面就是创建快捷方式的方法,这里面我们需要注意的是,BubbleTextView调用了SetOnClickListener()方法,设置点击监听器。监听器的回调函数就是Launcher.java类里面的onCLick方法。因为Launcher类继承了View.OnClickListener接口,当然Launcher里面还继承了其他的接口,例如触摸、长按的Listener。下面我们先看看BUbbleTextView的onTouchEvent方法:
//Edited by mythou
//http://www.cnblogs.com/mythou/
public boolean onTouchEvent(MotionEvent event)
{
// 调用TextView的onToucEvent方法,主要是获取返回值,这个返回值作用很大,会响应点击事件的回调。
// 可以把这返回值打印出来看看。当点击启动程序时,这里肯定会返回true。
//原因上面已经说了很多次,这里不再啰嗦 OWL
boolean result = super.onTouchEvent(event); switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//下面这里主要是根据触摸状态值,做快捷方式的状态显示,例如是否需要显示按下状态。
if (mPressedOrFocusedBackground == null) {
mPressedOrFocusedBackground = createGlowingOutline(
mTempCanvas, mPressedGlowColor, mPressedOutlineColor);
}
if (isPressed())
{
mDidInvalidateForPressedState = true;
setCellLayoutPressedOrFocusedIcon();
} else {
mDidInvalidateForPressedState = false;
} mLongPressHelper.postCheckForLongPress();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (!isPressed()) {
mPressedOrFocusedBackground = null;
}
//这里根据状态判断是否取消长按的触摸
mLongPressHelper.cancelLongPress();
break;
}
//返回值十分重要,直接影响是否会掉用onCLick方法。
return result;
}
上面就是BubbleTextView的onTouchEvent的方法,上面强调很多次返回值,这里如果是点击流程,会返回true。然后执行onClick方法。
5、onClick()方法
最后点击会调用Launcher里面的onClick方法,不管你是点击快捷方式、文件夹、还是AllAPP的按钮,都是从这里响应回调。
//Edited by mythou
//http://www.cnblogs.com/mythou/ public void onClick(View v) {
//.......... Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
//这里点击打开我们上面分析的快捷方式图标。打开的方式也是调用startActivity,
// 只是这里调用的startActivitySafely对startActivity进行了封装 mythou
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[], pos[],
pos[] + v.getWidth(), pos[] + v.getHeight())); boolean success = startActivitySafely(v, intent, tag); if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
//文件夹点击响应
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
//AllAPP按钮点击响应
} else if (v == mAllAppsButton) {
if (isAllAppsVisible()) {
showWorkspace(true);
} else {
onClickAllAppsButton(v);
}
}
}
上面给出了最后点击启动快捷方式图标的方式,最后调用的也是startActivity,不过Launcher对它进行了封装,里面加入异常处理,包括是否启动成功等操作。而且里面分开了startActivity和startActivityForResult两种方式。但是最后启动的时候,还是调用了我们平时使用的启动Activity的方法。下面给出我加入打印消息后的代码流程跟踪截图。
6、总结
Launcher里面有关点击启动程序的方法流程就是上述所说的那样,当然,里面有些方法判断执行了很多方法,这里不做详细分析,又兴趣的朋友可以跟我一样加入一些打印消息,跟踪Log分析代码流程。里面还有一个相当重要的部分就是如何区分是页面滑动还是按钮点击。文章开头说的问题就是因为把点击事件判断为页面滑动,导致经常点击按钮的时候执行了滑动页面的操作,导致用户觉得点击按钮都没反应。我最后的修改方法是修改了判断是否进行页面滑动的方法,解决了该问题。
今天就说到这里,这个流程分析个人觉得还是有点复杂,因为涉及了Android触摸事件的传递方式,如果不了解这个,无法理解Launcher是如何处理这些事件的。最后欢迎各位转载Launcher的分析文章,但请不要修改文章内容,并附上原文链接和作者信息。
Launcher分析系列文章:
Android Launcher分析和修改1——Launcher默认界面配置(default_workspace)
Android Launcher分析和修改2——Icon修改、界面布局调整、壁纸设置
Android Launcher分析和修改3——Launcher启动和初始化
Android Launcher分析和修改4——初始化加载数据
Android Launcher分析和修改5——HotSeat分析
Android Launcher分析和修改6——页面滑动(PagedView)
Android Launcher分析和修改7——AllApp全部应用列表(AppsCustomizeTabHost)
Android Launcher分析和修改8——AllAPP界面拖拽元素(PagedViewWithDraggableItems)
Edited by mythou
原创博文,转载请标明出处:http://www.cnblogs.com/mythou/p/3187881.html
Android Launcher分析和修改9——Launcher启动APP流程的更多相关文章
- Android Launcher分析和修改3——Launcher启动和初始化
前面两篇文章都是写有关Launcher配置文件的修改,代码方面涉及不多,今天开始进入Launcher代码分析. 我们开机启动Launcher,Launcher是由Activity Manager启动的 ...
- Android Launcher分析和修改1——Launcher默认界面配置(default_workspace)
最近工作都在修改Launcher,所以打算把分析源码和修改源码的过程记录下来,最近会写一些关于Launcher的分析和修改博文.因为我是修改4.0.3的Launcher,所以后面文章里面的Launch ...
- Android Launcher分析和修改13——实现Launcher编辑模式(1) 壁纸更换
已经很久没更新Launcher系列文章,今天不分析源码,讲讲如何在Launcher里面添加桌面设置的功能.目前很多第三方Launcher或者定制Rom都有简单易用的桌面设置功能.例如小米MIUI的La ...
- Android Launcher分析和修改10——HotSeat深入进阶
前面已经写过Hotseat分析的文章,主要是讲解如何在Launcher里面配置以及修改Hotseat的参数.今天主要是讲解一下如何在Hotseat里面的Item显示名称.这个小问题昨天折腾了半天,最后 ...
- Android Launcher分析和修改4——初始化加载数据
上面一篇文章说了Launcher是如何被启动的,Launcher启动的过程主要是加载界面数据然后显示出来, 界面数据都是系统APP有关的数据,都是从Launcher的数据库读取,下面我们详细分析Lau ...
- Android Launcher分析和修改5——HotSeat分析
今天主要是分析一下Launcher里面的快捷方式导航条——HotSeat,一般我们使用手机底下都会有这个导航条,但是如果4.0的Launcher放到平板电脑里面运行,默认是没有HotSeat的,刚好我 ...
- Android Launcher分析和修改6——页面滑动(PagedView)
本来打算分析CellLayout的源码,不过因为它们之间是容器包含关系,所以打算先把PagedView分析.PagedView代码很多,今天主要是分析跟核心功能相关的代码.PagedView主要实现一 ...
- Android Launcher分析和修改7——AllApp全部应用列表(AppsCustomizeTabHost)
今天主要是分析一下Launcher里面的所有应用列表.Android4.0 Launcher的所有应用列表跟2.X比较大的区别就是多了Widget的显示.下面会详细分析Launcher里面有关所有应用 ...
- Android Launcher分析和修改8——AllAPP界面拖拽元素(PagedViewWithDraggableItems)
接着上一篇文章,继续分析AllAPP列表界面.上一篇文章分析了所有应用列表的界面构成以及如何通过配置文件修改属性.今天主要是分析PagedViewWithDraggableItems类,因为在我们分析 ...
随机推荐
- HDU 2491 Priest John's Busiest Day
贪心.. #include<iostream> #include<string.h> #include<math.h> #include <stdio.h&g ...
- C++基础笔记(二)C++对C的扩展
Xcode创建C++项目 1.新建一个MAC工程(command line tool) 2.导入头文件 3.修改文件后缀(*.m-->*.mm) 4.修改主函数中的OC代码为C++的代码 动 ...
- 书籍推荐 《移动Web手册》 奇舞团
书籍推荐 <移动Web手册> 奇舞团
- Volley(六 )—— 从源码带看Volley的缓存机制
磁盘缓存DiskBasedCache 如果你还不知道volley有磁盘缓存的话,请看一下我的另一篇博客请注意,Volley已默认使用磁盘缓存 DiskBasedCache内部结构 它由两部分组成,一部 ...
- smarty初始化文件
为了smarty初始化文件能方便其他目录下的脚步包含使用,应该将初始化文件里的引入smarty类及其他路径设置为全局路径,而不是相对路径 另外,如果模板里有文件的路径的引用,一定要是相对PHP脚本文件 ...
- Pureftp-安全的ftp服务器部署
一.简介: Pure-FTPd 是一款免费(BSD)的,安全的,高质量和符合标准的FTP服务器. 侧重于运行效率和易用性. 它提供了简单的答案,他满足了大众化的需求,包括普通用户以及主机供应商们 Pu ...
- 介绍Git版本控制器的使用
Git 简介 Git 是什么?大家肯定会说不就是版本控制器嘛,是的Git是目前世界上最先进的分布式版本控制系统(没有之一). 1.那什么是版本控制器呢? 举个简单的例子,比如我们用Word写文章,那你 ...
- 导航 tab
- Win2008R2配置WebDeploy
一.配置服务器 1.安装管理服务 2.点击管理服务进行配置 3.安装WebDeploy 3.1通过离线安装包方式安装: https://www.iis.net/downloads/microsoft/ ...
- benchmark
redis benchmark How many requests per second can I get out of Redis? Using New Relic to Understand R ...