Android 面试题笔记

什么是 ANR?

ANR(Application NotResponding):应用程序无响应。

如果应用程序有一段时间响应不够灵敏,系统会弹出一个对话框,提示应用无响应。

发生无响应的时间

(均为前台)

  • Activity 5秒
  • BroadcastReceiver 10秒
  • Service 20秒

ANR LOG路径:/data/anr/traces.txt

栈的打印方式,最新log在最开头。

什么可能导致 ANR?怎么解决?

存在的问题:主线程存在耗时的计算,主线程中出现错误操作,比如人为调用 Thread.wait() 或者 Thread.sleep() 等。

解决思路:

使用子线程处理耗时操作。

使用 Handler 处理工作线程的结果返回,而不是使用 Thread.wait() Thread.sleep() 来阻塞线程。

注意

需要注意的是使用 Thread 或者 HandlerThread 时,调用 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND) 设置线程优先级,否则仍然会降低程序的响应,因为默认 Thread 的优先级和主线程相同。

Activity Fragment 的生命周期有哪些?

Activity

onCreate() onStart() onResume onPause() onStop() onDestroy() onRestart() 共7个。

生命周期的几种普遍情况

  1. 针对开启的B页面Activity,第一次启动,回调如下:onCreate()->onStart()->onResume()
  2. 用户打开B页面Activiy的时候,A页面的Activity【处于不可见】的回调如下:onPause()->onStop()
  3. 再次从B页面按back键回到A页面原Activity时,A页面【从不可见到可见】回调如下:onRestart()->onStart()->onResume()
  4. 按back键回退时,B页面Activity回调如下:onPause()->onStop()->onDestory()
  5. 按Home键切换到桌面后又回到A页面该Actitivy,回调如下:onPause()->onStop()->onRestart()->onStart()->onResume()
  6. 调用finish()方法后,回调如下:onDestory()(以在onCreate()方法中调用为例,不同方法中回调不同,通常都是在onCreate()方法中调用)

Fragment

onAttach() onCreate() onCreateView() onActivityCreated() onStart() onResume() onPause() onStop() onDestroyView() onDestroy() onDetach() 共11个。

能说说横竖屏切换时 Activity 的生命周期吗?

  • 不设置 Activity 的 android:configChanges 时,切屏会重新回调各个生命周期,切横屏执行一次,切竖屏执行两次。
  • 设置 Activity 的 android:configChanges="orientation" 时,切屏时回调各个生命周期,横竖屏都只执行一次。
  • 设置 Activity 的 android:configChanges="orientation|keyboardHidden" 时,切屏时不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法

AsyncTask 是什么?说说它的原理。

AsyncTask 是一种轻量级的异步任务类,

  • 可以在线程池种执行后台任务,然后把执行的进度最终结果传递给主线程,并在主线程更新 UI。

AsyncTask 是一个抽象类,提供三个泛型参数。

  • Params 表示参数的类型
  • Progress 表示后台任务的执行进度类型
  • Result 表示后台任务的返回结果的类型

AsyncTask 对应的线程池 ThreadPoolExecutor 都是进程范围内共享的。

AsyncTask 线程池默认 corePoolSize 数量为 CPU+1;maximumPoolSize 数量为 CPU*2+1;workQueue 数量为128

onSaveInstanceState() 与 onRestoreInstanceState()

  • 这两种方法不是生命周期方法
  • 由系统销毁一个 Activity 时,onSaveInstanceState() 会被调用。当用户主动销毁(按返回键)则不会调用
  • onSaveInstanceState() 方法只适用于保存一些临时状态,onPause() 适合数据的持久化保存
  • Activity 被杀掉之前调用 onSaveInstanceState(),以保证可以在 onCreate(Bundle) 或者 onSaveInstanceState(Bundle) 中传入由 onSaveInstanceState() 封装好的 Bundle 参数。

Jar 与 Aar 的区别

  • Jar 中只有代码
  • Aar 中包括代码、资源文件(比如 drawable 文件、xml资源文件)

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

Android 程序一般 16M ,也有 24M。
应用程序一般 200M.

Android 进程优先级

优先级越大,越不容易被回收。优先级从高到底如下:

  1. 前台进程
    与用户正在交互的 Activity 或者用到的 Service
  2. 可见进程
    处于暂停状态 onPause 的 Activity 或绑定在其上的 Service
  3. 服务进程
    运行着 startService 方法启动的 Service
  4. 后台进程
    运行着执行 onStop 而停止的程序,例如后台挂着的微信。
  5. 空进程
    不包含任何应用程序的进程,系统一般不会让它存在。

Bundle 传递对象为什么要序列化?

因为 Bundle 只支持基本数据类型,所以传递对象时需要序列化转换成可存储或者可传输的字节流。

Serializable 和 Parcelable 的区别

Serializable(Java自带)表示将一个对象转换成存储或可传输的状态

Parcelable(Android专用)的实现原理是将一个完整的对象进行分解,分解后的每一部分都是 Intent 支持的数据类型,也就实现传递对象的功能了。

待续

知识点补充

线程池原理

摘子《石杉的架构笔记 -- 线程池-无限队列问题》

当我们在代码中构建一个线程池时,比如下面的代码:

ExecutorService threadPool = Executors.newFixedThreadPool(10);

查看 newFixedThreadPool 的源码:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

刚开始,线程池都是空的,一个线程没有。

如果使用线程池提交一个任务进去,比如:

threadPool.execute(new Runnable() {
    @Override
    public void run() {
        //TODO
    }
});

线程池会先看一下 当前池子里的线程数量有没有达到 corePoolSize。此时线程池为空,因此会新建一个线程

当该线程处理完任务后,线程不会被销毁,会一直等待下一个提交过来的任务。

线程池会维护一个 workQueue,线程处理完任务之后,就会用阻塞的方式尝试从 workQueue 中获取任务,如果 workQueue 是空的,则会一直阻塞住等待任务。

接着再次提交任务,线程池发现线程数量小于 corePoolSize(10个),则继续在线程池中创建一个线程,然后处理该任务,处理完也阻塞式等待获取任务。

重复以上操作,直到线程池中有10个线程了。

此时,来了任务都放在 workQueue 中,线程从 workQueue 中阻塞式获取任务。

假设 workQueue 中只能放10个任务(有界队列),同时线程池里的10个线程都来不及处理这些任务了,导致 workQueue 满了,此时任务再入队列就会失败。

此时就会尝试在线程池中创建线程,直到线程数量达到 maximumPoolSize

假设 maximumPoolSize 为20(默认可能和 corePoolSize 一致)则会一直创建线程,如下图。

当线程数量达到 maximumPoolSize,并且 workQueue 也满了,此时会 reject 掉,默认会抛出一个异常。

超出 corePoolSize 的线程会尝试等待 workQueue 里的任务,一旦超过 keepAliveTime 指定的时间还获取不到任务,就会自动释放掉,销毁该线程。

无限队列问题

如果 workQueue 特别大,近乎无限,比如 LinkedBlockingQueue 默认的最大任务数量是 Integer.MAX_VALUE。

只要队列不满,就不会创建超过 corePoolSize 数量的线程。

特别极端的情况下,线程池中的线程处理任务时间特别长,导致任务队列积压越来越多的任务,最后导致内存使用量飙升,导致JVM OOM。

posted @ 2021-03-15 13:28  张小凡I4CU  阅读(77)  评论(0编辑  收藏  举报