Android面试指南(二)————Android基础篇(一)

parcelable使用复杂,但高效,适用于内存序列化

Parcelable一定比Serializable快吗?

单论内存中的传输速度,Parcelable一定快于Serializable,但是Parcelable没有缓存的概念 Serializable存在缓存,会将解析过的内容放置在HandleTable,下次解析到同一类型的对象时就可以直接复用

为什么java使用Serializable序列化对象,而不是json或者xml?

因为历史遗留问题,在json和xml出来之前,java已经设计了Serializable,对于Java的庞大体系,并不容易修改这个问题。java官方文档也推荐使用json库,因为他简单、易读、高效

简析Binder机制

在Android通信中并不是所有的进程通信都使用Binder,当fork()进程时,使用的是Socket()通信,因为fork不允许多线程,Binder是多线程模式,所以不被允许

进程空间划分

一个进程空间分为用户空间内核空间

用户空间:数据独享,其他进程无法访问

内核空间:数据共享,其他进程可以访问

所有的进程共用1个内核空间

如何看待ServiceManager?

ServiceManager管理系统中所有的服务,服务需要使用时都要在ServiceManager中进行注册,他的存在类似于DNS,提供client访问某一个服务的查询。

Binder原理

binder驱动属于进程中的内核空间,即共享空间,在client发起请求时,需要将数据从用户空间拷贝到内核空间,binder通过传输内核空间中数据存储的引用映射给服务端,供服务端调用,服务端处理后,将返回值放在内核空间,通过binder传递引用映射给客户端进行处理

img

简述通信流程

总体通信流程就是:

  • 客户端通过代理对象向服务器发送请求。
  • 代理对象通过Binder驱动发送到服务器进程
  • 服务器进程处理请求,并通过Binder驱动返回处理结果给代理对象
  • 代理对象将结果返回给客户端。
详细的通信过程
  • 服务端跨进程的类都要继承Binder类,所以也就是服务端对应的Binder实体。这个类并不是实际真实的远程Binder对象,而是一个Binder引用(即服务端的类引用),会在Binder驱动里还要做一次映射。
  • 客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数
  • transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存。
  • 然后把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。
  • 远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。

通过Binder将客户端,服务端的共享内存中的数据进行读写,放入对方的共享内存中,并通知。

Binder在Android中的应用?
  • 系统服务及四大组件的启动调用工作:系统服务是通过getSystemService获取的服务,内部也就是通过ServiceManager。例如四大组件的启动调度等工作,就是通过Binder机制传递给ActivityManagerService,再反馈给Zygote。而我们自己平时应用中获取服务也是通过getSystemService(getApplication().WINDOW_SERVICE)代码获取。
  • AIDL(Android Interface definition language)。例如我们定义一个IServer.aidl文件,aidl工具会自动生成一个IServer.java的java接口类(包含Stub,Proxy等内部类)。
  • 前台进程通过bindService绑定后台服务进程时,onServiceConnected(ComponentName name, IBinder service)传回IBinder对象,并且可以通过IServer.Stub.asInterface(service)获取IServer的内部类Proxy的对象,其实现了IServer接口。
为什么选择Binder机制?他的优势是什么?
  1. 性能高,效率高:
    传统的IPC(socket,管道,消息队列)需要拷贝两次内存,Binder只需要拷贝一次内存、共享内存不需要拷贝数据,只需要传递引用
  2. 安全性好:
    C/S通信添加UID/PID,方便验证,安全机制完善。
  3. 利用C/S架构,通过多线程控制一个服务端多个客户端的情况
Android中IPC的几种方式详细分析与优缺点分析
  1. Bundle
  2. 文件共享
  3. Messenger:内部实现AIDL机制,c/s架构,通过handler接收message对象
  4. AIDL
  5. ContentProvider
  6. Binder连接池

Handler

Android Handler机制之总目录

Android面试题:Handler

其实并不是每一次添加消息时,都会唤醒线程。当该消息插入到队列头时,会唤醒该线程;如果有延迟消息,插入到头部,也会唤醒线程后在休眠

一句话概括Handler,并简述其原理

android中用于主线程和子线程之间通信的工具

主要包含Handler,Looper,MessageQueue,ThreadLocal.

Handler:封装了消息的发送和接收looper分发过来的Message

Looper:协调Handler和MessageQueue之间的桥梁,Looper的作用是循环从MessageQueue中取出message,并分发

给相应的Handler,Handler则存储在Message中的target中

message:单节点,存储handler传输的数据

MessageQueue:内部结构为单链表,由Looper创建,具体代码为Looper.prepare();先进先出原则(队列),根据 Message.when进行插入队列(队列中是按时间执行顺序排序)

ThreadLocal:负责存储和获取本线程的Looper

handler.sendMessage(message)将message发送到MessageQueue,MessageQueue执行enqueueMessage()方法入队,Looper执行looper.loop()方法从MessageQueue中取出message,执行message.target.dispatchMessage(message)方法将消息发送到Handler中,在handleMessage()方法中拿到回调

Looper.loop()是在主线程的死循环,为什么没有造成线程阻塞?

真正的ANR是在生命周期的回调中等待的时间过长导致的,深层次的讲,就是Looper.loop()没有及时取出消息进行分发导致的。一旦没有消息,Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,Android里调用了linux层的代码实现在适当时会睡眠主线程。

MessageQueue包含jni调用,无消息时,通知epoll休眠,来消息时,线程启动

looper.loop()中循环,判空退出怎么理解?

public static void loop() {

for (;😉 {
Message msg = queue.next(); // might block(可能会阻塞)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

在queue.next中,会通过jni调用,通过Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,只有当应用程序退出时,才会执行if语句退出循环。

为什么不能在子线程更新UI?

因为如果要在子线程中更新UI,势必要考虑线程安全,加锁机制,这样很耗时,不加锁又很容易发生错误,这些错误是致命的,所以在设计时只允许UI线程更新UI,避免这些错误。

真的不能在子线程更新UI吗?

ViewRootImpl中会进行通过checkThread()进行线程检测

public ViewRootImpl(Context context, Display display) {

mThread = Thread.currentThread();

}

void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
“Only the original thread that created a view hierarchy can touch its views.”);
}
}

由此得出:viewRootImpl在那个线程被初始化,就会在那个线程更新UI,大部分情况下,ViewRootImpl都是在UI线程中初始化的,所以只能在UI线程更新UI,部分情况下可以在子线程更新UI(比如Dialog是在addView中初始化ViewRootImpl)

SurfaceView可以在子线程中更新

ViewRootImpl是什么时候被创建的?

在Acitivty中,ViewRootImpl是在onResume中创建的,所以在onCreate中进行子线程更新是可以绕过checkThread()检测的。

一个Thread中可以有几个Looper?几个Handler

一个Thread中只有一个Looper,可以存在无数个Handler,但是使用MessageQueue都是同一个,也就是一个Looper

可以在子线程直接new一个Handler吗?那该怎么做?

thread= new Thread(){
@Override
public void run() {
super.run();
//创建Looper,Looper再创建MessageQueue
Looper.prepare();
//新建Handler
handler=new Handler();
//循环取出消息并执行
Looper.loop();
}
}.start();

需要创建Looper,Looper会创建MessageQueue,循环从MessageQueue中取消息。

Message可以如何创建?哪种效果更好,为什么?

享元模式

数据重复利用

  1. Message msg = new Message();
  2. Message msg2 = Message.obtain();
  3. Message msg1 = handler1.obtainMessage();

2,3从整个Messge池中返回一个新的Message实例,通过obtainMessage能避免重复Message创建对象。

所以2,3都可以避免重复创建Message对象,所以建议用第二种或者第三种任何一个创建Message对象。

messge就是一个节点,存在就是一条链表,链表中存储的都是可以复用的message,在handleMessage和callback

方法执行完成后执行message.recycle()方法,进行信息重置后加入闲置链表头部中,每次调用obtain方法会从闲置链表中取出头节点,如果闲置链表为空,则新建message。

Message缓存池大小为50

使用Hanlder的postDelay()后消息队列会发生什么变化?

postDelay()内部调用sendMessageDelayed()

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

时间:SystemClock.uptimeMillis() + delayMillis

SystemClock.uptimeMills()是从开机到现在的时间,不使用currentMills,因为其是可变的,uptimeMills()期间不包括休眠的时间,是一个相对时间

  1. Handler.postDelayed(Runnable r, long delayMillis)
  2. Handler.sendMessageDelayed(getPostMessage®, delayMillis)
  3. Handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
  4. Handler.enqueueMessage(queue, msg, uptimeMillis)
  5. MessageQueue.enqueueMessage(msg, uptimeMillis)

消息入队的时候会根据when判断时间,最终按照时间大小排序,时间短的在链表头,时间长的在链表尾部。

案例如下:

  1. postDelay()一个10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;
  2. 紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;
  3. MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;
  4. Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;
  5. 直到阻塞时间到或者下一次有Message进队;
同步消息、异步消息和同步屏障消息是什么?具体应用场景是什么?

同步消息:handler默认无参构造的形式是同步消息

异步消息:async传入true,则为异步消息

屏障消息:msg.target == null,使用postSyncBarrier()方法打开同步屏障,导致同步消息不执行,优先执行异步消息,规则同同步消息一样,当执行完毕后关闭同步屏障。

应用场景:在view的更新过程中,draw,requestLayout、invalidate中都用到这个方法,系统会优先处理这些异步消息,等处理结束后再处理同步消息。这样可以优先处理我们指定的系统消息。

postSyncBarrier()该方法为私有方法,所以api不允许我们在开发中调用,我们只要知道原理就好了

调用该方法,会直接发送一个屏障消息进入messageQueue,则队列头部为屏障消息

ThreadLocal,谈谈你的理解

跟HashMap功能类似,为什么不直接用HashMap呢? 原因:

  1. HashMap太大了,太臃肿了。ThreadLocal的key值只有Thread,value为looper,而HashMap的key值则可以 是string、int等数据类型,我们可以不用考虑这些数据类型;
  2. 线程隔离:我们的线程是系统中唯一的,用ThreadLocal来管理这些唯一的线程和其对应的value值会非常方便,
  3. ThreadLocal参照了HashMap,简化了HashMap,便于我们使用。
  4. HashMap线程不安全

ThreadLocal的理解

为什么子线程中不能使用Handler,而UI线程可以?

UI线程就是ActivityThread,他在初始化的时候创建了Looper,MessageQueue,所以可以直接使用Handler,而新创建的子线程没有创建Looper,所以创建了就可以使用了

Handler的构造方法中使用Looper.myLooper()获取了looper,但是在子线程中并没有looper

Handler如何引起内存泄露?怎么解决?

非静态内部类或匿名内部类默认持有外部类的引用,当外部类被回收时,因为内部类持有外部类的引用,导致外部类不能被回收,造成内存泄露。

  1. Activity销毁时及时清理消息队列;
  2. 自定义静态Handler类+弱引用。
MessageQueue.next()会因为发现了延迟消息,而进行阻塞。那么为什么后面加入的非延迟消息没有被阻塞呢?

首先非延时消息会入队,并且插入链表头,这时唤醒线程,进行循环取出message,非延时消息出队,到延迟消息后,如果事件未到,触发next的阻塞机制,如果时间到了,取出message,执行消息

Handler延时机制保时吗?

不保时

chatIflyHandler.post(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
chatIflyHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.e(“handler”,“执行延时1s操作”);
}
},1000);

首先一个非延时消息入队,紧接着一个延时消息入队,执行第一个非延时消息,用时3s后执行延时消息,对比when,然后直接执行,测试总共耗时3420ms

Handler的入队机制是线程安全的(synchronized)

messageQueue.equeue(){
synchronized(this){

}
}

如何精确计时?
  • 使用timer(子线程处理TimerThread)

  • 误差补偿算法(TextClock控件方法)

private final Runnable mTicker = new Runnable() {
public void run() {
onTimeChanged();

long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);

getHandler().postAtTime(mTicker, next);
}
};

整秒数执行,当上次执行累积到1200,在下次执行时,通过next的计算后保证下次执行的时间不被累加到2200,而是同样在2000

IdleHandler是什么?用处是什么?

messageQueue中有一个addIdleHandler()方法,添加IdleHandler接口

  • 添加时messageQueue不为空,则在线程休眠(没有消息,延时消息)时回掉方法
  • 添加时messageQueue为空,则当时不会触发回掉,当线程被唤醒时才会执行

就是在启用IdleHandler的时候,如果线程处于休眠状态,要等到下次休眠状态才会生效。如果不是休眠状态,则下一次休眠立即生效。

启用IdleHandler后,主线程下次休眠时会通知

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//do something
return false;
}
});

如果return true,则表示这个IdleHandler可多次使用
如果return false,则表示这个IdleHandler只能使用一次

主线程的Looper何时退出?能否手动退出?

在app退出或者异常终止时,会退出Looper。在正常退出时,ActivityThread主线程中的mH(Handler)会接收到回调信息,调用quit()方法,强制退出

//ActivityThread.java
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;

  • Looper.quit():调用后直接终止Looper,不在处理任何Message,所有尝试把Message放进消息队列的操作都会失败,比如Handler.sendMessage()会返回 false,但是存在不安全性,因为有可能有Message还在消息队列中没来的及处理就终止Looper了。

  • Looper.quitSafely():调用后会在所有消息都处理后再终止Looper,所有尝试把Message放进消息队列的操作也都会失败。

当尝试在主线程手动退出looper时,会报错:

Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
at android.os.MessageQueue.quit(MessageQueue.java:428)
at android.os.Looper.quit(Looper.java:354)
at com.jackie.testdialog.Test2Activity.onCreate(Test2Activity.java:29)
at android.app.Activity.performCreate(Activity.java:7802)
at android.app.Activity.performCreate(Activity.java:7791)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread H . h a n d l e M e s s a g e ( A c t i v i t y T h r e a d . j a v a : 2016 ) a t a n d r o i d . o s . H a n d l e r . d i s p a t c h M e s s a g e ( H a n d l e r . j a v a : 107 ) a t a n d r o i d . o s . L o o p e r . l o o p ( L o o p e r . j a v a : 214 ) a t a n d r o i d . a p p . A c t i v i t y T h r e a d . m a i n ( A c t i v i t y T h r e a d . j a v a : 7356 ) a t j a v a . l a n g . r e f l e c t . M e t h o d . i n v o k e ( N a t i v e M e t h o d ) a t c o m . a n d r o i d . i n t e r n a l . o s . R u n t i m e I n i t H.handleMessage(ActivityThread.java:2016) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit H.handleMessage(ActivityThread.java:2016)atandroid.os.Handler.dispatchMessage(Handler.java:107)atandroid.os.Looper.loop(Looper.java:214)atandroid.app.ActivityThread.main(ActivityThread.java:7356)atjava.lang.reflect.Method.invoke(NativeMethod)atcom.android.internal.os.RuntimeInitMethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

因为主线程退出意味着app的关闭,这种操作不和规范,需要使用正式的退出操作

如何退出app
  1. 记录Activity任务栈,全部finish
  2. System.exit(0);//正常退出
    System.exit(1);//非正常退出
  3. android.os.Process.killProcess(android.os.Process.myPid()); 关闭进程,如果系统发现进程未正常关闭,会重新启动进程
  4. 在Intent中直接加入标识Intent.FLAG_ACTIVITY_CLEAR_TOP,这样开启B时,会清除该进程空间的所有Activity。
  5. 2.2版本之前使用ActivityManager关闭

ActivityManager am = (ActivityManager)getSystemService (Context.ACTIVITY_SERVICE);
am.restartPackage(getPackageName());

2.2版本以后

ActivityManager am = (ActivityManager)getSystemService (Context.ACTIVITY_SERVICE);
am.killBackgroundProcesses(getPackageName());
System.exit(0);

该方法只是结束后台进程的方法,不能结束当前应用移除所有的 Activity。如果需要退出应用,需要添加System.exit(0)方法一起使用,并且只限栈内只有一个Activity,如果有多个Activity时,正如上面 方法 2 所说,就不起作用了。

  1. 将MainActivity设置为singleTask,返回MainActivity后会清空所有的Activity,这样直接在MainActivity执行finish()方法即可
如何看待sendMessageAtFrontOfQueue()

public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}

传入的延迟时间为0,头插入消息队列,即消息队列下一次立即执行的消息,

如何看待Handler构造中的CallBack方法?

@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

public void dispatchMessage(Message msg) {
//这里的 callback 是 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

由源码可以看出构造函数中传入Callback参数,调用dispatch方法时,会优先调用callback方法,在调用handleMessage方法,即

Callback接口可以在handleMessage前收到消息,如果返回true,则不会调用handleMessage方法

我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息!

Looper.prepareMain()和Looper.perpare的区别?

区别是一个boolean值,主线程的looper永不退出,除非调用

AsyncTask

如何理解AsyncTask?
  • 必须创建在主线程

AscncTask内部封装了Handler+线程池

包含两个线程池,一个是用来排队的 ,一个才是真正的执行,通过Handler将状态回掉到主线程

  • 核心线程数,最少两个,最多四个
  • 最大线程数= cpu核心数*2+1
  • 核心线程无超时限制,非核心线程在闲置时的超时时间为1s
  • 任务队列容量为128

execute方法执行,加入排队线程池排队,等待任务执行后通过handler通知主线程,调用状态回调方法,内部实现因为排队线程池阻塞,导致任务是串行的,即同时只有一个任务会进入线程池执行

executeOnExecutor执行调用异步操作

谷歌为何弃用AsyncTask
  1. 使用多线程更加复杂,使bug难以定位
  2. 太过复杂
  3. 滥用继承,effic java推荐“使用组合而不是继承”,使类多,且低效
  4. 默认的THREAD_POOL_EXECUTOR线程池配置不太合适

线程池

线程

简述线程池

android中线程池主要实现是ThreadPoolExecutor

参数:

  • 核心线程数:如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间,就会被销毁掉
  • 最大线程数: = 核心线程数 + 非核心线程数
  • 超时时间:非核心线程的闲置超时时间
  • 超时时间单位:非核心线程的闲置超时时间单位
  • 线程等待队列:当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务
  • 线程创建工厂:线程池的拒绝策略,可以出错,也可以顾虑

allowCoreThreadTimeOut设置为true,非核心线程超时时间同样用于核心线程,如果为false,核心线程永远不会终止

  1. SynchronousQueue(空集合):这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大

  2. LinkedBlockingQueue(大小无限):这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

  3. ArrayBlockingQueue(大小可以设置):可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

  4. DelayQueue(延迟出队,大小可以设置):队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

规则:

  1. 未达到核心线程数,新建核心线程
  2. 达到或者大于核心线程数,任务被插入任务队列等待执行
  3. 步骤2中无法插入队列(队列满了),线程数量小于线程池最大值,启动一个非核心线程执行任务
  4. 步骤3 中线程数量达到最大值,则拒绝执行此任务
android主要分为几种线程池?

四种

FixedThreadPool:全是核心线程,没有超时机制,任务队列没有大小限制

CachedThreadPool:全是非核心线程,最大为Integer.MAX_VALUE,空闲线程60s超时(60s内可以复用,60s后回收),适用于执行短时的大量任务,空闲时也不占用cpu资源

ScheduledThreadPool:核心线程固定,非核心线程为Interger.MAX_VALUE,非核心线程无超时机制(执行完就被回收),适用执行定时任务和固定周期任务

SingleThreadExecutor:只有一个核心线程,无超时机制,保证只在一个线程中执行任务

线程池中一个线程崩溃会导致线程池崩溃吗?

不会,线程池存在两种方式去执行线程任务,submit和execute方式。 当发生线程崩溃时,execute下会将线程关闭,开辟新的线程,submit会返回异常,但不会关闭线程。

submit:

  • 继承自ExecutorService
  • 不会抛出栈堆异常,通过Future.get方法获取异常信息
  • submit通过构造一个RunnableFuture后,执行execute方法,RunnableFuture内部使用状态管理,通过死循环判断任务执行状态,在执行完或者cancle后返回,

get()方法是一个阻塞方法,在调用时需要注意
execute:

  • 继承自Executor
  • 会抛出堆栈异常信息,关闭该线程并创建新的线程
如何检测线程池中的崩溃问题呢?

submit的get()方法可以获取崩溃,但是该方法是阻塞的,可用性不高,于是我们使用另一种方法

  1. execute+ThreadFactory.UncaughtExceptionHandler
    在submit下UncaughtExceptionHandler失效,因为FutureTask会捕获异常并保存不会放入UncaughtExceptionHandler中
  2. 在run方法中自行捕获
  3. 重写ThreadLocalExecutor.afterExecute方法
  4. submit+get方法

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

总之啊,家里没矿的同学们,如果你们想以后的日子过得好一些,多想想你们的业余时间怎么安排吧;

技术方面的提升肯定是重中之重,但是技术外的一些“软实力”也不能完全忽视,很多时候升职确实是因为你的技术足够强,但也与你的“软实力”密切相关

在这我也分享一份大佬自己收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅并给下属员工学习的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

相信自己,没有做不到的,只有想不到的

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

m/2024/03/13/H4lCoPEF.jpg" />

最后

总之啊,家里没矿的同学们,如果你们想以后的日子过得好一些,多想想你们的业余时间怎么安排吧;

技术方面的提升肯定是重中之重,但是技术外的一些“软实力”也不能完全忽视,很多时候升职确实是因为你的技术足够强,但也与你的“软实力”密切相关

在这我也分享一份大佬自己收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅并给下属员工学习的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

[外链图片转存中…(img-yOgfKhhs-1712739164137)]

[外链图片转存中…(img-bhMV81Mx-1712739164137)]

相信自己,没有做不到的,只有想不到的

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值