其他分享
首页 > 其他分享> > 史上最全!押题率90%的 Android 中高级工程师面试复习大纲及真题答案整理(终章)

史上最全!押题率90%的 Android 中高级工程师面试复习大纲及真题答案整理(终章)

作者:互联网

缘起

转眼间2020就接近尾声了,年后有跳槽想法的小伙伴们心里应该也有自己的决定了。金三银四青铜五,总不能到跳槽的黄金期再开始复习吧。没办法,都是兄弟,宠着!2020年度Android中高级面试复习大全奉上。

篇幅过长,预计分三篇文章讲解,好兄弟们记得点个关注或者点赞Mark插个眼,后续不容错过哦

上一篇Java基础,计算机网络相关面试题点这里:
史上最全!押题率90%的 Android 中高级工程师面试复习大纲及真题答案整理(上篇)

上一篇Android基础夯实99题,点这里
史上最全!押题率90%的 Android 中高级工程师面试复习大纲及真题答案整理(中篇)

上篇Android高级面试题,点这里
史上最全!押题率90%的 Android 中高级工程师面试复习大纲及真题答案整理(下篇)

4、跨进程通信。

Android中进程和线程的关系?区别?

如何开启多进程?应用是否可以开启N个进程?

在AndroidManifest中给四大组件指定属性android:process开启多进程模式,在内存允许的条件下可以开启N个进程。

为何需要IPC?多进程通信可能会出现的问题?

所有运行在不同进程的四大组件(Activity、Service、Receiver、ContentProvider)共享数据都会失败,这是由于Android为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类的对象会产生多份副本。比如常用例子(通过开启多进程获取更大内存空间、两个或者多个应用之间共享数据、微信全家桶)。

一般来说,使用多进程通信会造成如下几方面的问题:

Android中IPC方式、各种方式优缺点?

讲讲AIDL?如何优化多模块都使用AIDL的情况?

AIDL(Android Interface Definition Language,Android接口定义语言):如果在一个进程中要调用另一个进程中对象的方法,可使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过它客户端可以实现间接调用服务端对象的方法。

AIDL的本质是系统提供了一套可快速实现Binder的工具。关键类和方法:

当有多个业务模块都需要AIDL来进行IPC,此时需要为每个模块创建特定的aidl文件,那么相应的Service就会很多。必然会出现系统资源耗费严重、应用过度重量级的问题。解决办法是建立Binder连接池,即将每个业务模块的Binder请求统一转发到一个远程Service中去执行,从而避免重复创建Service。

工作原理:每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。服务端只需要一个Service并提供一个queryBinder接口,它会根据业务模块的特征来返回相应的Binder对象,不同的业务模块拿到所需的Binder对象后就可以进行远程方法的调用了。

为什么选择Binder?

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

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

而对于Binder来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程,如图:

共享内存不需要拷贝,Binder的性能仅次于共享内存。

Binder机制的作用和原理?

Linux系统将一个进程分为用户空间和内核空间。对于进程之间来说,用户空间的数据不可共享,内核空间的数据可共享,为了保证安全性和独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的,这就需要跨进程之间的数据通信方式。普通的跨进程通信方式一般需要2次内存拷贝,如下图所示:

一次完整的 Binder IPC 通信过程通常是这样:

Binder框架中ServiceManager的作用?

Binder框架 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder驱动,其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。如下图所示:

最后,结合Android跨进程通信:图文详解 Binder机制 的总结图来综合理解一下:

Binder 的完整定义

手写实现简化版AMS(AIDL实现)

与Binder相关的几个类的职责:

aidl文件只是用来定义C/S交互的接口,Android在编译时会自动生成相应的Java类,生成的类中包含了Stub和Proxy静态内部类,用来封装数据转换的过程,实际使用时只关心具体的Java接口类即可。为什么Stub和Proxy是静态内部类呢?这其实只是为了将三个类放在一个文件中,提高代码的聚合性。通过上面的分析,我们其实完全可以不通过aidl,手动编码来实现Binder的通信,下面我们通过编码来实现ActivityManagerService:

1、首先定义IActivityManager接口:

public interface IActivityManager extends IInterface {
    //binder描述符
    String DESCRIPTOR = "android.app.IActivityManager";
    //方法编号
    int TRANSACTION_startActivity = IBinder.FIRST_CALL_TRANSACTION + 0;
    //声明一个启动activity的方法,为了简化,这里只传入intent参数
    int startActivity(Intent intent) throws RemoteException;
}

2、然后,实现ActivityManagerService侧的本地Binder对象基类:

// 名称随意,不一定叫Stub
public abstract class ActivityManagerNative extends Binder implements IActivityManager {

    public static IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in = (IActivityManager) obj.queryLocalInterface(IActivityManager.DESCRIPTOR);
        if (in != null) {
            return in;
        }
        //代理对象,见下面的代码
        return new ActivityManagerProxy(obj);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            // 获取binder描述符
            case INTERFACE_TRANSACTION:
                reply.writeString(IActivityManager.DESCRIPTOR);
                return true;
            // 启动activity,从data中反序列化出intent参数后,直接调用子类startActivity方法启动activity。
            case IActivityManager.TRANSACTION_startActivity:
                data.enforceInterface(IActivityManager.DESCRIPTOR);
                Intent intent = Intent.CREATOR.createFromParcel(data);
                int result = this.startActivity(intent);
                reply.writeNoException();
                reply.writeInt(result);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }
}

3、接着,实现Client侧的代理对象:

public class ActivityManagerProxy implements IActivityManager {
    private IBinder mRemote;

    public ActivityManagerProxy(IBinder remote) {
        mRemote = remote;
    }

    @Override
    public IBinder asBinder() {
        return mRemote;
    }

    @Override
    public int startActivity(Intent intent) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        int result;
        try {
            // 将intent参数序列化,写入data中
            intent.writeToParcel(data, 0);
            // 调用BinderProxy对象的transact方法,交由Binder驱动处理。
            mRemote.transact(IActivityManager.TRANSACTION_startActivity, data, reply, 0);
            reply.readException();
            // 等待server执行结束后,读取执行结果
            result = reply.readInt();
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }
}

4、最后,实现Binder本地对象(IActivityManager接口):

public class ActivityManagerService extends ActivityManagerNative {
    @Override
    public int startActivity(Intent intent) throws RemoteException {
        // 启动activity
        return 0;
    }
}

简化版的ActivityManagerService到这里就已经实现了,剩下就是Client只需要获取到AMS的代理对象IActivityManager就可以通信了。

简单讲讲 binder 驱动吧?

从 Java 层来看就像访问本地接口一样,客户端基于 BinderProxy 服务端基于 IBinder 对象,从 native 层来看来看客户端基于 BpBinder 到 ICPThreadState 到 binder 驱动,服务端由 binder 驱动唤醒 IPCThreadSate 到 BbBinder 。跨进程通信的原理最终是要基于内核的,所以最会会涉及到 binder_open 、binder_mmap 和 binder_ioctl这三种系统调用。

跨进程传递大内存数据如何做?

binder 肯定是不行的,因为映射的最大内存只有 1M-8K,可以采用 binder + 匿名共享内存的形式,像跨进程传递大的 bitmap 需要打开系统底层的 ashmem 机制。

请按顺序仔细阅读下列文章提升对Binder机制的理解程度:

写给 Android 应用工程师的 Binder 原理剖析

Binder学习指南

Binder设计与实现

老罗Binder机制分析系列或Android系统源代码情景分析Binder章节

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

Android系统启动的核心流程如下:

系统是怎么帮我们启动找到桌面应用的?

通过意图,PMS 会解析所有 apk 的 AndroidManifest.xml ,如果解析过会存到 package.xml 中不会反复解析,PMS 有了它就能找到了。

6、启动一个程序,可以主界面点击图标进入,也可以从一个程序中跳转过去,二者有什么区别?

是因为启动程序(主界面也是一个app),发现了在这个程序中存在一个设置为的activity, 所以这个launcher会把icon提出来,放在主界面上。当用户点击icon的时候,发出一个Intent:

Intent intent = mActivity.getPackageManager().getLaunchIntentForPackage(packageName);
mActivity.startActivity(intent);   

跳过去可以跳到任意允许的页面,如一个程序可以下载,那么真正下载的页面可能不是首页(也有可能是首页),这时还是构造一个Intent,startActivity。这个intent中的action可能有多种view,download都有可能。系统会根据第三方程序向系统注册的功能,为你的Intent选择可以打开的程序或者页面。所以唯一的一点 不同的是从icon的点击启动的intent的action是相对单一的,从程序中跳转或者启动可能样式更多一些。本质是相同的。

7、AMS家族重要术语解释。

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,那么对这概念应该不陌生。

8、App启动流程(Activity的冷启动流程)。

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

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

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

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

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

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

最后,再看看另一幅启动流程图来加深理解:

9、ActivityThread工作原理。

10、说下四大组件的启动过程,四大组件的启动与销毁的方式。

广播发送和接收的原理了解吗?

11、AMS是如何管理Activity的?

12、理解Window和WindowManager。

1.Window用于显示View和接收各种事件,Window有三种型:应用Window(每个Activity对应一个Window)、子Widow(不能单独存在,附属于特定Window)、系统window(toast和状态栏)

2.Window分层级,应用Window在1-99、子Window在1000-1999、系统Window在2000-2999.WindowManager提供了增改View的三个功能。

3.Window是个抽象概念:每一个Window对应着一个ViewRootImpl,Window通过ViewRootImpl来和View建立联系,View是Window存在的实体,只能通过WindowManager来访问Window。

4.WindowManager的实现是WindowManagerImpl,其再委托WindowManagerGlobal来对Window进行操作,其中有四种List分别储存对应的View、ViewRootImpl、WindowManger.LayoutParams和正在被删除的View。

5.Window的实体是存在于远端的WindowMangerService,所以增删改Window在本端是修改上面的几个List然后通过ViewRootImpl重绘View,通过WindowSession(每Window个对应一个)在远端修改Window。

6.Activity创建Window:Activity会在attach()中创建Window并设置其回调(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy类创建PhoneWindow实现的。然后通过Activity#setContentView()调用PhoneWindow的setContentView。

13、WMS是如何管理Window的?

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

APK的安装流程如下所示:

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

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

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

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

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

安装完成后,发送广播。

15、Android的打包流程?(即描述清点击 Android Studio 的 build 按钮后发生了什么?)apk里有哪些东西?签名算法的原理?

apk打包流程

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

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

具体说来:

apk组成

MANIFEST.MF(清单文件):其中每一个资源文件都有一个SHA-256-Digest签名,MANIFEST.MF文件的SHA256(SHA1)并base64编码的结果即为CERT.SF中的SHA256-Digest-Manifest值。

CERT.SF(待签名文件):除了开头处定义的SHA256(SHA1)-Digest-Manifest值,后面几项的值是对MANIFEST.MF文件中的每项再次SHA256并base64编码后的值。

CERT.RSA(签名结果文件):其中包含了公钥、加密算法等信息。首先对前一步生成的MANIFEST.MF使用了SHA256(SHA1)-RSA算法,用开发者私钥签名,然后在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被修改。

签名算法的原理

为什么要签名?
什么是签名?

在Apk中写入一个“指纹”。指纹写入以后,Apk中有任何修改,都会导致这个指纹无效,Android系统在安装Apk进行签名校验时就会不通过,从而保证了安全性。

数字摘要

对一个任意长度的数据,通过一个Hash算法计算后,都可以得到一个固定长度的二进制数据,这个数据就称为“摘要”。

补充:

特征:

签名和校验的主要过程

签名就是在摘要的基础上再进行一次加密,对摘要加密后的数据就可以当作数字签名。

签名过程:
校验过程:
数字证书

如何保证公钥的可靠性呢?答案是数字证书,数字证书是身份认证机构(Certificate Authority)颁发的,包含了以下信息:

接收方收到消息后,先向CA验证证书的合法性,再进行签名校验。

注意:Apk的证书通常是自签名的,也就是由开发者自己制作,没有向CA机构申请。Android在安装Apk时并没有校验证书本身的合法性,只是从证书中提取公钥和加密算法,这也正是对第三方Apk重新签名后,还能够继续在没有安装这个Apk的系统中继续安装的原因。

keystore和证书格式

keystore文件中包含了私钥、公钥和数字证书。根据编码不同,keystore文件分为很多种,Android使用的是Java标准keystore格式JKS(Java Key Storage),所以通过Android Studio导出的keystore文件是以.jks结尾的。

keystore使用的证书标准是X.509,X.509标准也有多种编码格式,常用的有两种:pem(Privacy Enhanced Mail)和der(Distinguished Encoding Rules)。jks使用的是der格式,Android也支持直接使用pem格式的证书进行签名。

两种证书编码格式的区别:

二进制格式,所有类型的证书和私钥都可以存储为der格式。

base64编码,内容以-----BEGIN xxx----- 开头,以-----END xxx----- 结尾。

jarsigner和apksigner的区别

Android提供了两种对Apk的签名方式,一种是基于JAR的签名方式,另一种是基于Apk的签名方式,它们的主要区别在于使用的签名文件不一样:jarsigner使用keystore文件进行签名;apksigner除了支持使用keystore文件进行签名外,还支持直接指定pem证书文件和私钥进行签名。

在签名时,除了要指定keystore文件和密码外,也要指定alias和key的密码,这是为什么呢?

keystore是一个密钥库,也就是说它可以存储多对密钥和证书,keystore的密码是用于保护keystore本身的,一对密钥和证书是通过alias来区分的。所以jarsigner是支持使用多个证书对Apk进行签名的,apksigner也同样支持。

Android Apk V1 签名原理

16、说下安卓虚拟机和java虚拟机的原理和不同点?(JVM、Davilk、ART三者的原理和区别)

JVM 和Dalvik虚拟机的区别

JVM:.java -> javac -> .class -> jar -> .jar

架构: 堆和栈的架构.

DVM:.java -> javac -> .class -> dx.bat -> .dex

架构: 寄存器(cpu上的一块高速缓存)

Android2个虚拟机的区别(一个5.0之前,一个5.0之后)

什么是Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

什么是ART:Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时。Google开发者已经花了两年时间开发更快执行效率更高更省电的替代ART运行时。ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装的时候就预编译字节码为机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

ART优点:

ART缺点:

ART和Davlik中垃圾回收的区别?

17、安卓采用自动垃圾回收机制,请说下安卓内存管理的原理?

开放性问题:如何设计垃圾回收算法?

18、Android中App是如何沙箱化的,为何要这么做?

19、一个图片在app中调用R.id后是如何找到的

20、JNI

Java调用C++

C++调用Java

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

so 的加载流程是怎样的,生命周期是怎样的?

这个要从 java 层去看源码分析,是从 ClassLoader 的 PathList 中去找到目标路径加载的,同时 so 是通过 mmap 加载映射到虚拟空间的。生命周期加载库和卸载库时分别调用 JNI_OnLoad 和 JNI_OnUnload() 方法。

21、请介绍一下NDK?

5、其他高频面试题

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

保活方案

1、AIDL方式单进程、双进程方式保活Service。(基于onStartCommand() return START_STICKY)

START_STICKY 在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。不久后service就会再次尝试重新创建,因为保留在开始状态,在创建 service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。

除了华为此方案无效以及未更改底层的厂商不起作用外(START_STICKY字段就可以保持Service不被杀)。此方案可以与其他方案混合使用

2、降低oom_adj的值(提升service进程优先级):

Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。

此方案无效果

成功对华为手机保活。小米8下也成功突破20分钟

复活方案

1、onDestroy方法里重启service

service + broadcast 方式,就是当service走onDestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。

2、JobScheduler:原理类似定时器,5.0,5.1,6.0作用很大,7.0时候有一定影响(可以在电源管理中给APP授权)。

只对5.0,5.1、6.0起作用。

3、推送互相唤醒复活:极光、友盟、以及各大厂商的推送。

4、同派系APP广播互相唤醒:比如今日头条系、阿里系。

此外还可以监听系统广播判断Service状态,通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活。

结论:高版本情况下可以使用弹出通知栏、双进程、无声音乐提高后台服务的保活概率。

2、Android动画框架实现原理。

Animation 框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View。实现原理:

每次绘制视图时,View 所在的 ViewGroup 中的 drawChild 函数获取该View 的 Animation 的 Transformation 值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,继续调用 invalidate() 函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源,最重要的是,动画改变的只是显示,并不能响应事件。

3、Activity-Window-View三者的差别?

Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutInflater像剪刀,Xml配置像窗花图纸。

在Activity中调用attach,创建了一个Window, 创建的window是其子类PhoneWindow,在attach中创建PhoneWindow。 在Activity中调用setContentView(R.layout.xxx), 其中实际上是调用的getWindow().setContentView(), 内部调用了PhoneWindow中的setContentView方法。

创建ParentView:

作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类), 将指定的R.layout.xxx进行填充, 通过布局填充器进行填充【其中的parent指的就是DecorView】, 调用ViewGroup的removeAllView(),先将所有的view移除掉,添加新的view:addView()。

参考文章

4、低版本SDK如何实现高版本api?

5、说说你对Context的理解?

6、Android的生命周期和启动模式

由A启动B Activity,A为栈内复用模式,B为标准模式,然后再次启动A或者杀死B,说说A,B的生命周期变化,为什么?

Activity的启动模式有哪些?栈里是A-B-C,先想直接到A,BC都清理掉,有几种方法可以做到?这几种方法产生的结果是有几个A的实例?

7、ListView和RecyclerView系列

RecyclerView和ListView有什么区别?局部刷新?前者使用时多重type场景下怎么避免滑动卡顿。懒加载怎么实现,怎么优化滑动体验。

ListView、RecyclerView区别?

一、使用方面:

ListView的基础使用:

RecyclerView 基础使用关键点同样有两点:

RecyclerView 相比 ListView 在基础使用上的区别主要有如下几点:

二、布局方面:

RecyclerView 支持 线性布局、网格布局、瀑布流布局 三种,而且同时还能够控制横向还是纵向滚动。

三、API提供方面:

ListView 提供了 setEmptyView ,addFooterView 、 addHeaderView.

RecyclerView 供了 notifyItemChanged 用于更新单个 Item View 的刷新,我们可以省去自己写局部更新的工作。

四、动画效果:

RecyclerView 在做局部刷新的时候有一个渐变的动画效果。继承 RecyclerView.ItemAnimator 类,并实现相应的方法,再调用 RecyclerView的 setItemAnimator(RecyclerView.ItemAnimator animator) 方法设置完即可实现自定义的动画效果。

五、监听 Item 的事件:

ListView 提供了单击、长按、选中某个 Item 的监听设置。

RecyclerView与ListView缓存机制的不同

想改变listview的高度,怎么做?

listview跟recyclerview上拉加载的时候分别应该如何处理?

如何自己实现RecyclerView的侧滑删除?

RecyclerView的ItemTouchHelper的实现原理

8、如何实现一个推送,消息推送原理?推送到达率的问题?

一:客户端不断的查询服务器,检索新内容,也就是所谓的pull 或者轮询方式。

二:客户端和服务器之间维持一个TCP/IP长连接,服务器向客户端push。

blog.csdn.net/clh604/arti…

www.jianshu.com/p/45202dcd5…

9、动态权限系列。

动态权限适配方案,权限组的概念

Runtime permission,如何把一个预置的app默认给它权限?不要授权。

10、自定义View系列。

Canvas的底层机制,绘制框架,硬件加速是什么原理,canvas lock的缓冲区是怎么回事?

双指缩放拖动大图

TabLayout中如何让当前标签永远位于屏幕中间

TabLayout如何设置指示器的宽度包裹内容?

自定义View如何考虑机型适配?

11、对谷歌新推出的Room架构。

12、没有给权限如何定位,特定机型定位失败,如何解决?

13、Debug跟Release的APK的区别?

14、android文件存储,各版本存储位置的权限控制的演进,外部存储,内部存储

15、有什么提高编译速度的方法?

16、Scroller原理。

Scroller执行流程里面的三个核心方法

mScroller.startScroll();
mScroller.computeScrollOffset();
view.computeScroll();

1、在mScroller.startScroll()中为滑动做了一些初始化准备,比如:起始坐标,滑动的距离和方向以及持续时间(有默认值),动画开始时间等。

2、mScroller.computeScrollOffset()方法主要是根据当前已经消逝的时间来计算当前的坐标点。因为在mScroller.startScroll()中设置了动画时间,那么在computeScrollOffset()方法中依据已经消逝的时间就很容易得到当前时刻应该所处的位置并将其保存在变量mCurrX和mCurrY中。除此之外该方法还可判断动画是否已经结束。

17、Hybrid系列。

webwiew了解?怎么实现和javascript的通信?相互双方的通信。@JavascriptInterface在?版本有bug,除了这个还有其他调用android方法的方案吗?

Android中Java和JavaScript交互
webView.addJavaScriptInterface(new Object(){xxx}, "xxx");
1
答案:可以使用WebView控件执行JavaScript脚本,并且可以在JavaScript中执行Java代码。要想让WebView控件执行JavaScript,需要调用WebSettings.setJavaScriptEnabled方法,代码如下:

WebView webView = (WebView)findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
//设置WebView支持JavaScript
webSettings.setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());

JavaScript调用Java方法需要使用WebView.addJavascriptInterface方法设置JavaScript调用的Java方法,代码如下:

webView.addJavascriptInterface(new Object()
{
    //JavaScript调用的方法
    public String process(String value)
    {
        //处理代码
        return result;
    }
}, "demo");       //demo是Java对象映射到JavaScript中的对象名

可以使用下面的JavaScript代码调用process方法,代码如下:

<script language="javascript">
    function search()
    {
        //调用searchWord方法
        result.innerHTML = "<font color='red'>" + window.demo.process('data') + "</font>";
    }

18、如果在当前线程内使用Handler postdelayed 两个消息,一个延迟5s,一个延迟10s,然后使当前线程sleep 5秒,以上消息的执行时间会如何变化?

答:照常执行

扩展:sleep时间<=5 对两个消息无影响,5< sleep时间 <=10 对第一个消息有影响,第一个消息会延迟到sleep后执行,sleep时间>10 对两个时间都有影响,都会延迟到sleep后执行。

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

20、下拉状态栏是不是影响activity的生命周期,如果在onStop的时候做了网络请求,onResume的时候怎么恢复

21、Android长连接,怎么处理心跳机制。

长连接:长连接是建立连接之后, 不主动断开. 双方互相发送数据, 发完了也不主动断开连接, 之后有需要发送的数据就继续通过这个连接发送.

心跳包:其实主要是为了防止NAT超时,客户端隔一段时间就主动发一个数据,探测连接是否断开。

服务器处理心跳包:假如客户端心跳间隔是固定的, 那么服务器在连接闲置超过这个时间还没收到心跳时, 可以认为对方掉线, 关闭连接. 如果客户端心跳会动态改变, 应当设置一个最大值, 超过这个最大值才认为对方掉线. 还有一种情况就是服务器通过TCP连接主动给客户端发消息出现写超时, 可以直接认为对方掉线.

22、CrashHandler实现原理?

获取app crash的信息保存在本地然后在下一次打开app的时候发送到服务器。

23、SurfaceView和View的最本质的区别?

SurfaceView是在一个新起的单独线程中可以重新绘制画面,而view必须在UI的主线程中更新画面。

在UI的主线程中更新画面可能会引发问题,比如你更新的时间过长,那么你的主UI线程就会被你正在画的函数阻塞。那么将无法响应按键、触屏等消息。当使用SurfaceView由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要在SurfaceView中的thread处理,一般就需要有一个event queue的设计来保存touchevent,这会稍稍复杂一点,因为涉及到线程安全。

24、Android程序运行时权限与文件系统权限

1、Linux 文件系统权限。不同的用户对文件有不同的读写执行权限。在android系统中,system和应用程序是分开的,system里的数据是不可更改的。

2、Android中有3种权限,进程权限UserID,签名,应用申明权限。每次安装时,系统根据包名为应用分配唯一的userID,不同的userID运行在不同的进程里,进程间的内存是独立的,不可以相互访问,除非通过特定的Binder机制。

Android提供了如下的一种机制,可以使两个apk打破前面讲的这种壁垒。

在AndroidManifest.xml中利用sharedUserId属性给不同的package分配相同的userID,通过这样做,两个package可以被当做同一个程序,系统会分配给两个程序相同的UserID。当然,基于安全考虑,两个package需要有相同的签名,否则没有验证也就没有意义了。

25、曲面屏的适配。

26、TextView调用setText方法的内部执行流程。

27、怎么控制另外一个进程的View显示(RemoteView)?

28、如何实现右滑finish activity?

29、如何在整个系统层面实现界面的圆角效果。(即所有的APP打开界面都会是圆角)

30、非UI线程可以更新UI吗?

可以,当访问UI时,ViewRootImpl会调用checkThread方法去检查当前访问UI的线程是哪个,如果不是UI线程则会抛出异常。执行onCreate方法的那个时候ViewRootImpl还没创建,无法去检查当前线程.ViewRootImpl的创建在onResume方法回调之后。

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

非UI线程是可以刷新UI的,前提是它要拥有自己的ViewRoot,即更新UI的线程和创建ViewRoot的线程是同一个,或者在执行checkThread()前更新UI。

31、如何解决git冲突?

32、单元测试有没有做过,说说熟悉的单元测试框架?

首先,Android测试主要分为三个方面:

WanAndroid项目和XXX项目中使用用到了单元测试和部分自动化UI测试,其中单元测试使用的是Junit4+Mockito+PowerMockito+Robolectric。下面我分别简单介绍下这些测试框架:

1、Junit4:

使用@Test注解指定一个方法为一个测试方法,除此之外,还有如下常用注解@BeforeClass->@Before->@Test->@After->@AfterClass以及@Ignore。

Junit4的主要测试方法就是断言,即assertEquals()方法。然后,你可以通过实现TestRule接口的方式重写apply()方法去自定义Junit Rule,这样就可以在执行测试方法的前后做一些通用的初始化或释放资源等工作,接着在想要的测试类中使用@Rule注解声明使用JsonChaoRule即可。(注意被@Rule注解的变量必须是final的。最后,我们直接运行对应的单元测试方法或类,如果你想要一键运行项目中所有的单元测试类,直接点击运行Gradle Projects下的app/Tasks/verification/test即可,它会在module下的build/reports/tests/下生成对应的index.html报告。

Junit4它的优点是速度快,支持代码覆盖率如jacoco等代码质量的检测工具。缺点就是无法单独对Android UI,一些类进行操作,与原生Java有一些差异。

2、Mockito:

可以使用mock()方法模拟各种各样的对象,以替代真正的对象做出希望的响应。除此之外,它还有很多验证方法调用的方式如Mockit.when(调用方法).thenReturn(验证的返回值)、verfiy(模拟对象).验证方法等等。

这里有一点要补充下:简单的测试会使整体的代码更简洁,更可读、更可维护。如果你不能把测试写的很简单,那么请在测试时重构你的代码。

最后,对于Mockito来说,它的优点是有各种各样的方式去验证"模仿对象"的互动或验证发生的某些行为。而它的缺点就是不支持mock匿名类、final类、static方法private方法。

3、PowerMockito:

因此,为了解决Mockito的缺陷,PoweMockito出现了,它扩展了Mockito,支持mock匿名类、final类、static方法、private方法。只要使用它提供的api如PowerMockito.mockStatic()去mock含静态方法或字段的类,PowerMockito.suppress(PowerMockito.method(类.class, 方法名)即可。

4、Robolectric

前面3种我们说的都是Java相关的单元测试方法,如果想在Java单元测试里面进行Android单元测试,还得使用Robolectric,它提供了一套能运行在JVM的Android代码。它提供了一系列类似ShadowToast.getLatestToast()、ShadowApplication.getInstance()这种方式来获取Android平台对应的对象。可以看到它的优点就是支持大部分Android平台依赖类的底层引用与模拟。缺点就是在异步测试的情况下有些问题,这是可以结合Mockito来将异步转为同步即可解决。

最后,自动化UI测试项目中我使用的是Expresso,它提供了一系列类似onView().check().perform()的方式来实现点击、滑动、检测页面显示等自动化的UI测试效果,这里在我的WanAndroid项目下的BasePageTest基类里面封装了一系列通用的方法,有兴趣可以去看看。

33、Jenkins持续集成。

34、工作中有没有用过或者写过什么工具?脚本,插件等等;比如:多人协同开发可能对一些相同资源都各自放了一份,有没有方法自动检测这种重复之类的。

35、如何绕过9.0限制?

如何限制?

区分出是系统调用还是开发者调用:

根据堆栈,回溯Class,查看ClassLoader是否是BootStrapClassLoader。

区分后,再区分是否是hidden api:

Method,Field都有access_flag,有一些备用字段,hidden信息存储其中。

如何绕过?

1、不用反射:

利用一个fakelib,例如写一个android.app.ActivityThread#currentActivityThread空实现,直接调用;

2、伪装系统调用:

jni修改一个class的classloder为BootStrapClassLoader,麻烦。

利用系统方法去反射:

利用原反射,即:getDeclaredMethod这个方法是系统的方法,通过getDeclaredmethod反射去执行hidden api。

3、修改Method,Field中存储hidden信息的字段:

利用jni去修改。

36、对文件描述符怎么理解?

37、如何实现进程安全写文件?

面试资料PDF电子书

快速入手通道:(点这里)百度网盘下载!诚意满满!!!

Android中高级面试题集解析电子书(1294页)

快速入手通道:(点这里)百度网盘下载!诚意满满!!!

听说一键三连的粉丝都面试成功了?如果本篇博客对你有帮助,请支持下小编哦

Android高级面试精选题、架构师进阶实战文档传送门:我的GitHub

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!!!

参考

https://juejin.cn/post/6844904079169159175

https://juejin.cn/post/6905226221357891592

https://juejin.cn/post/6888222422760488974

https://juejin.cn/post/6844904155153170439

https://juejin.cn/post/6844904087566155784

标签:调用,Java,真题,Activity,押题,Binder,90%,进程,Android
来源: https://blog.csdn.net/zzz777qqq/article/details/112321383