前言
最近准备换工作,梳理和回顾被问及的技术问题和知识点 , 希望能为Android开发者提供有益的参考.
腾讯二面 Aug 1st, 2024
Activity相关
-
android 中的四大组件
-
- Activities(活动):
-
Activity 是用户与应用程序交互的主要方式之一,代表一个用户界面屏幕。
-
每个 Activity 都是一个独立的窗口,用于展示用户界面元素和接收用户输入。
-
应用程序通常由多个 Activity 组成,用户可以在这些 Activity 之间进行切换。
-
- Services(服务):
-
Service 是在后台执行长时间运行操作或处理多次请求的组件,没有用户界面。
-
Service 可以在后台运行,即使用户切换到其他应用程序,Service 也可以继续运行。
-
可以通过 Service 处理网络请求、播放音乐、执行文件 I/O 等操作。
-
- Broadcast Receivers(广播接收器):
-
Broadcast Receiver 是用于接收系统广播消息或应用程序内部广播消息的组件。
-
可以监听系统事件或应用程序内部的事件,并在事件发生时做出响应。
-
例如,可以通过 Broadcast Receiver 接收系统启动完成的广播、电量低警报等。
-
- Content Providers(内容提供器):
-
Content Provider 用于管理应用程序的数据,并提供数据给其他应用程序。
-
可以通过 Content Provider 将应用程序的数据共享给其他应用程序,实现数据共享和访问。
-
Content Provider 可以用于访问应用程序内部的数据库、文件等数据。
-
-
activity的启动模式
-
standard:标准启动模式(默认启动模式),每次都会启动一个新的 activity 实例;
-
singleTop:单独使用使用这种模式时,如果 Activity 实例位于当前任务栈顶,就重用栈顶实例,而不新建,并回调该实例
onNewIntent()
方法,否则走新建流程; -
singleTask:这种模式启动的 Activity 只会存在相应的 Activity 的 taskAffinit 任务栈中,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,会重新唤起该实例,并清理当前 Task 任务栈该实例之上的所有 Activity,同时回调
onNewIntent()
方法; -
singleInstance:这种模式启动的 Activity 独自占用一个 Task 任务栈,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,只会唤起原实例,并回调
onNewIntent()
方法;singleInstance
的属性是禁止与其他 Activities 共享任务栈,所以启动模式为SingleInstance
的 Activity 启动其他 Activity 时会默认带有FLAG_ACTIVITY_NEW_TASK
属性。所以 Activity E 启动 Activity F 后,最后会存在三个任务栈,Activity F 会单独存在于一个任务栈中
-
-
startActivity() 和 startActivityForResult()的区别
-
startActivity()
-
startActivity()
方法用于启动一个新的 Activity,但不期望获取从该 Activity 返回的结果。 -
适用于单向的页面跳转,比如从一个页面导航到另一个页面,不需要返回结果。
-
-
startActivityForResult()
-
startActivityForResult()
方法用于启动一个新的 Activity,期望并等待从该 Activity 返回结果。 -
被启动的 Activity 在完成后会返回一个结果给调用它的 Activity。
-
适用于需要在新 Activity 完成后获取结果的情况,比如从设置页面返回用户的选择结果等。
-
-
使用示例:
-
startActivity() 示例:
// 启动一个新的 Activity,不需要获取返回结果 Intent intent = new Intent(this, SecondActivity.class); startActivity(intent);
startActivityForResult() 示例:
// 启动一个新的 Activity,并等待结果返回 Intent intent = new Intent(this, SecondActivity.class); startActivityForResult(intent, REQUEST_CODE);
-
-
在被启动的 Activity 中设置返回结果:
-
// 在被启动的 Activity 中设置返回结果 Intent returnIntent = new Intent(); returnIntent.putExtra("result", "Hello from SecondActivity!"); setResult(Activity.RESULT_OK, returnIntent); finish();
-
-
在调用它的 Activity 中处理返回结果:
-
// 在调用它的 Activity 中处理返回结果 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { String result = data.getStringExtra("result"); // 处理返回的结果 } } }
-
-
-
Activity.startActivity() 和 Application.startActivity()的区别
-
Activity.startActivity()
将新的Activity
加入当前Activity
的任务栈。 -
Application.startActivity()
必须使用FLAG_ACTIVITY_NEW_TASK
标志,因为Application
不属于任何任务栈- 如果不使用FLAG_ACTIVITY_NEW_TASK,会抛出异常
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
- 如果不使用FLAG_ACTIVITY_NEW_TASK,会抛出异常
-
-
Activity各生命周期
-
onCreate():
Activity
被创建时调用,用于初始化。 -
onStart():
Activity
即将变得可见时调用。 -
onResume():
Activity
获取用户焦点并可以与用户交互时调用。 -
onPause():
Activity
失去用户焦点但仍然可见时调用。 -
onStop():
Activity
不再可见时调用。 -
onDestroy():
Activity
被销毁之前调用,用于清理资源。
-
-
activity A 打开 activity B 再回退到 ActivityA 的生命周期
-
生命周期是固定的吗?有其他的可能吗?
-
taskAffinity属性的作用
-
应用打开系统相册这个页面,相册这个页面是加到当前应用的任务栈里面吗
- 加入到当前的任务栈中
service相关
-
service的两种启动模式
- Service 的启动方式
-
activity A startService, activity B bindService ,然后ActivityA stopService,会发生什么
-
Service 停止:当
Activity A
调用stopService()
时,Service
会被停止,并最终调用onDestroy()
来销毁Service
。 -
绑定的 Activity 处理:
Activity B
的ServiceConnection
会收到onServiceDisconnected()
通知,表示Service
已经被销毁。
-
-
activity A startService, activity B bindService ,然后ActivityB unBindService,会发生什么
-
Service 的生命周期:
-
Service
在被startService()
启动后会继续运行,直到显式地调用stopSelf()
或stopService()
。 -
当
Activity A
调用stopService()
时,Service
会被停止,即使Activity B
仍然绑定到它。
-
-
unbindService():
-
当
Activity A
调用stopService()
,即使Activity B
仍然绑定到Service
,Service
也会被停止。 -
一旦
Service
被停止,系统会调用onDestroy()
方法来销毁Service
。此时,Activity B
的ServiceConnection
回调方法(如onServiceDisconnected()
)将被调用,以通知Activity B
其绑定的Service
已经被销毁。
-
-
Activity B 的行为:
-
在
Service
被停止后,Activity B
会收到通知(onServiceDisconnected()
),并且必须处理Service
连接的断开情况。 -
如果
Activity B
需要继续与Service
交互,它可能需要在Service
重新启动或重新绑定。
-
-
-
service怎么单独放在一个进程
android:process=":my_process"
指定Service
运行在一个名为my_process
的独立进程中。前缀:
表示这是一个私有进程,仅属于该应用。如果不加前缀,表示这是一个全局进程,可能会与其他应用共享。
-
service怎么单独放在一个进程有什么好处
-
长时间运行的任务:
- 当你有需要长时间运行的后台任务时,将
Service
放在单独的进程中可以确保这些任务在后台稳定运行,而不会干扰前台的用户体验。
- 当你有需要长时间运行的后台任务时,将
-
高内存消耗任务:
- 当
Service
执行的任务需要大量内存(如大数据处理、复杂计算等),将其放在单独进程中可以防止其影响主进程的内存使用。
- 当
-
高安全性需求:
- 处理敏感数据或需要高安全性操作的服务可以放在单独进程中,以提高安全性和隔离性。
-
-
不同进程获取的service对象是同一个吗
- 不是
-
同一个进程获取的server是同一个对象吗
-
单一实例:
- Android 确保在同一个进程中,一个
Service
只有一个实例。这意味着无论你通过startService()
还是bindService()
来访问服务,都会引用到同一个Service
实例。
- Android 确保在同一个进程中,一个
-
绑定和启动服务的行为:
-
当你调用
bindService()
时,onBind()
方法会被调用,并返回一个IBinder
对象。 -
当你调用
startService()
时,onStartCommand()
方法会被调用。 -
如果你同时使用
bindService()
和startService()
来访问同一个服务,这些调用都会作用在同一个Service
实例上。
-
-
-
Aidl 客户端掉用的是A方法,在remote端掉用的是B方法,什么原因导致的
广播相关
-
广播的两种启动方式和区别
-
有序广播
-
怎么制定广播只发送给B接收器
-
广播可以开启子线程去请求网络吗
- 在 Android 中,
BroadcastReceiver
是在主线程(UI 线程)中运行的,因此网络操作直接在主线程中是不允许的,因为这会导致应用的 ANR(Application Not Responding,应用无响应)问题。你需要将网络请求放在子线程中进行。
- 在 Android 中,
-
广播中可以调用startActivity吗
ContentProvider相关
- ContentProvider怎么使用
- 怎么调用系统的ContentProvider
- ContentProvider可以进行多线程操作吗
- 多个应用操作同一个ContentProvider,会有问题吗
其它
- ListView中图片加载错乱问题
- Glide 源码
- 如何加载一张大图
- sp构造方法传入的模式有什么区别
快手二面 Jul 29th, 2024
算法
- 【23】合并K个生序链表
给你一个链表数组,每个链表都已经按升序排列。
// 示例 1:
//
// 输入:lists = 1,4,5],[1,3,4],[2,6
//输出:[1,1,2,3,4,4,5,6]
//解释:链表数组如下:
//[
// 1->4->5,
// 1->3->4,
// 2->6
//]
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
Queue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
for (ListNode node: lists) {
if (node != null) {
pq.offer(node);
}
}
ListNode dummyHead = new ListNode(0);
ListNode tail = dummyHead;
while (!pq.isEmpty()) {
ListNode minNode = pq.poll();
tail.next = minNode;
tail = minNode;
if (minNode.next != null) {
pq.offer(minNode.next);
}
}
return dummyHead.next;
}
}
腾讯一面 Jul 9th, 2024
-
SDK提供给外部应用,外部应用稳定复现的问题,我们sdk复现不了。外部应用提给我们的只有release包,应该怎么解决
-
编写sdk应该注意什么
-
58同城首页关注哪些核心业务指标
-
了解过bugly的原理吗
-
android 奔溃日志输出的原理
-
sdk 怎么去获取崩溃日志
-
好看app视频关注哪些核心业务指标
-
视频帧率、码率怎么监控
-
ANR和卡顿是怎么监控
-
加载一张大图
-
对于普通的图片,加载的思路很简单就是压缩大小,用Options来获得大小然后和当前屏幕大小进行比较,然后以一定的值压缩。
-
获取大图尺寸
-
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.qb, options);
-
-
获取ImageVIew的尺寸,缩放减小内存
-
根据ImageView大小计算缩放比例inSampleSize
-
选择占内存小的颜色模式,设置option.inPreferredConfig,有以下值可以选择
-
-
加载
-
-
但对于超大图片,Android提供了[BitmapRegionDecoder]
-
商汤二面 Jul 8th, 2024
-
View 绘制流程的优化点
-
怎么学习前沿知识
-
android 不同版本的对比
-
android 组件化
-
android 进程间通信
京东四面 Jul 8th, 2024
- SAN
- 电量优化
- 视频帧率优化
- 用户反馈进入应用显示无网,但是其他app都是有网
- android 绘制的流程
- 自定义View,写一个棋盘,棋盘个数50*50,每个棋盘的宽度自适应;2500个棋盘,随机1秒将背景颜色变成蓝色
- 过滤掉重复的区域
- android 新特性
- 跨端方面
美团二面 Jul 5th, 2024
- RN 为主,andorid原生比较少
猿辅导一面 Jul 5th, 2024
-
kotlin的扩展方法原理
-
代理属性原理
-
by lazy 的原理
https://www.jianshu.com/p/5d5bff9269f1
-
kotlin 协程原理
-
handler 原理
-
handler的内存泄漏
-
handler的内存泄漏 的gcRoot 是那个类
-
主线程 Looper 的静态成员变量sMainLooper
-
子线程 应该是当前线程对象
-
Thread成员变量 -> ThreadLocalMap 成员变量 -> Entry[] ->
-
Entry是继承WeakReference ,WeakReference构造方法,
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
-
key 是ThreadLocal,value 时 Looper对象
-
Looper持有 MessageQunue
-
MessageQunue 持有 Message
-
-
项目相关
-
增量编译
-
MVC、MVP、MVVM
-
LiveData的问题
-
jetpack都有哪些
-
性能优化、启动优化、层级优化
-
算法题:View的最大层级
public static int maxDeep(View view) {
//view不会有子view所以就返回0
if (!(view instanceof ViewGroup)) {
return 0;
}
ViewGroup viewGroup = (ViewGroup) view;
//是viewgroup如果并没有子view,那么也是最底层的view
if (viewGroup.getChildCount() == 0) {
return 0;
}
//记录最大层数
int max = 0;
//广度遍历view
for (int i = 0; i < viewGroup.getChildCount(); i++) {
//如果viewGroup拥有子view,所以层数+1,然后递归算它的子view的层数
int deep = maxDeep(viewGroup.getChildAt(i)) + 1;
//取最大值
max = Math.max(deep,max);
}
return max;
}
最后
整理中。。。