Picasso(4) -Dispatcher

一时失言乱红尘 2022-05-19 03:25 257阅读 0赞

系列文章

Picasso(1) - 使用(踩坑)
Picasso(2) - 自定义配置
Picasso(3) - 图片加载流程
Picasso(4) - Dispatcher
Picasso(5) - Rocket


前言

前几篇文章是对 Picasso 配置 、图片加载流程的分析 , 是宏观上的分析 。 本篇文章将对 Dispatcher 源码进行研究。


什么是 Dispatcher

Dispatcher 是 Picasso 用于分发事件 、线程切换的类。


Dispatcher 分发的事件

  1. REQUEST_SUBMIT = 1; // 请求提交
  2. REQUEST_CANCEL = 2; // 请求取消
  3. REQUEST_GCED = 3; // 给主线程发送REQUEST_GCED消息,让其清除弱引用并取消请求。
  4. HUNTER_COMPLETE = 4; // BitmapHunter获取了 Bitmap
  5. HUNTER_RETRY = 5; // 失败重试
  6. HUNTER_DECODE_FAILED = 6; // 获取 Bitmap 失败
  7. HUNTER_DELAY_NEXT_BATCH = 7; // 延迟批处理
  8. HUNTER_BATCH_COMPLETE = 8; // 批处理完成
  9. NETWORK_STATE_CHANGE = 9; // 网络变化
  10. AIRPLANE_MODE_CHANGE = 10; // 飞行模式
  11. TAG_PAUSE = 11; // 暂停
  12. TAG_RESUME = 12; // 继续
  13. REQUEST_BATCH_RESUME = 13; // 继续批处理

相信你已经被 HUNTER_COMPLETE 、HUNTER_DELAY_NEXT_BATCH 、HUNTER_BATCH_COMPLETE 搞晕了,先写一下这三个事件的执行顺序。
HUNTER_COMPLETE -> HUNTER_DELAY_NEXT_BATCH -> HUNTER_BATCH_COMPLETE。


构造器

首选来看下 Dispatcher 的构造器 。

  1. Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
  2. Downloader downloader, Cache cache, Stats stats) {
  3. this.dispatcherThread = new DispatcherThread(); // 子线程,分发的事件都在该线程执行。
  4. this.dispatcherThread.start(); // 启动线程。
  5. Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
  6. this.context = context;
  7. this.service = service; // 线程池
  8. this.hunterMap = new LinkedHashMap<>(); // 处理结果 Map
  9. this.failedActions = new WeakHashMap<>(); // 请求失败 Map
  10. this.pausedActions = new WeakHashMap<>(); // 请求暂停 Map
  11. this.pausedTags = new LinkedHashSet<>(); // 暂停的 Tag 集合
  12. this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this); // 重点关注
  13. this.downloader = downloader; // 下载器: Picasso 中默认为 OkHttp3Downloader
  14. this.mainThreadHandler = mainThreadHandler; // 主线程 Handler
  15. this.cache = cache; // 内存缓存
  16. this.stats = stats; // Picasso 中的统计类
  17. this.batch = new ArrayList<>(4); // 批处理(每隔 200 ms,将需要处理的结果放到列表中 )
  18. this.airplaneMode = Utils.isAirplaneModeOn(this.context); //是否为飞行模式
  19. this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE); //是否有监听网络状态变化的权限
  20. this.receiver = new NetworkBroadcastReceiver(this); // 广播接收者,监听网络状态变化
  21. receiver.register(); //注册广播
  22. }

线程切换

构造器中的 DispatcherThread 继承自 HandlerThread 。设置了线程名称 以及线程优先级 。

  1. static class DispatcherThread extends HandlerThread {
  2. DispatcherThread() {
  3. super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
  4. }
  5. }

构造器中 DispatcherHandler 传入的 Looper 对象为 DispatcherThread 的 Looper , 这意味着 Handler 的
handleMessage () 方法执行是在 HandlerThread 中,也就是说是在子线程执行。

  1. this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);

我们注意到 Dispatcher 中有非常多以 dispatch 开头命名的方法。而这些方法统统通过 handler 来发送消息。

  1. void dispatchSubmit(Action action) {
  2. handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
  3. }
  4. void dispatchCancel(Action action) {
  5. handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
  6. }
  7. void dispatchPauseTag(Object tag) {
  8. handler.sendMessage(handler.obtainMessage(TAG_PAUSE, tag));
  9. }
  10. void dispatchResumeTag(Object tag) {
  11. handler.sendMessage(handler.obtainMessage(TAG_RESUME, tag));
  12. }
  13. void dispatchComplete(BitmapHunter hunter) {
  14. handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
  15. }
  16. void dispatchRetry(BitmapHunter hunter) {
  17. handler.sendMessageDelayed(handler.obtainMessage(HUNTER_RETRY, hunter), RETRY_DELAY);
  18. }
  19. void dispatchFailed(BitmapHunter hunter) {
  20. handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter));
  21. }
  22. void dispatchNetworkStateChange(NetworkInfo info) {
  23. handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
  24. }
  25. void dispatchAirplaneModeChange(boolean airplaneMode) {
  26. handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE, airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0));
  27. }

这么做的目的只有一个, 切换线程,在 HandlerThread 中进行逻辑处理。即 Dispatcher 中以 perform 开头的方法都是在 HandlerThred 中执行。
Dispatcher 中有不少的集合类 , 但是 Dispatcher 中并没有使用 synchronized 关键字来保障线程安全。
因为操作集合的方法都在同一个线程 HandlerThread 当中。 Picasso 这样写,性能更好,也更优雅。


事件分发

Dispatcher 分发的事件, 分发的事件大体如下 :
这里写图片描述

看到上面这个图所分发的事件,大家有没有想到其他功能。
没错 ! 这些分发的事件不就是一个下载框架应该具有的功能吗。
我们完全可以照着 Dispatcher 来写一个项目通用的下载框架 , 支持下载 apk、图片、mp3 等。
下载框架这里暂且不提,我们先来分析 Dispatcher 的源码。


submit

当你提交一个请求到 Dispatcher 中, 首选会调用 dispatchSubmit () , 然后线程切换到 HandlerThread , 调用 performSubmit()。

  1. void performSubmit(Action action) {
  2. performSubmit(action, true);
  3. }
  4. void performSubmit(Action action, boolean dismissFailed) {
  5. // 提交的请求是否需要暂停,当你调用了 Picasso.get().pauseTag() 后 ,如果请求正在下载, Picasso 会暂停下载。
  6. // 如果之后提交的请求 tag 和 暂停的 tag 相等, 那么也将添加到暂停队列中,不会去下载。
  7. if (pausedTags.contains(action.getTag())) {
  8. pausedActions.put(action.getTarget(), action);
  9. if (action.getPicasso().loggingEnabled) {
  10. log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
  11. "because tag '" + action.getTag() + "' is paused");
  12. }
  13. return;
  14. }
  15. //请求是否正在下载
  16. BitmapHunter hunter = hunterMap.get(action.getKey());
  17. if (hunter != null) {
  18. // 避免重复请求多次下载 。
  19. hunter.attach(action);
  20. return;
  21. }
  22. // 线程池是否已经释放
  23. // 通常使用 Picasso 的时候是单例使用 。如果你创建了其他 Picasso 实例, 并且释放其他 Picasso 实例的时候 ,service.isShutdown() 返回值为 true 。
  24. // 通俗一点讲, 线程池已经释放了,那么提交过来的请求,就不要再去执行了。
  25. if (service.isShutdown()) {
  26. if (action.getPicasso().loggingEnabled) {
  27. log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
  28. }
  29. return;
  30. }
  31. // 创建 BitmapHunter 对象 , 这个对象就相当于OkHttp 中的 Response 。
  32. hunter = forRequest(action.getPicasso(), this, cache, stats, action);
  33. // 提交请求到线程池
  34. hunter.future = service.submit(hunter);
  35. // 将请求放到下载队列中
  36. hunterMap.put(action.getKey(), hunter);
  37. // 是否存下载失败队列中移除请求
  38. if (dismissFailed) {
  39. failedActions.remove(action.getTarget());
  40. }
  41. if (action.getPicasso().loggingEnabled) {
  42. log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
  43. }
  44. }

cancel

  1. void performCancel(Action action) {
  2. String key = action.getKey();
  3. BitmapHunter hunter = hunterMap.get(key);
  4. //是否在下载队列中
  5. if (hunter != null) {
  6. //移除回调
  7. hunter.detach(action);
  8. //是否取消成功
  9. if (hunter.cancel()) {
  10. //移除请求
  11. hunterMap.remove(key);
  12. if (action.getPicasso().loggingEnabled) {
  13. log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId());
  14. }
  15. }
  16. }
  17. //当前请求是否在暂停队列中。
  18. if (pausedTags.contains(action.getTag())) {
  19. // 从暂停队列中移除
  20. pausedActions.remove(action.getTarget());
  21. if (action.getPicasso().loggingEnabled) {
  22. log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId(),
  23. "because paused request got canceled");
  24. }
  25. }
  26. // 从失败队列中移除
  27. Action remove = failedActions.remove(action.getTarget());
  28. if (remove != null && remove.getPicasso().loggingEnabled) {
  29. log(OWNER_DISPATCHER, VERB_CANCELED, remove.getRequest().logId(), "from replaying");
  30. }
  31. }

重复请求的处理

讲 pause() 方法之前先讲下 Picasso 对重复请求的处理。不然大家会对 pause() 相关代码的时候会有疑惑。
BitmapHunter 与 请求之间的关系如下图 :

这里写图片描述

我给大家举个例子 :
想要下一张图片显示在 ImageView 中,图片下载地址为 http://i.imgur.com/DvpvklR.png 。
Picasso 将下载结果封装为 BitmapHunter 对象 。将请求和 ImagView 封装到 初始请求 Action 对象中。
图片还没下载好, 我又想把图片显示到 ImageView2中。
Picasso 为了避免重复下载 , 使用刚才创建的 BitmapHunter, 将请求和 ImageView2 添加到另一个 Aciton 对象中 , 添加到 List actions 中。 actions 相当于回调列表。
当我们取消请求的时候, 取消的请求可能为初始请求 Action ,也可能为重复请求列表中的某个请求 。
因此我们可以看到 picasso 分发事件的时候 , 会对 action 和 actions 分别做判断。
有人肯定会问 ? 为什么初始 Action 要单独作为一个对象 , 直接放到 List 不是更简单吗 ?
我认为这样写也可以。


pause

  1. void performPauseTag(Object tag) {
  2. // 是否已经加入到 pauseTags 集合中。
  3. if (!pausedTags.add(tag)) {
  4. return;
  5. }
  6. // 遍历所有的请求,如果请求的 tag 和 暂停的 tag 相同
  7. // 那么取消请求。
  8. for (Iterator<BitmapHunter> it = hunterMap.values().iterator(); it.hasNext();) {
  9. BitmapHunter hunter = it.next();
  10. boolean loggingEnabled = hunter.getPicasso().loggingEnabled;
  11. // 第一个请求
  12. Action single = hunter.getAction();
  13. // 重复请求
  14. List<Action> joined = hunter.getActions();
  15. boolean hasMultiple = joined != null && !joined.isEmpty();
  16. // BitmapHunter 中没有请求要处理
  17. if (single == null && !hasMultiple) {
  18. continue;
  19. }
  20. if (single != null && single.getTag().equals(tag)) {
  21. // 移除请求
  22. hunter.detach(single);
  23. // 放入暂停队列
  24. pausedActions.put(single.getTarget(), single);
  25. if (loggingEnabled) {
  26. log(OWNER_DISPATCHER, VERB_PAUSED, single.request.logId(),
  27. "because tag '" + tag + "' was paused");
  28. }
  29. }
  30. // 如果有重复请求
  31. if (hasMultiple) {
  32. for (int i = joined.size() - 1; i >= 0; i--) {
  33. Action action = joined.get(i);
  34. // 请求 tag 是否相等
  35. if (!action.getTag().equals(tag)) {
  36. continue;
  37. }
  38. // 移除请求
  39. hunter.detach(action);
  40. // 放入暂停队列
  41. pausedActions.put(action.getTarget(), action);
  42. if (loggingEnabled) {
  43. log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
  44. "because tag '" + tag + "' was paused");
  45. }
  46. }
  47. }
  48. // 取消请求
  49. if (hunter.cancel()) {
  50. it.remove();
  51. if (loggingEnabled) {
  52. log(OWNER_DISPATCHER, VERB_CANCELED, getLogIdsForHunter(hunter), "all actions paused");
  53. }
  54. }
  55. }
  56. }

真正从线程池中取消请求是执行 hunter.cancel() ,可以看到想要成功取消请求是有条件的。
action : 第一个请求,
actions : 重复请求。
future : ExecutorService.submit() 提交的返回值 。
future.cancel(false) : 如果任务正在线程池中执行,那么不中断任务。如果任务正在线程池队列中, 那么取消任务。

  1. boolean cancel() {
  2. return action == null
  3. && (actions == null || actions.isEmpty())
  4. && future != null
  5. && future.cancel(false);
  6. }

resume

  1. void performResumeTag(Object tag) {
  2. // 是否在暂停队列中
  3. if (!pausedTags.remove(tag)) {
  4. return;
  5. }
  6. List<Action> batch = null;
  7. // 遍历暂停队列
  8. for (Iterator<Action> i = pausedActions.values().iterator(); i.hasNext();) {
  9. Action action = i.next();
  10. if (action.getTag().equals(tag)) {
  11. if (batch == null) {
  12. batch = new ArrayList<>();
  13. }
  14. // 添加到批处理列表当中
  15. batch.add(action);
  16. i.remove();
  17. }
  18. }
  19. // 切换到主线程:如果内存缓存中有 Bimmap ,直接显示 Bitmap ,否则执行 submit 流程。
  20. if (batch != null) {
  21. mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(REQUEST_BATCH_RESUME, batch));
  22. }
  23. }

从代码中可以看出 , Picasso 中的 pause() 和 resume() , 其实相当于执行了 cancel() 和 submit() 。


retry

下载重试 , 这个功能很实用。

  1. void performRetry(BitmapHunter hunter) {
  2. // 请求是否取消
  3. if (hunter.isCancelled()) return;
  4. // 线程池是否已经释放
  5. if (service.isShutdown()) {
  6. performError(hunter, false);
  7. return;
  8. }
  9. NetworkInfo networkInfo = null;
  10. // 是否有监听网络变化的权限
  11. if (scansNetworkChanges) {
  12. ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
  13. networkInfo = connectivityManager.getActiveNetworkInfo();
  14. }
  15. // 是否应该重试
  16. if (hunter.shouldRetry(airplaneMode, networkInfo)) {
  17. if (hunter.getPicasso().loggingEnabled) {
  18. log(OWNER_DISPATCHER, VERB_RETRYING, getLogIdsForHunter(hunter));
  19. }
  20. // 如果是 ContentLengthException 则不从磁盘缓存中读取数据。
  21. if (hunter.getException() instanceof NetworkRequestHandler.ContentLengthException) {
  22. hunter.networkPolicy |= NetworkPolicy.NO_CACHE.index;
  23. }
  24. // 线程池提交任务
  25. hunter.future = service.submit(hunter);
  26. } else {
  27. //如果网络变化并且支持重试
  28. boolean willReplay = scansNetworkChanges && hunter.supportsReplay();
  29. // 分发失败事件
  30. performError(hunter, willReplay);
  31. if (willReplay) {
  32. // 添加到失败列表中,如果网络变为可用,下载重试。
  33. markForReplay(hunter);
  34. }
  35. }
  36. }

complete

  1. void performComplete(BitmapHunter hunter) {
  2. // 根据内存策略判断是否写入内存缓存
  3. if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
  4. cache.set(hunter.getKey(), hunter.getResult());
  5. }
  6. // 移除下载队列
  7. hunterMap.remove(hunter.getKey());
  8. //批处理
  9. batch(hunter);
  10. if (hunter.getPicasso().loggingEnabled) {
  11. log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
  12. }
  13. }
  14. private void batch(BitmapHunter hunter) {
  15. // 请求是否取消
  16. if (hunter.isCancelled()) {
  17. return;
  18. }
  19. if (hunter.result != null) {
  20. hunter.result.prepareToDraw();
  21. }
  22. batch.add(hunter);
  23. // 每隔 200 ms , 对处理一批请求。
  24. if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
  25. // 发送消息后调用了 performBatchComplete() 方法。
  26. handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
  27. }
  28. }
  29. void performBatchComplete() {
  30. // 将 200 ms 内待处理的请求放入 copy 列表中。
  31. List<BitmapHunter> copy = new ArrayList<>(batch);
  32. batch.clear();
  33. // 给 mainThreadHandler 发送休息,主线程处理返回结果。
  34. mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
  35. logBatch(copy);
  36. }
  37. static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
  38. @Override public void handleMessage(Message msg) {
  39. switch (msg.what) {
  40. case HUNTER_BATCH_COMPLETE: {
  41. @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
  42. // 遍历待处理列表
  43. for (int i = 0, n = batch.size(); i < n; i++) {
  44. BitmapHunter hunter = batch.get(i);
  45. // 调用 picasso 的 complete 方法。
  46. hunter.picasso.complete(hunter);
  47. }
  48. break;
  49. }
  50. // picasso 的 complete() 方法 。
  51. void complete(BitmapHunter hunter) {
  52. //初始请求是否存在
  53. Action single = hunter.getAction();
  54. List<Action> joined = hunter.getActions();
  55. // 是否有重复请求
  56. boolean hasMultiple = joined != null && !joined.isEmpty();
  57. boolean shouldDeliver = single != null || hasMultiple;
  58. // 如果没有一个请求都没有,直接 return 。这种情况,一般是主动取消所有请求。
  59. if (!shouldDeliver) {
  60. return;
  61. }
  62. Uri uri = hunter.getData().uri;
  63. Exception exception = hunter.getException();
  64. Bitmap result = hunter.getResult();
  65. // 图片加载的位置:内存/磁盘/网络
  66. LoadedFrom from = hunter.getLoadedFrom();
  67. // 分发单个请求
  68. if (single != null) {
  69. deliverAction(result, from, single, exception);
  70. }
  71. // 分发重复请求
  72. if (hasMultiple) {
  73. //noinspection ForLoopReplaceableByForEach
  74. for (int i = 0, n = joined.size(); i < n; i++) {
  75. Action join = joined.get(i);
  76. deliverAction(result, from, join, exception);
  77. }
  78. }
  79. // 下载过程中有一场。 listener 为全局回调。 在 Picasso 的 Builder 中配置。
  80. if (listener != null && exception != null) {
  81. listener.onImageLoadFailed(this, uri, exception);
  82. }
  83. }
  84. // 分发处理 Action
  85. private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
  86. // 是否 Action 被取消
  87. if (action.isCancelled()) {
  88. return;
  89. }
  90. // 图片下载是否会重试。
  91. // 如 : 图片因 IO 异常加载失败, 当前网络不可用。
  92. // 此时应用能够监听网络变化,并且当前 RequestHandler 支持重试的时候, willReplay = 值为 true 。
  93. // 表示当网络可用的时候,会重新加载图片。
  94. if (!action.willReplay()) {
  95. targetToAction.remove(action.getTarget());
  96. }
  97. if (result != null) {
  98. if (from == null) {
  99. throw new AssertionError("LoadedFrom cannot be null.");
  100. }
  101. //图片加载成功, 将 Bitmap 交给 Action 处理。
  102. action.complete(result, from);
  103. if (loggingEnabled) {
  104. log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
  105. }
  106. } else {
  107. //图片加载失败
  108. action.error(e);
  109. if (loggingEnabled) {
  110. log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());
  111. }
  112. }
  113. }

error

error 的处理过程比较简单 。直接从结果队列中移除, 然后调用 batch () 方法。
batch() 方法在 complete() 的时候已经分析过了,这里就不再分析了。

  1. void performError(BitmapHunter hunter, boolean willReplay) {
  2. if (hunter.getPicasso().loggingEnabled) {
  3. log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter),
  4. "for error" + (willReplay ? " (will replay)" : ""));
  5. }
  6. hunterMap.remove(hunter.getKey());
  7. batch(hunter);
  8. }

NetworkStateChange

当前网络状态由不可用变为可用的时候, Picasso 会尝试加载之前下载失败的图片。

  1. void performNetworkStateChange(NetworkInfo info) {
  2. // 根据当前网络状态,调整线程池中线程数目。
  3. if (service instanceof PicassoExecutorService) {
  4. ((PicassoExecutorService) service).adjustThreadCount(info);
  5. }
  6. // 当前网络可用
  7. if (info != null && info.isConnected()) {
  8. flushFailedActions();
  9. }
  10. }
  11. private void flushFailedActions() {
  12. //是否有下载失败请求
  13. if (!failedActions.isEmpty()) {
  14. Iterator<Action> iterator = failedActions.values().iterator();
  15. // 遍历下载失败请求
  16. while (iterator.hasNext()) {
  17. Action action = iterator.next();
  18. iterator.remove();
  19. if (action.getPicasso().loggingEnabled) {
  20. log(OWNER_DISPATCHER, VERB_REPLAYING, action.getRequest().logId());
  21. }
  22. //提交请求,重新走图片下载的流程。
  23. performSubmit(action, false);
  24. }
  25. }
  26. }

flushStackLocalLeaks

在构造器中有这么一行代码。 Utils.flushStackLocalLeaks(dispatcherThread.getLooper());

  1. Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
  2. // THREAD_LEAK_CLEANING_MS 为 1000 ms 即 1秒
  3. static void flushStackLocalLeaks(Looper looper) {
  4. Handler handler = new Handler(looper) {
  5. @Override public void handleMessage(Message msg) {
  6. sendMessageDelayed(obtainMessage(), THREAD_LEAK_CLEANING_MS);
  7. }
  8. };
  9. handler.sendMessageDelayed(handler.obtainMessage(), THREAD_LEAK_CLEANING_MS);
  10. }

可以看到创建了 Handler , 每隔 1 s 发送了一个空的消息。代码中的注释说 :
这个方法主要用于 Android 5.0 之前 。HandlerThread 总是持有上一个发送 message 的堆栈本地应用 , 因此为了保证上一个 messsage 能够被及时回收 , 每隔一秒发送了一个 message 。
对这里的代码,我有点疑惑。因为我不知道如何才能重现这样的 Bug 。也没看到添加 Android 5.0 之前的判断。(注释写的 Android 5 , 我怀疑作者少写了 .0)。

总结

以上就是对 Dispatcher 所有代码的分析。
如果是第一次阅读 Dispatcher 的源码 , 难免会有很多的疑惑。
静下心细细阅读,会发现作者这样写的好处。

#

发表评论

表情:
评论列表 (有 0 条评论,257人围观)

还没有评论,来说两句吧...

相关阅读