android ui 简介

本文深入探讨Android UI框架,重点讲解ViewRootImpl作为视图管理者如何处理输入事件,以及SurfaceView和TextureView在UI显示中的作用。ViewRootImpl接收并传递input事件,SurfaceView用于显示相机预览或视频,而TextureView则通过SurfaceTexture显示数据流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在介绍Framework相关模块的内容时,相比于把详细的流程代码贴出来,我更愿意把模块的层次结构、各个组件的关联以图形的形式呈现出来。所以按照国际惯例,直接上图,下面就开始介绍ViewrootImpl,SurfaceView(GlSurfaceView 本身继承自SurfaceView,没有太多可介绍的,所以没有画出来),TextureView。


1、ViewRootImpl

  这个类是view的管理者,真正的根节点是mView(DecorView实例),这里将ViewRootImpl画出来,更能清楚显示出ui的结构和相关模块。如图中的WindowInputEventReceiver,就是负责接收InputManager上报上来的input事件。Surface是BufferQueue的producer端,渲染后的buffer通过它送到surfaceflinger去显示。IwindowSession和IWindow.Stub是和WindowManagerService打交道,一个充当binder的proxy端,另一个充当server端,这样就可以实现双向通信。除了和WindowManagerService有联系,还和很多其他模块相关联,这里就不一一画出来了,

1)input 事件上报

在InputManagerService中有两个线程,InputReaderThread负责从EventHub读取Event,经过处理后,交给InputDispatcherThread上报给上层,忽略其他Service对特殊事件的处理,可以简单的认为InputDispatcherThread将input事件给到了Activity(应用)。

InputReader从input driver读到input evnet后,会经过多个模块的处理,其中InputMapper的处理最为负责,有兴趣可以看一下源码,然后丢给QueuedInputListener去notify。

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

有些同学可能不理解mInnerListener 是什么,这个其实是InputDispatcher实例,所以InputReader读到的事件,经过各种处理和包装,到这里就丢到InputDispatcher的队列里面去了,接下来就由InputDispatcher (InputDispatcherThread线程)将事件给到应用。这里是一个跨进程的数据传输,这里用到的是socket方式,InputChannel就是对socket两端读写操作的封装。

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    ...
    return OK;
}

status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);
    ...
    return OK;
}

封装得再好,最终还是得调用send(…)和recv(…)接口来发送和接收数据,这种方式和应用从sensorservice读数据方式是一样的,都是用到了socket,具体说来是socketpair。

ViewRootImpl的WindowInputEventReceiver收到input事件后,可能经常一系列的处理后,传给DecorView,例如touch事件

ViewRootImpl.java

private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            mAttachInfo.mUnbufferedDispatchRequested = false;
            final View eventTarget =
                    (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
                            mCapturingView : mView;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = eventTarget.dispatchPointerEvent(event);
            maybeUpdatePointerIcon(event);
            mAttachInfo.mHandlingPointerEvent = false;
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

View.java

 public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

DecorView.java

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
如果还不清楚这个流程的话,好吧,我把调用的backtrack也贴出来一下吧
                      java.lang.RuntimeException
                      at com.hxiong.camerademo.CameraDemoActivity.debug(CameraDemoActivity.java:44)
                      at com.hxiong.camerademo.CameraDemoActivity.dispatchTouchEvent(CameraDemoActivity.java:39)
                      at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:375)
                      at android.view.View.dispatchPointerEvent(View.java:10243)
                      at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)
                      at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4306)
                      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
                      at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
                      at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
                      at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3999)
                      at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
                      at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4056)
                      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
                      at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
                      at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
                      at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
                      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
                      at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6246)
                      at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6220)
                      at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6181)
                      at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6349)
                      at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
                      at android.os.MessageQueue.nativePollOnce(Native Method)
                      at android.os.MessageQueue.next(MessageQueue.java:323)
                      at android.os.Looper.loop(Looper.java:136)
                      at android.app.ActivityThread.main(ActivityThread.java:6119)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

2)图中WindowState、WindowStateAnimator、SurfaceControl是WindowManagerService管理Surface的模块,前面讲过了Surface只是producer端的封装。这里只是简单将一下apk的ui是怎么绘制出来的,不考虑硬件加速的情况。

(1)通过Surface dequeue出一块可用的buffer

(2)将这块buffer关联到skia 的SkBitmap

(3)再从SkBitmap里抽象出SkCanvas

(4)然后用SkPaint先在SkCanvas上进行绘制

(5)最后真正同步绘制到SkBitmap上,buffer也就被修改了

(6)通过Surface queue出buffer,送给Surfaceflinger去显示

2、SurfaceView

SurfaceView里面最关键的同样是Surface,这个surface跟应用ui用的surface不是同一个,它的创建过程和ViewRootImpl中的surface差不多一样。通过Session.relayout(…)创建出来。一般用它来显示camera的preview或者播放的视频画面,本质上就是把surface(producer端)传给其他模块,让其他模块往这个producer端送数据。当然应用也可以自己在SurfaceView上绘制东西,例如

Surface.lockCanvas();

… //做各种各样的绘制

Surface.unlockCanvasAndPost();

其实这个过程和上面ui的绘制差不多,Surface.lockCanvas()就是去关联skia,用skia提供的接口实现不同功能的绘制,最用通过Surface.unlockCanvasAndPost()真正修改到surface buffer,然后送去surfaceflinger显示。

3、TextureView

这个怎么说好呢,它主要的作用就是把SurfaceTexture收到的数据显示在UI上,所以它一定是会和一个SurfaceTexture关联。在SurfaceTexture初始化的时候,通过BufferQueue创建了producer和consumer,而SurfaceTexture就充当了consumer端,也就是GLConsumer。它一般会将producer提供给其他模块,用于从其他模块获取数据流,然后再通知TextureView去更新,也就是是显示到UI上。

static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
        jint texName, jboolean singleBufferMode, jobject weakThiz)
{
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);

    if (singleBufferMode) {
        consumer->setMaxBufferCount(1);
    }

    sp<GLConsumer> surfaceTexture;
    if (isDetached) {
        surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
                true, !singleBufferMode);
    } else {
        surfaceTexture = new GLConsumer(consumer, texName,
                GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
    }

    if (surfaceTexture == 0) {
        jniThrowException(env, OutOfResourcesException,
                "Unable to create native SurfaceTexture");
        return;
    }
    surfaceTexture->setName(String8::format("SurfaceTexture-%d-%d-%d",
            (isDetached ? 0 : texName),
            getpid(),
            createProcessUniqueId()));

    // If the current context is protected, inform the producer.
    if (isProtectedContext()) {
        consumer->setConsumerUsageBits(GRALLOC_USAGE_PROTECTED);
    }

    SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
    SurfaceTexture_setProducer(env, thiz, producer);

    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        jniThrowRuntimeException(env,
                "Can't find android/graphics/SurfaceTexture");
        return;
    }

    sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
            clazz));
    surfaceTexture->setFrameAvailableListener(ctx);
    SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
}

SurfaceTexture本身并没有提供接口给应用拿Producer送过来的数据,所有应用并没有办法直接那到数据,TextureView通过把它绑定到HardwareLayer,利用Render把数据内容绘制到UI上,当然还有很多其他关联的模块。可以向TextureView设置SurfaceTextureListener,在onSurfaceTextureUpdated()回调的时候通过getBitmap()接口拿到数据。

            textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {
               Bitmap bitmep= textureView.getBitmap();
                // TODO
            }
        });










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值