先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
参考回答:
-
主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
-
造成ANR的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI。
-
阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处理,程序是不会无响应的。
7、使用Handler的postDealy后消息队列会有什么变化?
参考回答:如果队列中只有这个消息,那么消息不会被发送,而是计算到时唤醒的时间,先将Looper阻塞,到时间就唤醒它。但如果此时要加入新消息,该消息队列的对头跟delay时间相比更长,则插入到头部,按照触发时间进行排序,队头的时间最小、队尾的时间最大。
8、可以在子线程直接new一个Handler吗?怎么做?
参考回答:不可以,因为在主线程中,Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。所以要在子线程开启Handler要先创建Looper,并开启Looper循环
9、Message可以如何创建?哪种效果更好,为什么?
参考回答:可以通过三种方法创建:
-
直接生成实例Message m = new Message
-
通过Message m = Message.obtain
-
通过Message m = mHandler.obtainMessage()
后两者效果更好,因为Android默认的消息池中消息数量是10,而后两者是直接在消息池中取出一个Message实例,这样做就可以避免多生成Message实例。
10、 什么是异步消息,同步屏障?
这个这里就不展开阐述了,留给大家自己五查,印象可能会更深刻。
==============================================================
1、线程池的好处?四种线程池的使用场景,线程池的几个参数的理解?
使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或则“过度切换”的问题,归纳总结就是:
-
重用存在的线程,减少对象创建、消亡的开销,性能佳。
-
可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
-
提供定时执行、定期执行、单线程、并发数控制等功能。
Android中的线程池都是直接或间接通过配置ThreadPoolExecutor来实现不同特性的线程池.Android中最常见的类具有不同特性的线程池分别为:
newCachedThreadPool: 只有非核心线程,最大线程数非常大,所有线程都活动时会为新任务创建新线程,否则会利用空闲线程 ( 60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能 )来处理任务.
- 优点:任何任务都会被立即执行(任务队列SynchronousQuue相当于一个空集合);比较适合执行大量的耗时较少的任务.
newFixedThreadPool: 只有核心线程,并且数量固定的,所有线程都活动时,因为队列没有限制大小,新任务会等待执行,当线程池空闲时不会释放工作线程,还会占用一定的系统资源。
- 优点:更快的响应外界请求
newScheduledThreadPool: 核心线程数固定,非核心线程(闲着没活干会被立即回收数)没有限制.
- 优点:执行定时任务以及有固定周期的重复任务
newSingleThreadExecutor: 只有一个核心线程,确保所有的任务都在同一线程中按序完成
- 优点:不需要处理线程同步的问题
通过源码可以了解到上面的四种线程池实际上还是利用 ThreadPoolExecutor 类实现的
2、Android中还了解哪些方便线程切换的类?
参考回答:
-
AsyncTask:底层封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作。
-
HandlerThread:一种具有消息循环的线程,其内部可使用Handler。
-
IntentService:是一种异步、会自动停止的服务,内部采用HandlerThread。
3、讲讲AsyncTask的原理:
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程。
sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载,否则同一个进程中的AsyncTask都将无法正常工作。
4、IntentService有什么用 ?
IntentService可用于执行后台耗时的任务,当任务执行完成后会自动停止,同时由于IntentService是服务的原因,不同于普通Service,IntentService可自动创建子线程来执行任务,这导致它的优先级比单纯的线程要高,不容易被系统杀死,所以IntentService比较适合执行一些高优先级的后台任务。
5、直接在Activity中创建一个thread跟在service中创建一个thread之间的区别?
在Activity中被创建:该Thread的就是为这个Activity服务的,完成这个特定的Activity交代的任务,主动通知该Activity一些消息和事件,Activity销毁后,该Thread也没有存活的意义了。
在Service中被创建:这是保证最长生命周期的Thread的唯一方式,只要整个Service不退出,Thread就可以一直在后台执行,一般在Service的onCreate()中创建,在onDestroy()中销毁。所以,在Service中创建的Thread,适合长期执行一些独立于APP的后台任务,比较常见的就是:在Service中保持与服务器端的长连接。
6、ThreadPoolExecutor的工作策略 ?
ThreadPoolExecutor执行任务时会遵循如下规则:
-
如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
-
如果线程池中的线程数量已经达到或则超过核心线程的数量,那么任务会被插入任务队列中排队等待执行。
-
如果在第2点无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果在线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
-
如果第3点中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。
7、Handler、Thread和HandlerThread的差别?
参考回答:
Handler:在android中负责发送和处理消息,通过它可以实现其他支线线程与主线程之间的消息通讯。
Thread:Java进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。
HandlerThread:一个继承自Thread的类HandlerThread,Android中没有对Java中的Thread进行任何封装,而是提供了一个继承自Thread的类HandlerThread类,这个类对Java的Thread做了很多便利的封装。HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,它在内部直接实现了Looper的实现,这是Handler消息机制必不可少的。有了自己的looper,可以让我们在自己的线程中分发和处理消息。如果不用HandlerThread的话,需要手动去调用Looper.prepare()和Looper.loop()这些方法。
8、ThreadLocal的原理:
ThreadLocal是一个关于创建线程局部变量的类。使用场景如下所示:
-
实现单个线程单例以及单个线程上下文信息存储,比如交易id等。
-
实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例。承载一些线程相关的数据,避免在方法中来回传递参数。
当需要使用多线程时,有个变量恰巧不需要共享,此时就不必使用synchronized这么麻烦的关键字来锁住,每个线程都相当于在堆内存中开辟一个空间,线程中带有对共享变量的缓冲区,通过缓冲区将堆内存中的共享变量进行读取和操作,ThreadLocal相当于线程内的内存,一个局部变量。每次可以对线程自身的数据读取和操作,并不需要通过缓冲区与 主内存中的变量进行交互。并不会像synchronized那样修改主内存的数据,再将主内存的数据复制到线程内的工作内存。ThreadLocal可以让线程独占资源,存储于线程内部,避免线程堵塞造成CPU吞吐下降。
在每个Thread中包含一个ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的对象,value是独享数据。
9、多线程是否一定会高效(优缺点)
多线程的优点:
-
方便高效的内存共享 - 多进程下内存共享比较不便,且会抵消掉多进程编程的好处
-
较轻的上下文切换开销 - 不用切换地址空间,不用更改CR3寄存器,不用清空TLB
-
线程上的任务执行完后自动销毁
多线程的缺点:
-
开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)
-
如果开启大量的线程,会占用大量的内存空间,降低程序的性能
-
线程越多,cpu在调用线程上的开销就越大
-
程序设计更加复杂,比如线程间的通信、多线程的数据共享
综上得出,多线程不一定能提高效率,在内存空间紧张的情况下反而是一种负担,因此在日常开发中,应尽量:
-
不要频繁创建,销毁线程,使用线程池
-
减少线程间同步和通信(最为关键)
-
避免需要频繁共享写的数据
-
合理安排共享数据结构,避免伪共享(false sharing)
-
使用非阻塞数据结构/算法
-
避免可能产生可伸缩性问题的系统调用(比如mmap)
-
避免产生大量缺页异常,尽量使用Huge Page
-
可以的话使用用户态轻量级线程代替内核线程
10、多线程中,让你做一个单例,你会怎么做?
多线程中建立单例模式考虑的因素有很多,比如线程安全 -延迟加载-代码安全:如防止序列化攻击,防止反射攻击(防止反射进行私有方法调用) -性能因素
实现方法有多种,饿汉,懒汉(线程安全,线程非安全),双重检查(DCL),内部类,以及枚举
11、除了notify还有什么方式可以唤醒线程?
参考回答:当一个拥有Object锁的线程调用 wait()方法时,就会使当前线程加入object.wait 等待队列中,并且释放当前占用的Object锁,这样其他线程就有机会获取这个Object锁,获得Object锁的线程调用notify()方法,就能在Object.wait 等待队列中随机唤醒一个线程(该唤醒是随机的与加入的顺序无关,优先级高的被唤醒概率会高)
如果调用notifyAll()方法就唤醒全部的线程。注意:调用notify()方法后并不会立即释放object锁,会等待该线程执行完毕后释放Object锁。
12、什么是ANR ? 什么情况会出现ANR ?如何避免 ?在不看代码的情况下如何快速定位出现ANR问题所在 ?
-
ANR(Application Not Responding,应用无响应):当操作在一段时间内系统无法处理时,会在系统层面会弹出ANR对话框
-
产生ANR可能是因为5s内无响应用户输入事件、10s内未结束BroadcastReceiver、20s内未结束Service
-
想要避免ANR就不要在主线程做耗时操作,而是通过开子线程,方法比如继承Thread或实现Runnable接口、使用AsyncTask、IntentService、HandlerThread等
==================================================================
1、Bitmap使用需要注意哪些问题 ?
选择合适的图片规格(bitmap类型):通常我们优化Bitmap时,当需要做性能优化或者防止OOM,我们通常会使用RGB_565,因为ALPHA_8只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多。:
-
ALPHA_8 每个像素占用1byte内存
-
ARGB_4444 每个像素占用2byte内存
《设计思想解读开源框架》
第一章、 热修复设计
-
第一节、 AOT/JIT & dexopt 与 dex2oat
-
第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题
-
第三节、热修复设计之热修复原理
-
第四节、Tinker 的集成与使用(自动补丁包生成)
第二章、 插件化框架设计
-
第一节、 Class 文件与 Dex 文件的结构解读
-
第二节、 Android 资源加载机制详解
-
第三节、 四大组件调用原理
-
第四节、 so 文件加载机制
-
第五节、 Android 系统服务实现原理
第三章、 组件化框架设计
-
第一节、阿里巴巴开源路由框——ARouter 原理分析
-
第二节、APT 编译时期自动生成代码&动态类加载
-
第三节、 Java SPI 机制
-
第四节、 AOP&IOC
-
第五节、 手写组件化架构
第四章、图片加载框架
-
第一节、图片加载框架选型
-
第二节、Glide 原理分析
-
第三节、手写图片加载框架实战
第五章、网络访问框架设计
-
第一节、网络通信必备基础
-
第二节、OkHttp 源码解读
-
第三节、Retrofit 源码解析
第六章、 RXJava 响应式编程框架设计
-
第一节、链式调用
-
第二节、 扩展的观察者模式
-
第三节、事件变换设计
-
第四节、Scheduler 线程控制
第七章、 IOC 架构设计
-
第一节、 依赖注入与控制反转
-
第二节、ButterKnife 原理上篇、中篇、下篇
-
第三节、Dagger 架构设计核心解密
第八章、 Android 架构组件 Jetpack
-
第一节、 LiveData 原理
-
第二节、 Navigation 如何解决 tabLayout 问题
-
第三节、 ViewModel 如何感知 View 生命周期及内核原理
-
第四节、 Room 架构方式方法
-
第五节、 dataBinding 为什么能够支持 MVVM
-
第六节、 WorkManager 内核揭秘
-
第七节、 Lifecycles 生命周期
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
为什么能够支持 MVVM**
-
第六节、 WorkManager 内核揭秘
-
第七节、 Lifecycles 生命周期
[外链图片转存中…(img-b6BuKLRT-1713460433608)]
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
[外链图片转存中…(img-5EzWmtqu-1713460433609)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-EueciMOT-1713460433609)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!