Android Handler/Looper视角看UI线程的原理

Android Handler机制深度解析:从线程通信原理到实战避坑指南 在Android开发中,线程间通信是保障应用流畅性的核心技术基础。其核心原理在于通过消息队列机制实现不同线程间的任务调度与数据传递,有效解决了多线程并发操作UI的安全性问题。这一机制的技术价值在于它能够将耗时的后台计算与UI更新解耦,避免主线程阻塞导致的界面卡顿和ANR问题。在实际应用场景中,开发者常利用Handler实现延迟任务、定时操作以及跨线程更新UI等常见功能。本文聚焦Android Handler机制,深入剖析其底层实现,并结合内存泄漏、消息延迟等高频问题,提供系统性的解决方案与性能优化实践。 阅读详情

概述

Handler/Looper机制是android系统非重要且基础的机制,即使在rtos或者linux操作系统上开发应用框架时,也经常借鉴这个机制。通过该机制机制可以让一个线程循环处理事件,事件处理逻辑即在Handler的handleMessge种。本文建议android8.1源码分析这套机制的实现原理。

Handler/Looper

Handler:顾名思义,处理消息message的类,Handler也可以发送消息。

Looper:顾名思义,一个循环体,这个循环体本质上就是不断取出Queue种的Message,分发给特定的目标,具体分发给哪个目标要具体情况具体分析。并且Looper内部含有一个Thread对象,很好理解,就是Looper这个循环体是在该线程中执行的。

MessageQueue:消息队列,通过Handler发送的Message加入到该队列中,并且被Looper运行在Thread线程中不断取出,又被Handler执行。

执行流程图:

应用程序使用Looper

应用程序使用Looper分为两种情况,主线程和普通线程:

普通线程
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }

这段代码在Android源码中非常典型,使用步骤有三:

  1. Looper.prepare创建线程私有的Looper对象。
  2. 创建处理消息的Handler。
  3. Looper.loop开始运作:线程一直循环取消息,处理消息,如果没有消息处理将休眠。

上面代码看似简单,越是简单的代码有时候越是难以理解,我们要理解上面三个步骤是怎么把Thread,MessageQueue,Handler和Looper联系起来的。

Looper.prepare源码
    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();
    }

可以看到prepare就给调用prepare的当前线程创建一个Looper对象,Looper对象创建的时候,内部新建了MessageQueue和Thread对象,特别注意,mThread是调用prepare的线程。

Handler对象创建
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };

可以想象,这里Handler必须要跟前面prepare创建的Looper产生联系才对,这个就要看Handler的构造函数:

public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);

我们要看下上面Handler()构造函数:

    public Handler() {
        this(null, false);
    }
    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;
        mCallback = callback;
        mAsynchronous = async;
    }

 可以看到默认构造函数中非常重要的一句,Looper.myLooper,会取得当前线程的Looper对象,也就是Looper.prepare创建的线程私有的对象,同时Handler's MessageQueue来自瑜当前线程Looper's MessageQueue。

注意:Thread和Handler是1:N关系。比如UI线程就可以有多个Handler对象。

Handler发送和处理消息

按照上面分析,调用mHandler可以发送消息,然后处理消息在该Handler的handleMessage函数,我们看看上述流程是如何实现的。

Looper从MessageQueue中取得一个Message后,首先会调用Handler.dispatchMessage进行消息分发,后者再根据具体的策略将Message分发给相应的责任人源码如下:

 Handler.java:

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {                                                                                                             
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {                                                                                                  
        message.callback.run();
    }

分发策略:

1. Meesage.callback对象不为空优先分发给message.callback。这种情况对应mHandler.post接口发送的runable对象:这种情况下Message.callback就是post的runnable对象

public final boolean post(Runnable r);
public final boolean postAtTime(Runnable r, long uptimeMillis);

public final boolean post(Runnable r)                                                                                                                  
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {                                                                                                    
   Message m = Message.obtain();
   m.callback = r;
   return m;
}

2.  Handler.mCallback不为空,分发给mCallback.handleMessage

3.  前两个都不存在,则调用handleMessage,比如我们当前普通线程场景,override了这个方法,就会调用我们自己Handler.handleMessage。这种情况主要对应是Handler send系列接口使用:

public boolean sendMessageAtTime(Message msg, long uptimeMillis);
public final boolean sendMessageDelayed(Message msg, long delayMillis);

比如如下使用:
Message msg = mHandler.obtainMessage();
mHandler.sendMessageAtTime(msg,xxxx);

public final Message obtainMessage()                                                                                                                   
{
    return Message.obtain(this);
}

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

 可以看到这种情况下Message.target = mHandler,而Looper在loop中分发代码:

     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            ...
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
            msg.recycleUnchecked();
        }
    }

 loop循环从Looper's MessageQueue取得消息,然后调用Message.target.dispatchMessage,正如上面的分析Message.target = mHandler,所以调用到了Handler.dispatchMessage方法。

总结:

1. Handler.post(runnable),在Handler.dispatchMessage方法由于优先分发给runnable对象,所以这个runnable对象被执行,而不是Handler.handleMessage。

2. 如果使用sendMessage接口,那么Handler.dispatchMessage分发给Handler override的handleMessage接口,也就是普通线程使用代码中Handler's handleMessage函数。

UI线程

 我们知道ActivityThread是主线程的入口点,我们看看android UI线程中Looper的使用源码:

    public static void main(String[] args) {                                                                                                               

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
      

与普通线程使用Looper类似,也分3个步骤:

1.准备Looper: Looper.prepareMainLooper

2.创建主线程私有的Handler:sMainThreadHandler = thread.getHandler

3.开始工作:Looper.loop

与普通线程的不同点:

1. 普通线程使用Looper.prepare,而主线程需要使用Looper.prepareMainLooper

2.Handler不同:普通线程生成一个与Looper绑定的Handler即可,而主线程是从当前线程总获取

我们分别分析下上面两个不同点

prepareMainLooper
    
    public static void prepareMainLooper() {                                                                                                               
        prepare(false);
        synchronized (Looper.class) {
            sMainLooper = myLooper();
        }
    }

    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));
    }

可以看到,prepareMainLooper也使用了prepare,不过参数false代表ui线程不能退出,并且创建的Looper要赋值给sMainLooper,这样其他线程可以通过调用getMainLooper获取该对象。

MainThreadHandler

ActivityThread对象创建时候,会在内部同时生成一个Handler对象:

final H mH = new H();

thread.getHandler获取的就是该mH,也就是说ActivityThread中主线程处理消息的Handler之一就是该Handler mH。

loop

分析loop函数之前,我们提一下一个图形窗口典型的main函数如下:

main() {
    initialize()//初始化
    CreateWindow();//创建窗口
    ShowWindow();
    while(GetMessage()) {
        //不断分发处理
        DispatchMessage();
    }
}

那么根据前面loop函数的分析,其实UI线程的loop函数也是在循环中不断取消息,处理消息,符合上述模型。

ViewRootImpl

我们知道ViewRootImpl也属于UI线程的范围,因为ViewRootImpl创建就是在UI线程中创建,那么根据Handler创建的默认构造函数我们知道,这个Handler()这个默认的构造函数,适合当前线程的Looper绑定的,那么ViewRootImpl中Handler()形式构建的Handler也是处理UI线程的相关消息。

final ViewRootHandler mHandler = new ViewRootHandler();
 final class ViewRootHandler extends Handler {
    public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_INVALIDATE:
                ((View) msg.obj).invalidate();
                break;
            case MSG_INVALIDATE_RECT:
                final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
                info.target.invalidate(info.left, info.top, info.right, info.bottom);
                info.recycle();
                break;
            case MSG_PROCESS_INPUT_EVENTS:
                mProcessInputEventsScheduled = false;
                doProcessInputEvents();
                break;
            case MSG_DISPATCH_APP_VISIBILITY:
                handleAppVisibility(msg.arg1 != 0);
                break;
            case MSG_DISPATCH_GET_NEW_SURFACE:
                handleGetNewSurface();
                break;
            ....

Choreographer

Choreographer也是工作在UI主线程的一个重要的类,跟Vsync有关,我们通过源码看看这个类为什么工作在UI主线程。

看是否是主线程,我们要看下Choreographer中的Handler/Looper是基于哪个Thread来创建,看下Choreographer的构造函数如下:


    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);                                                                                                               
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;

        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }

所以重点就是构建函数中Looper是哪里构建的,Choreographer是个单例类,所以看下其初始化的代码:

    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =                                                                                      
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Choreographer(looper, VSYNC_SOURCE_APP);
        }
    };

也就是说最终Looper.myLooper获取的是当前线程私有的Looper对象,而Choreographer的getInstance是在public ViewRootImpl(Context context, Display display) 构造函数中调用的,根据ViewRootImpl小节我们知道,ViewRootImpl在主线程构建,所以Choreographer是主线程的

 Vsync信号处理

我们都知道vsync信号处理的实际逻辑是在ui主线程当中,我们看下具体实现,首先要看下vsync信号接受和处理的地方,这个在Choreographer当中:

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {

            ...
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }

可以看到接收到vsync信号之后,使用mHandler向该Handler所在的线程发送消息,根据Choreographer小节我们知道,这里mHandler和其Looper是UI线程的,所以上面代码中发送的消息在主线程中处理,而FrameDisplayEventReceiver是个runnable对象,所以最终主线程收到这消息的处理函数是:doFrame函数:

            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);

vsync信号过来在主线程处理, 最终处理函数是doFrame->doCallbacks,而doCallbacks就是要处理应用已经请求,且记录到queue里面的各种事件,比如应用请求渲染的时候调用invalidate调用栈:

invalidate() : ViewRootImpl.java
    --->scheduleTraversals() : ViewRootImpl.java
        --->mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

最终postCallback的实现:


private void postCallbackDelayedInternal(int callbackType,
		Object action, Object token, long delayMillis) {
	synchronized (mLock) {
		final long now = SystemClock.uptimeMillis();
		final long dueTime = now + delayMillis;
		//将要执行的回调封装成CallbackRecord对象,保存到mCallbackQueues数组中
		mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
		//函数执行时间到
		if (dueTime <= now) {
			scheduleFrameLocked(now);
		} else {//通过异步消息方式实现函数延时执行
			Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
			msg.arg1 = callbackType;
			msg.setAsynchronous(true);
			mHandler.sendMessageAtTime(msg, dueTime);
		}
	}

1.将请求的事件保存在mCallbackQueue,等到vsync信号来到,doFrame中取出执行。

2. 发送MSG_DO_SCHEDULE_CALLBACK消息,最终调用native层的mDisplayEventReceiver.scheduleVsync();请求接收下一次vsync信号

HWUI源码剖析(一) - 渲染中RenderProxy视角看一种很有用的编程模式 实际的工程项目中,往往都是多线程编程,也就是必然有某些业务逻辑或者功能要运行在特定的线程,比如android开发中常见的IO线程,UI线程,Render线程,这样会涉及从线程A向这些工作线程通信或者函数调用的代码编程,具体实现上可以有多种多样,但是他们的核心思想其实都是一致的,Android Handler/Looper视角看UI线程的原理_nginux的博客-优快云博客。 阅读详情

相关推荐

Android Framework面试核心:Binder、Handler、View绘制与性能优化全解析

进程间通信(IPC)是操作系统实现多任务协同的基础技术,它允许不同进程安全高效地交换数据。在Android系统中,Binder机制作为核心IPC方案,通过内存映射和一次拷贝原理,在性能与安全性上超越了传统Socket或管道。其C/S架构和面向对象的设计,为系统服务如ActivityManagerService(AMS)和WindowManagerService(WMS)提供了通信骨架,是理解Android系统运行机制的关键。消息循环机制则是应用层异步编程的基石,Handler、Looper与MessageQ

weixin_32612263的博客 226

Android Looper简介

安卓Looper详解

qq_36063677的博客 3348

Android Handler机制深度解析:从原理到避坑指南

在Android开发中,异步消息处理是构建响应式应用的核心基础。其核心原理基于事件驱动模型,通过消息队列机制实现线程间安全通信,有效解决了多线程环境下的UI更新与数据同步问题。这项技术的核心价值在于将任务生产与执行解耦,保证消息处理的有序性,并支持延时任务与资源复用,从而提升应用性能与稳定性。在实际应用场景中,它广泛应用于UI更新、后台任务调度、线程间通信等关键环节。本文将聚焦Handler、Looper、MessageQueue三大核心组件,深入剖析其协作机制与设计哲学,并结合内存泄漏、ANR预防等常见问

weixin_29978783的博客 363

java并发编程之进程和线程

在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程。当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通使用的,需要在实践中不断积累。由于并发肯定涉及到多线程,因此在进入并发编程主题之前,我们先来了解一下进程和线程的由来,这对后面对并发编程的理解将会有很大的帮助。 下面是本文的目录大纲: 一.操作系统中为什么会出现进程?

NerveMore 581

数学建模竞赛省一实战复盘:团队分工、工具链与动态决策策略

数学建模是将实际问题抽象为数学模型,并通过计算求解以指导决策的核心方法。其原理在于通过合理的假设与简化,运用数学工具描述复杂系统,最终通过编程实现求解。这一过程的技术价值在于培养系统性思维、解决复杂问题的能力以及团队协作精神,是连接理论与工程实践的关键桥梁。在应用场景上,数学建模广泛应用于算法设计、数据分析、系统优化和科学研究等多个领域。本文聚焦于数学建模竞赛这一典型实践场景,深入复盘了从团队构建、工具链统一到动态决策的全过程。文中详细阐述了如何通过清晰的**角色分工**(建模手、编程手、写手)形成高效协作

weixin_34252090的博客 316

Android 中Looper机制详解

在《Android 基于Handler 剖析消息机制》一文中,以 Handler 类为起点详细分析了异步通信,分析了Java 端Handler与LooperMessage之前的通信关系。框架如下:在Java 端的 Looper 中会创建一个 Java 端的 MessageQueue实例,并在loop() 函数中的死循环里通过 queue.next() 不停的获取监听到的下一个 Message,然后将其通过 dispatchMessage() 分发处理。详细看。

私房菜 5955

Message Looper Handler MessageQueue

大概过程: 1.新建线程调用Looper.prepare,初始化和Looper 和 MessageQueue 2.Looper.loop() 循环queue.next() 并 分发消息dispatchMessage 3.Handler.sendMessage -- enqueueMessage把消息连到Message.next上,如链表结构,就放到了消息队列(Messa...

wd229047557的博客 220

【Android知识笔记】UI体系(二)

Android知识笔记之UI体系:应用的UI线程和主线程原理解析、View.post()原理、View的绘制流程原理解析、invalidate()解析、LayoutInflater解析等相关内容。

川峰的博客 914

Android中多线程和Handler的结合,以及Handler的消息处理机制

handler机制的原理 分类:android application2012-10-03 13:3927608人阅读评论(14)收藏举报 目录(?)[+] andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用...

chuangcuicuo2022的博客 303

Android之Handler、Message、MessageQueue、Looper详解

Android之Handler、Message、MessageQueue、Looper详解

是阿超 3527

实例讲解MOS管电源开关电路的软启动

1、控制电源开关的输入信号 Control 为低电平或高阻时,三极管Q2的基极被拉低到地,为低电平,Q2不导通,进而MOS管Q1的Vgs = 0,MOS管Q1不导通,+5V_OUT 无输出。此时将 Control 设为低电平,三极管Q2关闭,电容C1与G极相连端通过电阻R2放电,电压逐渐上升到5V,起到软关闭的效果。不过从作者的描述来看,只是电路参数有区别,能通过的电流、能承受的耐压等不一样,但是软启动的原理是一样的。从公式可以看出,当电容容量越大,电压越高,时间越短,电流就会越大,从而形成浪涌电流。

U_YAO的博客 2万+

Android线程模型与UI更新实战方案解析

在Android开发中,线程管理与UI更新是核心技术难点。Android采用单线程模型处理UI操作,通过主线程(UI线程)确保界面操作的线程安全。这种设计虽然保证了流畅性,但也带来了子线程更新UI的挑战。常见的解决方案包括Handler消息机制、runOnUiThread便捷方法以及View.post()等,这些技术本质上都利用了Android的消息循环机制。随着技术演进,Kotlin协程和Jetpack组件提供了更现代化的线程管理方案。在实际开发中,正确处理线程切换不仅能避免CalledFromWrong

weixin_30780649的博客 305

考试遇到 Assistant API 考点时,该掌握哪些要点?

【摘要】聚焦阿里云大模型 ACP 认证中智能体 Assistant API 考点,我结合自学认证成功经验,分享复习攻略。先解析 assistants、threads 等五个核心组件作用与关系,点明各组件支持及不支持的方法,最后给出计算 Python 代码调用大模型次数的思路,助力考生攻克考点、顺利拿证 。

davidkorhenn的博客 494

【Android 核心复习】Handler 全家桶:从底层原理到内存泄露、ThreadLocal 深度解析

本文系统解析Android的Handler机制核心原理及面试要点:1.核心组件包括Message、Handler、MessageQueue和Looper,形成线程间通信的消息循环体系;2.重点剖析ThreadLocal实现线程隔离、消息复用机制和内存泄漏防范;3.深入底层唤醒机制,基于epoll实现高效休眠/唤醒;4.对比多种跨线程通信方案,强调Handler的基础地位;5.提供线程安全实现方案,涵盖ThreadLocal、CAS等技术。全文从原理到实践,完整覆盖Handler机制的关键技术点,是掌握And

2301_79871637的博客 542

在x86机器上运行jailhouse

jailhouse jaihouse提供了一个demo ,https://github.com/siemens/jailhouse-images可以用于编译一整套可用的demo。 适用于多个平台。编译过程主要参考https://blog.youkuaiyun.com/weixin_39541632/article/details/110422345 此demo可以生成多个平台所用的操作系统image。 ...

闲人的专栏 3004

vhr 微人事系统安装与部署教程(SpringBoot + Vue 项目实战)

vhr(微人事)是一个开源的人力资源管理系统项目。系统采用前后端分离架构,主要技术栈包括:后端:MyBatis前端:VueElement UIAxios数据库:MySQL系统通常包含以下模块:员工管理部门管理职位管理薪资管理权限管理系统管理整体功能已经接近企业级后台系统。vhr(微人事)是一个非常经典的开源人力资源管理系统项目,基于SpringBoot + Vue 前后端分离架构。系统包含:员工管理部门管理权限管理薪资管理。

m0_69484557的博客 486

深度解密 Android 核心运行机制:从 Activity 启动到 UI 显示的全链路追踪

本文深入解析了Android开发三大核心机制:1. Activity启动流程,详细剖析了从跨进程通信到Zygote进程孵化的完整链路;2. UI显示原理,揭示从XML布局到屏幕像素的转换过程;3. UI线程机制,阐述单线程模型的实现原理。文章采用"剧院"比喻形象化说明各组件协作关系,并标注关键知识点难度系数,帮助开发者建立系统级认知架构。适合中高级Android工程师进阶学习,掌握底层实现原理。

xhxh510573522125的博客 1035

【DeepSeek应用实践】手把手教程:用 AnythingLLM + Ollama + DeepSeek-R1 搭建本地企业知识库

在众多的技术方案中,我们选择了 AnythingLLM、Ollama 和 DeepSeek-R1 这一组合来搭建本地知识库。Ollama 作为一个轻量级的本地化大语言模型部署工具,支持多种模型,易于安装和使用,能够快速在本地运行强大的语言模型。DeepSeek-R1 是一个专为知识库问答设计的检索增强生成(RAG)模型,它结合了检索和生成的优势,能够快速从知识库中检索相关信息并生成精准答案。而 AnythingLLM 则是一个用户友好的界面工具,可以方便地连接 Ollama 和 DeepSeek-R1

寻道AI,探索AI无限可能! 2566

android学习之多线程编程

   本来昨晚想整理来着,结果竟然太累了直接睡着。。  尽管今天也比较累了,但是还是要咬牙坚持一下将这里的知识整理一下再去娱乐或者休息。。    接触android后,我们知道,非UI线程不能操控UI。 android4.0以后,主线程还不能进行联网操作。  这使得我长期以来养成的编程思路完全找不到思路了。。 因为以前我的思路都是单线程的,即我的操作要么完成,要么抛错,要么阻塞的这样一种非黑即白...

automannn 2301

生存曲线绘制及图例标签添加(使用R语言)

数据应该包含两列:一个是表示个体生存时间的时间变量(通常称为"时间(time)“)和一个是表示个体是否发生事件(如死亡)的事件变量(通常称为"事件(event)”)。通过修改上述代码中的数据和参数,您可以根据自己的需求绘制生存曲线,并添加相应的图例信息和标签。生存曲线是在生存分析中常用的工具,用于描述一组个体随时间的存活概率。函数的第一个参数指定图例的位置,这里我们选择了右上角(“topright”)。包来进行生存曲线的绘制,并通过添加图例信息和标签来增强可视化效果。的数据框,包含这两列数据。

CyberXZ的博客 555

在 SAP SD(销售与分销)模块中,销售合同 / 订单的抬头(Header) 和行项目(Item) 核心数据表如下,按 “核心表 + 扩展表” 分类说明,同时区分销售合同(Contract,类型 W

SAP SD模块中销售合同/订单的核心数据表分为抬头和行项目两类。核心抬头表包括VBAK(基础数据)和VBUK(状态数据),核心行项目表包括VBAP(基础数据)和VBUP(状态数据)。扩展表包含VBAKE(抬头增强)、VBAPe(行项增强)、VBEP(计划行)、VBFA(凭证流)等。销售合同(WK/WC)和订单(OR)通过VBAK-AUART字段区分,核心表结构相同。查询时需注意单据类型筛选,并通过VBELN主键关联抬头和行项数据,KNUMV关联定价条件,VBFA表追踪凭证流。

金牌架构师 1090

Android面试进阶:从Handler原理到性能优化的26个核心问题解析

在Android开发领域,理解核心框架原理是构建稳定应用的基础。消息驱动模型通过Handler机制实现线程间安全通信,其背后的Looper、MessageQueue协同工作,并利用对象池化技术优化性能,这是GUI系统的经典设计。深入掌握这些原理,能有效解决UI卡顿、内存泄漏等工程难题,提升应用流畅度与用户体验。性能优化涉及内存管理、渲染流程及网络请求等多个维度,例如通过LeakCanary排查内存泄漏,或使用协程优化异步任务。本文聚焦Handler机制、View绘制、性能优化及架构设计等高频考点,为Andr

weixin_29175469的博客 215

LangGraph实战训练营-打造 WhatsApp 全自动消息收发AI智能助手

本文系统梳理了项目架构(WhatsApp Bridge + Talon Host + LangGraph Agent Harness三层设计)、完整环境搭建流程(Docker/本地双模式部署)、核心原理(WhatsApp桥接机制、NVIDIA Parakeet语音转录管线、Cron定时调度、Agent图状状态机),并提供了基础对话、文件操作、语音处理、定时任务等六大实战示例。同时涵盖安全策略、性能优化、数据备份等生产级运维实践,以及十个高频问题的排查方案。

program哲学 190

深入解析Android ActivityThread:应用进程核心调度机制与性能优化

在Android应用开发中,主线程消息循环机制是UI流畅性和应用响应性的基础。其核心原理是通过Looper和Handler构建的消息队列模型,实现任务的异步调度与线程切换。这一机制的技术价值在于保障了UI操作的安全性与事件处理的顺序性,是避免ANR(应用无响应)的关键。在实际应用场景中,开发者常需优化启动速度、监控卡顿和分析内存泄漏,这些都离不开对主线程调度机制的深入理解。ActivityThread作为Android应用进程的“大脑”,正是这一机制的核心实现者,它负责协调四大组件生命周期、维护主线程消息队

weixin_30256901的博客 472

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Handler 机制和源码解析

现在网上关于Handler的资料,已经是数不胜数,总归还是要亲自走一遭才能深刻的理解。 在之前,我们先来了解下Handler、Looper、MessageQueue、Message之间的关系 它们的关系就想全家桶Rxjava+RxAndroid+ReTrofit2+okHttp3一样亲密→_→ Handler:用来发送消息,处理消息 Looper:一个消息轮询器,内部有一个loop()方法,不停的...

u013346208的博客 342

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Android多线程消息处理机制

(1)主线程和ANR 主线程:UI线程,界面的修改只能在主线程中,其它线程对界面进行修改会造成异常。这样就解决了多线程竞争UI资源的问题。 一旦主线程的代码阻塞,界面将无法响应,这种行为就是Application Is Not Respond(ANR),应用失去响应。 如果主线程中某个事件操作时间超过5秒没有得到响应,Android可能会弹出一个应用程序没有响应的对话框。activity可能...

weixin_30852367的博客 116

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: multi-generation lru系列 - workingset的适配
下一篇: HWUI源码剖析(一) - 渲染中RenderProxy视角看一种很有用的编程模式
OS技术解密
博客等级 码龄16年 3760粉丝 189原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值