浅析Android View绘制过程中的Surface

news2024/11/26 10:37:51

前言

在《浅析Android中View的测量布局流程》中我们对VSYNC信号到达App进程之后开启的View布局过程进行了分析,经过对整个App界面的View树进行遍历完成了测量和布局,确定了View的大小以及在屏幕中所处的位置。但是,如果想让用户在屏幕上看到View的内容,还需要根据View的绘制生成图形数据并交由硬件进行屏幕刷新。

View的绘制主要负责将业务层的各种API调用转换为指令,然后交给渲染引擎进行处理,最终生成能够被硬件直接处理的数据。这个过程主要分为渲染数据的生产以及消费,一般来说,渲染数据的生产者是各个App进程,而消费者则是SurfaceFlinger进程,这里会涉及到渲染数据的跨进程传输问题。下面将会对渲染数据的跨进程传输的实现进行分析。

Surface作为数据载体负责打通App进程与SurfaceFlinger进程之间的数据交互,同时Surface是属于App进程内的资源,因此先从App进程这个生产者出发,基于Surface的创建流程及其使用,对渲染数据的传递机制进行分析。

绘制前的准备

在《浅析Android中View的测量布局流程》中有分析到,当测量数据发生变化时,需要对窗体大小进行更新,因为测量数据的变化导致视图展示区域随之发生变化。

根据分析测量布局流程的相关源码实现可知,一个ViewRootImpl对象被创建时,都会创建一个Surface对象以及SurfaceControl对象,但是Surface对象并不是立即可用于绘制渲染的,而只是一个壳子,其真正的实现是在native层。而在测量之后宽高如果发生变化,则需要对窗口大小进行更新,此时会对Surface以及SurfaceControl对象进行处理,即更新native层的Surface以及SurfaceControl对象,之后Surface对象将进入可用状态。

SurfaceControl的创建

首先,App进程为每一个Activity创建了一个Window,而每一个Window会对应一个ViewRootImpl,每一个ViewRootImpl持有一个Surface以及SurfaceControl对象。然后,SystemServer进程会对应地为App进程的每一个Window创建一个Window,相对应地,也会为每一个Window创建一个SurfaceControl对象。下面我们看下SystemServer进程中的SurfaceControl的创建过程。

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl {
	final W mWindow; // mWindow = new W(this);
    public final Surface mSurface = new Surface();
	private final SurfaceControl mSurfaceControl = new SurfaceControl();
	// ...
    private void performTraversals() {
    	// ...
    	boolean windowShouldResize = layoutRequested && windowSizeMayChange && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && frame.width() < desiredWindowWidth && frame.width() != mWidth) || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && frame.height() < desiredWindowHeight && frame.height() != mHeight));
    	// ...
		if (mFirst || windowShouldResize || viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
            try {
            	// ...
            	// 更新window
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                // ...
            } catch (RemoteException e) {
            } finally {
                // ...
            }
		}
		// ...
	}

	private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException {
		// ...
		if (LOCAL_LAYOUT) {
			// ...
		} else {
			// mWindowSession是IWindowSession类型的对象,即Binder代理对象,对应实现是SystemServer进程中的Session类的实例,每个进程的IWindowSession对应SystemServer进程中一个Session实例
			relayoutResult = mWindowSession.relayout(mWindow, params, requestedWidth, requestedHeight, viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mRelayoutBundle);
			// ...
		}
		// ...
		// mSurfaceControl实例可用之后,根据mSurfaceControl的Surface信息对mSurface进行更新
		if (mSurfaceControl.isValid()) {
            if (!useBLAST()) {
                mSurface.copyFrom(mSurfaceControl);
            } else {
                updateBlastSurfaceIfNeeded();
            }
            // ...
        } else {
            // ...
        }
        
		// ...
		return relayoutResult;
	}
	
	// ...
}

relayoutWindow方法会通过Binder请求到SystemServer进程,对之前SystemServer进程中创建的window实例进行更新。mWindowSession.relayout会调用到WindowManagerService中的相关逻辑,经过relayout方法调用了WindowManagerService#createSurfaceControl方法,完成SystemServer进程中SurfaceControl对象的创建。

SystemServer进程返回之后,通过getSurfaceControl方法将新创建的SurfaceControl对象的属性拷贝回App进程的SurfaceControl对象(即ViewRootImpl#mSurfaceControl)中。

// com.android.server.wm.Session
/**
 * Session代表一个活跃的客户端session。SystemServer进程中会为每个进程维护一个Session对象,用于window相关的Binder通信。
 */
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
	// ...
    @Override
    public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, int lastSyncSeqId, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Bundle outSyncSeqIdBundle) {
        // ...
        int res = mService.relayoutWindow(this, window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq, lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls, outSyncSeqIdBundle);
        // ...
        return res;
    }
	
	// ...
}

// com.android.server.wm.WindowManagerService
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
	// ...
	public int relayoutWindow(Session session, IWindow client, LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq, int lastSyncSeqId, ClientWindowFrames outFrames, MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Bundle outSyncIdBundle) {
        // ...
        synchronized (mGlobalLock) {
			// 获取窗体状态描述,每一个Window(通常对应一个Activity)都对应一个WindowState。
            final WindowState win = windowForClientLocked(session, client, false);
            
            // 获取WindowStateAnimator,用于创建SurfaceControl
          	WindowStateAnimator winAnimator = win.mWinAnimator;
          	
          	// ...
          	
            // 只有view可见或者相关联的appToken没有隐藏时才应该relayout。
            final boolean shouldRelayout = viewVisibility == View.VISIBLE && (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING || win.mActivityRecord.isClientVisible());

			// ...
            if (shouldRelayout && outSurfaceControl != null) {
                try {
                	// 创建SurfaceControl并将其拷贝到outSurfaceControl,outSurfaceControl
                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                } catch (Exception e) {
                    // ...
                    return 0;
                }
            }
			// ...
        }

        // ...
		return result;
    }

    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win, WindowStateAnimator winAnimator) {
        if (!win.mHasSurface) {
            result |= RELAYOUT_RES_SURFACE_CHANGED;
        }

        WindowSurfaceController surfaceController;
        try {
            // 1. 通过WindowStateAnimator对象创建WindowSurfaceController对象
            surfaceController = winAnimator.createSurfaceLocked();
        } finally {
            // ...
        }
        if (surfaceController != null) {
        	// 2. 通过WindowSurfaceController将SurfaceController拷贝到outSurfaceControl中,outSurfaceControl对应App进程中的ViewRootImpl的mSurfaceControl变量
            surfaceController.getSurfaceControl(outSurfaceControl);            
        } else {
            // ...
            outSurfaceControl.release();
        }

        return result;
    }
	
	// ...
}

/**
 * 为单个WindowState跟踪动画和surface的操作.
 **/
 // com.android.server.wm.WindowStateAnimator
class WindowStateAnimator {
	final WindowState mWin;
	WindowSurfaceController mSurfaceController;

	// 创建WindowSurfaceController
    WindowSurfaceController createSurfaceLocked() {
        final WindowState w = mWin;
		// 如果已经创建过就不会重复创建了
        if (mSurfaceController != null) {
            return mSurfaceController;
        }
        try {
            // ...
            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format, flags, this, attrs.type);
            // ...
        } catch (OutOfResourcesException e) {
           // ...
            return null;
        } catch (Exception e) {
            // ...
            return null;
        }
		// ...
        return mSurfaceController;
    }

	// ...
}

// com.android.server.wm.WindowSurfaceController
class WindowSurfaceController {
    WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator, int windowType) {
       	// ...
        final SurfaceControl.Builder b = win.makeSurface()
                .setParent(win.getSurfaceControl())
                .setName(name)
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
                .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
                .setCallsite("WindowSurfaceController");
        // ...
        mSurfaceControl = b.build();
    }
	
    public static class Builder {
        @NonNull
        public SurfaceControl build() {
			// ....
            return new SurfaceControl(mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata, mLocalOwnerView, mCallsite);
        }
	}
}

可以看出,WindowSurfaceControllerSurfaceControl的包装类,通过持有SurfaceControl对象来对Surface进行操作,WindowSurfaceController以及SurfaceControl都是在SystemServer进程创建的。接下来,继续跟着SurfaceControl的构造函数看下,SurfaceControl的创建具体做了哪些事情。

/**
 * 持有一个由系统合成器管理的Surface对象。这个SurfaceControl对象由buffer以及如何显示buffer的信息组成。
 * 通过构造的Surface对象可以提交数据到buffer,用于合成上屏。
 */
public final class SurfaceControl implements Parcelable {
    public long mNativeObject;
    private long mNativeHandle;
	
    private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags, SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView, String callsite) throws OutOfResourcesException, IllegalArgumentException {
        // ...
        long nativeObject = 0;
        try {
            // ...
            nativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0, metaParcel);
        } finally {
            metaParcel.recycle();
        }
        // ...
        assignNativeObject(nativeObject, callsite);
        // ...
    }

	private static native long nativeCreate(SurfaceSession session, String name, int w, int h, int format, int flags, long parentObject, Parcel metadata) throws OutOfResourcesException;
    
    private void assignNativeObject(long nativeObject, String callsite) {
        if (mNativeObject != 0) {
            release();
        }
        if (nativeObject != 0) {
            mFreeNativeResources = sRegistry.registerNativeAllocation(this, nativeObject);
        }
        // 记录native层Surface对象的句柄值
        mNativeObject = nativeObject;
        mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
        if (sDebugUsageAfterRelease && mNativeObject == 0) {
            mReleaseStack = new Throwable("Assigned invalid nativeObject");
        } else {
            mReleaseStack = null;
        }
        setUnreleasedWarningCallSite(callsite);
        addToRegistry();
    }
}

从源码可以看出,Java层的SurfaceControl对象其实是一个壳,其内部的主要实现是在native层的SurfaceControl对象中的,通过持有native层的SurfaceControl对象的句柄值对其进行调用。因此,SurfaceControl的关键实现在nativeCreate这个native方法中。

// frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject, jobject metadataParcel) {
    // 获取SurfaceComposerClient对象,SurfaceComposerClient对象负责与SurfaceFlinger进程进行交互
    sp<SurfaceComposerClient> client;
    if (sessionObj != NULL) {
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
        client = SurfaceComposerClient::getDefault();
    }
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
    // ...
    // 创建SurfaceControl对象
    status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface, flags, parentHandle, std::move(metadata));
    if (err == NAME_NOT_FOUND) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return 0;
    } else if (err != NO_ERROR) {
        jniThrowException(env, OutOfResourcesException, statusToString(err).c_str());
        return 0;
    }

    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());
}

从源码中得知,通过SurfaceComposerClient调用createSurfaceChecked方法进行native层的SurfaceControl的创建,内部通过成员变量mClient跨进程调用到SurfaceFlinger进程,mClient的远程实现是SurfaceFlinger进程的Client类。

// frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h, PixelFormat format, sp<SurfaceControl>* outSurface, int32_t flags, const sp<IBinder>& parentHandle, LayerMetadata metadata, uint32_t* outTransformHint) {
    status_t err = mStatus;

    if (mStatus == NO_ERROR) {
        gui::CreateSurfaceResult result;
        // 请求SurfaceFlinger进程创建一个SurfaceControl
        binder::Status status = mClient->createSurface(std::string(name.c_str()), flags, parentHandle, std::move(metadata), &result);
        err = statusTFromBinderStatus(status);
		// ...        
        if (err == NO_ERROR) {
        	// 根据CreateSurfaceResult构造SurfaceControl对象,并将其返回
        	// result.handle是一个Binder对象,封装了SurfaceFlinger进程的Layer以及SurfaceFlinger
            *outSurface = new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName), w, h, format, result.transformHint, flags);
        }
    }
    return err;
}

// frameworks/native/libs/gui/SurfaceControl.cpp
SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, int32_t layerId, const std::string& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t transform, uint32_t flags)
      : mClient(client),
        mHandle(handle),
        mLayerId(layerId),
        mName(name),
        mTransformHint(transform),
        mWidth(w),
        mHeight(h),
        mFormat(format),
        mCreateFlags(flags) {}

SurfaceFlinger进程调用Client::createSurface创建Surface对象,并根据方法的返回结果构造了SystemServer进程的SurfaceControl对象,并将其返回最终拷贝回App进程的SurfaceControl对象,即ViewRootImpl#mSurfaceControl

但是,到这里其实并没有发现跨进程传输数据相关的代码实现,因此只能继续看下SurfaceFlinger进程的Client::createSurface方法具体做了什么事情。

// frameworks/native/services/surfaceflinger/Client.cpp
binder::Status Client::createSurface(const std::string& name, int32_t flags, const sp<IBinder>& parent, const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult) {
    sp<IBinder> handle;
    LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(), static_cast<uint32_t>(flags), std::move(metadata));
    args.parentHandle = parent;
    // 为App进程的SurfaceControl创建Layer
    const status_t status = mFlinger->createLayer(args, *outResult);
    return binderStatusFromStatusT(status);
}

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) {
    status_t result = NO_ERROR;
    sp<Layer> layer;

    switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
        case ISurfaceComposerClient::eFXSurfaceContainer:
        case ISurfaceComposerClient::eFXSurfaceBufferState:
            args.flags |= ISurfaceComposerClient::eNoColorFill;
            [[fallthrough]];
        case ISurfaceComposerClient::eFXSurfaceEffect: {
        	// 根据args创建Layer
            result = createBufferStateLayer(args, &outResult.handle, &layer);
            std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
            if (pendingBufferCounter) {
                std::string counterName = layer->getPendingBufferCounterName();
                mBufferCountTracker.add(outResult.handle->localBinder(), counterName, pendingBufferCounter);
            }
        } break;
        default:
            result = BAD_VALUE;
            break;
    }

    if (result != NO_ERROR) {
        return result;
    }

    sp<Layer> parent = LayerHandle::getLayer(args.parentHandle.promote());

    uint32_t outTransformHint;
    // 将创建好的Layer添加到缓存
    result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
    outResult.layerId = layer->sequence;
    outResult.layerName = String16(layer->getDebugName());
    return result;
}

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle, sp<Layer>* outLayer) {
    *outLayer = getFactory().createBufferStateLayer(args);
    *handle = (*outLayer)->getHandle();
    return NO_ERROR;
}

// frameworks/native/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
sp<Layer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
    return sp<Layer>::make(args);
}

// frameworks/native/services/surfaceflinger/Layer.cpp
Layer::Layer(const surfaceflinger::LayerCreationArgs& args)
      : sequence(args.sequence),
        mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)),
        mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
        mClientRef(args.client),
        mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
        mLayerCreationFlags(args.flags),
        mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName, this)) {
        // ...
}

SurfaceFlinger进程中调用了SurfaceFlinger::createBufferStateLayer方法创建了Layer对象,因此SystemServer进程中的SurfaceControl对象对应SurfaceFlinger进程的Layer对象。

上面提到,经过App进程到SystemServer进程再到SurfaceFlinger进程,最终完成了一系列window相关的对象创建。最终依次返回并将相关信息跨进程拷贝回App进程。之后因为App进程的mSurfaceControl处于有效状态,此时将会App进程的mSurface进行更新操作。

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl {
	final W mWindow; // mWindow = new W(this);
    public final Surface mSurface = new Surface();
	private final SurfaceControl mSurfaceControl = new SurfaceControl();

    private boolean mUseBLASTAdapter;
    private boolean mForceDisableBLAST;
	
	// ...

	public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
		synchronized (this) {
			if (mView == null) {
                mView = view;
                // ...
                int res;
                // ...
                try {
                    // ...
                    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets, mTempControls);
                    // ...
                } catch (RemoteException e) {
                    // ...
                } finally {
                    // ...
                }
                // ...
                // 是否开启BLAST取决于SystemServer进程返回的res
                if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
                    mUseBLASTAdapter = true;
                }
                // ...
			}
		}
	}
   
	private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException {
		// ...
		if (LOCAL_LAYOUT) {
			// ...
		} else {
			relayoutResult = mWindowSession.relayout(mWindow, params, requestedWidth, requestedHeight, viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mRelayoutBundle);
			// ...
		}
		// ...
		// mSurfaceControl实例可用之后,根据mSurfaceControl的Surface信息对mSurface进行更新
		if (mSurfaceControl.isValid()) {
            if (!useBLAST()) {
                mSurface.copyFrom(mSurfaceControl);
            } else {
                updateBlastSurfaceIfNeeded();
            }
            // ...
        } else {
            // ...
        }
        
		// ...
		return relayoutResult;
	}
	
	boolean useBLAST() {
        return mUseBLASTAdapter && !mForceDisableBLAST;
    }
	
	// ...
}

在更新mSurface之前会判断是否使用BLAST,而是否使用BLAST是在addView的时候由SystemServer进程决定的,根据源码可知Android S开始默认开启BLAST,因此最后调用了updateBlastSurfaceIfNeeded方法,其内部会调用Surface#transferFrom方法,进而拷贝mNativeObject的值。

// com.android.server.wm.Session
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    @Override
    public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame, float[] outSizeCompatScale) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibleTypes, outInputChannel, outInsetsState, outActiveControls,
                outAttachedFrame, outSizeCompatScale);
    }
}

// com.android.server.wm.WindowManagerService
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
	// ...
	
    // Whether the system should use BLAST for ViewRootImpl
    final boolean mUseBLAST; // Android S之后默认开启
    
    // ...

	private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
            DisplayWindowSettingsProvider displayWindowSettingsProvider,
            Supplier<SurfaceControl.Transaction> transactionFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
		// ...
		mUseBLAST = Settings.Global.getInt(resolver, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, 1) == 1;
		// ...
	}
    
    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
            float[] outSizeCompatScale) {
		// ...
		int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
                appOp);
        if (res != ADD_OKAY) {
            return res;
        }
		// ...
		synchronized (mGlobalLock) {
			// ...
			res = ADD_OKAY;

            if (mUseBLAST) {
                res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
            }
            // ...
		}
		Binder.restoreCallingIdentity(origId);
        return res;
	}
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl {
	final W mWindow; // mWindow = new W(this);
    public final Surface mSurface = new Surface();
	private final SurfaceControl mSurfaceControl = new SurfaceControl();
    private BLASTBufferQueue mBlastBufferQueue;
    
    private boolean mUseBLASTAdapter;
    private boolean mForceDisableBLAST;
	
	// ...
    void updateBlastSurfaceIfNeeded() {
        if (!mSurfaceControl.isValid()) {
            return;
        }
		// 如果对应的native层的SurfaceControl对象是同一个,则直接更新即可,不需要重新创建BLASTBufferQueue
        if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
            mBlastBufferQueue.update(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
            return;
        }

        // 如果更新了SurfaceControl,那么销毁并重建BBQ(BLASTBufferQueue)来重置BufferQueue及BLASTBufferQueue的状态.
        if (mBlastBufferQueue != null) {
            mBlastBufferQueue.destroy();
        }
        mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
        mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
        Surface blastSurface = mBlastBufferQueue.createSurface();
        // Only call transferFrom if the surface has changed to prevent inc the generation ID and
        // causing EGL resources to be recreated.
        mSurface.transferFrom(blastSurface);
    }
	// ...
}

因此,Android S开始由App进程创建管理BufferQueue,即ViewRootImpl#mBlastBufferQueue。那BLASTBufferQueue是在什么时候创建的呢?查看源码发现,其实就是在ViewRootImpl#updateBlastSurfaceIfNeeded方法中创建的。下面看下BLASTBufferQueue的创建过程。

BLASTBufferQueue的创建

根据上面的分析得出,App进程在创建完SurfaceControl之后,会继续创建BLASTBufferQueue,并且在创建BLASTBufferQueue之前并没有发现和图像数据跨进程传输相关的代码实现,所以不妨假设下,BLASTBufferQueue的创建其实就是为跨进程传输图像数据做准备。

// android.graphics.BLASTBufferQueue
public final class BLASTBufferQueue {
    // Note: This field is accessed by native code.
    public long mNativeObject; // BLASTBufferQueue*
	
    /** Create a new connection with the surface flinger. */
    public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {
        this(name, true /* updateDestinationFrame */);
        update(sc, width, height, format);
    }

    public BLASTBufferQueue(String name, boolean updateDestinationFrame) {
        mNativeObject = nativeCreate(name, updateDestinationFrame);
    }

    private static native long nativeCreate(String name, boolean updateDestinationFrame);
    private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height, int format);
}

BLASTBufferQueue的构造函数可以看到,和SurfaceControl以及Surface类似,都是通过JNI调用到native层去创建对应的对象。因此,Java层的BLASTBufferQueue对象也是native层的BLASTBufferQueue的壳,因此,进一步分析native层的代码。

// frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jboolean updateDestinationFrame) {
    ScopedUtfChars name(env, jName);
    sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);
    queue->incStrong((void*)nativeCreate);
    return reinterpret_cast<jlong>(queue.get());
}

static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, jlong height, jint format) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
}

native层在创建了BLASTBufferQueue对象之后,调用了BLASTBufferQueue::update方法。

// frameworks/native/libs/gui/BLASTBufferQueue.cpp
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
      : mSurfaceControl(nullptr),
        mSize(1, 1),
        mRequestedSize(mSize),
        mFormat(PIXEL_FORMAT_RGBA_8888),
        mTransactionReadyCallback(nullptr),
        mSyncTransaction(nullptr),
        mUpdateDestinationFrame(updateDestinationFrame) {
    // 1. 先创建BufferQueue,然后初始化mProducer以及mConsumer
    createBufferQueue(&mProducer, &mConsumer);
    // 因为是在client进程,因此为dequeue操作设置超时来保证dequeueBuffer时会阻塞线程。
    mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
    // buffer的默认数量为2
    mProducer->setMaxDequeuedBufferCount(2);
    // 封装BufferQueueConsumer
    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE, 1, false, this);
    
    static std::atomic<uint32_t> nextId = 0;
    mProducerId = nextId++;
    mName = name + "#" + std::to_string(mProducerId);
    auto consumerName = mName + "(BLAST Consumer)" + std::to_string(mProducerId);
    mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(mProducerId);
    mBufferItemConsumer->setName(String8(consumerName.c_str()));
    // 2. 设置监听器,用于当帧数据可用时消费Buffer数据
    mBufferItemConsumer->setFrameAvailableListener(this);

    ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
    mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
    mNumAcquired = 0;
    mNumFrameAvailable = 0;

    TransactionCompletedListener::getInstance()->addQueueStallListener(
            [&](const std::string& reason) {
                std::function<void(const std::string&)> callbackCopy;
                {
                    std::unique_lock _lock{mMutex};
                    callbackCopy = mTransactionHangCallback;
                }
                if (callbackCopy) callbackCopy(reason);
            },
            this);
}

void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer) {
    sp<BufferQueueCore> core(new BufferQueueCore());
    // 将BufferQueueCore传入BBQBufferQueueProducer,这样当producer发起调用时可以异步化,通过持有的BufferQueueCore将结果返回
    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
    consumer->setAllowExtraAcquire(true);

    *outProducer = producer;
    *outConsumer = consumer;
}

// frameworks/native/libs/gui/BufferQueueCore.cpp
BufferQueueCore::BufferQueueCore()
      : mMutex(),
        mIsAbandoned(false),
        mConsumerControlledByApp(false), // consumer不是给App控制的
        mConsumerName(getUniqueName()),
        mConsumerListener(),
        mConsumerUsageBits(0),
        mConsumerIsProtected(false),
        mConnectedApi(NO_CONNECTED_API),
        mLinkedToDeath(),
        mConnectedProducerListener(),
        mBufferReleasedCbEnabled(false),
        mBufferAttachedCbEnabled(false),
        mSlots(), // BufferSlot数组,长度为64
        mQueue(), // 元素类型为BufferItem的Vector变量
        mFreeSlots(), // 元素类型为int的set,对应没有buffer的索引位置
        mFreeBuffers(), // 元素类型为int的list,对应空闲buffer的索引位置
        mUnusedSlots(), // 元素类型为int的list,对应可以被释放的buffer的索引位置
        mActiveBuffers(), // 元素类型为int的set,对应正在使用的buffer的索引位置
        mDequeueCondition(),
        mDequeueBufferCannotBlock(false),
        mQueueBufferCanDrop(false),
        mLegacyBufferDrop(true),
        mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
        mDefaultWidth(1),
        mDefaultHeight(1),
        mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
        mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
        mMaxAcquiredBufferCount(1),
        mMaxDequeuedBufferCount(1),
        mBufferHasBeenQueued(false),
        mFrameCounter(0),
        mTransformHint(0),
        mIsAllocating(false),
        mIsAllocatingCondition(),
        mAllowAllocation(true),
        mBufferAge(0),
        mGenerationNumber(0),
        mAsyncMode(false),
        mSharedBufferMode(false),
        mAutoRefresh(false),
        mSharedBufferSlot(INVALID_BUFFER_SLOT),
        mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
                           HAL_DATASPACE_UNKNOWN),
        mLastQueuedSlot(INVALID_BUFFER_SLOT),
        mUniqueId(getUniqueId()),
        mAutoPrerotation(false),
        mTransformHintInUse(0) {
    int numStartingBuffers = getMaxBufferCountLocked();
    for (int s = 0; s < numStartingBuffers; s++) {
        mFreeSlots.insert(s);
    }
    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
            s++) {
        mUnusedSlots.push_front(s);
    }
}

这里创建了BufferQueueCore对象,并基于BufferQueueCore对象创建了BBQBufferQueueProducer以及BufferQueueConsumer,分别是BufferQueueCore的生产者和消费者,并被BLASTBufferQueuemProducermConsumer持有,mProducer的主要操作包括dequeueBufferqueueBuffermConsumer的主要操作包括acquireBufferreleaseBuffer

可以看到这里主要是创建了一个BufferQueueCore对象,用于管理Buffer,而Buffer是通过mProducermConsumer使用的,所以看下mProducermConsumer的内部实现。

BBQBufferQueueProducer的创建

通过源码可以看出,mProducermConsumer都是通过持有BufferQueueCore来使用buffer的,而mProducermConsumer中都提供了使用buffer的方法。

// frameworks/native/libs/gui/BLASTBufferQueue.cpp
BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq) : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/), mBLASTBufferQueue(std::move(bbq)) {}


BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
    mCore(core),
    mSlots(core->mSlots),
    mConsumerName() {}

// frameworks/native/libs/gui/BufferQueueProducer.cpp
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
        bool consumerIsSurfaceFlinger) :
    mCore(core),
    mSlots(core->mSlots),
    mConsumerName(),
    mStickyTransform(0),
    mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
    mLastQueueBufferFence(Fence::NO_FENCE),
    mLastQueuedTransform(0),
    mCallbackMutex(),
    mNextCallbackTicket(0),
    mCurrentCallbackTicket(0),
    mCallbackCondition(),
    mDequeueTimeout(-1),
    mDequeueWaitingForAllocation(false) {}

// frameworks/native/libs/gui/include/gui/BufferQueueProducer.h

// dequeueBuffer获取下一个buffer的slot索引给producer使用。
// 如果有一个可用的buffer slot,那么就把slot索引写入参数处并返回,否则返回-EBUSY。
virtual status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override;
                                   
// requestBuffer返回GraphicBuffer到第N个slot。
// 通常是在dequeueBuffer第一次slot N的时候。但是如果dequeueBuffer返回的flags表明之前返回的buffers已经失效的话,就必须再次调用requestBuffer。
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);

// queueBuffer返回一个填充过的buffer到BufferQueue。
// 调用方必须提供一个fence在所有渲染操作完成之后发送信号。
virtual status_t queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output);



// frameworks/native/libs/gui/BufferQueueConsumer.cpp
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
    mCore(core),
    mSlots(core->mSlots),
    mConsumerName() {}

// frameworks/native/libs/gui/include/gui/BufferQueueConsumer.h

// acquireBuffer尝试获取BufferQueue中的下一个pending的buffer的使用权,如果没有pending的buffer就返回NO_BUFFER_AVAILABLE。 如果一个buffer被成功的获取到,将会返回一个包含buffer相关的信息的BufferItem。
virtual status_t acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber = 0) override;

// releaseBuffer从消费者处释放一个buffer slot到BufferQueue中。releaseBuffer调用时有可能还在访问buffer的内容。
// 当buffer不再使用的时候,fence将会发出信号。
virtual status_t releaseBuffer(int slot, uint64_t frameNumber, const sp<Fence>& releaseFence, EGLDisplay display, EGLSyncKHR fence);

当完成BLASTBufferQueue的创建之后,通过update方法更新持有的SurfaceControl变量,将其指向新的SurfaceControl对象。

// frameworks/native/libs/gui/BLASTBufferQueue.cpp
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format) {
    std::lock_guard _lock{mMutex};
    if (mFormat != format) {
        mFormat = format;
        mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
    }

    const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
    bool applyTransaction = false;

    // 更新持有的SurfaceControl变量
    mSurfaceControl = surface;
    SurfaceComposerClient::Transaction t;
    if (surfaceControlChanged) {
        t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure);
        applyTransaction = true;
    }
    mTransformHint = mSurfaceControl->getTransformHint();
    mBufferItemConsumer->setTransformHint(mTransformHint);
   
    ui::Size newSize(width, height);
    if (mRequestedSize != newSize) {
        mRequestedSize.set(newSize);
        mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);
        if (mLastBufferInfo.scalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
            // If the buffer supports scaling, update the frame immediately since the client may
            // want to scale the existing buffer to the new size.
            mSize = mRequestedSize;
            if (mUpdateDestinationFrame) {
                t.setDestinationFrame(mSurfaceControl, Rect(newSize));
                applyTransaction = true;
            }
        }
    }
    if (applyTransaction) {
        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
        t.setApplyToken(mApplyToken).apply(false, true);
    }
}

Surface的创建

在创建完BLASTBufferQueue之后,会通过新创建的BLASTBufferQueue对象创建一个Surface对象,并用新的Surface对象更新ViewRootImpl#mSurface,主要是将内部持有的native层的句柄值更新为BLASTBufferQueue对象创建的native的句柄值。

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl {
	final W mWindow; // mWindow = new W(this);
    public final Surface mSurface = new Surface();
	private final SurfaceControl mSurfaceControl = new SurfaceControl();
    private BLASTBufferQueue mBlastBufferQueue;
    
    private boolean mUseBLASTAdapter;
    private boolean mForceDisableBLAST;
	
	// ...
    void updateBlastSurfaceIfNeeded() {
        if (!mSurfaceControl.isValid()) {
            return;
        }
		// 如果对应的native层的SurfaceControl对象是同一个,则直接更新即可,不需要重新创建BLASTBufferQueue
        if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
            mBlastBufferQueue.update(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
            return;
        }

        // 如果更新了SurfaceControl,那么销毁并重建BBQ(BLASTBufferQueue)来重置BufferQueue及BLASTBufferQueue的状态.
        if (mBlastBufferQueue != null) {
            mBlastBufferQueue.destroy();
        }
        mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
        mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
        // 通过新创建的BLASTBufferQueue对象创建Surface对象,并用新的Surface对象更新mSurface
        Surface blastSurface = mBlastBufferQueue.createSurface();
        // Only call transferFrom if the surface has changed to prevent inc the generation ID and
        // causing EGL resources to be recreated.
        mSurface.transferFrom(blastSurface);
    }
	// ...
}

可以看到,最终还是BLASTBufferQueue调用nativeGetSurface到了native层去创建Surface对象,这里Surface对象就持有了BLASTBufferQueuemProducer,这样就可以通过Surface访问BBQBufferQueueProducer,然后通过BBQBufferQueueProducer访问BufferQueueCore,最终实现Buffer的访问使用。

public final class BLASTBufferQueue {

    /**
     * @return a new Surface instance from the IGraphicsBufferProducer of the adapter.
     */
    public Surface createSurface() {
        return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);
    }

	private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
}

// frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr, jboolean includeSurfaceControlHandle) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    return android_view_Surface_createFromSurface(env, queue->getSurface(includeSurfaceControlHandle));
}

// frameworks/native/libs/gui/BLASTBufferQueue.cpp
sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
    std::lock_guard _lock{mMutex};
    sp<IBinder> scHandle = nullptr;
    if (includeSurfaceControlHandle && mSurfaceControl) {
        scHandle = mSurfaceControl->getHandle();
    }
    return new BBQSurface(mProducer, true, scHandle, this);
}

// frameworks/native/libs/gui/BLASTBufferQueue.cpp
public:
    BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp, const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq) : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}

// frameworks/native/libs/gui/Surface.cpp
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp, const sp<IBinder>& surfaceControlHandle)
      : mGraphicBufferProducer(bufferProducer),
        mCrop(Rect::EMPTY_RECT),
        mBufferAge(0),
        mGenerationNumber(0),
        mSharedBufferMode(false),
        mAutoRefresh(false),
        mAutoPrerotation(false),
        mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
        mSharedBufferHasBeenQueued(false),
        mQueriedSupportedTimestamps(false),
        mFrameTimestampsSupportsPresent(false),
        mEnableFrameTimestamps(false),
        mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {
    // Initialize the ANativeWindow function pointers.
    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
    ANativeWindow::cancelBuffer     = hook_cancelBuffer;
    ANativeWindow::queueBuffer      = hook_queueBuffer;
    ANativeWindow::query            = hook_query;
    ANativeWindow::perform          = hook_perform;

    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
    ANativeWindow::cancelBuffer_DEPRECATED  = hook_cancelBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED    = hook_lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED   = hook_queueBuffer_DEPRECATED;

    const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
    const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;

    // ...
}

总结

首先,当App进程的View的测量数据发生变化时,会导致窗体大小发生变化,此时会调用ViewRootImpl#relayoutWindow请求SystemServer进程的WindowManagerService更新窗体的大小,SystemServer进程会做如下工作:

  • SystemServer进程通过WindowManagerService创建Java层的SurfaceControl对象,对应App进程中Java层的SurfaceControl对象;
  • SystemServer进程创建的Java层的SurfaceControl对象是一个壳,其内部会通过JNI调用到native层,创建native层的SurfaceControl对象,并将native层的SurfaceControl对象的句柄值拷贝到App进程的SurfaceControl对象;
  • 在native层的SurfaceControl对象创建的过程中会通过SurfaceComposerClient请求到SurfaceFlinger进程,调用Client::createSurface创建Layer对象,并将Layer对象的关键信息返回给 SystemServer进程,并用于构造native层的SurfaceControl对象;

接着,当App进程的Java层的SurfaceControl对象更新了native层的SurfaceControl对象之后,便会创建Java层的BLASTBufferQueue对象,同样地,Java层的BLASTBufferQueue对象会触发native层的BLASTBufferQueue对象的创建,native层BLASTBufferQueue对象的创建会做如下工作:

  • 创建BufferQueueCore并通过BufferQueueCore创建IGraphicBufferProducerBBQBufferQueueProducer)以及BufferQueueConsumer,并被BLASTBufferQueue对象持有;
  • 调用BufferQueueCore#update方法将BufferQueueCore持有的SurfaceControl变量指向之前创建的新SurfaceControl对象;

最后,当SurfaceControlBLASTBufferQueue都创建完成之后,通过BLASTBufferQueue对象创建一个native层的Surface对象(持有了BLASTBufferQueueIGraphicBufferProducer),并将其赋值给App进程的ViewRootImpl#mSurface持有的句柄值,最终实现通过Surface访问BBQBufferQueueProducer,然后通过BBQBufferQueueProducer访问BufferQueueCore,最终对Buffer进行访问使用。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2228318.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费送源码:Java+ssm+Springboot Springboot小型仪器公司生产管理系统 计算机毕业设计原创定制

摘 要 本论文主要论述了如何使用java语言开发一个Springboot小型仪器公司生产管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述小型仪器公司生产管理系统…

Java学习Day56:暴打舔狗!(SpringBoot)

1.springboot简介 核心能力&#xff1a;Spring容器、日志、自动配置AutoCongfiguration、Starters web应用的能力&#xff1a;MVC、嵌入式Web服务器 数据访问(持久化)&#xff1a;关系型数据库、非关系型数据库 强大的整合其他技术的能力 只要是Java中牛逼的技术&#xff0c…

nascpolarssh

安装cpolarCpolar快速入门教程&#xff1a;群晖NAS系列 - cpolar 极点云官网 设置ssh: 然后&#xff1a;ssh tobecpolard.26.tcp.cpolar.top -p 12774即可进入 跳板机&#xff1a; 设置端口转向ssh -f -N -L 23:localhost:22 tobe192.168.31.44 ssh tobecpolard.26.tcp.cpola…

相关矩阵图——Python实现

import matplotlib.pyplot as plt import numpy as np# 创建一些正常分布的数据 mean [0, 0] cov [[1, 1], [1, 2]] x, y np.random.multivariate_normal(mean, cov, 3000).T# Set up the axes with gridspec fig plt.figure(figsize(6, 6)) grid plt.GridSpec(4, 4, hspa…

学习笔记:黑马程序员JavaWeb开发教程(2024.10.30)

5.3 请求响应-请求-简单参数 客户端使用postman实现&#xff0c;服务端通过idea&#xff0c;又两种请求方式&#xff0c;springboot的更加简洁方便 对于RequestParam&#xff0c;可以通过设置requiredflase来让参数不是必须传递的 在请求参数过多的时候&#xff0c;可以将请求参…

rabbitmq高级特性(2)TTL、死信/延迟队列、事务与消息分发

目录 1.TTL 1.1.设置消息过期时间 1.2.设置队列过期时间 2.死信队列 2.1.介绍 2.2.演示 3.延迟队列 3.1.模拟实现延迟队列 3.2.延迟队列插件 4.事务与消息分发 4.1.事务 4.2.消息分发 1.TTL 所谓的ttl&#xff0c;就是过期时间。对于rabbitmq&#xff0c;可以设置…

pip使用

pip全称pip install package,是python第三方包sitepackage管理的工具&#xff0c;安装&#xff0c;卸载第三方包。安装python时可以选择安装pip&#xff0c;或自己安装pip 查看pip是否安装&#xff1a;pip --version 安装pip &#xff1a;pip python -m pip install --upgrade…

早期进程间的通信

目录 IO进程&#xff08;day06&#xff09; 无名管道 有名管道 信号 IO进程&#xff08;day06&#xff09; 无名管道 原理图 无名管道的特点 只能用于有亲缘关系之间的进程无名管道可以看成是一种特殊的文件&#xff0c;对于它的读写可以使用文件IO如read、write函数.无名管道是…

Linux系统块存储子系统分析记录

1 Linux存储栈 通过网址Linux Storage Stack Diagram - Thomas-Krenn-Wiki-en&#xff0c;可以获取多个linux内核版本下的存储栈概略图&#xff0c;下面是kernel-4.0的存储栈概略图&#xff1a; 2 存储接口、传输速度 和 协议 2.1 硬盘 《深入浅出SSD&#xff1a;固态存储核心…

Python爬虫入门篇!

毕设是做爬虫相关的&#xff0c;本来想的是用java写&#xff0c;也写了几个爬虫&#xff0c;其中一个是爬网易云音乐的用户信息&#xff0c;爬了大概100多万&#xff0c;效果不是太满意。之前听说Python这方面比较强&#xff0c;就想用Python试试&#xff0c;之前也没用过Pytho…

从0开始搭建一个生产级SpringBoot2.0.X项目(三)SpringBoot接口统一返回和全局异常处理

前言 最近有个想法想整理一个内容比较完整springboot项目初始化Demo。 SpringBoot接口统一返回和全局异常处理&#xff0c;使用ControllerAdvice ExceptionHandler 的组合来实现。 一、pom文件新增依赖 <dependency><groupId>com.alibaba</groupId><ar…

【MySQL】实战篇—项目需求分析:ER图的绘制与关系模型设计

在软件开发中&#xff0c;数据库是信息系统的核心部分&#xff0c;合理的数据库设计能够显著提高系统的性能和可维护性。 ER图&#xff08;实体-关系图&#xff09;是数据库设计的重要工具&#xff0c;它通过图形化的方式描述了数据实体及其相互关系&#xff0c;帮助开发者和设…

输入整数n,求,i从1到n的和

// 第一题&#xff0c;输入整数n&#xff0c;求&#xff0c;i从1到n的和 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() {int n 0;printf("请输入一个整数n:");scanf("%d", &n);int i 0;int j 0;for (j 1; j < n; j){i…

频率限制:WAF保护网站免受恶意攻击的关键功能

频率限制&#xff08;Rate Limiting&#xff09;是一项有效的安全措施&#xff0c;用于控制每个 IP 地址的访问速率&#xff0c;以防止恶意用户利用大量请求对网站进行攻击&#xff0c;例如防止 CC 攻击等。频率限制不仅能保护网站资源&#xff0c;还能提升服务的稳定性。 下面…

C++基础: string(3)

文章目录 1. 两道题目1. [387. 字符串中的第一个唯一字符 - 力扣&#xff08;LeetCode&#xff09;](https://leetcode.cn/problems/first-unique-character-in-a-string/description/)2.[415. 字符串相加 - 力扣&#xff08;LeetCode&#xff09;](https://leetcode.cn/proble…

建设NFS服务器并实现文件共享

关闭防火墙和s0 systemctl stop firewalld setenforce 0 安装NFS yum install nfs-utils -y 新建共享目录并设置权限 echo "hello" > /nfs/shared/test1 chmod -Rf 777 /nfs/shared/ 配置服务端的NFS配置文件 vim /etc/exports /nfs/shared *(ro) 启动…

【Java】方法的使用 —— 语法要求、方法的重载和签名、方法递归

目录 1. 方法基础知识 1.1 方法的概念 1.2 语法格式 * 注意事项【与C不同】 1.3 return —— 返回值的严格检查【比C语言严格】 2. 形参与实参的关系 3. 方法重载 3.1 什么是方法重载&#xff1f;为什么要方法重载&#xff1f; 3.2 方法重载的规则 4. 方法签名 5. 递…

Chrome和Firefox如何保护用户的浏览数据

在当今数字化时代&#xff0c;保护用户的浏览数据变得尤为重要。浏览器作为我们日常上网的主要工具&#xff0c;其安全性直接关系到个人信息的保密性。本文将详细介绍Chrome和Firefox这两款主流浏览器如何通过一系列功能来保护用户的浏览数据。&#xff08;本文由https://chrom…

20241030在荣品PRO-RK3566开发板的适配Rockchip原厂的buildroot的时候配置DTS中的电源域

20241030在荣品PRO-RK3566开发板的适配Rockchip原厂的buildroot的时候配置DTS中的电源域 2024/10/30 17:38 请问 RK3566开发板上的 电源配置 和 DTS文件是如何对应的&#xff1f; 底板原理图 PRO-RK3566-B-20210329原理图.pdf vccio4-supply 是1.8V。 对不上呀&#xff1f; Z:…

Java 内部类(13/30)

目录 Java 内部类 1. 内部类的概念和类型 1.1 成员内部类 1.2 局部内部类 1.3 匿名内部类 1.4 静态内部类 2. 内部类的用途和优势 3. 内部类的注意事项 总结与后续 Java 内部类 内部类&#xff08;Inner Class&#xff09;是定义在另一个类内部的类。在 Java 中&…