尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
//发现泄露,初始值false,不看往下走
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//注释1
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can’t create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//注释2
mQueue = mLooper.mQueue;
//null
mCallback = callback;
//false
mAsynchronous = async;
}
在无参构造器里调用了重载的构造方法并分别传入 null 和 false。并且在构造方法中给两个全局变量赋值:mLooper 和 mQueue。mLooper是通过 Looper 来获取。mQueue 是通过 mLooper.mQueue 获取。这说明他们都来找于 Looper 。咱们先看myLooper吧。
Looper.myLooper()
final MessageQueue mQueue;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal.get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings(“unchecked”)
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可以看出,myLooper 通过一个线程本地变量中的存根,然后 mQueue 是 Looper 中的一个全局变量,类型是 MessageQueue 类型。
MessageQueue:保存要由 Looper 调度的消息列表。 Message不是直接添加到 MessageQueue 的,而是通过与 Looper 关联的 Handler 对象添加的。你可以使用 Looper.myQueue() 检索当前线程的 MessageQueue。
Looper 介绍
=========
默认情况下,线程没有与之关联的消息循环;要创建一个,在运行循环的线程中调用 prepare ,然后循环让它处理消息,直到循环停止。
大多数与消息循环的交互是通过 Handler 类进行的。
这是一个Looper线程实现的典型例子,利用prepare和loop的分离,创建了一个初始Handler与Looper进行通信。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper()) {
public void handleMessage(Message msg) {
// 在这里处理传入的消息
}
};
Looper.loop();
}
}
启动一个 java 程序的入口函数是 main 方法,但是当 main 函数执行完毕之后此程序停止运行,也就是进程会自动终止。
但是当我们打开一个 Activity 之后,只要我们不按下返回键 Activity 会一直显示在屏幕上,也就是 Activity 所在进程会一直处于运行状态。实际上 Looper 内部维护一个无限循环,保证 App 进程持续进行。
Looper初始化
Activity.attach() 方法中会传入一个ActivityThread,ActivityThread 的 main 方法是一个新的 App 进程的入口。
ActivityThread.main()
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “ActivityThreadMain”);
// 安装选择性系统调用拦截
AndroidOs.install();
// 禁止CloseGuard
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
//确保 TrustedCertificateStore 查找 CA 证书的正确位置
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// 调用每个进程的主线模块初始化。
initializeMainlineModules();
Process.setArgV0(“”);
//重点:注释1
//初始化当前进程的 Looper 对象
Looper.prepareMainLooper();
…
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
…
//重点:注释2
//调用 Looper 的 loop 方法开启无限循环。
Looper.loop();
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}
注释1:就初始化当前进程的 Looper 对象;
注释2:调用 Looper 的 loop 方法开启无限循环(具体下面讲到)。
Looper.prepareMainLooper()
public static void prepareMainLooper() {
//注释1:创建一个Looper
//下面把方法贴出来
prepare(false);
//加个同步方法对象锁
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
//这个要等prepare执行完再看
//注释1
sMainLooper = myLooper();
}
}
Looper.prepare()
private static void prepare(boolean quitAllowed) {
//判断是否绑定过 Looper 对象
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed));
}
Looper.prepare 方法其实就是new 一个 Looper。核心之处在于将 new 出的 Looper 设置到了线程本地变量 sThreadLocal.set(looper) 中。也就是说创建的 Looper 与当前线程发生了绑定。
**注意:**在创建 Looper 对象之前,会判断 sThreaLocal 中是否已经绑定过 Looper 对象,如果是则抛出异常。这行代码的目的是确保在一个线程中 Looper.prepare() 方法只能被调用 1 次。
new Looper()
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper在构造方法中初始化了消息队列 MessageQueue 对象。
prepare 方法执行完之后,会在 Looper.prepareMainLooper 中处调用 myLooper() 方法,从 sThreadLocal 中取出 Looper 对象并赋值给 sMainLooper 变量。
Looper.prepare() 只能被调用1次
从上面代码可以看出Activity在被创建时已经在 Looper.prepareMainLooper()中调用了一次Looper.prepare(),我决定在onCreate()里面再调用一次Looper.prepare(),祝福我吧。 我现在我准备在MainActivity中加一行代码
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Looper.prepare();
}
结果惨兮兮:
注意:
- prepare 方法在一个线程中只能被调用 1 次;
- Looper 的构造方法在一个线程中只能被调用 1 次;
- MessageQueue 在一个线程中只会被初始化 1 次。
结论:也就是说 UI 线程中只会存在 1 个 MessageQueue 对象,后续我们通过 Handler 发送的消息都会被发送到这个 MessageQueue 中。
Looper 干啥的?
总结 Looper 做的事情就是:不断从 MessageQueue 中取出 Message,然后处理 Message 中指定的任务。
回到原点,在 ActivityThread 的 main 方法中,除了调用 Looper.prepareMainLooper 初始化 Looper 对象之外,还调用了 Looper.loop 方法开启无限循环,Looper 的主要功能就是在这个循环中完成的。
Looper.loop()
public static void loop() {
//取出 Looper 对象并赋值给 me
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}
if (me.mInLoop) {
Slog.w(TAG, “Loop again would have the queued messages be executed”
+ " before this one completed.");
}
me.mInLoop = true;
final MessageQueue queue = me.mQueue;
…
//下面这个死循环,进去就甭想出来了。
for (;😉 {
//注释1
//调用 MessageQueue 的 next 方法取出 Message
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
…
try {
//注释2
//msg不为null,进行处理
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
…
msg.recycleUnchecked();
}
}
上面代码表示 loop 方法中执行了一个死循环,这也是一个 Android App 进程能够持续运行的原因。
注释1:不断地调用 MessageQueue 的 next 方法取出 Message。
注释2:如果 message 不为 null,则处进行后续处理。具体就是从 Message 中取出 target 对象,然后调用其 dispatchMessage 方法处理 Message 自身。target是谁?
Message.target
public final class Message implements Parcelable {
…
@UnsupportedAppUsage
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public long when;
/package/ Bundle data;
@UnsupportedAppUsage
/package/ Handler target;
@UnsupportedAppUsage
/package/ Runnable callback;
// sometimes we store linked lists of these things
@UnsupportedAppUsage
/package/ Message next;
/** @hide */
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
…
}
查看后其实就是个Handler。那咱们再看看Handler 的 dispatchMessage 方法
Handler.dispatchMessage()
/**
* Handle system messages here.
* 在这里处理系统消息。
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* Subclasses must implement this to receive messages.
* 子类必须实现它才能接收消息。
*/
public void handleMessage(@NonNull Message msg) {
}
可以看出,在 dispatchMessage 方法中会调用一个空方法 handleMessage,而这个方法也正是我们创建 Handler 时需要覆盖的方法。那么 Handler 是何时将其设置为一个 Message 的 target 的呢?
Handler.sendMessage()
Handler 有几个重载的 sendMessage 方法,但是基本都大同小异。咱使用最普通的 sendMessage 方法来分析,代码具体如下:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
Handler.sendMessageDelayed()
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
Handler.sendMessageAtTime()
public boolean sendMessageAtTime(@NonNull 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);
}
经过几层调用之后,在这里我们拿到了在 ActivityThread 的 main 方法中通过 Looper 创建的 MessageQueue。
并且最后调用 enqueueMessage 方法将 Message 插入到消息队列 MessageQueue 中。
Handler.emqueueMessage()
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//注释
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
注释:将 Handler 自身设置为 Message的target(Handler) 对象。下来咱们看看 MessageQueue 的 enqueueMessage 方法。因此后续 Message 会调用此 Handler 的 dispatchMessage 方法来处理。
MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
//注释1,非空判断
if (msg.target == null) {
throw new IllegalArgumentException(“Message must have a target.”);
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
//注释2,从这朝下都蛮重要的
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;😉 {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
注释1:会判断msg.target == null 没有设置,则直接抛出异常;
注释2:会按照 Message 的时间 when 来有序得插入 MessageQueue 中,可以看出 MessageQueue 实际上是一个有序队列,只不过是按照 Message 的执行时间来排序。
后续就是通过 ActivityThread 的 main 方法中 Looper 创建的 MessageQueue。Looper 从 MessageQueue 中取出 Message 之后,会调用 dispatchMessage 方法进行处理。
至此 Handler 的发送消息和消息处理流程已经介绍完毕。
重点关注
====
Handler 的 post(Runnable) 与 sendMessage(Message msg) 有什么区别
样例:
public class HandlerActivity extends ActivityBase{
ActivityHandlerBinding binding ;
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
binding.tvTotle.setText(String.format(“哈哈哈哈考了%d”,msg.arg1));
break;
}
return false;
}
});
@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHandlerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.btnSendMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = new Message();
msg.what=1;
msg.arg1=100;
handler.sendMessage(msg);
}
});
binding.btnPost.setOnClickListener(new View.OnClickListener() {
最后
分享一份NDK基础开发资料
分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
哈哈哈哈考了%d",msg.arg1));
break;
}
return false;
}
});
@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHandlerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.btnSendMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = new Message();
msg.what=1;
msg.arg1=100;
handler.sendMessage(msg);
}
});
binding.btnPost.setOnClickListener(new View.OnClickListener() {
最后
分享一份NDK基础开发资料
[外链图片转存中…(img-GTlPefpO-1714815468497)]
分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!