Android常问的面试题(二)

(一)图片

1、图片库对比

2、LRUCache原理

LruCache是个泛型类,主要原理是:把最近使用的对象用强引用存储在LinkedHashMap中,当缓存满时,把最近最少使用的对象从内存中移除,并提供get/put方法完成缓存的获取和添加。LruCache是线程安全的,因为使用了synchronized关键字。

当调用put()方法,将元素加到链表头,如果链表中没有该元素,大小不变,如果没有,需调用trimToSize方法判断是否超过最大缓存量,trimToSize()方法中有一个while(true)死循环,如果缓存大小大于最大的缓存值,会不断删除LinkedHashMap中队尾的元素,即最少访问的,直到缓存大小小于最大缓存值。当调用LruCache的get方法时,LinkedHashMap会调用recordAccess方法将此元素加到链表头部。

3、图片加载原理

4、自己去实现图片库,怎么做?

5、Glide源码解析

1)Glide.with(context)创建了一个RequestManager,同时实现加载图片与组件生命周期绑定:在Activity上创建一个透明的ReuqestManagerFragment加入到FragmentManager中,通过添加的Fragment感知Activty\Fragment的生命周期。因为添加到Activity中的Fragment会跟随Activity的生命周期。在RequestManagerFragment中的相应生命周期方法中通过liftcycle传递给在lifecycle中注册的LifecycleListener

2)RequestManager.load(url) 创建了一个RequestBuilder对象 T可以是Drawable对象或是ResourceType等

3) RequestBuilder.into(view)

-->into(glideContext.buildImageViewTarget(view, transcodeClass))返回的是一个DrawableImageViewTarget, Target用来最终展示图片的,buildImageViewTarget-->ImageViewTargetFactory.buildTarget()根据传入class参数不同构建不同的Target对象,这个Class是根据构建Glide时是否调用了asBitmap()方法,如果调用了会构建出BitmapImageViewTarget,否则构建的是GlideDrawableImageViewTarget对象。

-->GenericRequestBuilder.into(Target),该方法进行了构建Request,并用RequestTracker.runRequest()

    Request request = buildRequest(target);//构建Request对象,Request是用来发出加载图片的,它调用了buildRequestRecursive()方法以,内部调用了GenericRequest.obtain()方法

    target.setRequest(request);

    lifecycle.addListener(target);

    requestTracker.runRequest(request);//判断Glide当前是不是处于暂停状态,若不是则调用Request.begin()方法来执行Request,否则将Request添加到待执行队列里,等暂停态解除了后再执行

-->GenericRequest.begin()

1)onSizeReady()--> Engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,

            priority, isMemoryCacheable, diskCacheStrategy, this) --> a)先构建EngineKey; b) loadFromCache从缓存中获取EngineResource,如果缓存中获取到cache就调用cb.onResourceReady(cached); c)如果缓存中不存在调用loadFromActiveResources从active中获取,如果获取到就调用cb.onResourceReady(cached);d)如果active中也不存在,调用EngineJob.start(EngineRunnable), 从而调用decodeFromSource()/decodeFromCache()-->如果是调用decodeFromSource()-->ImageVideoFetcher.loadData()-->HttpUrlFetcher()调用HttpUrlConnection进行网络请求资源-->得于InputStream()后,调用decodeFromSourceData()-->loadProvider.getSourceDecoder().decode()方法解码-->GifBitmapWrapperResourceDecoder.decode()-->decodeStream()先从流中读取2个字节判断是GIF还是普通图,若是GIF调用decodeGifWrapper()来解码,若是普通静图则调用decodeBitmapWrapper()来解码-->bitmapDecoder.decode()

6、Glide使用什么缓存?

1) 内存缓存:LruResourceCache(memory)+弱引用activeResources

Map>> activeResources正在使用的资源,当acquired变量大于0,说明图片正在使用,放到activeResources弱引用缓存中,经过release()后,acquired=0,说明图片不再使用,会把它放进LruResourceCache中

2)磁盘缓存:DiskLruCache,这里分为Source(原始图片)和Result(转换后的图片)

第一次获取图片,肯定网络取,然后存active\disk中,再把图片显示出来,第二次读取相同的图片,并加载到相同大小的imageview中,会先从memory中取,没有再去active中获取。如果activity执行到onStop时,图片被回收,active中的资源会被保存到memory中,active中的资源被回收。当再次加载图片时,会从memory中取,再放入active中,并将memory中对应的资源回收。

之所以需要activeResources,它是一个随时可能被回收的资源,memory的强引用频繁读写可能造成内存激增频繁GC,而造成内存抖动。资源在使用过程中保存在activeResources中,而activeResources是弱引用,随时被系统回收,不会造成内存过多使用和泄漏。

7、Glide内存缓存如何控制大小?

Glide内存缓存最大空间(maxSize)=每个进程可用最大内存*0.4(低配手机是   每个进程可用最大内存*0.33)

磁盘缓存大小是250MB   int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;

(二)网络和安全机制

    网络框架对比和源码分析

    自己去设计网络请求框架,怎么做?

    okhttp源码

    网络请求缓存处理,okhttp如何处理网络缓存的

(1)网络缓存优先考虑强制缓存,再考虑对比缓存

    首先判断强制缓存中的数据的是否在有效期内。如果在有效期,则直接使用缓存。如果过了有效期,则进入对比缓存。

    在对比缓存过程中,判断ETag是否有变动,如果服务端返回没有变动,说明资源未改变,使用缓存。如果有变动,判断Last-Modified。

    判断Last-Modified,如果服务端对比资源的上次修改时间没有变化,则使用缓存,否则重新请求服务端的数据,并作缓存工作。

(2)okhttp缓存

开启使用Okhttp的缓存其实很简单,只需要给OkHttpClient对象设置一个Cache对象即可,创建一个Cache时指定缓存保存的目录和缓存最大的大小即可。

    //新建一个cache,指定目录为外部目录下的okhttp_cache目录,大小为100M

    Cache cache = new Cache(new File(Environment.getExternalStorageDirectory() + "/okhttp_cache/"), 100 * 1024 * 1024);

    //将cache设置到OkHttpClient中,这样缓存就开始生效了。

    OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();

相关的类有:

1)CacheControl( HTTP中的Cache-Control和Pragma缓存控制):指定缓存规则

2)Cache(缓存类)

3)DiskLruCache(文件化的LRU缓存类)

(1)读取缓存:先获限OkHttpClient的Cache缓存对象,就是上面创建OkHttpClient设置的Cahce; 传Request请求到Cache的get方法查找缓存响应数据Response;构造一个缓存策略,再调用它的get去决策使用网络请求还是缓存响应。若使用缓存,它的cacheResponse不为空,networkRequest为空,用缓存构造响应直接返回。若使用请求,则cacheResponse为空,networkRequest不为空,开始网络请求流程。

Cache的get获取缓存方法,计算request的key值(请求url进行md5加密),根据key值去DisLruCache查找是否存在缓存内容,存则则创建绘存Entry实体。ENTRY_METADATA代表响应头信息,ENTRY_BODY代表响应体信息。如果缓存存在,在指定目录下会有两个文件****.0    *****.1分别存储某个请求缓存响应头和响应体信息。

CacheStrategy的get方法:1)若缓存响应为空或 2)请求是https但缓存响应没有握手信息;3)请求和缓存响应都是不可缓存的;4)请求是onCache,并且又包含if-Modified-Since或If-None-Match则不使用缓存; 再计算请求有效时间是否符合响应的过期时间,若响应在有效范围内,则缓存策略使用缓存,否则创建一个新的有条件的请求,返回有条件的缓存策略。

(2)存储缓存流程:从HttpEngine的readResponse()发送请求开始,判断hasBody(userResponse),如果缓存的话,maybeCache()缓存响应头信息,unzip(cacheWritingResponse(storeRequest, userResponse))缓存响应体。

    从网络加载一个10M的图片,说下注意事项

    TCP的3次握手和四次挥手

    TCP与UDP的区别

    TCP与UDP的应用

    HTTP协议

    HTTP1.0与2.0的区别

    HTTP报文结构

    HTTP与HTTPS的区别以及如何实现安全性

    如何验证证书的合法性?

    https中哪里用了对称加密,哪里用了非对称加密,对加密算法(如RSA)等是否有了解?

    client如何确定自己发送的消息被server收到?

    谈谈你对WebSocket的理解

    WebSocket与socket的区别

    谈谈你对安卓签名的理解。

    请解释安卓为啥要加签名机制?

    视频加密传输

    App 是如何沙箱化,为什么要这么做?

    权限管理系统(底层的权限是如何进行 grant 的)?

(三)数据库

    sqlite升级,增加字段的语句

    数据库框架对比和源码分析

    数据库的优化

    数据库数据迁移问题

(四)算法

    排序算法有哪些?

    最快的排序算法是哪个?

    手写一个冒泡排序

    手写快速排序代码

    快速排序的过程、时间复杂度、空间复杂度

    手写堆排序

    堆排序过程、时间复杂度及空间复杂度

    写出你所知道的排序算法及时空复杂度,稳定性

    二叉树给出根节点和目标节点,找出从根节点到目标节点的路径

    给阿里2万多名员工按年龄排序应该选择哪个算法?

    GC算法(各种算法的优缺点以及应用场景)

    蚁群算法与蒙特卡洛算法

    子串包含问题(KMP 算法)写代码实现

    一个无序,不重复数组,输出N个元素,使得N个元素的和相加为M,给出时间复杂度、空间复杂度。手写算法

    万亿级别的两个URL文件A和B,如何求出A和B的差集C(提示:Bit映射->hash分组->多文件读写效率->磁盘寻址以及应用层面对寻址的优化)

    百度POI中如何试下查找最近的商家功能(提示:坐标镜像+R树)。

    两个不重复的数组集合中,求共同的元素。

    两个不重复的数组集合中,这两个集合都是海量数据,内存中放不下,怎么求共同的元素?

    一个文件中有100万个整数,由空格分开,在程序中判断用户输入的整数是否在此文件中。说出最优的方法

    一张Bitmap所占内存以及内存占用的计算

一张图片(bitmap)占用的内存影响因素:图片原始长、宽,手机屏幕密度,图片存放路径下的密度,单位像素占用字节数

bitmapSize=图片长度*(inTargetDensity手机的density / inDensity图片存放目录的density)*宽度*(手机的inTargetDensity / inDensity目标存放目录的density)*单位像素占用的字节数(图片长宽单位是像素)

1)图片长宽单位是像素:单位像素字节数由其参数BitmapFactory.Options.inPreferredConfig变量决定,它是Bitmap.Config类型,包括以下几种值:ALPHA_8图片只有alpha值,占用一个字节;ARGB_4444 一个像素占用2个字节,A\R\G\B各占4bits;ARGB_8888一个像素占用4个字节,A\R\G\B各占8bits(高质量图片格式,bitmap默认格式);ARGB_565一个像素占用2字节,不支持透明和半透明,R占5bit, Green占6bit, Blue占用5bit. 从Android4.0开始该项无效。

2) inTargetDensity 手机的屏幕密度(跟手机分辨率有关系)

inDensity原始资源密度(mdpi:160;   hdpi:240;   xhdpi:320;   xxhdpi:480; xxxhdpi:640)

当Bitmap对象在不使用时,应该先调用recycle(),再将它设置为null,虽然Bitmap在被回收时可通过BitmapFinalizer来回收内存。但只有系统垃圾回收时才会回收。Android4.0之前,Bitmap内存分配在Native堆中,Android4.0开始,Bitmap的内存分配在dalvik堆中,即Java堆中,调用recycle()并不能立即释放Native内存。

    2000万个整数,找出第五十大的数字?

    烧一根不均匀的绳,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?

    求1000以内的水仙花数以及40亿以内的水仙花数

    5枚硬币,2正3反如何划分为两堆然后通过翻转让两堆中正面向上的硬8币和反面向上的硬币个数相同

    时针走一圈,时针分针重合几次

    N*N的方格纸,里面有多少个正方形

    x个苹果,一天只能吃一个、两个、或者三个,问多少天可以吃完?

(五)插件化、模块化、组件化、热修复、增量更新、Gradle

    对热修复和插件化的理解

    插件化原理分析

    模块化实现(好处,原因)

    热修复,插件化

    项目组件化的理解

    描述清点击 Android Studio 的 build 按钮后发生了什么

(六)架构设计和设计模式

    谈谈你对Android设计模式的理解

    MVC MVP MVVM原理和区别

    你所知道的设计模式有哪些?

    项目中常用的设计模式

    手写生产者/消费者模式

    写出观察者模式的代码

    适配器模式,装饰者模式,外观模式的异同?

    用到的一些开源框架,介绍一个看过源码的,内部实现过程。

    谈谈对RxJava的理解

RxJava是基于响应式编程,基于事件流、实现异步操(类似于Android中的AsyncTask、Handler作用)作的库,基于事件流的链式调用,使得RxJava逻辑简洁、使用简单。RxJava原理是基于一种扩展的观察者模式,有四种角色:被观察者Observable 观察者Observer 订阅subscribe 事件Event。RxJava原理可总结为:被观察者Observable通过订阅(subscribe)按顺序发送事件(Emitter)给观察者(Observer), 观察者按顺序接收事件&作出相应的响应动作。

RxJava中的操作符:

1)defer():直到有观察者(Observer)订阅时,才会动态创建被观察者对象(Observer)&发送事件,通过Observer工厂方法创建被观察者对象,每次订阅后,都会得到一个刚创建的最新的Observer对象,可以确保Observer对象里的数据是最新的。defer()方法只会定义Observable对象,只有订阅操作才会创建对象。

    Observable observable = Observable.defer(new Callable>() {

        @Override

        public ObservableSource call() throws Exception {

            return Observable.just();

        }

    }

2)timer() 快速创建一个被观察者(Observable),延迟指定时间后,再发送事件

    Observable.timer(2, TimeUnit.SECONDS)//也可以自定义线程timer(long, TimeUnit, Scheduler)

        .subscribe(new Observer() {

            @Override

            public void onSubscribe(Disposable d) {

            }

            ...

         });

3) interval() intervalRange() 快速创建一个被观察者对象(Observable),每隔指定时间就发送事件

    //interval三个参数,参数1:第一次延迟时间  参数2:间隔时间数字   参数3:时间单位

    Observable.interval(3, 1, TimeUnit.SECONDS).subscribe();

    //intervalRange五个参数,参数1:事件序列起始点  参数2:事件数量  参数3:第一次延迟时间 参数4:间隔时间数字   参数5:时间单位

    Observable.intervalRange(3, 10, 2, 1, TimeUnit.SECONDS).subscribe();

    RxJava的功能与原理实现

Rxjava发送事件步骤:

1)创建被观察者对象Observable&定义需要发送的事件

    Observable.create(new ObservableOnSubscribe(){

        @Override

        public void subscribe(ObservableEmitter emitter) throws Exception {

            //定义发送事件的行为

        }

    });

Observable.create()方法实际创建了一个ObservableCreate对象,它是Observable的子类,传入一个ObservableOnSubscribe对象,复写了发送事件行为的subscribe()方法。

2)创建观察者对象Observer&定义响应事件的行为

    Observer observer = new Observer() {

        @Override

        public void onSubscribe(Disposable d){//Disposable对象可用于结束事件

            //默认最先调用

        }

        @Override

        public void onNext(T t){

        }

        @Override

        public void onError(Throwable d){

        }

        @Override

        public void onComplete(){

        }

    }

3)通过subscribe()方法使观察者订阅被观察者

    Observable.subscribe(Observer observer);//实际调用的是ObservableCreate.subscribeActual()方法,具体实现如下

    protected void subscribeActual(Observer observer) {

                  // 1. 创建1个CreateEmitter对象用于发射事件(封装成1个Disposable对象)

                CreateEmitter parent = new CreateEmitter(observer);

                // 2. 调用观察者(Observer)的onSubscribe()

                observer.onSubscribe(parent);

                try {

                    // 3. 调用source对象的(ObservableOnSubscribe对象)subscribe()

                    source.subscribe(parent);

                } catch (Throwable ex) {

                    Exceptions.throwIfFatal(ex);

                    parent.onError(ex);

                }

        }

    RxJava的作用,与平时使用的异步操作来比的优缺点

    说说EventBus作用,实现方式,代替EventBus的方式

    从0设计一款App整体架构,如何去做?

    说一款你认为当前比较火的应用并设计(比如:直播APP,P2P金融,小视频等)

    谈谈对java状态机理解

    Fragment如果在Adapter中使用应该如何解耦?

    Binder机制及底层实现

    对于应用更新这块是如何做的?(解答:灰度,强制更新,分区域更新)?

    实现一个Json解析器(可以通过正则提高速度)

    统计启动时长,标准

(七)性能优化

    如何对Android 应用进行性能分析以及优化?

    ddms 和 traceView

    性能优化如何分析systrace?

    用IDE如何分析内存泄漏?

    Java多线程引发的性能问题,怎么解决?

    启动页白屏及黑屏解决?

    启动太慢怎么解决?

在冷启动系统要执行三个任务:加载和启动应用程序;在app启动后立即显示一个空白窗体;创建APP进程。

应用启动后会执行:创建应用程序对象;启动主线程;创建Main Activity;初始化构造View;在屏幕上布局;执行初始化绘制操作;

应用启动,空白窗口会一直存在直到系统完成了应用的首次绘制操作,此时,系统会替换启动窗口,让用户能和APP进行交互。

对于热启动,如果应用的Activity驻留在内存中,应用就可避免重复进行对象初始化。如果系统执行了内存回收并触发GC,如onTrimMemory(),热启动时对象仍需重建,这样系统进程也会一直显示白屏直到应用完成Activity的渲染。

测量应用启动时间:1) 可通过logcat中查看Displayed中显示启动类耗时; 2) 通过adb shell am start -S -W 包名/启动类全限定名,-S表示重启当前应用,命令进行检测启动app的耗时。3) 使用reportFullyDrawn()方法来测量应用启动到所有资源和视图层次结构完整显示之间的所经过的时间。

优化方法:1)减少view的层级,减少复用和布局嵌套使布局扁平化,不要加载对用户不可见的布局,如使用ViewStub;

2)将需要在主线程中初始化但可不立即完成的延迟加载,部分组件放到子线程中初始化。

3)减少Application.onCreate和启动页和第一个界面onCreate中方法的耗时操作。

4)设置闪屏页,将闪屏页设置为启动页的activity窗口背景windowBackground属性,为启动屏幕提供一个背景,

    怎么保证应用启动不卡顿?

应用卡顿的主要原因有:速度曲线不够流畅,掉帧、触摸响应速度

Android显示机制:app->SurfaceFlinger->Display

开发者选项->GPU呈现模式分析->在屏幕上显示为条形图,可以在屏幕上看到显示每帧绘制花费的时间,有条基准线是16ms,超过这条基线很可能出现掉帧。如果蓝线很长,则说明软件draw太费时,可通过traceview来继续分析draw的java代码。如果中间红色部分很长则说明是openGL ES绘制太费时,用gltrace来分析OpernGL ES调用过程。用systrace分析,用traceview来看代码,

    App启动崩溃异常捕捉

    自定义View注意事项

    现在下载速度很慢,试从网络协议的角度分析原因,并优化(提示:网络的5层都可以涉及)。

    Https请求慢的解决办法(提示:DNS,携带数据,直接访问IP)

    如何保持应用的稳定性

1)需求明确清楚,编码时明确知道要实现的功能和实现方法,技术选型等,对一些库进行封装再使用。防止代码冗余、避免多线程导致的问题;

2)异常崩溃处理捕获,在使用对象前做判空处理等

3)提高编码质量,用lint\findbugs进行代码静态分析;

4)OOM和内存泄漏检测

5)框架测试,兼容性测试、单元测试、monkey测试

6)发布维度,灰度,选择部分渠道进行发布,收集问题;

7)热更新。

    RecyclerView和ListView的性能对比

    ListView的优化

    RecycleView优化

    View渲染

    Bitmap如何处理大图,如一张30M的大图,如何预防OOM

    java中的四种引用的区别以及使用场景

    强引用置为null,会不会被回收?

如果对象没有被GC Roots引用到,会被GC回收到,但什么时候回收需根据JVM的特性什么时候触发GC操作。即使调用了System.gc() JVM也不一定会触发GC。

(八)NDK、jni、Binder、AIDL、进程通信有关

    请介绍一下NDK

    什么是NDK库?

    jni用过吗?

    如何在jni中注册native函数,有几种注册方式?

    Java如何调用c、c++语言?

    jni如何调用java层代码?

1)Java类中要调用jni方法,需要在java类中声明本地方法,public native void methodName();//本地方法。还要在类的静态代码块中导入so库 static { System.loadLibrary("MyJni");}

2)在C/C++获取类的对象的方法有两种:

a)通过c/c++创建java对象,通过对象获取类,通过类获取类的构造方法的ID,基于方法ID和类,创建新对象。

    JNIEXPORT void JNICALL JAVA_nativeMethod(JNIEnv *env, jobject this, jint i) {

        jclass clazz = (*env).GetObjectClass(thiz);

        jmethodID mid = (*env).GetMethodID(clazz, "","()V");

        jobject obj = (*env).NewObject(clazz, mid);

    }

b) 通过c/c++创建不同类对象,通过FindClass方法获取需要的类;通过类获取类的构造方法的ID,基于方法ID和类,创建对象

    JNIEXPORT void JNICALL JAVA_nativeMethod(JNIEnv *env, jobject this, jint i) {

        jclass clazz = (*env).FindClass("com/packagepath/className");

        jmethodID mid = (*env).GetMethodID(clazz, "","()V");

        jobject obj = (*env).NewObject(clazz, mid);

    }

调用java方法跟上面调用构造函数类似,获取类的方法ID,基于对象的方法id调用Java方法

    JNIEXPORT void JNICALL JAVA_nativeMethod(JNIEnv *env, jobject thiz, jint i) {

        jclass clazz = (*env).GetObjectClass(thiz);

        m_Object = (*env).NewGlobalRef(thiz);

        m_mid = (*env).GetMethodID(clazz, "methodName", "()V");//获取Java方法的ID

        m_fid = (*env).GetFieldID(clazz, "a","I");//获取Java变量的ID

        (*env).SetIntField(m_Object, m_fid, i);

        (*env).CallVoidMethod(m_Object, m_mid);

    }

    进程间通信的方式?

    Binder机制

    简述IPC?

    什么是AIDL?

    AIDL解决了什么问题?

    AIDL如何使用?

    Android 上的 Inter-Process-Communication 跨进程通信时如何工作的?

    多进程场景遇见过么?

    Android进程分类?

前台进行(当前正在前台运行的进程,说明用户当前正在与该进程交互), 满足以下至少一个条件的叫做 foreground progcess:

  a.有一个Activity在前台获得焦点可与用户互动

  b.有一个 BroadcastReceiver组件正在运行onReceive()方法

  c.有一个Sevice组件正在运行onCreate()/onStart()/onDestory()方法

可见进程(可见,但用户不能直接与之交互)满足以下条件之一称为可见进程:a.有一个Activity能被用户看见但是失去焦点(处于onPause()状态) b.有一个 Service调用了startForeground()方法 c.绑定了一个Service,系统将该Service作为一个特殊的用户知道的功能使用如自动更换壁纸,输入法服务等。

服务进程(拥有service的进程,一般在后台为用户服务的),通过startService()方法开启的没有绑定在activity上的Service的进程,Service长时间运行 (超过30分钟以上)会被降级到cached process

后台进程(对用户作用不大,缺少该进程一般不会影响用户对系统的体验)

空进程(一般作为缓存机制服务的)

    进程和 Application 的生命周期?

    进程调度

    谈谈对进程共享和线程安全的认识

Android进程共享可通过共享用户ID来实现,

对于SharedPreferences想实现多进程共享需要设置MODE_MULTI_PROCESS,设置了这个Flag后,每次调用Context.getSharedPreferences时系统会重新从SP

SharedPreferences myPrefs = context.getSharedPreferences(MY_FILE_NAME, Context.MODE_MULTI_PROCESS | Context.MODE_PRIVATE);

    谈谈对多进程开发的理解以及多进程应用场景

    什么是协程?

(九)framework层、ROM定制、Ubuntu、Linux之类的问题

    java虚拟机的特性

    谈谈对jvm的理解

    JVM内存区域,开线程影响哪块内存

    对Dalvik、ART虚拟机有什么了解?

    Art和Dalvik对比

    虚拟机原理,如何自己设计一个虚拟机(内存管理,类加载,双亲委派)

    谈谈你对双亲委派模型理解

    JVM内存模型,内存区域

    类加载机制

    谈谈对ClassLoader(类加载器)的理解

    谈谈对动态加载(OSGI)的理解

    内存对象的循环引用及避免

    内存回收机制、GC回收策略、GC原理时机以及GC对象

    垃圾回收机制与调用System.gc()区别

System.gc()只是通知垃圾回收器要进行垃圾回收操作,但并没有立即执行垃圾回收。它只是建议JVM安排GC运行,还有可能被拒绝。

    Ubuntu编译安卓系统

    系统启动流程是什么?(提示:Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程)

    大体说清一个应用程序安装到手机上时发生了什么

    简述Activity启动全部过程

1)Activity.startActivity-->startActivityForResult()

2)-->Instrumentation.execStartActivity()-->execStartActivity()

3)ActivityManager.getService().startActivity()通过Binder到ActivityManagerService.startActivity()

4)-->ActivityStarter.startActivityMayWait()-->startActivityLocked()-->startActivityUnchecked()

5)--ActivityStackSupervisor.resumeFocusedStackTopActivityLocked()-->ApplicationThread$scheduleLaunchActivity

6)ApplicationThread.schedulelaunchActivity()通过ActivityThread.sendMessage,再处理消息,进入handleLaunchActivity

-->Instrumentation$newActivity创建Activity的实例,使用类加载器创建Activity对象。

-->makeApplication创建Application对象,调用它的Application.onCreate()方法

Instrumentation这个类就是完成对Application和Activity初始化和生命周期的工具类。

    App启动流程,从点击桌面开始

1)点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发送startActivity()

2)system_server进程向Zygote发送创建进程的请求(AMS中通过startActivity()方法,调用startProcessLocked()函数),Zygote通过socket通信的方式让Zygote进程 fork一个新进程出来作为App进程;

3)App进程通过Binder IPC向system_server进程发起attachApplication请求(ActivityThread的main()函数里会创建Application,还调用ActivityStackSupervisor.attachApplicationLocked);

4)system_server进程收到请求后,进行一系列准备工作,再通过binder IPC向APP进程发送scheduleLaunchActivity请求;

5)App进程的binder线程(ApplicationThread)收到请求后,通过handler向主线程发送LAUNCHER_ACTIVITY消息;

6)主线程收到Message消息后,通过反射机制创建出Activity并回调Activity.onCreate()等方法;

7)App正式启动,开始进入Activity的生命周期。

    逻辑地址与物理地址,为什么使用逻辑地址?

    Android为每个应用程序分配的内存大小是多少?

根据应用实际使用情况分配,初始给进程分配为8M,应用最大可分配的可能是64M\128M\256M等

    Android中进程内存的分配,能不能自己分配定额内存?

进程内存分配跟手机配置有关,不同手机可能不一样,有64M\128M\256M等,heapgrowthlimit是一个普通应用的内存限制,可通过ActivityManager.getLargeMemoryClass()获得,在mainfest中设置了largeHeap=true后,可以使应用可用内存增大到原来两倍。并不能自己定额分配内存,android系统是根据应用所需要内存大小,先分配初始大小heapstartsize,当应用申请更多内存,系统会再进行分配,但不得超过最大内存,超过了会报OOM。

    进程保活的方式

1)模拟前台进程,startForeground()将当前进程伪装成前台进程,将进程优先级提高到最高,现在前台进程会显示在通知栏中,取消不掉。

2)JobScheduler机制唤醒,系统会根据自己实现定时去调用改接口传递进程去实现一些操作,且这个接口被强制停止后仍能正常启动。在调用JobSchedule.schedule()来启动任务。

3)实现Service类时,将onStartCommand()返回值设置为START_STICKY,利用系统机制在service挂掉后自动拉活,但这种方式只适合原生系统,像小米、华为等定制化比较高的第三方厂商,这些都限制了。

4)一像素的Activity,

5)应用之间相互唤醒。

    如何保证一个后台服务不被杀死?(相同问题:如何保证service在后台不被kill?)比较省电的方式是什么?

    App中唤醒其他进程的实现方式

1)启动其他进程的Activity\Service或是发送一条广播给相应的应用(该应用得静态注册此广播)

OOM定位与分析,如何定位哪块原因导致应用最终发生OOM?

OOM发生后,可以用Android Studio自带的Android Monitor dump出HPROF文件,再用SDK中hprof-conv工具转换为标准的Java堆转储文件格式,再用MAT继续分析。切换到histogram视图,按shadow heap降序排序,对实例对象占用内存大小排序,再查看实例到GC ROOT的路径。

一般可能导致的如图片:直接加载超大尺寸图片(对图片尺寸缩放预处理后再加载)、图片加载后未及时释放(利用LRU机制保证图片个数和占用内存);在页面中,加载非常多的图片(避免同时加载大量图片)

JNI层的crash如何捕获?

通过ndk安装包中的addr2line objdump  ndk-stack工具进行分析crash,ndk针对不同的CPU架构实现了多套相同的工具,在选择add2line objdump时要根据目标机器CPU架构来选择。

一般JNI发生异常,会出现一个Fatal signal信号量,大概知道是哪个函数引起的,再看下面的backtrace日志,backtrace是JNI调用堆栈信息,以“#两位数字 pc”开头,找到对应的函数,再用addr2line进行定位出错的位置。addr2line -C -f -e ./obj/armeabi/xxx.so 000eea70

使用ndk-stack协助我们获取底层崩溃的堆栈信息,adb logcat | ndk-stack -sym ./obj/armeabi/xxx.so

应用卡顿定位

1)使用UI线程的Looper打印日志

Android主线程更新UI,如果1S钟刷新少于60次,即FPS小于60,一帧加载超过16.67ms的话,用户就会产生卡顿的感觉。Android使用消息机制进行UI更新,UI线程中有个Looper,其loop方法会不断提取message,调用其绑定的Handler在UI线程执行。如果handler.dispatchMessage方法中有耗时操作,就会发生卡顿。如果有卡顿,就打印出UI线程的堆栈信息。

优点:用户使用app就可以监控卡顿情况,但因需另开子线程获取堆栈信息,会消耗系统资源。

2)使用Choreographer.FrameCallback监控卡顿

Android系统每16ms会发出SYNC信息,通知界面重绘、渲染,每一次同步的周期为16.6ms,代表一帧的刷新频率可以在两次回调时间周期来判断是否发生卡顿。(Android4.1以上才支持)。可以通过Choreographer.FrameCallback回调doFrame(long)函数,如果两次doFrame之间间隔大于16.67ms说明发生了卡顿。这种方法从app层面来监控卡顿,同时可实时计算帧率和掉帧数,实时监测APP页面的帧率数据,一旦发现帧率过低,可自动保存现场堆栈信息。

卡顿监控系统处理流程:开发修复-》用户上报(后台配置下灰度0.2%的用户量进行卡顿监控和上报,每个用户一天上报一次,上报后删除文件不影响存储空间)-》后台解析(过滤、去重、分类、反解堆栈、入库)-》平台展示-》自动提单

Http2.0有关多路复用:

多路复用原理:HTTP2流和多路复用   HTTP2.0原理详细解析

原理是二进制分帧层+流实现了多路复用,OKHttp是怎么支持的呢,那就是读帧,读流,Okhttp对http2的支持简单分析

ViewStub是怎么实现延时加载的?

ViewStub是一个不可见、大小为0的View。具体体现在ViewStub的构造函数中会进行设置setVisibility(GONE)设置控件不可见,同时会设置setWillNotDraw(true),即本View不会调用onDraw()方法绘制内容。在它的onMeasure函数中会调用setMeasureDimenssion(0, 0)即不会测量视图,直接设置一个大小为0的View.

对于ViewStub.inflate()机制是:1)调用LayoutInflate.flate(mLayoutResource, parent, false)来加载ViewStub中android:layout设置的布局view(最后一个参数false是代表attachToRoot设置成false,说明忽略android:layout中布局根节点的layoutParams参数);2)从父视图中获取当前ViewStub在父视图中位置(在加载父视图时会用一个占位符来代表ViewStub);3)将当前ViewStub从parent中移除;4)将android:layout中的布局view add到父视图中,如果StubView中设置了layoutParams属性就会用ViewStub中设置的。

应用场景:如网络加载失败页面,评论区域内的listView(当没有评论或是请求失败的时候不加载)

加载方式:findViewById(R.id.stubViewId).setVisibility(View.VISIBLE);或是((ViewStub)findViewById(R.id.StubViewId)).inflate();其实设置Visibility最终也是调用inflate来加载布局的。

如果ViewStub标签下写上width/height,在ViewStub相应的layout xml文件中也进行了宽高定义,会以谁为准?

其实上面也分析过了,会以ViewStub中设置的layoutParams为准。

ViewStub可不可以加载多次呢?

不能,上面也分析过了,ViewStub调用inflate()方法后,会把自己从父视图中移除掉,并把自身所包含的内容添加到父视图中,再重新加载,找不到在父视图了,就会抛出ViewStub must have a non-null ViewGroup viewparent。

merge标签

merge一般可以和include/ViewStub结合使用,当include/ViewStub从外部导入xml结构时,可以将被导入的xml用merge作为根节点表示,当被加载到父布局中可以将它们融合到父级结构中,而不会出现冗余的节点。因为它是直接将其中的子元素添加到merge标签的parent中,这样就保证不会引入额外的层级了。

注意:1)只可以作为xml layout的根节点;2)当需要扩展的xml layout本身的根节点也是merge时,需要将被导入的xml layout置于ViewGroup中,且设置attachToRoot为true。

你可能感兴趣的:(Android常问的面试题(二))