加载WebView的同时,请求H5页面数据。
因此常见的方法是: 全局WebView。 客户端代理页面请求。WebView初始化完成后向客户端请求数据。 asset存放离线包。
除此之外还有一些其他的优化手段: 脚本执行慢,可以让脚本最后运行,不阻塞页面解析。 DNS链接慢,可以让客户端复用使用的域名与链接。 React框架代码执行慢,可以将这部分代码拆分出来,提前进行解析。
1.15如何优化自定义View
为了加速你的view,对于频繁调用的方法,需要尽量减少不必要的代码。先从onDraw开始,需要特别注意不应该在这里做内存分配的事情,因为它会导致GC,从而导致卡顿。在初始化或者动画间隙期间做分配内存的动作。不要在动画正在执行的时候做内存分配的事情。
你还需要尽可能的减少onDraw被调用的次数,大多数时候导致onDraw都是因为调用了invalidate().因此请尽量减少调用invaildate()的次数。如果可能的话,尽量调用含有4个参数的invalidate()方法而不是没有参数的invalidate()。没有参数的invalidate会强制重绘整个view。
另外一个非常耗时的操作是请求layout。任何时候执行requestLayout(),会使得Android UI系统去遍历整个View的层级来计算出每一个view的大小。如果找到有冲突的值,它会需要重新计算好几次。另外需要尽量保持View的层级是扁平化的,这样对提高效率很有帮助。
如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup来执行他的layout操作。与内置的view不同,自定义的view可以使得程序仅仅测量这一部分,这避免了遍历整个view的层级结构来计算大小。
1.16TraceView的实现原理,分析数据误差来源。
Andorid性能优化之traceview
2.Android Framework相关
2.1View的事件分发机制?滑动冲突怎么解决?
触摸事件对应的是MotionEvent类,事件的类型主要有如下三种: ACTION_DOWN ACTION_MOVE(移动的距离超过一定的阈值会被判定为ACTION_MOVE操作) ACTION_UP View事件分发本质就是对MotionEvent事件分发的过程。即当一个MotionEvent发生后,系统将这个点击事件传递到一个具体的View上。
事件分发流程 事件分发过程由三个方法共同完成: dispatchTouchEvent:方法返回值为true表示事件被当前视图消费掉;返回为super.dispatchTouchEvent表示继续分发该事件,返回为false表示交给父类的onTouchEvent处理。 onInterceptTouchEvent:方法返回值为true表示拦截这个事件并交由自身的onTouchEvent方法进行消费;返回false表示不拦截,需要继续传递给子视图。如果return super.onInterceptTouchEvent(ev), 事件拦截分两种情况: ? 1.如果该View存在子View且点击到了该子View, 则不拦截, 继续分发 给子View 处理, 此时相当于return false。 2.如果该View没有子View或者有子View但是没有点击中子View(此时ViewGroup 相当于普通View), 则交由该View的onTouchEvent响应,此时相当于return true。
注意:一般的LinearLayout、 RelativeLayout、FrameLayout等ViewGroup默认不拦截, 而 ScrollView、ListView等ViewGroup则可能拦截,得看具体情况。 onTouchEvent:方法返回值为true表示当前视图可以处理对应的事件;返回值为false表示当前视图不处理这个事件,它会被传递给父视图的onTouchEvent方法进行处理。如果return super.onTouchEvent(ev),事件处理分为两种情况: 1.如果该View是clickable或者longclickable的,则会返回true, 表示消费 了该事件, 与返回true一样; 2.如果该View不是clickable或者longclickable的,则会返回false, 表示不 消费该事件,将会向上传递,与返回false一样。
注意:在Android系统中,拥有事件传递处理能力的类有以下三种: Activity:拥有分发和消费两个方法。 ViewGroup:拥有分发、拦截和消费三个方法。 View:拥有分发、消费两个方法。
2.2如何解决View的事件冲突?
常见开发中事件冲突的有ScrollView与RecyclerView的滑动冲突、RecyclerView内嵌同时滑动同一方向。
滑动冲突的处理规则: 对于由于外部滑动和内部滑动方向不一致导致的滑动冲突,可以根据滑动的方向判断谁来拦截事件。 对于由于外部滑动方向和内部滑动方向一致导致的滑动冲突,可以根据业务需求,规定何时让外部View拦截事件,何时由内部View拦截事件。 对于上面两种情况的嵌套,相对复杂,可同样根据需求在业务上找到突破点。
滑动冲突的实现方法: 外部拦截法:指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。具体方法:需要重写父容器的onInterceptTouchEvent方法,在内部做出相应的拦截。 内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器,如果子容器需要此事件就直接消耗,否则就交由父容器进行处理。具体方法:需要配合requestDisallowInterceptTouchEvent方法。
2.3View的绘制流程?
DecorView被加载到Window中
- 从Activity的startActivity开始,最终调用到ActivityThread的handleLaunchActivity方法来创建Activity,首先,会调用performLaunchActivity方法,内部会执行Activity的onCreate方法,从而完成DecorView和Activity的创建。然后,会调用handleResumeActivity,里面首先会调用performResumeActivity去执行Activity的onResume()方法,执行完后会得到一个ActivityClientRecord对象,然后通过r.window.getDecorView()的方式得到DecorView,然后会通过a.getWindowManager()得到WindowManager,最终调用其addView()方法将DecorView加进去。
- WindowManager的实现类是WindowManagerImpl,它内部会将addView的逻辑委托给WindowManagerGlobal,可见这里使用了接口隔离和委托模式将实现和抽象充分解耦。在WindowManagerGlobal的addView()方法中不仅会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView通过root.setView()把DecorView加载到Window中。这里的ViewRootImpl是ViewRoot的实现类,是连接WindowManager和DecorView的纽带。View的三大流程均是通过ViewRoot来完成的。
2.4Android中进程和线程的关系?区别?
- 线程是CPU调度的最小单元,同时线程是一种有限的系统资源;而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。
- 一般来说,一个App程序至少有一个进程,一个进程至少有一个线程(包含与被包含的关系),通俗来讲就是,在App这个工厂里面有一个进程,线程就是里面的生产线,但主线程(即主生产线)只有一条,而子线程(即副生产线)可以有多个。
- 进程有自己独立的地址空间,而进程中的线程共享此地址空间,都可以并发执行。
2.5为何需要IPC?多进程通信可能会出现的问题?
所有运行在不同进程的四大组件(Activity、Service、Receiver、ContentProvider)共享数据都会失败,这是由于Android为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类的对象会产生多份副本。比如常用例子(通过开启多进程获取更大内存空间、两个或者多个应用之间共享数据、微信全家桶)。
一般来说,使用多进程通信会造成如下几方面的问题:
- 静态成员和单例模式完全失效:独立的虚拟机造成。
- 线程同步机制完全失效:独立的虚拟机造成。
- SharedPreferences的可靠性下降:这是因为Sp不支持两个进程并发进行读写,有一定几率导致数据丢失。
- Application会多次创建:Android系统在创建新的进程时会分配独立的虚拟机,所以这个过程其实就是启动一个应用的过程,自然也会创建新的Application。
2.6为什么选择Binder? 为什么选用Binder,在讨论这个问题之前,我们知道Android也是基于Linux内核,Linux现有的进程通信手段有以下几种:
- 管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
- 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
- 共享内存:无须复制,共享缓冲区直接附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- 套接字:作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通信;
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
既然有现有的IPC方式,为什么重新设计一套Binder机制呢。主要是出于以上三个方面的考量:
- 1、效率:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。从Android进程架构角度分析:对于消息队列、Socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝,如图:
而对于Binder来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程,如图:
共享内存不需要拷贝,Binder的性能仅次于共享内存。
- 2、稳定性:上面说到共享内存的性能优于Binder,那为什么不采用共享内存呢,因为共享内存需要处理并发同步问题,容易出现死锁和资源竞争,稳定性较差。Socket虽然是基于C/S架构的,但是它主要是用于网络间的通信且传输效率较低。Binder基于C/S架构 ,Server端与Client端相对独立,稳定性较好。
- 3、安全性:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID,且在Binder通信时会根据UID/PID进行有效性检测。
Binder机制的作用和原理?
Linux系统将一个进程分为用户空间和内核空间。对于进程之间来说,用户空间的数据不可共享,内核空间的数据可共享,为了保证安全性和独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的,这就需要跨进程之间的数据通信方式。普通的跨进程通信方式一般需要2次内存拷贝,如下图所示:
一次完整的 Binder IPC 通信过程通常是这样:
- 首先 Binder 驱动在内核空间创建一个数据接收缓存区。
- 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系。
- 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
Binder框架中ServiceManager的作用?
Binder框架 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder驱动,其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。如下图所示:
- Server&Client:服务器&客户端。在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。
- ServiceManager(如同DNS域名服务器)服务的管理者,将Binder名字转换为Client中对该Binder的引用,使得Client可以通过Binder名字获得Server中Binder实体的引用。
- Binder驱动(如同路由器):负责进程之间binder通信的建立,计数管理以及数据的传递交互等底层支持。
最后,结合Android跨进程通信:图文详解 Binder机制 的总结图来综合理解一下:
2.7AMS家族重要术语解释。
1.ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期。 2.ActivityThread,App的真正入口。当开启App之后,调用main()开始运行,开启消息循环队列,这就是传说的UI线程或者叫主线程。与ActivityManagerService一起完成Activity的管理工作。 3.ApplicationThread,用来实现ActivityManagerServie与ActivityThread之间的交互。在ActivityManagerSevice需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通信。 4.ApplicationThreadProxy,是ApplicationThread在服务器端的代理,负责和客户端的ApplicationThread通信。AMS就是通过该代理与ActivityThread进行通信的。 5.Instrumentation,每一个应用程序只有一个Instrumetation对象,每个Activity内都有一个对该对象的引用,Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。 6.ActivityStack,Activity在AMS的栈管理,用来记录经启动的Activity的先后关系,状态信息等。通过ActivtyStack决定是否需要启动新的进程。 7.ActivityRecord,ActivityStack的管理对象,每个Acivity在AMS对应一个ActivityRecord,来记录Activity状态以及其他的管理信息。其实就是服务器端的Activit对象的映像。 8.TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。如果你清楚Activity的4种launchMode,那么对这概念应该不陌生。
2.8ActivityThread工作原理。
2.9广播发送和接收的原理了解吗?
- 继承BroadcastReceiver,重写onReceive()方法。
- 通过Binder机制向ActivityManagerService注册广播。
- 通过Binder机制向ActivityMangerService发送广播。
- ActivityManagerService查找符合相应条件的广播(IntentFilter/Permission)的BroadcastReceiver,将广播发送到BroadcastReceiver所在的消息队列中。
- BroadcastReceiver所在消息队列拿到此广播后,回调它的onReceive()方法。
2.10apk打包流程
1.通过AAPT工具进行资源文件(包括AndroidManifest.xml、布局文件、各种xml资源等)的打包,生成R.java文件。 2.通过AIDL工具处理AIDL文件,生成相应的Java文件。 3.通过Java Compiler编译R.java、Java接口文件、Java源文件,生成.class文件。 4.通过dex命令,将.class文件和第三方库中的.class文件处理生成classes.dex,该过程主要完成Java字节码转换成Dalvik字节码,压缩常量池以及清除冗余信息等工作。 5.通过ApkBuilder工具将资源文件、DEX文件打包生成APK文件。 6.通过Jarsigner工具,利用KeyStore对生成的APK文件进行签名。 7.如果是正式版的APK,还会利用ZipAlign工具进行对齐处理,对齐的过程就是将APK文件中所有的资源文件距离文件的起始距位置都偏移4字节的整数倍,这样通过内存映射访问APK文件的速度会更快,并且会减少其在设备上运行时的内存占用。
2.11说下安卓虚拟机和java虚拟机的原理和不同点?
JVM、Davilk、ART三者的原理和区别 JVM 和Dalvik虚拟机的区别 JVM:.java -> javac -> .class -> jar -> .jar 架构: 堆和栈的架构. DVM:.java -> javac -> .class -> dx.bat -> .dex 架构: 寄存器(cpu上的一块高速缓存)
3.Android优秀三方库源码及架构设计
3.1原理性问题
网络底层框架:OkHttp实现原理 网络封装框架:Retrofit实现原理 响应式编程框架:RxJava实现原理 图片加载框架:Glide实现原理 事件总线框架:EventBus实现原理 内存泄漏检测框架:LeakCanary实现原理 依赖注入框架:ButterKnife实现原理 依赖全局管理框架:Dagger2实现原理 数据库框架:GreenDao实现原理 MVC MVP MVVM原理和区别?
3.2为什么要在项目中使用OKHTTP这个库?
1.OkHttp 提供了对最新的 HTTP 协议版本 HTTP/2 和 SPDY 的支持,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接。 2.如果 HTTP/2 和 SPDY 不可用,OkHttp 会使用连接池来复用连接以提高效率。 3.OkHttp 提供了对 GZIP 的默认支持来降低传输内容的大小。 4.OkHttp 也提供了对 HTTP 响应的缓存机制,可以避免不必要的网络请求。 5.当网络出现问题时,OkHttp 会自动重试一个主机的多个 IP 地址。
3.3OKhttp针对网络层有哪些优化?
3.4网络请求缓存处理,okhttp如何处理网络缓存的?
3.5从网络加载一个10M的图片,说下注意事项?
3.6WebSocket与socket的区别?
3.7Retrofit优势
1、功能强大: 支持同步、异步 支持多种数据的解析 & 序列化格式 支持RxJava 2、简洁易用: 通过注解配置网络请求参数 采用大量设计模式简化使用 3、可扩展性好: 功能模块高度封装 解耦彻底,如自定义Converters
3.8为什么用Glide
1、多样化媒体加载:不仅可以进行图片缓存,还支持Gif、WebP、缩略图,甚至是Video。 2、通过设置绑定生命周期:可以使加载图片的生命周期动态管理起来。 3、高效的缓存策略:支持内存、Disk缓存,并且Picasso只会缓存原始尺寸的图片,内Glide缓存的是多种规格,也就是Glide会根据你ImageView的大小来缓存相应大小的图片尺寸。 4、内存开销小:默认的Bitmap格式是RGB_565格式,而Picasso默认的是ARGB_8888格式,内存开销小一半。
3.9Glide源码机制的核心思想:
使用一个弱引用map activeResources来盛放项目中正在使用的资源。Lrucache中不含有正在使用的资源。资源内部有个计数器来显示自己是不是还有被引用的情况,把正在使用的资源和没有被使用的资源分开有什么好处呢??因为当Lrucache需要移除一个缓存时,会调用resource.recycle()方法。注意到该方法上面注释写着只有没有任何consumer引用该资源的时候才可以调用这个方法。那么为什么调用resource.recycle()方法需要保证该资源没有任何consumer引用呢?glide中resource定义的recycle()要做的事情是把这个不用的资源(假设是bitmap或drawable)放到bitmapPool中。bitmapPool是一个bitmap回收再利用的库,在做transform的时候会从这个bitmapPool中拿一个bitmap进行再利用。这样就避免了重新创建bitmap,减少了内存的开支。而既然bitmapPool中的bitmap会被重复利用,那么肯定要保证回收该资源的时候(即调用资源的recycle()时),要保证该资源真的没有外界引用了。这也是为什么glide花费那么多逻辑来保证Lrucache中的资源没有外界引用的原因。
3.10Glide内存缓存如何控制大小?
3.11加载bitmap过程(怎样保证不产生内存溢出)
3.12Bitmap如何处理大图,如一张30M的大图,如何预防OOM?
3.13MVP与MVC的主要区别:
1、(最主要区别)View与Model并不直接交互,而是通过与Presenter交互来与Model间接交互。而在MVC中View可以与Model直接交互。 2、Presenter与View的交互是通过接口来进行的,更有利于添加单元测试。
3.14MVC->MVP->MVVM演进过程
MVC -> MVP -> MVVM 这几个软件设计模式是一步步演化发展的,MVVM 是从 MVP 的进一步发展与规范,MVP 隔离了MVC中的 M 与 V 的直接联系后,靠 Presenter 来中转,所以使用 MVP 时 P 是直接调用 View 的接口来实现对视图的操作的,这个 View 接口的东西一般来说是 showData、showLoading等等。M 与 V已经隔离了,方便测试了,但代码还不够优雅简洁,所以 MVVM 就弥补了这些缺陷。在 MVVM 中就出现的 Data Binding 这个概念,意思就是 View 接口的 showData 这些实现方法可以不写了,通过 Binding 来实现。
3.15MVC的情况下怎么把Activity的C和V抽离?
3.16MVP 架构中 Presenter 定义为接口有什么好处;
3.17MVP如何管理Presenter的生命周期,何时取消网络请求?
3.18Fragment如果在Adapter中使用应该如何解耦?
3.19项目框架里有没有Base类,BaseActivity和BaseFragment这种封装导致的问题,以及解决方法?
3.20从0设计一款App整体架构,如何去做?
3.21实现一个库,完成日志的实时上报和延迟上报两种功能,该从哪些方面考虑?
3.22你最优秀的工程设计项目,是怎么设计和实现的;扩展,如何做成一个平台级产品?
4.其他高频面试题
4.1Android动画框架实现原理。
4.2Activity-Window-View三者的差别?
4.3低版本SDK如何实现高版本api?
4.4Android的生命周期和启动模式
4.5RecyclerView和ListView有什么区别?局部刷新?前者使用时多重type场景下怎么避免滑动卡顿。懒加载怎么实现,怎么优化滑动体验。 ListView、RecyclerView区别?
4.6说说谷歌新推出的Room架构。
4.7Debug跟Release的APK的区别?
他高频面试题 4.1Android动画框架实现原理。
4.2Activity-Window-View三者的差别?
4.3低版本SDK如何实现高版本api?
4.4Android的生命周期和启动模式
4.5RecyclerView和ListView有什么区别?局部刷新?前者使用时多重type场景下怎么避免滑动卡顿。懒加载怎么实现,怎么优化滑动体验。 ListView、RecyclerView区别?
4.6说说谷歌新推出的Room架构。
4.7Debug跟Release的APK的区别?
|