当前位置: 首页 > news >正文

郑州 科技有限公司 网站建设国外 外贸 网站 源码

郑州 科技有限公司 网站建设,国外 外贸 网站 源码,百度排名查询,快递服务平台最近想写个播放器demo#xff0c;里面要用到 Looper Handler#xff0c;看了很多资料都没能理解透彻#xff0c;于是决定自己看看相关的源码#xff0c;并在此记录心得体会#xff0c;希望能够帮助到有需要的人。 本文会以 猜想 log验证 的方式来学习 Android Looper Ha…最近想写个播放器demo里面要用到 Looper Handler看了很多资料都没能理解透彻于是决定自己看看相关的源码并在此记录心得体会希望能够帮助到有需要的人。 本文会以 猜想 log验证 的方式来学习 Android Looper Handler对于一些复杂的代码会进行跳过能够理解它们的设计原理即可。本文观点皆个人拙见如有错误恳请赐教。 1、Looper Handler MessageQueue Looper 在整个消息处理机制中起着 消息等待与消息分发 的作用 消息等待Looper 阻塞程序运行等待即将到来的消息消息分发Looper 收到消息后将消息分发给指定处理者在当前线程处理。 首先来看阻塞的问题app 启动时会创建一个 Looper并且调用 loop 函数阻塞 main 函数这个 Looper 被称为主线程/主进程 Looper代码参考 ActivityThread.java public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ActivityThreadMain);Looper.prepareMainLooper();Looper.loop();}主线程 Looper或者又叫 mainLooper有着阻止程序结束的作用这里就会引申出其他问题为什么它阻塞了程序还能继续运行呢 上文的消息等待作用中我们说到 Looper 阻塞程序运行时会等待即将到来的消息什么消息会到来呢 我写了一个 demo界面上只有一个 Button点击图标启动 app 并且点击按钮接着我们来观察 log 2023-08-28 22:17:51.969 6768-6768/com.example.loopertest D/MainActivity: onCreate 2023-08-28 22:17:51.993 6768-6768/com.example.loopertest D/MainActivity: onStart 2023-08-28 22:17:51.993 6768-6768/com.example.loopertest D/MainActivity: onPostCreate 2023-08-28 22:17:51.994 6768-6768/com.example.loopertest D/MainActivity: onResume 2023-08-28 22:17:51.994 6768-6768/com.example.loopertest D/MainActivity: onPostResume 2023-08-28 22:17:54.424 6768-6768/com.example.loopertest D/MainActivity: onClick可以看到 log 的线程号和进程号相同也就是说所有的消息都是在主线程UI线程/主进程中进行处理的那 Looper 收到有 Activity 的启动事件、按钮的点击事件引申来说主进程将会收到并处理所有的 UI 事件。 Looper 是如何接收事件并处理的呢loop 方法中有一个死循环不断执行 loopOnce阻塞也在此发生 public static void loop() {for (;;) {if (!loopOnce(me, ident, thresholdOverride)) {return;}}}loopOnce 中会看到一个 MessageQueue 对象这是 Looper 所持有的消息队列它内部维护着一个链表消息等待也由它完成。 private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg me.mQueue.next(); // might blockif (msg null) {// No message indicates that the message queue is quitting.return false;}...try {msg.target.dispatchMessage(msg);if (observer ! null) {observer.messageDispatched(token, msg);}dispatchEnd needEndTime ? SystemClock.uptimeMillis() : 0;} ....return true;}MessageQueue.next 是一个阻塞方法当没有消息时它将阻塞等待可以防止上一层 for 循环空转有消息时就返回 Message分发给指定的 Handler 来处理。 我们再看下 loop 循环要如何退出从上面的代码我们可以看到当返回的 Message 是 null 时loopOnce 返回 false整个 loop 循环结束。 MessageQueue.next 代码如下 Message next() {......for (;;) {......nativePollOnce(ptr, nextPollTimeoutMillis);......if (mQuitting) {dispose();return null;} } }这里的代码比较长核心内容是 调用 nativePollOnce 在 native 层阻塞 poll 消息将 poll 到的消息根据指定的执行时间进行排序如果没有指定时间则按消息到底的先后顺序排序如果链表中第一条消息需要延时则继续调用 nativePollOnce并且设定一个超时时间将需要处理的消息返回给 loopOnce如果调用了 quit 方法则返回 null终止循环。 以上代码我们看到消息的获取方式是调用 nativePollOnce 进行等待这里的 native 消息可能就是 android 系统发送给 mainLooper 的例如触摸点击事件等等。除此之外我们也可以主动给 mainLooper 发消息这就需要使用 Handler 了。 我们调用 Handler 的 sendMessage 方法最终会执行到 enqueueMessage 中 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);}会调用 MessageQueue.enqueueMessage 将消息加入到 Message 链表中。问题来了这里的 MessageQueue 是哪里来的 这就需要追溯到 Handler 的构造函数需要将一个 Looper 作为参数传递进去MessageQueue 就是从 Looper 中获得的。 这也就意味着每个 Handler 只能处理一个 Looper 的事务。为什么只能处理一个 Looper 的事务呢我的理解是这样Looper 将所有的消息或事务进行收集接着再一条一条转发执行虽然消息是异步发出的但是 Handler 执行任务是同步的这就没有了线程同步的问题。 boolean enqueueMessage(Message msg, long when) {if (msg.target null) {throw new IllegalArgumentException(Message must have a target.);}synchronized (this) {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();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;}if (needWake) {nativeWake(mPtr);}}return true;}进入到 MessageQueue.enqueueMessage 后消息会被加入到队列当中如果当前消息队列是空的就会调用 nativeWake 来打断 nativePollOnce 的执行从而立即处理我们主动 post 的消息。 Looper 进行消息分发时应该交给谁来处理呢答案是哪个 Handler send 的消息消息就由哪个 Handler 处理这一点可以查看 enqueueMessage 方法。 除了用 Handler post 消息外Message 本身也有一个 sendToTarget 方法可以将自己发送给指定的 Handler由 Hander 再加入到 MessageQueue 队列中有兴趣可以阅读相关代码。 除了用 Handler sendMessage 之外我们还常常见到用 Handler post Runnable。Runnable是什么 被 sendMessage 发出的 Message 常常会设定有 what 信息之后 Handler 会根据 what 来做对应的处理代码示例如下 Handler handler new Handler(getMainLooper()) {Overridepublic void handleMessage (Message msg) {switch (msg.what) {case 1:break;default:break;}}};Message msg Message.obtain();msg.what 1;handler.sendMessage(msg);有的时候我们并不希望 Handler 对我们发出的消息进行识别处理而是仅仅是想完成一项任务这时候我们可以去 post Runnable。Runnable 会以 callback 的形式封装称为一个 Message在分发处理时直接执行 Runnable 中所写的事务就不需要再进入到 handleMessage 方法中了。 public void dispatchMessage(NonNull Message msg) {if (msg.callback ! null) {handleCallback(msg);} else {if (mCallback ! null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}我们也可以 dump Looper 中的 Message看到 MessageQueue 中的内容以及 Looper 当前的状态以下是在 Button 的 onClick 方法中进行 dumplog 如下 2023-08-28 23:50:15.953 7495-7495/com.example.loopertest D/MainActivity: onClick 2023-08-28 23:50:15.953 7495-7495/com.example.loopertest D/MainActivity: Looper (main, tid 2) {f4105a7} 2023-08-28 23:50:15.954 7495-7495/com.example.loopertest D/MainActivity: Message 0: { when-2ms callbackandroid.view.View$UnsetPressedState targetandroid.view.ViewRootImpl$ViewRootHandler } 2023-08-28 23:50:15.954 7495-7495/com.example.loopertest D/MainActivity: (Total messages: 1, pollingfalse, quittingfalse)到这里Looper Handler MessageQueue 的基本原理就讲完了接下来我们来了解它们的实际是如何使用的。 2、How to use 2.1、Thread 在了解如何使用之前有两点需要先注意 UI内容的处理只能放在主线程当中一个线程只能有一个 Looper 第二点一个线程只能有一个 Looper是因为每个线程只能有一处阻塞如果有一个线程有两个 Looper那么其中一个 Looper 的 loop 循环会被另一个 Looper 的 loop 循环所阻塞。 根据前面的内容我们了解到app 在启动时会自动创建一个 MainLooper 用于处理 UI 相关的消息但是在实际 app 编写中会有各种各样的任务如果将所有的任务都放在 UI 线程中执行那么可能就会影响 UI 事件的处理出现 ANR 等情况。 例如我在 onCreate 中加入以下代码 new Handler(getMainLooper()).post(new Runnable() {Overridepublic void run() {for (int i 0; i 5; i) {try {Log.d(LOG_TAG, i i thread id Process.myTid());sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});可以看到启动的时候需要等待 Runnable 执行结束才能渲染出 UI这显然是不太合适的。为了解决一般事务占用 UI 线程的问题常常会在一个新的线程中来处理与 UI 不相干的事务或者一些耗时的事务。 线程 与 Looper Handler 协同使用的最简单的方式如下 // 问题示例private Handler handler;new Thread(new Runnable() {Overridepublic void run() {Looper.prepare();handler new Handler(Looper.myLooper()) {Overridepublic void handleMessage (Message msg) {switch (msg.what) {case 1:Log.d(LOG_TAG, Process Message 1);break;default:break;}}};Looper.loop();}}).start();Message msg Message.obtain();msg.what 1;handler.sendMessage(msg);想要在子线程中使用 Looper Handler我们有3件事需要做 创建并启动线程在线程中创建 Looper并且调用 Looper.loop 阻塞线程创建 Handler 并且与线程中的 Looper 相绑定 以上代码是否有问题呢我们将 app 跑起来看一下呀会有空指针的错误 08-28 16:05:44.815 8436 8436 E AndroidRuntime: Process: com.example.loopertest, PID: 8436 08-28 16:05:44.815 8436 8436 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.loopertest/com.example.loopertest.MainActivity}: java.lang.NullPointerExceptio n: Attempt to invoke virtual method boolean android.os.Handler.sendMessage(android.os.Message) on a null object reference冷静分析这是因为 线程启动 会和 sendMessage 并列执行当执行到 sendMessage 时handler 可能还没有创建所以会出现空指针的错误。解决办法是在 sendMessage 之前加一个 delay保证 handler 已经被创建 // 解决办法Message msg Message.obtain();msg.what 1;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}handler.sendMessage(msg);这时候可能有人要问了我为什么不在线程外部先创建 Looper然后在线程内部调用 Looper.loop 呢好问题还记得我们之前说过一个线程只能有一个 Looper 吗外部是主线程如果在外部创建主线程就会有两个 Looper 了。来看 Looper 的声明 public final class Looper {static final ThreadLocalLooper sThreadLocal new ThreadLocalLooper();private static Looper sMainLooper; // guarded by Looper.classpublic static void prepare() {prepare(true);}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));}sMainLooper 是一个静态变量意味着我们的进程中只能存在一个 MainLooper也就是我们所说的主线程 Looper另外java还提供了一种线程的局部静态变量 sThreadLocal 保证每个线程的静态变量是不同的。 prepare方法用于在线程中创建 Looper如果一个线程中调用两次 prepare那么就会抛出异常了这也印证了我们上文所说的一个线程只能有一个 Looper。 这时候可能又有人要问了为什么我不在不在外面创建 Handler 实例然后将 Thread 的 Looper 与 Handler 绑定呢不错我们来试试 class MyThread extends Thread {Overridepublic void run() {Looper.prepare();Looper.loop();}Looper getLooper() {return Looper.myLooper();}}private MyThread myThread;myThread new MyThread();myThread.start();handler new Handler(myThread.getLooper()) {Overridepublic void handleMessage (Message msg) {switch (msg.what) {case 1:Log.d(LOG_TAG, Process Message 1);break;default:break;}}};Message msg Message.obtain();msg.what 1;handler.sendMessage(msg);实际跑起来结果如下没有问题以后就这么用了 2023-08-29 21:27:16.583 9284-9284/com.example.loopertest D/MainActivity: Process Message 12.2、HandlerThread 是不是要为我们的机智鼓个掌呢稍等android 好像已经实现了我们所想的方法这就是 HandlerThreadHandlerThread 简直就和我们的想法一模一样所以用法也是一模一样这里给出示例就不再多做解释了。 handlerThread new HandlerThread(Test HandlerThread);handlerThread.start();Message msg Message.obtain();msg.what 1;new Handler(handlerThread.getLooper()) {Overridepublic void handleMessage (Message msg) {switch (msg.what) {case 1:Log.d(LOG_TAG, Process Message 1);break;default:break;}}}.sendMessage(msg);2023-08-29 00:15:51.853 8743-8743/com.example.loopertest D/MainActivity: onCreate 2023-08-29 00:15:51.871 8743-8858/com.example.loopertest D/MainActivity: Process Message 1 2023-08-29 00:15:51.871 8743-8743/com.example.loopertest D/MainActivity: onStart 2023-08-29 00:15:51.872 8743-8743/com.example.loopertest D/MainActivity: onPostCreate 2023-08-29 00:15:51.872 8743-8743/com.example.loopertest D/MainActivity: onResume 2023-08-29 00:15:51.872 8743-8743/com.example.loopertest D/MainActivity: onPostResume2.3、线程间互发消息 我们可能经常会听到 子线程往主线程发送消息主线程往子线程发送消息子线程往子线程发送消息一看到这么多情况是不是有点晕 其实这三种互发消息的原理是一致的还记得我们上文说过Handler 创建时要与一个 Looper 绑定 public Handler(NonNull Looper looper) {this(looper, null, false); }绑定 Looper 之后Handler 只能处理绑定的 Looper 分发的消息了那绑定的 Looper 中的消息怎么来的除了 native 发送的消息外我们是不是可以调用与 Looper 绑定的 Handler 来发送消息 也就是说要让消息在指定 Looper 线程中执行只要调用与该线程 Looper 绑定的 Handler 的 post / sendMessage 方法。 就是这么简单 2.4、Looper 如何停止 这里要谈一谈网上讲的最多的内存泄漏的问题首先给个示例不知道我理解的对不对哈 我们在 onCreate 方法中增加以下代码打开 app 后立刻关闭 new Handler(getMainLooper()).postDelayed(new Runnable() {Overridepublic void run() {for (int i 0; i 5; i) {try {Log.d(LOG_TAG, i i thread id Process.myTid());sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}, 5000);可以看到以下现象 2023-08-29 21:51:30.568 9599-9599/com.example.loopertest D/MainActivity: onCreate 2023-08-29 21:51:30.586 9599-9599/com.example.loopertest D/MainActivity: onStart 2023-08-29 21:51:30.587 9599-9599/com.example.loopertest D/MainActivity: onPostCreate 2023-08-29 21:51:30.587 9599-9599/com.example.loopertest D/MainActivity: onResume 2023-08-29 21:51:30.587 9599-9599/com.example.loopertest D/MainActivity: onPostResume 2023-08-29 21:51:31.783 9599-9599/com.example.loopertest D/MainActivity: onKeyDown keyCode 4 2023-08-29 21:51:31.873 9599-9599/com.example.loopertest D/MainActivity: onPause 2023-08-29 21:51:32.399 9599-9599/com.example.loopertest D/MainActivity: onStop 2023-08-29 21:51:32.399 9599-9599/com.example.loopertest D/MainActivity: onDestroy 2023-08-29 21:51:35.624 9599-9599/com.example.loopertest D/MainActivity: i 0 thread id 9599 2023-08-29 21:51:36.635 9599-9599/com.example.loopertest D/MainActivity: i 1 thread id 9599 2023-08-29 21:51:37.677 9599-9599/com.example.loopertest D/MainActivity: i 2 thread id 9599 2023-08-29 21:51:38.701 9599-9599/com.example.loopertest D/MainActivity: i 3 thread id 9599 2023-08-29 21:51:39.744 9599-9599/com.example.loopertest D/MainActivity: i 4 thread id 9599嘿明明已经调用了 onDestroy 了为什么还能打印出循环内容呢不知道这是不是其他博文中所说的内存泄漏退出 activity 时仍占用着 activity 的资源导致资源无法正常释放 网上的一些解法是将 Handler 声明为静态类这里我并不认为这是好的解法。 退出程序或者 activity 时Looper 所在的线程正在运行我们正常是需要停止线程的。由于 Looper.loop 正在阻塞运行我们要调用 Looper.quit 或者 Looper.quitSafely 退出死循环。 这里我要提出一个问题调用了 quit / quitSafely 后Looper 就真的停止了吗 答案是否定的Looper 并不一定会立即停止它需要执行完当前的任务才能停止如果当前任务是一个耗时任务那么会等他执行完 Looper 才会真正停止。 我们所要做的是什么呢 把耗时工作放到 Looper 中执行时添加打断机制ativity 结束时调用 Looper.quit调用 Thread.join 阻塞等待线程结束 我觉得这样资源可以正常释放结束不知道我理解的是否正确。接下来用 HandlerThread 给出一个示例 在 onCreate 和 onDestroy 中添加以下代码 protected void onCreate(Bundle savedInstanceState) {handlerThread new HandlerThread(Test HandlerThread quit);handlerThread.start();Message msg Message.obtain();msg.what 1;new Handler(handlerThread.getLooper()).post(new Runnable() {Overridepublic void run() {int i 0;while (i 10) {try {Log.d(LOG_TAG, i i thread id Process.myTid());sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}i;}}});}protected void onStop() {Log.d(LOG_TAG, onStop);super.onStop();handlerThread.quitSafely();}启动 app 后立刻退出 app可以看到我们已经调用了 quitSafely线程仍然没有立即停止资源没有正常释放 2023-08-29 22:43:24.101 10559-10559/com.example.loopertest D/MainActivity: onCreate 2023-08-29 22:43:24.203 10559-10586/com.example.loopertest D/MainActivity: i 0 thread id 10586 2023-08-29 22:43:24.207 10559-10559/com.example.loopertest D/MainActivity: onStart 2023-08-29 22:43:24.210 10559-10559/com.example.loopertest D/MainActivity: onPostCreate 2023-08-29 22:43:24.211 10559-10559/com.example.loopertest D/MainActivity: onResume 2023-08-29 22:43:24.212 10559-10559/com.example.loopertest D/MainActivity: onPostResume 2023-08-29 22:43:25.205 10559-10586/com.example.loopertest D/MainActivity: i 1 thread id 10586 2023-08-29 22:43:26.004 10559-10559/com.example.loopertest D/MainActivity: onKeyDown keyCode 4 2023-08-29 22:43:26.207 10559-10586/com.example.loopertest D/MainActivity: i 2 thread id 10586 2023-08-29 22:43:26.231 10559-10559/com.example.loopertest D/MainActivity: onPause 2023-08-29 22:43:26.765 10559-10559/com.example.loopertest D/MainActivity: onStop 2023-08-29 22:43:26.766 10559-10559/com.example.loopertest D/MainActivity: onDestroy 2023-08-29 22:43:27.208 10559-10586/com.example.loopertest D/MainActivity: i 3 thread id 10586 2023-08-29 22:43:28.242 10559-10586/com.example.loopertest D/MainActivity: i 4 thread id 10586 2023-08-29 22:43:29.258 10559-10586/com.example.loopertest D/MainActivity: i 5 thread id 10586 2023-08-29 22:43:30.275 10559-10586/com.example.loopertest D/MainActivity: i 6 thread id 10586 2023-08-29 22:43:31.293 10559-10586/com.example.loopertest D/MainActivity: i 7 thread id 10586 2023-08-29 22:43:32.308 10559-10586/com.example.loopertest D/MainActivity: i 8 thread id 10586 2023-08-29 22:43:33.348 10559-10586/com.example.loopertest D/MainActivity: i 9 thread id 10586 我们在 quitSafely 后面加上 join可以看到退出程序时onStop 阻塞在 join 的位置上等待线程结束 2023-08-29 22:43:56.616 10618-10618/com.example.loopertest D/MainActivity: onCreate 2023-08-29 22:43:56.745 10618-10644/com.example.loopertest D/MainActivity: i 0 thread id 10644 2023-08-29 22:43:56.748 10618-10618/com.example.loopertest D/MainActivity: onStart 2023-08-29 22:43:56.749 10618-10618/com.example.loopertest D/MainActivity: onPostCreate 2023-08-29 22:43:56.750 10618-10618/com.example.loopertest D/MainActivity: onResume 2023-08-29 22:43:56.750 10618-10618/com.example.loopertest D/MainActivity: onPostResume 2023-08-29 22:43:57.746 10618-10644/com.example.loopertest D/MainActivity: i 1 thread id 10644 2023-08-29 22:43:58.748 10618-10644/com.example.loopertest D/MainActivity: i 2 thread id 10644 2023-08-29 22:43:59.240 10618-10618/com.example.loopertest D/MainActivity: onKeyDown keyCode 4 2023-08-29 22:43:59.354 10618-10618/com.example.loopertest D/MainActivity: onPause 2023-08-29 22:43:59.750 10618-10644/com.example.loopertest D/MainActivity: i 3 thread id 10644 2023-08-29 22:43:59.863 10618-10618/com.example.loopertest D/MainActivity: onStop 2023-08-29 22:44:00.784 10618-10644/com.example.loopertest D/MainActivity: i 4 thread id 10644 2023-08-29 22:44:01.797 10618-10644/com.example.loopertest D/MainActivity: i 5 thread id 10644 2023-08-29 22:44:02.840 10618-10644/com.example.loopertest D/MainActivity: i 6 thread id 10644 2023-08-29 22:44:03.881 10618-10644/com.example.loopertest D/MainActivity: i 7 thread id 10644 2023-08-29 22:44:04.922 10618-10644/com.example.loopertest D/MainActivity: i 8 thread id 10644 2023-08-29 22:44:05.926 10618-10644/com.example.loopertest D/MainActivity: i 9 thread id 10644 2023-08-29 22:44:06.948 10618-10618/com.example.loopertest D/MainActivity: onDestroy我们再给线程退出加上一个条件 new Handler(handlerThread.getLooper()).post(new Runnable() {Overridepublic void run() {int i 0;while (i 10) {if (isQuit)break;try {Log.d(LOG_TAG, i i thread id Process.myTid());sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}i;}}});可以看到我们点击返回键退出 app 时程序可以正常退出并且子线程没有再打印出内容了这样是不是就不会有内存泄漏了呢。 2023-08-29 22:48:03.391 10748-10748/com.example.loopertest D/MainActivity: onCreate 2023-08-29 22:48:03.525 10748-10773/com.example.loopertest D/MainActivity: i 0 thread id 10773 2023-08-29 22:48:03.528 10748-10748/com.example.loopertest D/MainActivity: onStart 2023-08-29 22:48:03.529 10748-10748/com.example.loopertest D/MainActivity: onPostCreate 2023-08-29 22:48:03.530 10748-10748/com.example.loopertest D/MainActivity: onResume 2023-08-29 22:48:03.530 10748-10748/com.example.loopertest D/MainActivity: onPostResume 2023-08-29 22:48:04.525 10748-10773/com.example.loopertest D/MainActivity: i 1 thread id 10773 2023-08-29 22:48:05.527 10748-10773/com.example.loopertest D/MainActivity: i 2 thread id 10773 2023-08-29 22:48:06.528 10748-10773/com.example.loopertest D/MainActivity: i 3 thread id 10773 2023-08-29 22:48:06.733 10748-10748/com.example.loopertest D/MainActivity: onKeyDown keyCode 4 2023-08-29 22:48:06.854 10748-10748/com.example.loopertest D/MainActivity: onPause 2023-08-29 22:48:07.399 10748-10748/com.example.loopertest D/MainActivity: onStop 2023-08-29 22:48:07.531 10748-10748/com.example.loopertest D/MainActivity: onDestroy好了关于 Android Looper Handler 机制就分析到这里如果觉得这篇文章有帮助还请不要吝啬点赞关注哦再见
http://www.yutouwan.com/news/184545/

相关文章:

  • 哪个网站做简历做钓鱼网站用哪种编程语言
  • 网站的建设与运营专业网络工程建设
  • 空间信息网站淘宝网请人做淘宝客网站
  • 网站建设 步骤汕头拿家做网站
  • 专业北京网站建设公司影楼和工作室的区别
  • 可信网站认证 技术支持单位上海搜索优化推广
  • 开发建设网站多久wordpress修改根目录
  • 租网站服务器一个月多少钱北京市建设厅门户网站
  • 成都优秀网站建设做网站要具备哪些
  • 网站开发公司盈利自己做的网站找不到了
  • 开封建站公司注册一个公司的所有流程
  • 网站建设时应该做的优化长沙推广优化公司
  • 电子商务网站建设服务免费公司取名在线
  • 免费查公司青岛官网seo价格
  • 上海交通大学网站建设微信网站建设协议
  • 某企业网站搜索引擎优化wordpress如何发照片
  • 一个服务器能放多少网站太原做网站的通讯公司有哪些
  • 搞一个网站多少钱做网站关于创新的
  • 国际商业网站做网站怎么套模板
  • 网站建设联系方式高校网站网页设计
  • 朝阳区北京网站建设新浪短链接在线生成
  • 小程序在哪里网站seo优化分析报告
  • 网站维护有文化建设费成都旅游网站建设地址
  • 艺术家网站建设中企业网站建设的策划初期的一些误区洛阳市做网站贴吧
  • 广德县建设协会网站网站头像设计免费制作
  • 荆轲网络做网站专业设计企业logo
  • 中国字体设计网站网站开发员工作职责
  • 王建设个人网站网站建设合同法
  • 网站代码图片wordpress投稿插件
  • php 视频网站开发网页设计职业