美文网首页安卓开发面试题面试专栏
你真的掌握了吗?Android面试知识点梳理(二)

你真的掌握了吗?Android面试知识点梳理(二)

作者: 6ad3c4d9ae59 | 来源:发表于2019-07-23 11:40 被阅读1次
    image

    4、 第三方库解析

    4.1、Retrofit网络请求框架

    概念:Retrofit是一个基于RESTful的HTTP网络请求框架的封装,其中网络请求的本质是由OKHttp完成的,而Retrofit仅仅负责网络请求接口的封装。

    原理:App应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数,Header、URL等信息,之后由OKHttp完成后续的请求,在服务器返回数据之后,OKHttp将原始的结果交给Retrofit,最后根据用户的需求对结果进行解析。

    retrofit使用

    1.在retrofit中通过一个接口作为http请求的api接口

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public interface NetApi {
     @GET("repos/{owner}/{repo}/contributors")
     Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo);
    }
    </pre>
    

    2.创建一个Retrofit实例

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Retrofit retrofit = new Retrofit.Builder()
     .baseUrl("https://api.github.com/")
     .build();
    </pre>
    

    3.调用api接口

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">NetApi repo = retrofit.create(NetApi.class);
    //第三步:调用网络请求的接口获取网络请求
    retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("username", "path");
    call.enqueue(new Callback<ResponseBody>() { //进行异步请求
     @Override
     public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
     //进行异步操作
     }
     @Override
     public void onFailure(Call<ResponseBody> call, Throwable t) {
     //执行错误回调方法
     }
    });
    </pre>
    

    retrofit动态代理

    retrofit执行的原理如下:

    1.首先,通过method把它转换成ServiceMethod。

    2.然后,通过serviceMethod,args获取到okHttpCall对象。

    3.最后,再把okHttpCall进一步封装并返回Call对象。

    首先,创建retrofit对象的方法如下:

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Retrofit retrofit = new Retrofit.Builder()
     .baseUrl("https://api.github.com/")
     .build();
    </pre>
    

    在创建retrofit对象的时候用到了build()方法,该方法的实现如下:

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public Retrofit build() {
     if (baseUrl == null) {
     throw new IllegalStateException("Base URL required.");
     }
     okhttp3.Call.Factory callFactory = this.callFactory;
     if (callFactory == null) {
     callFactory = new OkHttpClient(); //设置kHttpClient
     }
     Executor callbackExecutor = this.callbackExecutor;
     if (callbackExecutor == null) {
     callbackExecutor = platform.defaultCallbackExecutor(); //设置默认回调执行器
     }
     // Make a defensive copy of the adapters and add the default Call adapter.
     List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
     adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
     // Make a defensive copy of the converters.
     List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
     return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
     callbackExecutor, validateEagerly); //返回新建的Retrofit对象
    }
    </pre>
    

    该方法返回了一个Retrofit对象,通过retrofit对象创建网络请求的接口的方式如下:

    <pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">NetApi repo = retrofit.create(NetApi.class);
    </pre>```
    
    retrofit对象的create()方法的实现如下:
    
    ```<pre spellcheck="false" style="box-sizing: border-box; margin: 5px 0px; padding: 5px 10px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 16px; line-height: inherit; font-family: inherit; vertical-align: baseline; cursor: text; counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; background-color: rgb(240, 240, 240); border-radius: 3px; white-space: pre-wrap; color: rgb(34, 34, 34); letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public <T> T create(final Class<T> service) {
     Utils.validateServiceInterface(service);
     if (validateEagerly) {
     eagerlyValidateMethods(service);
     }
     return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
     new InvocationHandler() {
     private final Platform platform = Platform.get();
     @Override public Object invoke(Object proxy, Method method, Object... args)
     throws Throwable {
     // If the method is a method from Object then defer to normal invocation.
     if (method.getDeclaringClass() == Object.class) {
     return method.invoke(this, args); //直接调用该方法
     }
     if (platform.isDefaultMethod(method)) {
     return platform.invokeDefaultMethod(method, service, proxy, args); //通过平台对象调用该方法
    ``` }
     ServiceMethod serviceMethod = loadServiceMethod(method); //获取ServiceMethod对象
     OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //传入参数生成okHttpCall对象
     return serviceMethod.callAdapter.adapt(okHttpCall); //执行okHttpCall
     }
     });
    }
    </pre>
    

    4.2、图片加载库对比

    Picasso:120K

    Glide:475K

    Fresco:3.4M

    Android-Universal-Image-Loader:162K

    图片函数库的选择需要根据APP的具体情况而定,对于严重依赖图片缓存的APP,例如壁纸类,图片社交类APP来说,可以选择最专业的Fresco。对于一般的APP,选择Fresco会显得比较重,毕竟Fresco3.4M的体量摆在这。根据APP对图片的显示和缓存的需求从低到高,我们可以对以上函数库做一个排序。

    Picasso < Android-Universal-Image-Loader < Glide < Fresco

    2.介绍:

    Picasso :和Square的网络库一起能发挥最大作用,因为Picasso可以选择将网络请求的缓存部分交给了okhttp实现。

    Glide:模仿了Picasso的API,而且在他的基础上加了很多的扩展(比如gif等支持),Glide默认的Bitmap格式是RGB_565,比 Picasso默认的ARGB_8888格式的内存开销要小一半;Picasso缓存的是全尺寸的(只缓存一种),而Glide缓存的是跟ImageView尺寸相同的(即56 56和128 128是两个缓存) 。

    FB的图片加载框架Fresco:最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。为什么说是5.0以下,因为在5.0以后系统默认就是存储在Ashmem区了。

    3.总结:

    Picasso所能实现的功能,Glide都能做,无非是所需的设置不同。但是Picasso体积比起Glide小太多如果项目中网络请求本身用的就是okhttp或者retrofit(本质还是okhttp),那么建议用Picasso,体积会小很多(Square全家桶的干活)。Glide的好处是大型的图片流,比如gif、Video,如果你们是做美拍、爱拍这种视频类应用,建议使用。

    Fresco在5.0以下的内存优化非常好,代价就是体积也非常的大,按体积算Fresco>Glide>Picasso

    不过在使用起来也有些不便(小建议:他只能用内置的一个ImageView来实现这些功能,用起来比较麻烦,我们通常是根据Fresco自己改改,直接使用他的Bitmap层)

    4.3、各种json解析库使用

    参考链接: https://www.cnblogs.com/kunpe...

    (1)Google的Gson

    Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后已被许多公司或用户应用。Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要例外额外的jar,能够直接跑在JDK上。而在使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。类里面只要有get和set方法,Gson完全可以将复杂类型的json到bean或bean到json的转换,是JSON解析的神器。Gson在功能上面无可挑剔,但是性能上面比FastJson有所差距。

    (2)阿里巴巴的FastJson

    Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。

    无依赖,不需要例外额外的jar,能够直接跑在JDK上。FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。

    综上Json技术的比较,在项目选型的时候可以使用Google的Gson和阿里巴巴的FastJson两种并行使用,如果只是功能要求,没有性能要求,可以使用google的Gson,如果有性能上面的要求可以使用Gson将bean转换json确保数据的正确,使用FastJson将Json转换Bean

    5、热点技术

    参考链接- Android组件化方案

    5.1、组件化

    (1)概念:

    组件化:是将一个APP分成多个module,每个module都是一个组件,也可以是一个基础库供组件依赖,开发中可以单独调试部分组件,组件中不需要相互依赖但是可以相互调用,最终发布的时候所有组件以lib的形式被主APP工程依赖打包成一个apk。

    (2)由来:

    1. APP版本迭代,新功能不断增加,业务变得复杂,维护成本高
    2. 业务耦合度高,代码臃肿,团队内部多人协作开发困难
    3. Android编译代码卡顿,单一工程下代码耦合严重,修改一处需要重新编译打包,耗时耗力。
    4. 方便单元测试,单独改一个业务模块,不需要着重关注其他模块。

    (3)优势:

    1. 组件化将通用模块独立出来,统一管理,以提高复用,将页面拆分为粒度更小的组件,组件内部出了包含UI实现,还可以包含数据层和逻辑层
    2. 每个组件度可以独立编译、加快编译速度、独立打包。
    3. 每个工程内部的修改,不会影响其他工程。
    4. 业务库工程可以快速拆分出来,集成到其他App中。
    5. 迭代频繁的业务模块采用组件方式,业务线研发可以互不干扰、提升协作效率,并控制产品质量,加强稳定性。
    6. 并行开发,团队成员只关注自己的开发的小模块,降低耦合性,后期维护方便等。

    (4)考虑问题:

    模式切换:如何使得APP在单独调试跟整体调试自由切换

    组件化后的每一个业务的module都可以是一个单独的APP(isModuleRun=false), release 包的时候各个业务module作为lib依赖,这里完全由一个变量控制,在根项目 gradle.properties里面isModuleRun=true。isModuleRun状态不同,加载application和AndroidManifest都不一样,以此来区分是独立的APK还是lib。

    在build.grade里面配置:

    image

    资源冲突

    当我们创建了多个Module的时候,如何解决相同资源文件名合并的冲突,业务Module和BaseModule资源文件名称重复会产生冲突,解决方案在于:

    每个 module 都有 app_name,为了不让资源名重名,在每个组件的 build.gradle 中增加 resourcePrefix “xxx_强行检查资源名称前缀。固定每个组件的资源前缀。但是 resourcePrefix 这个值只能限定 xml 里面的资源,并不能限定图片资源。

    依赖关系

    多个Module之间如何引用一些共同的library以及工具类

    组件通信

    组件化之后,Module之间是相互隔离的,如何进行UI跳转以及方法调用,具体可以使用阿里巴巴ARouter或者美团的WMRouter等路由框架。

    各业务Module之前不需要任何依赖可以通过路由跳转,完美解决业务之间耦合。

    入口参数

    我们知道组件之间是有联系的,所以在单独调试的时候如何拿到其它的Module传递过来的参数

    Application

    当组件单独运行的时候,每个Module自成一个APK,那么就意味着会有多个Application,很显然我们不愿意重复写这么多代码,所以我们只需要定义一个BaseApplication即可,其它的Application直接继承此BaseApplication就OK了,BaseApplication里面还可定义公用的参数。

    关于如何进行组件化,可以参考: 安居客Android项目架构演进

    5.2、插件化

    参考链接- 插件化入门

    (1)概述

    提到插件化,就不得不提起方法数超过65535的问题,我们可以通过Dex分包来解决,同时也可以通过使用插件化开发来解决。插件化的概念就是由宿主APP去加载以及运行插件APP。

    (2优点)

    在一个大的项目里面,为了明确的分工,往往不同的团队负责不同的插件APP,这样分工更加明确。各个模块封装成不同的插件APK,不同模块可以单独编译,提高了开发效率。

    解决了上述的方法数超过限制的问题。可以通过上线新的插件来解决线上的BUG,达到“热修复”的效果。

    减小了宿主APK的体积。

    (3缺点)

    插件化开发的APP不能在Google Play上线,也就是没有海外市场。

    6、屏幕适配

    6.1、基本概念

    屏幕尺寸

    含义:手机对角线的物理尺寸 单位:英寸(inch),1英寸=2.54cm

    Android手机常见的尺寸有5寸、5.5寸、6寸,6.5寸等等

    屏幕分辨率

    含义:手机在横向、纵向上的像素点数总和

    一般描述成屏幕的”宽x高”=AxB 含义:屏幕在横向方向(宽度)上有A个像素点,在纵向方向

    (高)有B个像素点 例子:1080x1920,即宽度方向上有1080个像素点,在高度方向上有1920个像素点

    单位:px(pixel),1px=1像素点

    UI设计师的设计图会以px作为统一的计量单位

    Android手机常见的分辨率:320x480、480x800、720x1280、1080x1920

    屏幕像素密度

    含义:每英寸的像素点数 单位:dpi(dots per ich)

    假设设备内每英寸有160个像素,那么该设备的屏幕像素密度=160dpi

    6.2、适配方法

    1.支持各种屏幕尺寸: 使用wrap_content, match_parent, weight.要确保布局的灵活性并适应各种尺寸的屏幕,应使用 “wrap_content”、“match_parent” 控制某些视图组件的宽度和高度。

    2.使用相对布局,禁用绝对布局。

    3.使用LinearLayout的weight属性

    假如我们的宽度不是0dp(wrap_content和0dp的效果相同),则是match_parent呢?

    android:layout_weight的真实含义是:如果View设置了该属性并且有效,那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比。

    从这个角度我们来解释一下上面的现象。在上面的代码中,我们设置每个Button的宽度都是match_parent,假设屏幕宽度为L,那么每个Button的宽度也应该都为L,剩余宽度就等于L-(L+L)= -L。

    Button1的weight=1,剩余宽度占比为1/(1+2)= 1/3,所以最终宽度为L+1/3*(-L)=2/3L,Button2的计算类似,最终宽度为L+2/3(-L)=1/3L。

    4.使用.9图片

    6.3、今日头条屏幕适配

    参考链接: 今日头条屏幕适配方案终极版

    相关文章

      网友评论

        本文标题:你真的掌握了吗?Android面试知识点梳理(二)

        本文链接:https://www.haomeiwen.com/subject/yylolctx.html