宁波IT培训
美国上市IT培训机构

0574-87236644

Android面试题集(二)

  • 时间:2019-01-09 14:52
  • 发布:郭孝星 Open软件开发小组
  • 来源:微信公众号

Android里的Intent传递的数据有大小限制吗,如何解决?

Intent传递数据大小的限制大概在1M左右,超过这个限制就会静默崩溃。处理方式如下:

  • 进程内:EventBus,文件缓存、磁盘缓存。

  • 进程间:通过ContentProvider进行款进程数据共享和传递。

描述一下Android的事件分发机制?

Android事件分发机制的本质:事件从哪个对象发出,经过哪些对象,最终由哪个对象处理了该事件。此处对象指的是Activity、Window与View。

Android事件的分发顺序:Activity(Window) -> ViewGroup -> View

Android事件的分发主要由三个方法来完成,如下所示:

// 父View调用dispatchTouchEvent()开始分发事件public boolean dispatchTouchEvent
(MotionEvent event){ boolean consume = false; // 父View决定是否拦截事件 if(onI
nterceptTouchEvent(event)){ // 父View调用onTouchEvent(event)消费事件,如果该方
法返回true,表示 // 该View消费了该事件,后续该事件序列的事件(Down、Move、Up)将
不会在传递 // 该其他View。 consume = onTouchEvent(event);
    }else{ // 调用子View的dispatchTouchEvent(event)方法继续分发事件 consume =
 child.dispatchTouchEvent(event);
    } return consume;
}

描述一下View的绘制原理?

View的绘制流程主要分为三步:

  1. onMeasure:测量视图的大小,从顶层父View到子View递归调用measure()方法,measure()调用onMeasure()方法,onMeasure()方法完成测量工作。

  2. onLayout:确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上。

  3. onDraw:绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为:① 绘制视图背景。② 绘制画布的图层。 ③ 绘制View内容。 ④ 绘制子视图,如果有的话。⑤ 还原图层。⑥ 绘制滚动条。

requestLayout()、invalidate()与postInvalidate()有什么区别?

  • requestLayout():该方法会递归调用父窗口的requestLayout()方法,直到触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为true,会触发onMesaure()与onLayout()方法,不一定 会触发onDraw()方法。

  • invalidate():该方法递归调用父View的invalidateChildInParent()方法,直到调用ViewRootImpl的invalidateChildInParent()方法,最终触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为false,不会 触发onMesaure()与onLayout()方法,当时会触发onDraw()方法。

  • postInvalidate():该方法功能和invalidate()一样,只是它可以在非UI线程中调用。

一般说来需要重新布局就调用requestLayout()方法,需要重新绘制就调用invalidate()方法。

Scroller用过吗,了解它的原理吗?

了解APK的打包流程吗,描述一下?

Android的包文件APK分为两个部分:代码和资源,所以打包方面也分为资源打包和代码打包两个方面,这篇文章就来分析资源和代码的编译打包原理。

APK整体的的打包流程如下图所示:

具体说来:

  1. 通过AAPT工具进行资源文件(包括AndroidManifest.xml、布局文件、各种xml资源等)的打包,文件。

  2. 通过AIDL工具处理AIDL文件,生成相应的Java文件。

  3. 通过Javac工具编译项目源码,生成Class文件。

  4. 通过DX工具将所有的Class文件转换成DEX文件,该过程主要完成Java字节码转换成Dalvik字节码,压缩常量池以及清除冗余信息等工作。

  5. 通过ApkBuilder工具将资源文件、DEX文件打包生成APK文件。

  6. 利用KeyStore对生成的APK文件进行签名。

  7. 如果是正式版的APK,还会利用ZipAlign工具进行对齐处理,对齐的过程就是将APK文件中所有的资源文件举例文件的起始距离都偏移4字节的整数倍,这样通过内存映射访问APK文件 的速度会更快。

了解APK的安装流程吗,描述一下?

APK的安装流程如下所示:

  1. 复制APK到/data/app目录下,解压并扫描安装包。

  2. 资源管理器解析APK里的资源文件。

  3. 解析AndroidManifest文件,并在/data/data/目录下创建对应的应用数据目录。

  4. 然后对dex文件进行优化,并保存在dalvik-cache目录下。

  5. 将AndroidManifest文件解析出的四大组件信息注册到PackageManagerService中。

  6. 安装完成后,发送广播。

当点击一个应用图标以后,都发生了什么,描述一下这个过程?

点击应用图标后会去启动应用的LauncherActivity,如果LancerActivity所在的进程没有创建,还会创建新进程,整体的流程就是一个Activity的启动流程。

Activity的启动流程图(放大可查看)如下所示:

整个流程涉及的主要角色有:

  • Instrumentation: 监控应用与系统相关的交互行为。

  • AMS:组件管理调度中心,什么都不干,但是什么都管。

  • ActivityStarter:Activity启动的控制器,处理Intent与Flag对Activity启动的影响,具体说来有:1 寻找符合启动条件的Activity,如果有多个,让用户选择;2 校验启动参数的合法性;3 返回int参数,代表Activity是否启动成功。

  • ActivityStackSupervisior:这个类的作用你从它的名字就可以看出来,它用来管理任务栈。

  • ActivityStack:用来管理任务栈里的Activity。

  • ActivityThread:最终干活的人,是ActivityThread的内部类,Activity、Service、BroadcastReceiver的启动、切换、调度等各种操作都在这个类里完成。

注:这里单独提一下ActivityStackSupervisior,这是高版本才有的类,它用来管理多个ActivityStack,早期的版本只有一个ActivityStack对应着手机屏幕,后来高版本支持多屏以后,就 有了多个ActivityStack,于是就引入了ActivityStackSupervisior用来管理多个ActivityStack。

整个流程主要涉及四个进程:

  • 调用者进程,如果是在桌面启动应用就是Launcher应用进程。

  • ActivityManagerService等所在的System Server进程,该进程主要运行着系统服务组件。

  • Zygote进程,该进程主要用来fork新进程。

  • 新启动的应用进程,该进程就是用来承载应用运行的进程了,它也是应用的主线程(新创建的进程就是主线程),处理组件生命周期、界面绘制等相关事情。

有了以上的理解,整个流程可以概括如下:

  1. 点击桌面应用图标,Launcher进程将启动Activity(MainActivity)的请求以Binder的方式发送给了AMS。

  2. AMS接收到启动请求后,交付ActivityStarter处理Intent和Flag等信息,然后再交给ActivityStackSupervisior/ActivityStack 处理Activity进栈相关流程。同时以Socket方式请求Zygote进程fork新进程。

  3. Zygote接收到新进程创建请求后fork出新进程。

  4. 在新进程里创建ActivityThread对象,新创建的进程就是应用的主线程,在主线程里开启Looper消息循环,开始处理创建Activity。

  5. ActivityThread利用ClassLoader去加载Activity、创建Activity实例,并回调Activity的onCreate()方法。这样便完成了Activity的启动。

BroadcastReceiver与LocalBroadcastReceiver有什么区别?

  • BroadcastReceiver 是跨应用广播,利用Binder机制实现。

  • LocalBroadcastReceiver 是应用内广播,利用Handler实现,利用了IntentFilter的match功能,提供消息的发布与接收功能,实现应用内通信,效率比较高。

Android Handler机制是做什么的,原理了解吗?

Android消息循环流程图如下所示:

主要涉及的角色如下所示:

  • Message:消息,分为硬件产生的消息(例如:按钮、触摸)和软件产生的消息。

  • MessageQueue:消息队列,主要用来向消息池添加消息和取走消息。

  • Looper:消息循环器,主要用来把消息分发给相应的处理者。

  • Handler:消息处理器,主要向消息队列发送各种消息以及处理各种消息。

整个消息的循环流程还是比较清晰的,具体说来:

  1. Handler通过sendMessage()发送消息Message到消息队列MessageQueue。

  2. Looper通过loop()不断提取触发条件的Message,并将Message交给对应的target handler来处理。

  3. target handler调用自身的handleMessage()方法来处理Message。

事实上,在整个消息循环的流程中,并不只有Java层参与,很多重要的工作都是在C++层来完成的。我们来看下这些类的调用关系。

注:虚线表示关联关系,实线表示调用关系。

在这些类中MessageQueue是Java层与C++层维系的桥梁,MessageQueue与Looper相关功能都通过MessageQueue的Native方法来完成,而其他虚线连接的类只有关联关系,并没有 直接调用的关系,它们发生关联的桥梁是MessageQueue。

Android Binder机制是做什么的,为什么选用Binder,原理了解吗?

Android Binder是用来做进程通信的,Android的各个应用以及系统服务都运行在独立的进程中,它们的通信都依赖于Binder。

为什么选用Binder,在讨论这个问题之前,我们知道Android也是基于Linux内核,Linux现有的进程通信手段有以下几种:

  1. 管道:在创建时分配一个page大小的内存,缓存区大小比较有限;

  2. 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

  3. 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;

  4. 套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;

  5. 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。6. 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

既然有现有的IPC方式,为什么重新设计一套Binder机制呢。主要是出于以上三个方面的考量:

  • 高性能:从数据拷贝次数来看Binder只需要进行一次内存拷贝,而管道、消息队列、Socket都需要两次,共享内存不需要拷贝,Binder的性能仅次于共享内存。

  • 稳定性:上面说到共享内存的性能优于Binder,那为什么不适用共享内存呢,因为共享内存需要处理并发同步问题,控制负责,容易出现死锁和资源竞争,稳定性较差。而Binder基于C/S架构,客户端与服务端彼此独立,稳定性较好。

  • 安全性:我们知道Android为每个应用分配了UID,用来作为鉴别进程的重要标志,Android内部也依赖这个UID进行权限管理,包括6.0以前的固定权限和6.0以后的动态权限,传荣IPC只能由用户在数据包里填入UID/PID,这个标记完全 是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能,因此Binder的安全性更高。


文章转载作者郭孝星 Open软件开发小组


有兴趣的小伙伴可以关注我们日后的疑难解答哦!当然,你想了解更多关于Java的知识,

可以填写下方的表单,有利于咨询老师更快的找到你哦!我们下次见~

预约申请免费试听课

怕钱不够?就业挣钱后再付学费!    怕学不会?从入学起,达内定制课程!     担心就业?达内多家实践企业供你挑选!

上一篇:Android面试题集(一)
下一篇:Android面试题集(三)

2020年Web前端面试问题总结之JS问题

2020年Web前端面试问题总结之VUE问题

2020年Web前端面试问题总结之React问题

2020年Web前端笔试题汇总

选择城市和中心
江西省

贵州省

广西省

海南省