京东Android笔试复盘:底层机制与性能优化是核心

京东Android笔试复盘:底层机制与性能优化是核心 先说一个判断如果你现在准备Android面试看到这份2018年秋招的京东笔试题第一反应可能觉得过时了。但我的看法相反——这类大厂笔试的考察逻辑这几年几乎没有变过变的只是包装方式。2018年考的是Handler机制、AsyncTask、Glide缓存2025年面试官问的还是Handler机制、协程调度、图片加载框架只不过把AsyncTask换成了Coroutine把Glide换成了Coil。技术栈在迭代底层的计算机基础、Android核心机制、性能优化思路一直都是大厂筛人的硬指标。顺手把我在京东笔试复盘里踩过的坑和总结的考点梳理出来希望能给你省点时间。1. 京东2018秋招Android笔试题整体解构考什么、为什么考、怎么答1.1 从题目结构反推大厂的考察逻辑先说试卷的宏观结构。京东2018秋招的Android笔试题型通常分两大部分客观题单选、多选、判断加主观编程题。客观题覆盖面非常广从Java基础、数据结构、操作系统到Android的四大组件、Handler机制、View绘制流程、性能优化几乎每个方向都会涉及。主观题则一般集中在算法链表、二叉树、动态规划和一个综合性较强的Android设计题上。这里要说一个很多应届生容易忽略的事实大厂笔试不是要你满分而是通过分数排序筛人。你不需要所有题都会但必须保证基础题不丢分、核心题能拿大部分分、难题至少能写出思路。所以备考的重点不是刷遍所有知识点而是把高频考点吃透形成条件反射式的答题能力。考察逻辑上京东这类电商大厂的Android岗特别关注几个点一是基本功扎不扎实Java、数据结构的底层理解二是对Android核心机制的掌握深度Handler、Binder、View体系这些三是工程化意识内存优化、启动优化、组件化架构。说白了他们招的不是会写页面的人而是能处理复杂业务、能解决性能问题、能和后端高效协作的工程师。1.2 我在复盘时发现的三个高频命题方向把当年的题目按知识点归类之后我发现高频方向其实非常集中这里直接列出来Java基础与并发HashMap原理包括JDK 1.7和1.8的差异、线程池参数和拒绝策略、synchronized与volatile的区别、wait/notify机制。这个方向考的是你的语言功底也是后续排查线上问题的基本功。Android核心机制Handler消息机制几乎必考、Activity启动模式、Binder原理、ANR的成因和排查。这一块是区分会用和理解的分水岭。性能优化与架构内存泄漏的常见场景、ListView/RecyclerView的缓存机制、图片压缩方案、MVP/MVVM的选择。电商App对性能极其敏感所以这类题基本是送分必考题。有意思的是很多人在复习时喜欢钻牛角尖去抠冷门源码但京东这类大厂的笔试反而更看重是否真的用这些技术解决过问题。比如考Glide它不问你Glide的源码第几行做了什么而是问如果让你设计一个图片加载框架你会考虑哪些点。这种题目从2018年到现在一直都是主流。2. Java与数据结构基础笔试里的送分题其实是送命题2.1 HashMap在JDK 1.7和1.8中的差异不只是背八股我当年复习HashMap的时候只是机械地背了1.7头插法、1.8尾插法、红黑树这几句话结果笔试考到为什么1.8要引入红黑树在什么条件下触发一下子就卡住了。后来复盘才知道这题考的是你对自己写的代码有没有敬畏心——HashMap几乎是Java里被使用频率最高的集合类它的一点改动都牵动着无数系统的性能。JDK 1.7的HashMap采用数组加链表的结构hash冲突时用头插法把新节点放到链表头部这样插入快但并发扩容时会形成环形链表导致死循环。JDK 1.8改成尾插法并且当链表长度超过8、数组长度超过64时把链表转成红黑树让查询时间从O(n)降到O(logn)。注意这里有个关键细节链表转红黑树的触发条件是两个同时满足——链表长度大于等于8且数组长度大于等于64如果数组长度不够会优先扩容而不是转树。这个细节很多人不知道面试官一问就露馅。实战中的建议是如果在做笔试题时遇到HashMap相关题目一定要先说自己对hash函数设计的理解再谈数据结构的变化。HashMap的hash函数是(h key.hashCode()) ^ (h 16)让高16位也参与低位的计算减少碰撞概率。这样回答显得你不仅知道结论还知道结论背后的计算逻辑。还有一个容易踩坑的考点HashMap的容量为什么总是2的n次幂因为index hash (table.length - 1)当length是2的n次幂时这个位运算等价于取模而且比取模高效得多。同时也保证了扩容后元素的位置要么在原位置要么在原位置加旧容量这样rehash的成本最低。这些推导过程笔试答出来就是加分项。2.2 线程池的核心参数计算Java并发题到底怎么答才能拿满分线程池这个知识点几乎是我见过的大厂笔试题里出现频率最高的Java考点之一。京东2018年的题目考得比较直接给了核心线程数、最大线程数、阻塞队列容量、拒绝策略这四要素问提交任务时的执行流程。但我在复盘时发现真正拿到高分的同学都会把流程背后的为什么讲清楚。完整的执行流程是这样的提交一个任务时如果当前运行的线程数小于核心线程数corePoolSize直接创建新线程执行如果大于等于核心线程数先把任务放进阻塞队列如果队列满了再判断当前线程数是否小于最大线程数maximumPoolSize如果小于创建临时线程执行如果等于或大于执行拒绝策略。这里有一段很关键的理解核心线程数与最大线程数的差值决定了系统的弹性空间。差值越大系统在面对突发流量时能临时拉起的线程越多但如果最大线程数设置过大CPU频繁切换上下文反而拖垮性能。关于参数计算我做笔试题时的经验公式是CPU密集型任务核心线程数设为CPU核数加1IO密集型任务设为CPU核数乘以2左右甚至更多。原理是CPU密集型任务线程大部分时间在运行线程数超过CPU核数后反而因为竞争和切换导致性能下降IO密集型任务线程经常阻塞在等待IO上此时可以多创建线程来抵消阻塞时间。拒绝策略的选择上Ajax和Dubbo这类场景适合CallerRunsPolicy让调用者执行而日志采集这类任务适合DiscardOldestPolicy丢弃最旧的任务因为日志丢几条无所谓但绝不希望影响主流程。2.3 内存泄漏场景在笔试中的改头换面内存泄漏在京东笔试里不一定会直接问什么是内存泄漏而是换了个马甲给出几个代码片段让你选哪个会导致内存泄漏。比如Activity里有一个静态Context引用、内部类持有Activity、Handler未移除消息、单例持有Activity、资源未关闭等等。这些场景里静态Context引用是最典型的坑。因为Activity是用户感知最强的组件一旦泄漏意味着整个Activity的View树、Bitmap、各种资源都还在内存里App的内存水位会越来越高最后被系统杀掉。静态引用为什么危险因为静态变量的生命周期跟随类加载器几乎等于App的整个生命周期它持有了ActivityActivity就永远无法被回收。我在实战中排查内存泄漏一般用LeakCanary配合MATMemory Analyzer来做。LeakCanary能快速定位到泄漏的Activity和引用链MAT则能精确定位到是哪个对象持有了引用。但在笔试中你只要能准确判断持有者是谁、被持有者是什么、为什么回收不了这三点就稳了。记住一个万能分析框架强引用链是否断开了如果没断开GC Roots能到达它吗如果能到达就是泄漏。3. Android核心机制深度解析从Handler源码说起3.1 Handler消息机制为什么必须理解ThreadLocal与Looper的一致性Handler这个点在2018年京东笔试里出现的是选择题以下关于Handler的说法正确的是选项涵盖了Looper.prepare的作用、主线程Looper的初始化、MessageQueue的阻塞唤醒机制、Handler的同步屏障等。我犯过的错误是把Handler当成了子线程更新UI的工具其实它的本质是线程间通信机制。理解Handler机制要从三个角色入手MessageQueue消息队列、Looper消息循环、Handler消息发送和处理。MessageQueue不是真的对列而是一个按执行时间排序的链表结构使用了单链表的方式管理消息插入的时候按执行时间排序把最快需要执行的消息放在链表头部。Looper则是一个死循环不断从MessageQueue里取消息没消息时就通过epoll机制让线程休眠有消息时唤醒。Handler的核心工作是发送消息和处理消息——enqueueMessage把消息插入队列dispatchMessage把消息分发给handleMessage。关键的难点在于ThreadLocal。每个线程只能有一个Looper这个Looper就存在ThreadLocal里。为什么用ThreadLocal因为线程隔离。你从主线程new Handler时Handler通过Looper.myLooper()拿到主线程的Looper所以即使在子线程里调用handler.post消息最终也是回到主线程被处理因为Handler始终持有创建它的那个线程的Looper。反过来你在子线程里创建Handler之前必须先调用Looper.prepare()创建这个子线程的Looper否则就会抛出Cant create handler inside thread that has not called Looper.prepare()的异常。我建议从源码角度把这条链路走一遍new Handler()构造方法里会调用Looper.myLooper()如果返回null就抛出异常sendMessage最终调用enqueueMessageMessageQueue.next()在有消息时返回、没消息时调用nativePollOnce阻塞Looper.loop()是一个for死循环调用queue.next()拿到消息然后msg.target.dispatchMessage(msg)最终回调handleMessage。需要注意的是ActivityThread.main方法在启动时已经帮主线程创建好了Looper和MessageQueue这就是为什么主线程可以直接new Handler而子线程必须手动调用prepare。3.2 消息屏障与同步消息Android UI刷新背后的秘密我在复盘京东笔试时发现一个冷门考点同步屏障SyncBarrier和空闲消息IdleHandler。这个考点在2018年出现的时候很多人直接蒙了因为常规的Handler八股文里很少提。同步屏障的作用是拦截同步消息让异步消息优先执行。大家知道我们平时使用Handler发送的消息默认都是同步消息那异步消息什么时候用呢ViewRootImpl在UI绘制时会发送一个异步消息并插入一个同步屏障确保UI绘制任务优先执行不被其他同步消息阻塞。同步屏障本质上是一个target为null的Message当MessageQueue的next()方法遇到target为null的消息时会跳过后面的同步消息只执行异步消息。另一个容易被忽略的是IdleHandler。MessageQueue里有一个IdleHandler列表当队列为空或者当前没有需要执行的消息时Looper会回调这些IdleHandler。这个机制非常适合做启动优化——把一些非紧急的初始化工作比如预加载、埋点上报放到启动后的空闲时间执行避免卡开机启动的流程。这里给一个实用技巧如果你在笔试或面试中回答Handler机制主动提到同步屏障和IdleHandler这两个细节面试官通常会判定你的源码阅读能力不错因为这是很多工作三五年的人都未必了解的内容。你可以结合场景说明比如我用IdleHandler做过冷启动的延迟初始化把非核心SDK的初始化延后到首帧绘制完成之后这样理论加实践都有了。3.3 Binder机制跨进程通信为什么是Android的基石Binder是Android里最绕、但也是最重要的底层机制之一。京东2018年笔试直接考了一道以下关于Binder的描述正确的是选项里混着Binder使用共享内存实现Binder使用Socket实现Binder通过内存映射实现一次拷贝Binder的性能低于Socket等等。Binder的设计核心是一次拷贝。传统的IPC比如Socket、管道需要内核空间和用户空间之间的多次拷贝Binder通过mmap在内核空间和用户空间之间映射同一块物理内存发送进程只需要把数据从用户空间拷贝到内核空间接收进程的用户空间通过映射直接读到数据省去了一次拷贝。这就是一次拷贝的由来。但这个机制在Android里并不仅仅是一个技术选型的问题它和Android的安全性设计紧密相关。每个进程在Binder通信时都带有调用方的UID和PID系统服务可以据此做权限校验。比如你调用AMSActivityManagerService去startActivityAMS会检查调用进程有没有对应的权限如果没权限直接抛出SecurityException。落实到笔试答题上答Binder题要分三个层次第一层说清楚Binder解决了什么跨进程通信、一次拷贝、安全性第二层说清楚Binder的四个角色Client、Server、ServiceManager、Binder驱动第三层能画出一张调用链路的时序图Client通过代理对象调用transact驱动处理Server收到onTransact回调返回结果。能做到第三层这题基本就保住了。4. View体系与性能优化电商App的命根子4.1 View的绘制流程从measure到draw每一步都要能讲出源码京东的App属于重内容、重交互的电商平台页面上有大量的商品卡片、图片流、feed流所以View的绘制效率和性能优化是笔试必考。2018年的题里直接有一道Activity的setContentView之后View是如何绘制到屏幕上的这个问题我的答法分四步第一步setContentView解析XML布局生成View树第二步ViewRootImpl通过requestLayout发起绘制流程触发performTraversals第三步perfromMeasure、performLayout、performDraw依次执行第四步View的draw方法把内容绘制到Canvas上最终通过SurfaceFlinger合成到屏幕。关键在于每一步的细节。Measure阶段View的measure方法会调用onMeasure对于自定义View来说onMeasure里要处理MeasureSpec的三种模式——UNSPECIFIED未指定父容器不限制子View想多大就多大、EXACTLY精确模式match_parent或具体dp值、AT_MOST最大模式wrap_content。我在处理自定义View时经常犯的错是没处理wrap_content导致自定义View在XML里写了wrap_content却表现得像match_parent就是因为onMeasure里没有正确处理AT_MOST模式。Layout阶段比较简单核心是确定每个子View的left、top、right、bottom四个坐标。Draw阶段则分为六个步骤绘制背景、保存Canvas图层、绘制View内容onDraw、绘制子ViewdispatchDraw、绘制装饰滚动条等、恢复图层。4.2 RecyclerView缓存机制为什么它能撑住千条商品流RecyclerView在2018年已经是主流京东笔试里也考了RecyclerView的缓存机制与ListView的区别。这个问题如果只答ViewHolder回收复用是拿不到高分的因为这是最表层的东西。RecyclerView的缓存分四级第一级是mAttachedScrap屏幕内缓存用于在布局过程中复用仍处于屏幕内的ItemView第二级是mCachedViews缓存池默认容量为2存放被滑出屏幕的ViewHolder第三级是mViewCacheExtension自定义缓存第四级是mRecyclerPool回收池池子里存放的是ViewHolder而不是View。关键点来了mCachedViews命中时不需要重新绑定数据因为ViewHolder还在数据也没变但mRecyclerPool里的ViewHolder被复用后必须走一次bindViewHolder重新绑定数据。所以从性能上说mCachedViews的命中效率最高这也就是为什么RecyclerView在快速滑动时即使数据复杂度高也能保持流畅的原因。但这里有个经验之谈mCachedViews默认容量是2如果你的页面一次滑动会超过两个Item缓存命中率会迅速下降。可以通过setItemViewCacheSize增大缓存池或者配合DiffUtil做精准的局部刷新而不是notifyDataSetChanged全量刷新。全量刷新会重建所有可见项性能损失很大在商品列表这种高频交互页面尤其致命。4.3 内存与启动优化应对笔试中你怎么保证App稳定不卡大厂的笔试题里很容易出现一类开放性问题比如你负责的App启动卡顿、内存吃紧你会怎么排查和优化。这类题目乍一看没标准答案但其实考察的是你是否有真实的性能优化经验。我的答题框架分为三块内存优化、启动优化、流畅度优化。内存优化方面我一般会提到LruCache与DiskLruCache的图片缓存策略、Bitmap的inSampleSize采样率压缩、WebView的内存泄漏治理和独立进程方案。图片加载是电商App内存占用的大头所以图片三件套采样压缩、缓存、复用是标配。启动优化方面核心思路是减少主线程工作量、延迟非必需任务、并行化初始化。冷启动过程主要分三步Application的onCreateActivity的onCreate到onResume首帧渲染。我的方案一般是把SDK初始化放到子线程或IdleHandler里用启动器框架实现任务的依赖排序和并行加载腾讯的启动器或者自研的TaskDispatcher都是很好的参考。流畅度优化方面除了常规的布局层级扁平化、避免过度绘制之外还要关注主线程的耗时操作。排查工具上Systrace和Perfetto看卡顿的帧耗时CPU Profiler定位主线程的耗代码Layout Inspector检查布局层级。在笔试题里你能把这些工具名和具体案例结合起来哪怕没有完整的数据支撑也比只写我用工具做了优化有说服力得多。5. 开放性设计与架构题从加分项变成必答题5.1 设计一个图片加载框架的答题思路京东2018年的笔试虽然没有网上流传的设计一个图片加载框架原题但我在复盘时发现很多Android大厂的笔试题里都有类似的设计题本质考察的是你对整个图片加载链路的理解。我的回答框架分五层第一层请求层。图片加载的起点是ImageLoader.load(url, imageView)需要考虑URL的唯一key问题因为不同尺寸、不同裁剪方式的图片URL会不一样缓存key也不能只靠URL来定。第二层缓存层。三级缓存策略——内存缓存LruCache、磁盘缓存DiskLruCache、网络加载。读取顺序是内存→磁盘→网络写入顺序是网络→磁盘→内存。这个顺序不是随便定的内存缓存访问最快但容量小磁盘缓存数据持久化但读取慢于内存网络只在兜底时走。第三层解码层。拿到输入流后要采样压缩先读图片边界inJustDecodeBounds再算采样率inSampleSize最后完整解码。如果图片尺寸超过控件尺寸不做采样压缩一张几MB的图片解出来可能是几十MB的内存卡顿和OOM就是这么来的。第四层显示层。回调到主线程设置到ImageView上同时处理生命周期。这里有个大厂必考细节图片加载要和View的生命周期绑定页面销毁时取消加载任务防止回调时View已经不可见甚至出现内存泄漏。第五层线程控制层。网络请求不能放主线程但回调必须切回主线程所以线程池和Handler的协作是基础。Glide用了一个自定义的线程池核心线程数跟CPU核心数相关任务分为加载和解码两类。如果笔试里遇到这种设计题我的建议是不要只列技术点而是用用户在列表中快速滑动的场景把整套机制串起来列表快速滑动时内存缓存命中直接显示滑动停止后磁盘缓存命中并异步解码都未命中则走网络加载并写缓存。串起来之后考官会认为你有真实业务场景的思考而不是背框架。5.2 组件化与模块化从单工程到多模块的架构演进关于架构设计题京东笔试里有道印象很深的单选以下关于组件化的描述正确的是选项涉及组件化的通信方式、资源冲突解决方案、组件间依赖关系。如果只是用过MVP或MVVM不做大型项目重构这题很容易猜错。组件化的核心目标是解耦和复用。一个大型App拆分成多个业务模块后模块之间不能互相依赖那通信怎么办主流方案有四种依次递进第一种通过接口下沉到基础库模块依赖基础库的接口再由路由框架实现第二种使用ARouter这类路由框架通过路径跳转完成页面间通信第三种使用EventBus等事件总线适合松耦合的跨模块事件通知第四种如果模块间需要共享数据可以抽到独立的Provider模块里。资源冲突问题也是组件化的经典坑。多个库模块里都定义了app_name字符串合到一起时就会出现资源合并冲突。我在实践中的方案是给每个模块的resourcePrefix加前缀在gradle里配置resourcePrefix module_book_从编译层面就强制资源命名唯一。组件化和模块化的区别也经常被问到模块化偏向于代码分层基础库、功能库、业务层组件化偏向于业务维度拆分购物车、订单、商品组件化的粒度更细解耦更彻底但成本也更高。答这个问题你说清楚组件化的目的是解决多人协作下的代码冲突和构建效率问题就抓住了本质。5.3 冷启动速度优化电商App永恒的痛点电商App对启动速度敏感得可怕。有数据表明启动时间每多1秒用户流失率就上一个台阶。所以京东笔试里出现App启动速度优化这种题一点都不意外。我的答题框架是三个减少第一个是减少主线程的初始化任务。Application的onCreate是启动的核心阻塞点。很多团队把所有SDK初始化都堆在onCreate里一个接一个地调初始化方法这是启动慢的罪魁祸首。实际项目里我做过一次重构把非核心的SDK初始化全部延后到子线程或者放到首页首帧渲染完成后通过IdleHandler执行冷启动时间直接从1200ms降到700ms。第二个是减少布局复杂度。启动页和首页的布局如果嵌套层级太深measure和layout的成本非常高。我的方法是用ConstraintLayout替换多层级嵌套的LinearLayout、RelativeLayout并且尽量用merge标签减少View层级用ViewStub懒加载不直接展示的布局区域。第三个是减少IO操作。SharedPreferences的第一次加载是阻塞主线程的所以启动阶段的SP初始化要特别小心。我自己踩过的坑是在Application里读了一个大的SP文件结果在那个版本的迭代里App启动卡了500ms后来改成异步加载和内存缓存才解决。这些优化点在笔试里不需要你给出精确的数据但如果你能说出我用Systrace定位到启动阶段的耗时方法然后用异步化优化把启动时间缩短了30%这个回答的含金量会比单背一两个技术名词高得多。6. 从2018到现在的面试演进经典考点都换了什么马甲6.1 技术栈迭代后核心考察能力没有变我在京东2018年笔试复盘里看到的一些考点放到现在的面试里其实都有对应的新版本只不过换了技术名词和实现方式从AsyncTask到协程当年考AsyncTask的三个方法现在考的是协程的Dispatcher切换和结构化并发但核心还是后台线程工作、回到主线程更新UI这个异步模型。从Handler到Handler协程Handler机制依然是基础但很多团队已经在用协程处理主线程消息面试官更关心你对两种机制各自优缺点的理解。从Glide到Coil图片加载框架的考点从Glide的缓存策略变成了Coil的协程支持和懒加载但三级缓存、采样压缩、生命周期绑定这些底层能力没有任何变化。从MVP到MVVMJetpack2018年的架构题还在讲MVP的接口要不要抽基类现在的架构题开始问ViewModel和LiveData、Compose和xml的选型但本质仍然是如何让代码更好维护、更好测试。所以我在复盘时的第一个结论是不要被新技术名词吓倒先把底层机制吃透。你理解了线程池的调度方式协程的Dispatcher就很容易理解你理解了View的绘制流程Compose的布局原理也能更快上手。6.2 备考建议从笔试题到完整面试体系的搭建如果说有什么经验值得分享我给准备Android校招的人三条建议都是自己踩过坑之后总结的第一建立知识树而不是刷题集。我见过太多同学抱着面试题合集背结果笔试换个问法就懵了。更好的做法是把Android知识按主线梳理Java基础→Android四大组件→Handler与Binder→View体系→性能优化→架构设计→第三方框架源码。每一条主线从头到尾吃透遇到任何变形题都能用底层知识推导出答案。第二用费曼学习法检验掌握程度。对一个知识点你能不能用自己的话讲给一个外行听并且让他听明白如果讲不清楚说明没真正理解。我通常会在准备面试时把每个高频考点写成文章或者录成口述讲一遍之后自己听回放很多以为自己会了的知识点就是这样暴露出来的。第三上手项目用项目反哺理论。笔试里的性能优化题如果你只是背概念很容易被追问卡住。但如果你真的用Profiler排查过一次内存泄漏用Systrace定位过一次卡顿面试官追问细节时你会非常自信。项目不用多一个完整的有优化过程的项目就够关键是每个优化点你都能说清楚为什么这么做做完有什么变化。6.3 大厂笔试的答题策略时间分配与心态管理最后聊一点应试策略层面的心得。大厂笔试的时间一般比较紧张我的分配原则是先易后难、控制单题上限。客观题里如果一道题超过2分钟还拿不准先标记跳过等后面做完再回头。编程题尤其是算法题如果5分钟没有思路赶紧换个解法或者写一个暴力解拿部分分不要死磕一道题。心态上有个很实用的技巧把笔试当成一次知识体检而不是生死判决。遇到不会的题反而是好事它告诉你知识盲区在哪里。我当年做京东笔试的时候有一道关于Binder的题答得特别差下来专门花了一周时间看Binder的源码和跨进程通信相关文章看得非常痛苦但那次之后Binder相关问题我再也没丢过分。这种过一道题、补一个坑的节奏比盲目刷十套卷子都有效。还有一点关于代码规范笔试里手写代码不要只追求跑通要让面试官觉得你是一个有工程习惯的人。变量命名规范、边界条件处理、关键行注释、时间和空间复杂度的标注这些细节在主观编程题里是实打实的加分项。我复盘一些拿到京东Offer的同学的笔试卷普遍都有这个特点——不是说题全做对了而是每一道会做的题都写得非常清楚、思路完整。我个人在实际操作中的体会是大厂笔试的题目其实就是一个加速版的日常工作。它考察的不是你记得多少知识点而是你在有限时间内能不能快速定位问题、调用知识储备、用规范的方式给出一个可行的答案。2018年的京东笔试题放到今天或许技术名词有点旧了但考察逻辑和这些底层能力的要求一点没有过时。你如果能把这份考卷吃透再对照上面这些考点重新梳理一遍自己的知识体系后面再做其他大厂的笔试大概率会发现那些题目都有一种似曾相识的感觉。祝顺利。