Android 面试题大汇总:基础组件篇之横竖屏切换时 Activity 的生命周期变化

admin 2024-08-21 220 0

原文链接:

前言

这是我最近收集的一个面试问题,专门针对计划找工作的学生。文章涉及的知识比较广泛或零散,一些比较基础的知识我省略了(比如Android的四大组成部分是什么),有的附上了自己的理解,有的附上了相关文章的详细链接。你可以选择你感兴趣的内容并查看一下,我以后还会继续添加。也许你现在不需要它,但当你需要它时寻找它已经太晚了,所以要准备好进行适当的梳理。

基本组件

当屏幕在横向和纵向模式之间切换时,活动的生命周期会发生什么变化?

1. 如果你不配置 android:ConfigChanges,那么让系统默认处理它,并且 Activity 会被重新构建,并且 Activity 的生命周期会再次经历。

android开发简历模板_安卓开发简历个人经验_安卓开发求职简历

onSaveInstanceState() 和 onRestoreIntanceState()。

如果资源相关系统的系统配置发生变化或资源不足,例如旋转屏幕时,会破坏当前活动,在 onStop 之前调用 onSaveInstanceState 调用来保存数据,在重新创建 Activity 时,会在 onStart 之后调用 onRestoreInstanceState。Bundle 数据将传递给 onCreate(不一定有数据)和 onRestoreInstanceState(必须有数据)。当用户或程序员主动销毁某个活动时,它不会被调用,在其他情况下会被调用以保存接口信息。如果代码中的 finish() 或用户按下后退键,则不会有回调。

2. 如果您>设置了 android:configChanges=“orientation|keyboardHidden|screenSize”,则不会重新访问活动的生命周期,也不会重建 activity,并且会调用 onConfigurationChanged 方法。

Activity 的 startActivity 和上下文的 startActivity 之间的差异

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ; 
mContext.startActivity(intent);

以下是 Android 应用启动过程的介绍

整个应用程序启动过程涉及许多步骤,但总的来说,有五个主要阶段:

Launcher 使用 Binder 进程间通信机制来通知 ActivityManagerService 它将要启动一个 Activity;

ActivityManagerService 通过 Binder 进程间通信机制通知 Launcher 进入 Paused 状态。

Launcher 通过 Binder 进程间通信机制通知 ActivityManagerService,它已准备好进入 Paused 状态,因此 ActivityManagerService 会创建一个新进程来启动一个 ActivityThread 实例,而即将到来的 Activity 正在此 ActivityThread 实例中运行;

ActivityThread 通过 Binder 进程间通信机制将 ApplicationThread 类型的 Binder 对象传递给 ActivityManagerService,以便 ActivityManagerService 将来可以通过此 Binder 对象与其通信。

ActivityManagerService 通过 Binder 进程间通信机制通知 ActivityThread,现在一切都准备好了,它实际上可以启动 Activity。

相关文章:Android App 进程启动源代码分析 ()。

如何确保服务不会被终止?

简要介绍 Acitivty 任务堆栈及其使用方法

任务堆栈是后进先出的结构。堆栈顶部的活动处于焦点中,当按下后退按钮时,堆栈中的活动将从堆栈中逐个删除,并调用它们的 onDestory() 方法。如果堆栈中没有活动,系统会回收堆栈,每个APP默认只有一个堆栈,以APP的包名命名

方式一:使用android:launchMode=“standard|singleInstance|single Task|singleTop”控制Accumulation任务栈。

方法 2:意图标志:

Intent intent=new Intent();
intent.setClass(MainActivity.this, MainActivity2.class);
intent.addFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

有许多标志,例如:

Intent.FLAG_ACTIVITY_NEW_TASK 等效于 singleTask

意图。FLAG_ACTIVITY_CLEAR_TOP 等同于 singleTop

相关文章:Android 摘要系列:Activity Intent 标志和任务相关属性 ()。

与上下文相关的问题

“活动”和“服务”和“应用程序”的上下文不同。Activity 继承自 ContextThemeWrapper。Others 继承自 ContextWrapper。每个 Activity、Service 和 Application Context 都是一个新的 ContextImpl 对象 getApplication(),用于获取 Application 实例。但是,此方法只能在 Activities and Services 中调用。

因此,也许在大多数情况下,我们在 Activity 或 Service 中使用 Application,但如果您想在其他场景中获取 Application 的实例,例如 BroadcastReceiver,您可以使用 getApplicationContext() 方法

getApplicationContext() 比 getApplication() 方法更广泛,任何 Context 实例都可以通过调用 getApplicationContext() 方法来获取我们的 Application 对象。

上下文数等于活动数 + 服务数 + 1,这 1 就是应用程序

广播接收机、内容提供商呢?Broadcast Receiver 和 Content Provider 不是 Contexts 的子类,它们持有的 Context 都是从其他地方传递的,因此它们不计入 Context 的总数。

如何在服务中创建对话框

?

1. 获取 Dialog 对象后,我们需要为其设置类型,即:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)

2. 向清单添加权限:

视图

非 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 线程有自己的 ViewRoot,它就可以刷新 UI,即更新 UI 的线程与创建 ViewRoot 的线程相同,或者在执行 checkThread() 之前更新 UI。

相关文章:Android 子线程真的无法更新 UI()。

解决 ScrollView 嵌套 ListView 和 GridView 冲突的方法

重写 ListView 的 onMeasure 方法以自定义高度:

@Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
       super.onMeasure(widthMeasureSpec, expandSpec);
   }

主要考察了对MeasureSpec的三种模式的理解,相关文章

自定义视图优化策略

线程

Handler、Message、Looper、MessageQueue

一、相关概念解释

主线程(UI 线程)。

定义:当程序首次启动时,Android 会同时启动一个 Main Thread:主线程主要负责处理 UI 相关的事件

消息

定义:由处理程序接收和处理的消息对象(bean 对象)

功能:通信过程中相关信息的存储和传输

线程本地

定义:ThreadLocal 是线程内部的一个存储类,允许您在每个线程中存储自己的私有数据。即数据存储后,在指定的线程中只能获取到存储的对象,其他线程无法获取当前线程存储的对象。

用途:负责存储和检索此线程的循环器

消息队列

定义:使用单向链表存储消息列表的数据结构

功能:用于存储通过Handler发送的消息,按照先进先出的方式执行

处理器

定义:消息的主处理器

功能:向消息队列发送消息并处理循环器分配的消息

活套

定义:充当消息队列和处理程序之间的桥梁

功能: 消息循环: 将检索到的消息传递给相应的处理程序

2.绘制自己的图表

android开发简历模板_安卓开发求职简历_安卓开发简历个人经验

3. 向 Handler 发送消息的方法有哪些?

4. 处理程序可以通过哪些方式处理消息?

这直接转到 dispatchMessage() 源代码:

public void dispatchMessage(Message msg) {
   if (msg.callback != null) {
       //1. post()方法的处理方法
       handleCallback(msg);
   } else {
       if (mCallback != null) {
           if (mCallback.handleMessage(msg)) {
               return;
           }
       }
       //2. sendMessage()方法的处理方法
       handleMessage(msg);
   }
}
//1. post()方法的最终处理方法
private static void handleCallback(Message message) {
   message.callback.run();
}
//2. sendMessage()方法的最终处理方法
public void handleMessage(Message msg) {
}

5.Message、Handler、MessageQueue和Looper之间是什么关系?

相关文章:

6. 为什么在子线程中创建 Handler 会引发异常?

Handler 的工作依赖于 Looper,而 Looper(以及消息队列)属于某个线程(ThreadLocal 是线程内部的一个数据存储类,通过它可以在指定的线程中存储数据,其他线程无法访问它),其他线程无法访问它。因此,Handler 间接绑定到线程。因此,为了使用 Handler,必须确保 Looper 对象存在于 Handler 创建的线程中,并且循环已启动。因为子线程默认没有looper,所以会报错。正确的使用方式是:

private final class WorkThread extends Thread {

       private Handler mHandler;

       public Handler getHandler() {
           return mHandler;
       }
         public void quit() {
           mHandler.getLooper().quit();
       }
       @Override
       public void run() {
           super.run();
           //创建该线程对应的Looper,
           // 内部实现
           // 1。new Looper()
           // 2。将1步中的lopper 放在ThreadLocal里,ThreadLocal是保存数据的,主要应用场景是:线程间数据互不影响的情况
           // 3。在1步中的Looper的构造函数中new MessageQueue();
           //其实就是创建了该线程对用的Looper,Looper里创建MessageQueue来实现消息机制
           //对消息机制不懂得同学可以查阅资料,网上很多也讲的很不错。
           Looper.prepare();
           mHandler = new Handler() {
               @Override
               public void handleMessage(Message msg) {
                   super.handleMessage(msg);
                   Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
               }
           };
           //开启消息的死循环处理即:dispatchMessage
           Looper.loop();
           //注意这3个的顺序不能颠倒
           Log.d("WorkThread", "end");
       }
   }

处理程序线程

1. HandlerThread 函数

当系统有多个耗时的任务要执行时,每个任务都会启动一个新线程来执行耗时的任务,这将导致系统多次创建和销毁线程,从而影响性能。为了解决这个问题,Google 提供了 HandlerThread,HandlerThread 就是在线程中创建一个 Looper 循环器,让 Looper 轮询消息队列,当一个耗时的任务进入队列时,你不需要启动一个新的线程,你可以在原来的线程中执行耗时的任务,否则线程会被阻塞。

2. HanlderThread的优缺点

相关文章:Android Multithreading IntentService 详解 ()。

3.意图服务

相关文章:Android Multithreading IntentService 详解 ()。

4.异步任务

1. 什么是AsyncTask

?

AsyncTask 是一个轻量级的异步任务类,它可以在线程池中执行后台任务,然后将执行进度和最终结果传递给主线程并在主线程中更新 UI,通过 AsyncTask 可以更方便地执行后台任务并访问主线程中的 UI,但 AsyncTask 不适合特别耗时的后台任务, 对于特别耗时的任务,建议使用线程池。

2. 如何使用 AsyncTask

三个参数: Params:表示后台任务执行时的参数类型,会传递给 AysncTaskProgress 的 doInBackground() 方法Progress:表示后台任务执行进度的参数类型,将作为 onProgressUpdate() 方法的参数 Result:表示后台任务返回结果的参数类型, 将用作 onPostExecute() 方法的参数 五种方法 onPreExecute(): 异步任务启动前回调,在主线程中执行 doInBackground():执行异步任务, 在线程池中执行 onProgressUpdate() : 在 doInBackground 中调用 publishProgress 时的回调, 在主线程中执行 onPostExecute() : 异步任务执行后的回调, 在主线程上执行 onCancelled():异步任务被取消时的回调

3. AsyncTask导致的内存泄漏

原因:非静态内部类包含对外部类的匿名引用,这会阻止释放活动

修复:将 AsyncTask 内部对外部活动的弱引用改为静态内部类活动,并在 onDestory() 中调用 AsyncTask.cancel()。

4. 失去结果

如果屏幕旋转或活动在后台被系统杀死,则会重新创建该活动,之前运行的 AsyncTask 将持有对前一个活动的引用,该引用不再有效,调用 onPostExecute() 更新接口将不再生效。

5. AsyncTask 并行或串行

AsyncTask 在 Android 2.3 之前默认为并行执行,AsyncTask 在 Android 2.3 之后默认为串行执行,如果需要在 Android 2.3 之后并行执行任务,可以调用 AsyncTask 的 executeOnExecutor();

6. AsyncTask 内部的线程池

私有静态易失性执行器 sDefaultExecutor = SERIAL_EXECUTOR;

sDefaultExecutor 是 SerialExecutor 的一个实例,它是一个静态变量。换句话说,进程中的所有 AsyncTask 对象共享同一个 SerialExecutor 对象。

相关文章:Android多线程-AsyncTask工作原理深度解析()。

一些访谈心经

一般来说,第一轮是基础面试,需要扎实的基础

最常用的 Android 基础知识

Java 基础知识

了解一些常见的东西是如何工作的,例如处理程序、线程等

项目中的技术要点

在第二轮中,你需要更深入地了解一些东西

Android 事件分发的工作原理

Android绘图机制如何

WindowManager知识

进程间传输的模式

Java 内存管理机制

一些常用的列表、映射原则以及子类之间的差异

进入第三轮基本上没有问题,但有以下几点需要注意

当问到工资问题时,要说一个合适的,小公司是武断的,大公司必须谨慎,当心里没有底的时候,你可以告诉对方,让对方给合理的工资。一般都是在原薪的基础上增长的,我听猎头说一般涨幅在15%-30%之间,超NB可以30%以上,如果你觉得自己还不错,很厉害,建议最高涨20%,一般人设定在15%左右最靠谱。根据公司的情况,公司内部一般都有一套机制。

我们的面试原则是拿到合理的薪水,拿到offer。

个人发展,这个问题很难回答,如果不符合公司的发展方向,很有可能与公司格格不入。建议试探性地询问公司缺少什么,以及是否可以给公司需要的东西。当然,对于那些有自我追求的人来说,他们可以被安全大胆地提及。我的方向是建筑师,哈哈哈,太极端了,别跟着我。我觉得选择都是双向的,所以我知道我需要什么。

你最擅长什么 UI,或者是什么其他东西?这个问题更难回答。如果你想说你擅长UI,那不是说你不擅长其他能力吗?虽然我不知道面试官的意图,但我能感觉到这个问题不是那么容易回答的,我会回答说我想做什么生意都可以做。也许答案不是很好,但简而言之,它与公司的位置相匹配,因此不会出错。

文章摘录:安卓开发者百度、阿里巴巴、居美优品、盈科五年访谈心经

简历模板推荐:

技巧:

我创建了一个技术交流群,里面有各行业的大佬,大家都可以在群里聊聊技术内容和文章推荐;如果有合作伙伴想加入,请添加我的微信账号[luotaosc]。

注意“添加组”。

此外,公众号还有一些个人喜欢的视频:

安卓开发求职简历_安卓开发简历个人经验_android开发简历模板

这篇文章不容易,如果你觉得它写得很好,扫码关注它并喜欢它,这是我最大的动力。

安卓开发简历个人经验_安卓开发求职简历_android开发简历模板

关注我,一定会有意想不到的事情在等着你:

每天专注于分享 Android 和 JAVA 干货

安卓开发简历个人经验_android开发简历模板_安卓开发求职简历

注:程序圈 LT