Android 图形显示框架

发表于 2年以前  | 总阅读数:4886 次

本篇对Android图形显示框架做一个概述,内容主要包含:SurfaceSession创建和销毁(添加/删除窗口),Surface创建和销毁,BufferQueue创建,以及BufferQueue的dequeueBuffer和queueBuffer、acquire和release大致流程梳理。

显示框架概述

Android与用户进行图形界面的交互,例如各个应用程序,他们的对话框、按钮、菜单等图形窗口。

这些窗口的管理都是由WindowManager负责,窗口管理位于Java层,真正的实现者是运行在System_server进程空间中的WindowManagerService

frameworks/base/services/java/com/android/server/SystemServer.java


/**
 * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.
 */
private void startOtherServices() {
    .......
        traceBeginAndSlog("StartWindowManagerService");
        // WMS needs sensor service ready
        ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
        mSensorServiceStart = null;
        wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
        ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
        traceEnd();
    ......
}

应用程序负责修改绘制窗口中的内容,而WindowManager负责窗口的生命周期、几何属性、坐标变换信息、用户输入焦点、动画等功能

他还管理着窗口状态的变化,如窗口位置、大小、透明度以及Z-order(前后遮盖顺序)等一系列的逻辑判断

这些WindowManager功能由一系列接口或类构成,包括ViewManager、WindowManager、WindowManagerImpl、WindowManagerService等。

SurfaceFlinger负责分配应用程序所需的图形缓冲区,并对系统中的整个图形窗口进行composition(合成)。

最终,图形窗口会更新显示到Display显示器上。


显示过程的三个进程

Android显示的整个过程由 App 进程、System_server 进程、SurfaceFlinger进程一起配合完成。

  1. App进程:App需要将自己的内容显示在屏幕上,所以需要负责发起Surface创建的请求。同时触发对控件的测量、布局、绘制以及输入事件的派发处理,这些主要在ViewRootImpl中触发;
  2. System_server进程:主要是WindowManagerService,负责接收App请求,同时和SurfaceFlinger建立连接,向SurfaceFlinger发起具体请求创建Surface,并且创建Surace的辅助管理类SurfaceControl(和window一一对应)(AMS作用是统一调度所有App的Activity);
  3. SurfaceFlinger:为App创建具体的Surface,在SurfaceFLinger对应成Layer,然后负责管理、合成所有图层,最终显示。


Activity、Window、PhoneWindow、DecorView、View的对应关系

  1. Window:每一个Activity都包含一个Window对象(抽象类,提供了绘制窗口的一组通用API),通常由PhoneWindow实现。

在Activity.java中定义:private Window mWindow;

  • 一个Activity对应创建一个Surface

2 . PhoneWindow:继承于Window,是Window类的具体实现。该类内部包含了一个DecorView对象,该DecorView对象是所有应用窗口(Activity界面)的根View。

简而言之,PhoneWindow类是把一个FrameLayout类,即DecorView对象进行一定的包装,将他作为应用窗口的根View,并提供一组通用的窗口操作接口

PhoneWindow是Android中最基本的窗口系统,每个Activity都会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。

在Activity.java的attach函数实例化:mWindow = new PhoneWindow(this, window, activityConfigCallback);

3 . DecorView:PhoneWindow构造函数中定义,继承FrameLayout类,是所有应用窗口的根View。

在PhoneWindow.java中定义,构造函数中初始化:private DecorView mDecor;

相关debug方法:

  • adb shell dumpsys activity
  • adb shell dumpsys window

![window包含关系]](phonewindow.png)


Activity生命周期

Activity onResume添加窗口

onCreate方法中调用setContentView来设置布局,此时只是完成了View Tree的创建。此处参考HWUI绘制文章

真正通知WMS添加窗口,是在回调onResume完成的。

调用onResume的方法在ActivityThread.java中是handleResumeActivity。之后调用到WMS.java的addWindow。


App进程中完成添加窗口操作

  1. 当一个新窗口(Window)被创建的时候,在ActivityThread.java的handleResumeActivity中调用addView(),然后调用到WindowManagerImpl的addView()函数。

frameworks/base/core/java/android/view/WindowManagerImpl.java


@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

2 . 这个函数将实际操作委托给mGlobal成员完成,这个成员随着WindowManagerImpl的创建而被初始化:private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

WindowManagerGlobal是一个单例模式,即一个进程中最多仅有一个WindowManagerGlobal实例。

3 . 调用mGlobal的addView函数后,将会创建一个ViewRootImpl对象,并且将窗口的控件、布局参数、ViewRootImpl对象入参到setView函数中,这个动作将导致ViewRootImpl向WMS添加新的窗口、申请Surface创建、绘制动作等。这才真正意义的完成了窗口的添加操作。

frameworks/base/core/java/android/view/WindowManagerGlobal.java


public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
            ......
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

窗口移除序列图(Activity destroy)

窗口被删除,Activity执行了handleDestroyActivity函数:

Surface Destroy(Activity pause或者stop状态)

可以结合以下Surface创建部分一起梳理,针对的情况是Surface被destroy,从framework/base到SurfaceFlinger模块Layer析构的流程。

但是就Activity而言,并没有被销毁,而是类似按了home键返回桌面,或者在后台运行的状态,具体可以通过日志观察。

SurfaceSession创建

此处是接着上面添加窗口的流程,分为以下两部分。

mGlobal.addView中创建ViewRootImpl对象

1 . 新建ViewRootImpl对象的时候,调用构造函数,会从WindowManagerGlobal中获取一个窗口session。

mWindowSession = WindowManagerGlobal.getWindowSession();

2 . 在WindowManagerGlobal中会通过Binder IPC跨进程创建一个session。

Session主要用于进程间通信,其他应用程序想要和WMS通信就需要经过Session,每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

@UnsupportedAppUsage
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    //获取WMS对象
                    IWindowManager windowManager = getWindowManagerService();
                    //创建Session
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

3 . WMS继承IWindowManager.Stub,调用到openSessio函数,创建一个新的session对象, 返回值是IWindowSession类型。用于在APP进程和WMS之间建立联系。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


final class H extends android.os.Handler {
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
    }
}

mGlobal.addView中调用ViewRootImpl.setView

在前面mGlobal创建ViewRootImpl对象之后,会调用ViewRootImpl对象的setView,然后通知到WMS创建一个SurfaceSession,建立WindowManagerService和Surfacelinger的连接。

一个SurfaceSession代表着一个到SurfaceFlinger的连接会话,在这个连接会话里,可以创建一个或多个surface,最后这些surface被合成送到Display上显示。

大致过程:(查看下面的序列图)

  1. 在setView()中调用mWindowSession.addToDisplay, mWindowSession是IWindowSession接口类型,而Session.java实现了该接口;
  2. Session.java 中调用mService.addWindow(…), mService是WMS类型;
  3. WMS.java的addWindow()创建WindowState对象win,调用win.attach()
  4. frameworks/base/services/core/java/com/android/server/wm/WindowState.java 调用attach
  5. frameworks/base/services/core/java/com/android/server/wm/Session.java 调用windowAddedocked,创建SurfaceSession类型的mSurfaceSession
  6. frameworks/base/core/java/android/view/SurfaceSession.java 构造函数调用JNI,然后在android_view_SurfaceSession.cpp中的nativeCreate创建SurfaceComposerClient, 调用Refase的incStrong然后实现onFirstRef,通过调用CreateConnection()建立和SF的连接;
  7. SF.cpp 调用CreateConnection()返回SF的Client类的Binder代理BpSurfaceComposerClient;

Surface创建

App进程请求创建Surface

Surface是Android图形系统的核心部分,图形界面上的一个窗口或对话框等都对应着一个Surface。

而这个Surface是一块绘制区域的抽象,它对应着Server服务端Surfacelinger中的一个图层Layer,这个图层的背后是一块图形缓冲区GraphicBuffer,Client客户端的应用程序的UI使用软件绘制、硬件绘制在Surface上各种渲染操作时,绘制操作的结果其实也就是在该图形缓冲区中。

这部分的内容是梳理Surface创建的过程。

  1. 在ViewRootImpl对象中,setView到requestLayout函数请求布局,到调用scheduleTraversals,该函数里面在Choreographer.java层层调用到Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);

此处的action即是新的线程启动。

  1. 启动ViewRootImp中单独的线程TraversalRunnable,然后调用到关键函数performTraversals()

这个关键函数有两个主要的函数:

  • relayoutWindow() ->布局窗口
  • performDraw() ->绘制渲染

请求创建Surface就从relayoutWindow函数开始。

在这个方法中调用IWindowSession的relayout,会调用到Session.java,然后调用到WMS的relayoutWindow从而达到跨进程:(流程图查看下面单独章节的序列图)

frameworks/base/core/java/android/view/ViewRootImpl.java

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
            ......
    //此处relayout会调用到WMS的relayoutWindow
    int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
            insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
            mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
            mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
    if (mSurfaceControl.isValid()) {
        mSurface.copyFrom(mSurfaceControl);
    } else {
        destroySurface();
    }
        }

System_server进程 —— WMS

  1. 跨进程到WMS后,从relayoutWindow函数调用到createSurfaceControl(outSurfaceControl)

(1)然后先是在WindowStateAnimator创建一个WindowSurfaceController对象,作为调用到WindowStateAnimator.java的createSurfaceLocked函数的返回值。

在createSurfaceLocked函数中,会new一个WindowSurfaceController对象,从而调用他的构造函数。

在他的构造函数中会创建一个SurfaceControl内部类Builder对象,调用该对象的build函数;

(2) WMS.java中调用WindowStateAnimator.java的createSurfaceLocked函数之后,会执行以下逻辑:

a. 如果surfaceController不为空,调用WindowSurfaceController的getSurfaceControl;

b. WindowSurfaceController.java调用

getSurfaceControl, outSurfaceControl.copyFrom(mSurfaceControl);,而mSurfaceControl就是之前的构造函数创建的。此处的copyFrom会经过JNI调用到Native层, 然后读取SurfaeControl。

2 . 在Builder内部类的build函数中创建Java层的SurfaceControl对象,在SurfaceControl的构造函数中调用JNI层的nativeCreate函数;

3 . android_view_SurfaceControl.cpp的nativeCreate函数会调用SurfaceComposerClient.cpp的createSurfaceChecked函数,创建一个surface(实际上是SurfaceControl),然后将surface返回。


SurfaceFlinger进程

SurfaceComposerClinet.cpp位于frameworks/native/libs/gui模块

libgui库主要被JNI层中的代码调用,从而和Surfacelinger进程进行交互,可以看做是Java层的Bn端,是SurfaceFlinger的Bp端。

比如此处的SurfaceComposerClinet通过Binder IPC(ISurfaceComposerClinet.cpp),跨进程到SurfaceFlinger进程。

1 . SurfaceComposerClinet作为Bp客户端调用:

frameworks/native/libs/gui/SurfaceComposerClient.cpp

status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                     PixelFormat format,
                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
                                                     SurfaceControl* parent,
                                                     LayerMetadata metadata) {
    sp<SurfaceControl> sur;
    status_t err = mStatus;

    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IBinder> parentHandle;
        sp<IGraphicBufferProducer> gbp;

        if (parent != nullptr) {
            parentHandle = parent->getHandle();
        }

        err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
                                     &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
        }
    }
    return err;
}

2 . Bn服务端是surfaceflinger模块的Client.cpp,此时跨进程到SurfaceFlinger进程,调用createSurface,从而请求到SurfaceFlinger创建Surface:

frameworks/native/services/surfaceflinger/Client.cpp

status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                               uint32_t flags, const sp<IBinder>& parentHandle,
                               LayerMetadata metadata, sp<IBinder>* handle,
                               sp<IGraphicBufferProducer>* gbp) {
    // We rely on createLayer to check permissions.
    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
                                 parentHandle);
}

3 . 在SurfaceFlinger::createLayer中创建Layer(创建surface的请求到SurfaceFlinger进程中就是创建Layer),创建的Layer有四种:

(1)createBufferQueueLayer (2)createBufferStateLayer (3)createColorLayer (4)createContainerLayer

通常情况下创建的是第一种Layer——BufferQueueLayer(在P中是BufferLayer),会创建一个<sp>BufferQueueLayer强指针对象

SF.cpp

status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name,
                                                uint32_t w, uint32_t h, uint32_t flags,
                                                LayerMetadata metadata, PixelFormat& format,
                                                sp<IBinder>* handle,
                                                sp<IGraphicBufferProducer>* gbp,
                                                sp<Layer>* outLayer) {
.....
    sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer(
            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
    status_t err = layer->setDefaultBufferProperties(w, h, format);
    if (err == NO_ERROR) {
        *handle = layer->getHandle();
        *gbp = layer->getProducer();
        *outLayer = layer;
    }

    ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err));
                                                }

序列图

该序列图包含上面部分的流程,包含APP进程和WMS进程之间的Session创建、SurfaceSession创建、Surface创建。


BufferQueue

关于BufferQueue只大致梳理他的创建流程,以及在渲染过程中的dequeuebuffer和queuebuffer流程、在合成过程中的acquire和release流程。

关于和GraphicsBuffer和再底层的逻辑,暂时不梳理。

BufferQueue概述

创建BuffeQueueLayer对象的onFirstRef中会创建一个BufferQueue。

BufferQueue是buffer流转的中转站。具体分成四个步骤:

  1. 生产者dequeue一块buffer,buffer状态->DEQUEUED,持有者->Producer,之后生产者可以填充数据(渲染绘制)。在dequeueBuffer之前,buffer状态是free,持有者是BufferQueue;
  2. 生产者填充完数据后,进行queue操作,buffer->QUEUED,持有者->BufferQueue。操作后producer会回调BufferQueue的onFrameAvailable函数,通知消费者有可用的buffer;
  3. 消费者进行acquire取出Buffer,buffer->ACQUIRED,持有者->Consumer;
  4. 消费者消费完这块buffer(已经合成),进行release操作释放,归还给BufferQueue

BufferQueue状态

  • DEQUEUED 状态:

Producer dequeue一个Buffer后,这个Buffer就变为 DEQUEUED 状态,release Fence发信号后,Producer就可以修改Buffer的内容,我们称为release Fence。

此时Buffer被Producer占用。

DEQUEUED状态的Buffer可以迁移到 QUEUED 状态,通过queueBuffer或attachBuffer流程。也可以迁移到FREE装,通过cancelBuffer或detachBuffer流程。

  • QUEUED 状态:

Buffer绘制完后,queue到BufferQueue中,给Consumer进行消费。此时Buffer可能还没有真正绘制完成,必现要等对应的Fence发信号出来后,才真正完成。

此时Buffer是BufferQueue持有,可以迁移到ACQUIRED状态,通过acquireBuffer流程。而且可以迁移到FREE状态,如果另外一个Buffer被异步的queue进来。

  • ACQUIRED 状态:

Buffer已经被Consumer获取,但是也必须要等对应的Fence发信号才能被Consumer读写,找个Fence是从Producer那边,queueBuffer的时候传过来的。

我们将其称为acquire fence。此时,Buffer被Consumer持有。状态可以迁移到FREE状态,通过releaseBuffer或detachBuffer流程。

除了从acquireBuffer流程可以迁移到ACQUIRED状态,attachBuffer流程也可以迁移到ACQUIRED状态。

  • FREE 状态:

FREE状态,说明Buffer被BufferQueue持有,可以被Producer dequeue,它将迁移到DEQUEUED状态,通过dequeueBuffer流程。

  • SHARED状态:

SHARED状态是一个特殊的状态,SHARED的Buffer并不参与前面所说的状态迁移。它说明Buffer被用与共享Buffer模式。

除了FREE状态,它可以是其他的任何状态。它可以被多次dequeued, queued, 或者 acquired。这中共享Buffer的模式,主要用于VR等低延迟要求的场合。


BufferQueue创建以及创建一个监听

BufferQueueLayer::onFirstRef调用BufferQueue::createBufferQueue()创建了bufferQueue、生产者、消费者,在创建生产者和消费者的过程中,将他们绑定到同一个BufferQueue上。

之后会创建一个BufferLayerConsumer对象mConsumer,这个对象继承了ConsumerBase类,所以会回调基类的构造函数,注册一个监听对象到BufferQueue(空对象)。

真正的监听是在mConsumer->setContentsChangedListener(this)基类构造函数中还会调用consumerConnect将消费者关联到BufferQueue中。

此时监听对象就赋给了BufferQueue的mConsumerListener成员(调用BufferQueueConsumer的connect函数)。

这个监听对象会在queueBuffer是触发,由生产者回调注册到BufferQueue的帧可用通知。


DequeuBuffer

BufferQueue创建后,首先由生产者执行 dequeueBuffer 请求一块Buffer。

Dequeue和Queue的操作都是在硬件渲染(软件绘制暂不考虑)的过程中,在ThreadedRenderer::draw函数中,updateRootDisplayList创建好一个RootDisplayList后,开始渲染一帧,在这时调用父类的syncAndDrawFrame函数,这个函数层层调到CanvasContext::draw函数,然后依次调用三个函数:

  1. mRenderPipeline->getFrame 执行dequeueBuffer请求一块buffer
  2. draw 填充buffer
  3. mRenderPipeline->swapBuffers 执行queueBuffer送到BufferQueue

在此处dequeuBuffer和queueBuffer两个操作调到gui/Surface.cpp的两个对应函数,这个流程基本一样。

大致都从SkiaOpenPipeline.cpp -> EglManager.cpp -> eglApi.cpp -> ANativeWindow.cpp,之后到Bp客户端libgui库的Surface.cpp,执行具体操作。

Surface::dequeueBuffer中调用IGraphicBufferProducer::dequeueBuffer。然后remote()->transact(DEQUEUE_BUFFER,data,&reply)调用到Bn端BufferQueueProducer::dequeueBuffer函数。

  1. 首先查找mSlots[found]的序列号found,mSlots是存放Buffer的地方,他的数量是64。即至多存放64个buffer。
  2. found是从waitForFreeSlotThenRelock中获取:
  • 从非Free的buffer中统计dequeue和acquire的数量;
  • 判断dequeueBufferCount数量不能大于最大允许dequeueBuffer的数量;
  • slot的获取主要来自两个集合,mFreeSlots和mFreeBuffers;两者包含的所有状态都是free,区别在于前者没有attached,后者以及attached;如果调用来自dequeueBuffer优先选择前者,如果调用来自attachBuffer,优先选择后者;
  • 如果没找到符合要求的buffer或者queue的buffer还有太多没有完成,就要等待这个buffer被acquired或者released,或者修改最大的buffer数量。

3 . 找到可用的slot,还要判断是否重新分配空间:如果Buffer(本质上是GraphicBuffer)是空,并且需要重新分配空间,则对这个mSlots[found]初始化;

4 . new GraphicBuffer为mSlots分配一个GraphicBuffer,赋值给BufferQueueCore中的变量mSlots[]的mGraphicBuffer;

mSlots[*outSlot].mGraphicBuffer = graphicBuffer;

Surface::dequeueBuffer从服务端申请到Buffer后,通过requestBuffer将客户端的buffer和服务端的buffer指向同一块物理内存。

具体是IGraphicBufferPruducer代理中通过REQUEST_BUFFER状态,在onTransact中将申请的GraphicBuffer,即mSlots[slot].mGraphicBuffer。将其写入reply,等待客户端读取。


QueueBuffer

queueBuffer是在渲染一帧后通过mRenderPipeline->swapBuffers调用到Surface::queueBuffer。将填充完数据的buffer放入BufferQueue,并且通过监听者通知消费者对象开始消费。

在Bn端BufferQueueProducer::queueBuffer L977中调用:frameAvailableListener->onFrameAvailable(item);

通知消费者,在BufferQueueLayer::onFrameAvailable中调用:mFlinger->signalLayerUpdate();

触发SurfaceFlinger的消息循环机制,开始处理SurfaceFlinger合成事件。


序列图

acquire & release

消费者SurfaceFlinger通过acquire从BufferQueue取出一块buffer消费。消费(合成)之后释放。

序列图

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/cRoixcUB6OfYME79DAfhvA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
简化Android的UI开发 5年以前  |  521184次阅读
Android 深色模式适配原理分析 4年以前  |  29550次阅读
Android阴影实现的几种方案 2年以前  |  12067次阅读
Android 样式系统 | 主题背景覆盖 4年以前  |  10216次阅读
 目录