工作需要总结,这样就能保证地基牢固,就能爬得更高;
----2013-01-07题记
转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/8478533
前段时间研究了Launcher的AllApps的加载流程,对这个进行了一点修改,呵呵,其实也不算太难,只要把Launcher的代码都能看个80%,基本就是想怎么改就怎么改!AllApps是什么,就是在Android的IDEL界面(主界面)点击MainMenu键进入后的界面,也就是所有应用程序界面;
先来看看它是怎么被手机加载上来的?
Step1:手机第一次开机,首先加载LauncherApplication,注册一些监听,共享数据,比如:LauncherModel对象,通过((LauncherApplication)getApplication());可以获取到LauncherApplication的对象;然后再加载Launcher.java这个类,先走onCreate()方法;里面调用如下方法:
if (!mRestoring) {
mModel.startLoader(this, true);
}
Step2:在Step1中这个方法调到了LauncheModel.java的类里面了,在这个方法里面主要的工作就是启动一个线程,下面我们来看看在线程的run()方法做了哪些操作;
public void run() {
// 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).
final Callbacks cbk = mCallbacks.get();
final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
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);
}
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
} else {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
loadAndBindAllApps();
}
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();
// second step
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
} else {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
loadAndBindWorkspace();
}
// 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");
for (Object key : sDbIconCache.keySet()) {
updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
}
sDbIconCache.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;
}
}
}
其实主要的操作就是加载workspace和AllApps;loadAndBindAllApps()这个方法就是加载AllApps的;
Step3:在这个loadAndBindAllApps()里面,会调用loadAllAppsByBatch(),批量加载AllApps;
先根据:
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
创建一个带有CATEGORY_LAUNCHER这种类型的mainIntent,然后再通过
List<ResolveInfo> apps=packageManager.queryIntentActivities(mainIntent, 0);
过滤出所有的apps,通过sort对apps进行排序:
Collections.sort(apps,new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
排序完成后,然后把这些apps逐个添加到ArrayList中去:代码如下:
for (int j=0; i<N && j<batchSize; j++) {
// This builds the icon bitmaps.
mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),
mIconCache, mLabelCache));
i++;
}
调到AllAppsList.java中的add方法:
public void add(ApplicationInfo info) {
if (findActivity(data, info.componentName)) {
return;
}
data.add(info);
added.add(info);
}
这个added的定义就是:
public ArrayList<ApplicationInfo> added =
new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
然后通过开启线程callback回调到Launcher.java的bindAllApplications()方法中:
final ArrayList<ApplicationInfo> added = mAllAppsList.added;
mAllAppsList.added = new ArrayList<ApplicationInfo>();
mHandler.post(new Runnable() {
public void run() {
final long t = SystemClock.uptimeMillis();
if (callbacks != null) {
if (first) {
callbacks.bindAllApplications(added);
} else {
callbacks.bindAppsAdded(added);
}
if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
+ (SystemClock.uptimeMillis() - t) + "ms");
}
} else {
Log.i(TAG, "not binding apps: no Launcher activity");
}
}
});
Step4:在Launcher.java中bindAllApplications()方法中做的事:如果有对话框存在,就remove对话框,主要是
mAppsCustomizeContent.setApps(apps);
Step5:在AppsCustomizePagedView.java中的setApps()中主要做的事就是,赋值给mApps,再次对apps进行排序,计算apps的页数和widget占用的页数;代码如下:
public void setApps(ArrayList<ApplicationInfo> list) {
mApps = list;
Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
updatePageCounts();
// The next layout pass will trigger data-ready if both widgets and apps are set, so
// request a layout to do this test and invalidate the page data when ready.
if (testDataReady()) requestLayout();
}
updatePageCounts()就是计算apps的页数和widget的页数;
Step6:而进入这个allapps的时候,就是进入到AppsCustomizePagedView.java这个类的时候会调用
onMeasure()这个方法;在这个里面首先会对allapps和widgets进行校验,
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (!isDataReady()) {
if (testDataReady()) {
setDataIsReady();
setMeasuredDimension(width, height);
onDataReady(width, height);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
通过testDataReady()这个方法来校验是否他们为空!如果为空就不加载他们;代码如下:
/**
* This differs from isDataReady as this is the test done if isDataReady is not set.
*/
private boolean testDataReady() {
// We only do this test once, and we default to the Applications page, so we only really
// have to wait for there to be apps.
// TODO: What if one of them is validly empty
return !mApps.isEmpty() && !mWidgets.isEmpty();
}
当allapps和widgets的数据都准备好了的时候,给这个view设置宽和高setMeasuredDimension(width, height);
然后调用onDataReady(width, height);在这个方法中会计算占用的页数,内容的宽度,细胞的数量,强制措施,以更新重新计算差距,存储页面,刷新数据显示上来通过invalidatePageData(Math.max(0, page), hostIsTransitioning);
这个调用到了PageView.java这个类(Launcher的主要精华类,写得相当有水准,看了好几遍,每次看都有收获)在这个方法里面主要做的是
(1)先加载apps和widgets的view,通过方法:
// Update all the pages
syncPages();
public void syncPages() {
removeAllViews();
cancelAllTasks();
Context context = getContext();
for (int j = 0; j < mNumWidgetPages; ++j) {
PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
mWidgetCountY);
setupPage(layout);
addView(layout, new PagedViewGridLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
}
for (int i = 0; i < mNumAppsPages; ++i) {
PagedViewCellLayout layout = new PagedViewCellLayout(context);
setupPage(layout);
addView(layout);
}
}
(2)再刷新数据到每页的view界面中,通过方法:
// Load any pages that are necessary for the current window of views
loadAssociatedPages(mCurrentPage, immediateAndOnly);
requestLayout();
在PageView.java中的loadAssociatedPages()方法中里面调用的主要的方法syncPageItems(i, (i == page) && immediateAndOnly);这个通过接口调到了AppsCustomizePagedView.java中的syncPageItems()方法中去了:
@Override
public void syncPageItems(int page, boolean immediate) {
if (page < mNumAppsPages) {
syncAppsPageItems(page, immediate);
} else {
syncWidgetPageItems(page - mNumAppsPages, immediate);
}
}
里面就是刷新apps或者是widget的每一页;
再来看看syncAppsPageItems()这个方法:
public void syncAppsPageItems(int page, boolean immediate) {
// ensure that we have the right number of items on the pages
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, mHolographicOutlineHelper);
icon.setOnClickListener(this);
icon.setOnLongClickListener(this);
icon.setOnTouchListener(this);
icon.setOnKeyListener(this);
int index = i - startIndex;
int x = index % mCellCountX;
int y = index / mCellCountX;
layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
items.add(info);
images.add(info.iconBitmap);
}
layout.createHardwareLayers();
/* TEMPORARILY DISABLE HOLOGRAPHIC ICONS
if (mFadeInAdjacentScreens) {
prepareGenerateHoloOutlinesTask(page, items, images);
}
*/
}
当你看到这个addViewToCellLayout()方法的时候,我相信你就会有“山穷水复疑无路,柳暗花明又一村”的感觉了!这就是加载每个icon到view的那个位置;
syncWidgetPageItems()这个也是同理,代码我相信大家自己都能看明白了吧!
Step7:而这个widgets的数据是怎么加载上来的呢???这个是在Launcher.java中的onCreate()方法中一步一步加载的:
(1)在Launcher.java中的onCreate()方法中:
// Update customization drawer _after_ restoring the states
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.onPackagesUpdated();
}
(2)调用到AppsCustomizePagedView.java中的onPackagesUpdated()的方法,这个里面主要做的是启动一个延迟的线程来加载widgets
public void onPackagesUpdated() {
// TODO: this isn't ideal, but we actually need to delay here. This call is triggered
// by a broadcast receiver, and in order for it to work correctly, we need to know that
// the AppWidgetService has already received and processed the same broadcast. Since there
// is no guarantee about ordering of broadcast receipt, we just delay here. Ideally,
// we should have a more precise way of ensuring the AppWidgetService is up to date.
postDelayed(new Runnable() {
public void run() {
updatePackages();
}
}, 500);
}
(3)通过updatePackages()这个方法来实现的加载widgets的下面来看看代码:
public void updatePackages() {
// Get the list of widgets and shortcuts
boolean wasEmpty = mWidgets.isEmpty();
mWidgets.clear();
List<AppWidgetProviderInfo> widgets =
AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
for (AppWidgetProviderInfo widget : widgets) {
if (widget.minWidth > 0 && widget.minHeight > 0) {
mWidgets.add(widget);
} else {
Log.e(LOG_TAG, "Widget " + widget.provider + " has invalid dimensions (" +
widget.minWidth + ", " + widget.minHeight + ")");
}
}
mWidgets.addAll(shortcuts);
Collections.sort(mWidgets,
new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager));
updatePageCounts();
if (wasEmpty) {
// The next layout pass will trigger data-ready if both widgets and apps are set, so request
// a layout to do this test and invalidate the page data when ready.
if (testDataReady()) requestLayout();
} else {
cancelAllTasks();
invalidatePageData();
}
}
相信大家看到这里,根据上面的分析,就应该明白了mWidgets数据的加载过程了吧!
Step8:置于里面的click事件就查看onClick()方法;
长按是调用到父类的PagedViewWithDraggableItems.java的onLongClick()事件:
@Override
public boolean onLongClick(View v) {
// Return early if this is not initiated from a touch
if (!v.isInTouchMode()) return false;
// Return early if we are still animating the pages
if (mNextPage != INVALID_PAGE) return false;
// When we have exited all apps or are in transition, disregard long clicks
if (!mLauncher.isAllAppsCustomizeOpen() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
return beginDragging(v);
}
然后回调子类的AppsCustomizePagedView.java的beginDragging()方法的:
private void beginDraggingApplication(View v) {
mLauncher.getWorkspace().onDragStartedWithItem(v);
mLauncher.getWorkspace().beginDragShared(v, this);
}
以后的流程大家可以自己跟跟,就明白拖拽事件的传递了,其实和Folder的拖拽是类似的原理;
今天就总结到这里吧!
2013年1月7日22:35于北京
分享到:
相关推荐
一>、Android重启功能 在androidjava层执行shell命令来完成。但是笔者在开发测试中同样的代码发现用Eng编译出的版本可以重新启动,user版本不能完成重启,挂在关机那个界面。 /********************************...
标题中提到的“基于ARM的智能家居红外控制系统设计”,涉及的关键知识点集中在智能家居的系统设计、红外控制技术、ARM技术、Linux嵌入式系统以及Android设备的集成应用。 首先,智能家居(Smart Home)是利用现代...
Java-美妆神域_3rm1m18i_221-wx.zip
51单片机的温度监测与控制(温控风扇)
电赛案例,C++简单的智能家居系统,其中包含了温度监测、光照控制和报警系统。该系统可以: 监控室内温度:当温度超过设定阈值时,触发警报。 自动调节光照:根据光线传感器的值自动调节LED灯的亮度。 入侵检测:通过红外传感器检测入侵,并触发警报。
圣诞树 html版 可修改祝福语。 记事本或vscode编辑html文件:ctrl+F寻找”myLabels“关键词,定位到该处即可修改祝福语
【资源说明】 基于python编写的selenium自动化测试框架,采用PO模式,页面元素采用yaml进行管理资料齐全+详细文档+高分项目+源码.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!
屏幕截图 2024-12-21 170434
基于SpringBoot的学生信息管理系统(前后端源码+数据库+文档+运行截图) 学生信息管理 班级信息管理 教师信息管理 课程信息管理 选课信息管理 考勤信息管理 请假信息管理 成绩信息管理 基于SpringBoot的学生信息管理系统(前后端源码+数据库+文档+运行截图) 学生信息管理 班级信息管理 教师信息管理 课程信息管理 选课信息管理 考勤信息管理 请假信息管理 成绩信息管理基于SpringBoot的学生信息管理系统(前后端源码+数据库+文档+运行截图) 学生信息管理 班级信息管理 教师信息管理 课程信息管理 选课信息管理 考勤信息管理 请假信息管理 成绩信息管理基于SpringBoot的学生信息管理系统(前后端源码+数据库+文档+运行截图) 学生信息管理 班级信息管理 教师信息管理 课程信息管理 选课信息管理 考勤信息管理 请假信息管理 成绩信息管理基于SpringBoot的学生信息管理系统(前后端源码+数据库+文档+运行截图) 学生信息管理 班级信息管理 教师信息管理 课程信息管理 选课信息管理 考勤信息管理
径向基函数内核 – 机器学习 内核在将数据转换为更高维空间方面发挥着重要作用,使算法能够学习复杂的模式和关系。在众多的内核函数中,径向基函数(RBF)内核作为一种多功能且强大的工具脱颖而出。在本文中,我们深入探讨了RBF内核的复杂性,探讨了它的数学公式、直观理解、实际应用及其在各种机器学习算法中的重要性。
详细介绍及样例数据:https://blog.csdn.net/samLi0620/article/details/144636765
51单片机控制的智能小车.7z
【资源说明】 基于卷积神经网络的数字手势识别安卓APP,识别数字手势0-10详细文档+全部资料+优秀项目+源码.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!
python 使用sqlserver必须要这个问题,没办法,只能满世界的找地方下载,终于让我下载到了,现在分享给大家使用
四川采矿场生产安全事故管理制度
简约灰粉共存版_8.0.53.apk
ECharts散点图-全国主要城市空气质量(百度地图)
四川采矿场安全检查管理规定
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于计算机科学与技术等相关专业,更为适合;
空中俯视物体检测9-YOLOv5数据集合集.rar使用YOLO算法从图像中检测对象-V2 2023-05-11 2:51 PM ============================= *与您的团队在计算机视觉项目上合作 *收集和组织图像 *了解和搜索非结构化图像数据 *注释,创建数据集 *导出,训练和部署计算机视觉模型 *使用主动学习随着时间的推移改善数据集 对于最先进的计算机视觉培训笔记本,您可以与此数据集一起使用 该数据集包括1015张图像。 以YOLO V5 PYTORCH格式注释检测对象 - 图像。 将以下预处理应用于每个图像: *像素数据的自动取向(带有Exif-Arientation剥离) *调整大小为640x640(拉伸) 没有应用图像增强技术。