先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
scheduleThreadPool(支持定时与周期性任务的线程池):核心线程固定,非核心线程不固定,当非核心线程空闲时,立即被回收。适合执行定时任务或有固定周期的任务。
SingleThreadExecutor(单线程化线程池):只有1个核心线程,没有非核心线程,确保所有任务能够在同一线程且按顺序执行,不用考虑同步问题
private static void testExecutor(){
//首先创建线程池,这儿拿单线程化线程池举例
ExecutorService executorService = Executors.newSingleThreadExecutor();
Thread thread1 = new Thread(() -> {
System.out.println(System.currentTimeMillis()+"首先执行线程1");
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+"然后执行线程2");
});
Thread thread3 = new Thread(() -> {
System.out.println(System.currentTimeMillis()+"最后执行线程3");
});
//提交线程
executorService.submit(thread1);
executorService.submit(thread2);
executorService.submit(thread3);
//关闭线程池
executorService.shutdown();
}
输出结果:会按顺序依次执行
1588349278957首先执行线程1
1588349279959然后执行线程2
1588349279959最后执行线程3
7,handlerThread的使用和原理
继承Thread,相当于在Thread中生成了一个loop循环器,可以进行Looper循环,即handlerThread=handler+Thread+looper。
HandlerThread继承Thread后,重写了run方法,可以看到,显示通过Looper.prepare()创建线程,然后采用同步代码,保证线程安全,一个线程没有执行完,则looper就是阻塞状态,执行完后,通过notifyAll()方法通知线程进入就绪状态:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
通知的就是getLooper()方法,如果Looper轮询器还在工作,则让线程等待,知道run方法执行notifyall()通知后,,才会返回Looper。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
特点:
(1)通过获取handlerThread的loop对象,传递给handler对象,在handlerMessage方法中执行异步任务
(2)不会阻塞,减少对性能消耗。但是不能同时多任务处理,需等待处理。
(3)和线程池注重并发不同,只是串行队列,内部只有一个线程。
使用:
(1)先写一个类TestHandler继承Handler,并重写handlerMessage方法
(2)初始化HandlerThread,并启动
HandlerThread handlerThread = new HandlerThread(“test_thread”);
handlerThread.start();//必须得先启动线程,不然没有创建handler,也就没有Looper对象
TestHandler handler = new TestHandler(handlerThread.getLooper());
handler.sendEmptyMessage(0);
(3)当不需要的时候记得调用HandlerThread.quit()方法移除可能未完成的操作避免内存泄露等危险养成一种良好的习惯。
8.IntentService和Service有什么区别
IntentService继承Service,两个都是服务。IntentService可以执行耗时操作,Service不能直接执行耗时操作,否则会ANR。
(1)创建一个测试类继承IntentService并重写onHandleIntent方法,用来处理相应事务
public class TestIntentService extends IntentService {…重写onHandleIntent(Intent intent)方法…}
(2)正常启动service即可
startService(new Intent().setClass(MainActivity.this, TestIntentService.class).putExtra(“content”, “传输的数据”));
(3)在IntentService的onCreate方法中会开启一个HandlerThread线程,内部loop会创建一个ServiceHandler,在onstart方法中,会把intent转到ServiceHandler中,
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread(“IntentService[” + mName + “]”);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
(4)ServiceHandler继承Handler,重写了handleMessage方法,可以看到调用了我们一开始重写的onHandleIntent方法,执行完毕后stopSelf结束service。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
Service不能直接执行耗时操作,否则会ANR,只能新开线程执行耗时操作。
二,异步消息机制Handler
1,异步消息机制
总共由四部分组成:handler,message,MessageQueue,Looper
(1)在主线程中创建 Handler 对象,并重写 handleMessage() 方法。
(2)子线程进行 UI 操作时,创建 Message 对象,通过第一步创建的Handler 发送消息,handler.sendMessage(message),handler将消息发送到MessageQueue中。
(3)Looper 通过loop()循环从 MessageQueue 中取出待处理消息。
(4)looper将取出的message分发回 Handler 的 handleMessage() 方法中处理。
原理:
https://blog.csdn.net/fnhfire_7030/article/details/79518819
2,什么是handler
handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。
(Message传递的是消息,MessageQueue是一个消息队列,Handler机制里Lopper轮询器会不断的从消息队列里获取消息,交给handler处理消息)
(1),可以让对应的message和runnable在未来的某个时间点进行相应的处理
(2),让自己想要处理的耗时操作放在子线程,让更新UI的操作放在主线程。
3,handler的使用(post(runnable)和 sendMessage(mesage))
(1),post(runnable)
(1.1)首先创建Handler()
Handler handler = new Handler();
(1.2)然后新开线程,创建Runnable对象,通过post方法,将runnable传到handler当中,handler会在合适的时候让主线程运行runnable更新UI的代码。
class DownloadThread extends Thread{
@Override
public void run() {
try{
System.out.println("开始下载文件");
Thread.sleep(2000);
System.out.println("文件下载完成");
Runnable runnable = new Runnable() {
@Override
public void run() {
//更新UI的一些操作
}
};
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
(1.3)在主线程中开启DownloadThread线程
DownloadThread downloadThread = new DownloadThread();
downloadThread .start();
(2),sendMessage(mesage)
(2.1)创建handler,并复写handleMessage方法
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
//执行UI操作
break;
}
}
};
(2.2)在新开线程中创建message对象,通过what,org1,org2进行赋值,然后handler发送该消息,handler通过其handleMessage的方法进行UI的处理
class DownloadThread extends Thread{
@Override
public void run() {
try{
System.out.println("开始下载文件");
Thread.sleep(2000);
System.out.println("文件下载完成");
Message msg = new Message();
msg.what = 1;//自定义的识别码,handler可以识别不同的额msg
handler.sendMessage(msg);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
(2.3)在主线程中开启DownloadThread线程
DownloadThread downloadThread = new DownloadThread();
downloadThread .start();
4,handler的内部机制原理
Handler:
创建handler对象时,则持有了当前线程的Looper和MessageQueue对象
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
“Can’t create handler inside thread that has not called Looper.prepare()”);
}
mQueue = mLooper.mQueue;
}
Looper:
每个线程都有的,通过prepare()创建Looper对象,保存在本线程的ThreadLocal中,保证每个线程的Looper的唯一性。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
通过loop()开启循环,不断调用MessageQueue.next()方法获取Message,进行消息的分发msg.target.dispatchMessage(msg),msg这个属性target就是Handler,其实就是通过handler的dispatchMessage()方法发送消息给消息队列,最终调用handleMessage()。
5,handler引起的内存泄漏
当Activity要回收时,handler没有被回收,对Activity的引用也不会释放。即静态内部类持有外部类的匿名引用,导致外部Activity无法释放。
解决方法:handler内部持有外部activity的弱引用,并把handler改为静态内部类,在activity的onDestroy方法中调用mHandler.removeCallback()方法。
private Handler mHandler = new WeakHandler(this);
static class WeakHandler extends Handler {
private WeakReference mActivity;
TestHandler(Activity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Activity activity = mActivity.get();
if (activity != null) {
//TODO:
}
}
}
6,handlerThread
1,产生背景:
开启Thread子线程进行耗时操作时,多次创建和销毁线程很耗系统资源。
2,本质
handler+Thread+Looper
本质上是一个线程类,继承了Thread;
但他有自己的内部Looper对象,可以进行looper循环;
通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务;
优点是不会堵塞,减少对性能的消耗,缺点是不能同时进行多任务处理,处理效率低。
与线程池注重并发不同,HandlerThread是一个串行队列,背后只有一个线程。
三,面试时会问到的问题
1,简要说下进程和线程的区别
进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,进程在执行过程中拥有独立的内存单元;
线程是CPU调度和分派的基本单位,是能独立运行的基本单位,一个进程至少有一个线程,多个线程共享进程的内存资源,但每个线程都拥有单独的栈内存用来存储本地数据。
2,Thread类中的start()方法和run()方法的区别
start方法用来启动线程,内部调用了run方法,run方法只是直接调用run方法,并没有开启新线程。
3,在多线程中,什么是上下文切换?
上下文切换时存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。
4,有三个线程,如果保证线程顺序执行?
(方法1)在线程3调用t1.join()方法,在线程2调用t1.join()方法。
(方法2)使用synchronized锁住Object对象,然后使Object的wait方法使当前线程进入等待状态并释放锁(对象监视器),在要执行的线程执行完任务后调用notify方法,等待该线程剩余代码执行完毕后释放锁(对象监视器),然后唤醒之前进入等待状态的线程。
(方法3)使用SingleThreadExecutor(单线程化线程池)
5,产生死锁的条件
死锁指两个以上的线程永远阻塞的状态。
(1)互斥条件:一个资源每次只能被一个进程使用
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已经获得的资源保持不放。
(3)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
6,什么是线程局部变量ThreadLocal?
ThreadLocal用于创建线程的本地变量,属于线程自身所有,这是一种实现线程安全的方式。一个对象的所有线程都会共享它的全局变量,所以这些线程是不安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,可以选择ThreadLocal变量,因为ThreadLocal是一种以空间换时间的做法在每个Thread内部维护了一个ThreadLocal,ThreadLocalMap把数据进行隔离,数据不共享,自然就不会有线程安全的问题了。
7,简要说下线程间通信的原理
通过Handler机制
(1)在Handler构造方法中:Looper通过Looper.myLooper()方法获取一个Looper对象,通过mLooper.mQueue拿到与这个Looper对应的MessageQueue;
(2)然后开启死循环,对消息队列进行不停的获取,获取到一个消息后,交给Message.target.dispatchMessage()方法对消息进行处理。
8,post和sendMessage的区别
sendMessage一般和handleMessage配合使用,在sendMessage中发送消息,在handleMessage中接收消息并进行UI的处理;
post方法直接在runnable的run方法中更新UI,但两者本质上没有区别,都是发送消息到消息队列中,只是post的方式更简单些。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//获得了message实例,将r赋给callback,接下来还是和sendMessage一致的操作,进入sendMessageDelayed
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//最终还是执行到sendMessageAtTime这个方法里面
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//接下来看下sendMessageAtTime方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
接下来看下post和sendMessage是如何进行消息的处理的
public void dispatchMessage(Message msg) {
//如果是post,callback不为空,直接进入handleCallback
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果是sendMessage,且创建handler时没有传入callback,则callback为空,直接进入handleMessage,也就是我们自己复写的处理Message的方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//直接run并不会启动新线程,所以这就是post的runnable里面可以直接更新UI的原因
private static void handleCallback(Message message) {
message.callback.run();
}
9,为什么主线程一直loop死循环但不会ANR(Application Not Responding)?
首先说下造成ANR的原因:
(1)当前事件没有机会处理:主线程正在处理前一个事件,没有及时的完成或者Looper被某种原因阻塞住了。
(2)当前事件正在处理,但没有及时完成。
所以为了避免ANR,采用了Handler消息处理机制,耗时操作在子线程运行。
先看下主线程中的Looper.loop()循环,在主线程入口类ActivityThread.java的入口函数中:
//先看下入口类的入口函数main函数
public static final void main(String[] args) {
...
//创建Looper和MessageQueue
Looper.prepareMainLooper();
##### **如何做好面试突击,规划学习方向?**
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
![img](https://img-blog.csdnimg.cn/img_convert/0d2e82b52dfe4756e53c00674658039f.jpeg)
我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)**
![img](https://img-blog.csdnimg.cn/img_convert/bbab1caf87a42fffff80a2cc7f1b0b92.png)
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
耗时操作在子线程运行。
先看下主线程中的Looper.loop()循环,在主线程入口类ActivityThread.java的入口函数中:
//先看下入口类的入口函数main函数
public static final void main(String[] args) {
...
//创建Looper和MessageQueue
Looper.prepareMainLooper();
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
[外链图片转存中…(img-uJgdELzo-1713457780768)]
我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-2r47HBfa-1713457780768)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!