原文链接:
前言
这是我最近收集的一个面试问题,专门针对计划找工作的学生。文章涉及的知识比较广泛或零散,一些比较基础的知识我省略了(比如Android的四大组成部分是什么),有的附上了自己的理解,有的附上了相关文章的详细链接。你可以选择你感兴趣的内容并查看一下,我以后还会继续添加。也许你现在不需要它,但当你需要它时寻找它已经太晚了,所以要准备好进行适当的梳理。
基本组件
当屏幕在横向和纵向模式之间切换时,活动的生命周期会发生什么变化?
1. 如果你不配置 android:ConfigChanges,那么让系统默认处理它,并且 Activity 会被重新构建,并且 Activity 的生命周期会再次经历。

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.绘制自己的图表

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 和 JAVA 干货

注:程序圈 LT