Android 面试题汇总

作者: JokAr_ | 来源:发表于2017-10-31 16:15 被阅读253次
  • 记录自己面试和网上别人面试遇到的题目汇总。部分内容来自网络,若有侵权请联系我删除
  • 后续补充会先在 GitHub 上更新,欢迎补充

基础知识


Service生命周期


Activity的创建过程


ActivityManagerService -> </br>
ActivityStackSupervisor -> </br>
ActivityStack -> </br>
ActivityStackSupervisor -> </br>
ApplicationThread

Handler


  • Handler 是Android类库提供的用于接受、传递和处理消息或Runnable对象的处理类,它结合Message、MessageQueue和Looper类以及当前线程实现了一个消息循环机制,用于实现任务的异步加载和处理

  • 主要用途

    • 执行定时任务 指定任务时间,在某个具体时间或某个时间段后执行特定的任务操作,例如使用Handler提供的postDelayed(Runnable r,long delayMillis)方法指定在多久后执行某项操作

    • 线程间的通信 在执行较为耗时的操作时,Handler负责将子线程中执行的操作的结果传递到UI线程,然后UI线程再根据传递过来的结果进行相关UI元素的更新

  • Handler用法

    示意图
  • 根据上面的图片,我们现在来解析一下异步消息处理机制

    • Message:消息体,用于装载需要发送的对象

    • handler:它直接继承自Object。作用是:在子线程中发送Message或者Runnable对象到MessageQueue中;在UI线程中接收、处理从MessageQueue分发出来的Message或者Runnable对象。发送消息一般使用Handler的sendMessage()方法,而发出去的消息经过处理后最终会传递到Handler的handlerMessage()方法中

    • MessageQueue用于存放Message或Runnable对象的消息队列。它由对应的Looper对象创建,并由Looper对象管理。每个线程中都只会有一个MessageQueue对象

    • Looper:是每个线程中的MessageQueue的管家,循环不断地管理MessageQueue接收和分发Message或Runnable的工作。调用Looper的loop()方法后,就会进入到一个无限循环中然后每当发现MessageQueue中存在一条消息,就会将它取出,并调用Handler的handlerMessage()方法。每个线程中也只会有一个Looper对象

  • Handler和Looper对象是属于线程内部的数据,不过也提供与外部线程的访问接口,Handler就是公开给外部线程的接口,用于线程间的通信。Looper是由系统支持的用于创建和管理MessageQueue的依附于一个线程的循环处理对象,而Handler是用于操作线程内部的消息队列的,所以Handler也必须依附一个线程,而且只能是一个线程

  • 链接

解释下Application类


  • 定义

    • 用于维护全局应用程序状态的基础类
    • 代表应用程序的类,也属于Android中的一个系统组件
    • 继承关系: 继承自 ContextWarpper 类
  • Application 与 Context

    • Activity、Service、Application 都是 Context 的子类。Context 是一个抽象类,具体的实现是在 ContextImpl 类中。因此应用程序 APP 共用的 Context 数目公式为:
      context数 = Service数 + Actvity数 + 1(Application 对应的 Context 实例)
      
  • 特点

    • 实例创建方式:单例模式

      • 每个 APP 运行时会首先自动创建 application 类并实例化 application 对象且只有一个(即 application 类是单例模式)
      • 可以通过继承 application 来实现自定义的 application 类
    • 实例形式:全局模式,即不同组件都可以获得 application 对象且都是同一个

    • 生命周期:等于 APP 的生命周期.

  • 连接

APK安装过程


  • APK安装的主要步骤

    • 将 apk 文件复制到 /data/app/ 目录下
    • 解压 apk ,拷贝文件, 创建应用的数据目录
    • 解析 apk 的AndroidManifinest.xml 文件
    • 显示快捷方式
  • 链接

MultiDex工作原理分析和优化方案


应用启动流程分析


View的测量宽/高和最终宽/高有什么区别(另一种问法: view 的 getMeasuredWidth 和 getWidth 有什么区别)


  • 在 View 的默认实现中 View 的测量宽/高和最终宽/高是相等的,只不过 测量宽高(getMeasureWidth/Height)是在 view 的 measure 过程中调用的,而 view 的 最终宽高(getWidth/Height)是在 view 的 layout 过程中调用的;即两者的赋值时机不同,测量宽高比最终宽高获取的时机稍微早点;

  • 在一般开发中这两者的值是一样的,但是也会存在特殊情况,

    • 比如在 view 的 layout 方法中:

      public void layout(int l, int t, int r, int b){
           super.layout(l, t, r+100, b+100);
      }
      

      这个步骤就会导致测量宽高比最终宽高小 100px

    • 另一种情况是在某些情况下 View 需要多次 measure 才能确定自己的测量宽高,在前几次的测量宽高过程中得出的值可能和最终宽高的不一致,但最终来说:测量宽高和最终宽高相等

事件分发流程


  • Android事件分发流程

    Activity(Windwos) -> ViewGroup -> View
    
    流程图说明
    • 对于dispatchTouchEvent , onTouchEvent 返回 true 就是自己消费了,返回 false 就传到父View 的onTouchEvent方法
    • ViewGroup 想把事件分发给自己的 onTouchEvent,需要在onInterceptTouchEvent方法中返回 true 把事件拦截下来
    • ViewGroup 的 onInterceptTouchEvent 默认不拦截,所以 super.onInterceptTouchEvent() = false
    • View(这里指没有子View)没有拦截器,所以 View 的dispatchTouchEventsuper.dispatchTouchEvent(event)默认把事件分发给自己的onTouchEvent

View的渲染机制


编译打包的过程


  • 等待补充

ANR的原理(源码角度)


  • 等待补充

属性动画的原理

  • 等待补充

Android有多个资源文件夹,应用在不同分辨率下是如何查找对应文件夹下的资源的,描述整个过程


  • 等待补充

应用最多占可被分配多少内存


-> 进程数*16M

一个应用中有多少个 Window


为什么Dialog不能用Application的Context


  • Dialog初化始时是通过Context.getSystemServer 来获取 WindowManager,而如果用Application或者Service的Context去获取这个WindowManager服务的话,会得到一个WindowManagerImpl的实例,这个实例里token也是空的。之后在Dialog的show方法中将Dialog的View(PhoneWindow.getDecorView())添加到WindowManager时会给token设置默认值还是null。
    如果这个Context是Activity,则直接返回Activity的mWindowManager,这个mWindowManager在Activity的attach方法被创建,Token指向此Activity的Token,mParentWindow为Activity的Window本身

  • 答案

    那为什么一定要是Activity的Token呢?我想使用Token应该是为了安全问题,通过Token来验证WindowManager服务请求方是否是合法的。如果我们可以使用Application的Context,或者说Token可以不是Activity的Token,那么用户可能已经跳转到别的应用的Activity界面了,但我们却可以在别人的界面上弹出我们的Dialog,想想就觉得很危险。

Android Activity 、 Window 、 View之间的关系


  • 如图

关于Android Force Close 出现的原因 以及解决方法


  • 原因

    • Error
    • OOM , 内存溢出
    • StackOverFlowError
    • RuntimeException(比如空指针异常)
  • 如何避免

    • 可以实现Thread.UncaughtExceptionHandler接口的uncaughtException方法

哪些情况会出现内存泄漏,如何解决


  • 对于使用了Boardcast Receive、ContentObserver、File、Cursor、Stream、Bitmap等资源的时候没有销毁。

    解决方法 -> 应该在不使用的时候关闭或者注销

  • 静态内部类持有外部成员变量

    解决方法 -> 可以使用若引用

  • 使用了 context 持有 Activity 导致 Activity无法释放

    解决方法 -> 使用 ApplicationContext

  • 集合中没有使用的对象没有及时 remove

  • handler 引起的内存泄漏

    解决方法 -> 使用若引用持有 Activity等的 Context

  • 设置过的Listener没有及时移除

    解决方法 -> 在destory 里 设置 Listener 为 null

Android系统的架构


关于第三库问题


Glide4.0源码解析


otto源码解析


  • otto 这个开源项目是一个event bus模式的消息框架,用于程序各个模块之间的通信,此消息框架可以使得各个
    模块之间减少耦合性。

  • 链接

Gilde怎么实现圆角图


  • 实现原理 利用 Transform
  • 代码示例
    public class GlideRoundTransform extends BitmapTransformation {
    
    private static float radius = 0f;
    
    /**
     * 构造函数 默认圆角半径 4dp
     *
     * @param context Context
     */
    public GlideRoundTransform(Context context) {
        this(context, 4);
    }
    
    /**
     * 构造函数
     *
     * @param context Context
     * @param dp      圆角半径
     */
    public GlideRoundTransform(Context context, int dp) {
        super(context);
        radius = Resources.getSystem().getDisplayMetrics().density * dp;
    }
    
    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return roundCrop(pool, toTransform);
    }
    
    private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;
    
        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }
    
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }
    
    @Override
    public String getId() {
        return getClass().getName() + Math.round(radius);
    }
    }
    

其他链接


相关文章

  • Android面试题汇总

    面试题汇总 Android复习资料——Android知识点汇总(一) 史上最全的Android面试题集锦 ForA...

  • 我的Android知识体系

    热门博客 Android知识体系汇总Gityuan数据结构与算法互联网公司Android面试题汇总隔壁老李头 23...

  • iOS最新面试题汇总(四)

    iOS最新面试题汇总:iOS最新面试题汇总(一)iOS最新面试题汇总(二)iOS最新面试题汇总(三)iOS最新面试...

  • iOS最新面试题汇总(三)

    iOS最新面试题汇总:iOS最新面试题汇总(一)iOS最新面试题汇总(二)iOS最新面试题汇总(三)iOS最新面试...

  • iOS最新面试题汇总(一)

    iOS最新面试题汇总:iOS最新面试题汇总(一)iOS最新面试题汇总(二)iOS最新面试题汇总(三)iOS最新面试...

  • iOS最新面试题汇总(二)

    iOS最新面试题汇总:iOS最新面试题汇总(一)iOS最新面试题汇总(二)iOS最新面试题汇总(三)iOS最新面试...

  • Android 面试题9

    2017下半年,一二线互联网公司Android面试题汇总 阿里巴巴 LRUCache原理 彻底解析Android缓...

  • 2021年Android面试题汇总(中级)

    2020年Android面试题汇总(初级)[https://www.jianshu.com/p/feb9584b4...

  • android面试总汇

    基础汇集常用三方库知识点开发模式性能优化Android NDK 面试题汇总Android使用C/C++来保存密钥

  • Android 面试资料

    史上最全的BAT大厂Android面试题汇集,以及常用的Android开发的一些技能点,冷门知识点汇总,开发中遇到...

网友评论

    本文标题:Android 面试题汇总

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