View繪制流程2-安卓如何執行measure/draw 全球最新
前言:
vsync信號其實主要有兩種,APP和SF兩種。
(資料圖)
兩種類似的信號分別具有以下的作用:
1.APP類型,我們常說的Vsync信號其實指的就是這種類型。
2.SF類型,我感覺有可能是SurfaceFlinger的縮寫,這個同步信號主要是通知進行數據buffer的合成。
本文主要探討的主APP類型的Vsync信號,SF類型的會在另外一篇文章,View繪制流程中講解。
一.整體流程簡介
我把app-VSync的整個流程分為四大塊:
第一塊,APP側發出請求信號,通知到SurfaceFlinger;
第二塊,SurfaceFlinger收到通知后,作為消費者側去緩存池中查詢是否存在VSYNC,如果有,則通知APP側。
第三塊,SurfaceFlinger中的生產者邏輯,生產下一次的Vsync信號。
第四塊,APP側收到Vsync信號后進行處理,最終完成繪制流程。
整體的流程圖如下圖所示,后續文章也會按照這四大塊流程去細講。
二.客戶端發出信號
2.1 java端流轉
我們把代碼的開始點設置為ViewRootImpl中的scheduleTraversals方法,如果想了解這個方法之前的流程,可以參看我的另外一篇文章:
View繪制流程2-安卓是如何執行measure/layout/draw三個繪制流程_失落夏天的博客-CSDN博客
scheduleTraversals是負責渲染流程的,界面上任何布局上的改動,最終都會執行這個方法進行刷新操作。scheduleTraversals會通過Choreographer的postCallback方法去請求Vsync信號并且設置回調方法。
mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
Choreographer中的最終實現是postCallbackDelayedInternal方法:
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { ... synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { scheduleFrameLocked(now); } ... } }
這個方法會把回調加入到mCallbackQueues中,然后通過scheduleFrameLocked方法開始請求Vsync信號。
scheduleFrameLocked中又會進行層層傳遞,最終調用到native方法。傳遞關系如下:
scheduleFrameLocked(Choreographer.java)->scheduleVsyncLocked(Choreographer.java)->scheduleVsync(DisplayEventReceiver.java)->nativeScheduleVsync(DisplayEventReceiver.java)
2.2 native方法中的邏輯流轉
nativeScheduleVsync在native層的注冊是DisplayEventReceiver.cpp中的nativeScheduleVsync方法,方法中通過NativeDisplayEventReceiver的scheduleVsync來完成請求:
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) { spreceiver = reinterpret_cast(receiverPtr); status_t status = receiver->scheduleVsync(); ...}
NativeDisplayEventReceiver是DisplayEventDispatcher的子類,scheduleVsync方法會執行到DisplayEventDispatcher中的scheduleVsync方法,調用DisplayEventReceiver的requestNextVsync繼續請求流程。
status_t DisplayEventDispatcher::scheduleVsync() { if (!mWaitingForVsync) { ... status_t status = mReceiver.requestNextVsync(); ... mWaitingForVsync = true; ... } return OK;}
這里有一個本地變量mWaitingForVsync,如果請求了一次sync,就會改為true,接下來的Vsync請求,都不會傳輸到SurfaceFlinger一層了,避免重復無意義請求。只有等到收到Vsync信號的時候,才會改為false。
DisplayEventReceiver中,會交給mEventConnection處理:
status_t DisplayEventReceiver::requestNextVsync() { if (mEventConnection != nullptr) { mEventConnection->requestNextVsync(); return NO_ERROR; } return NO_INIT;}
mEventConnection其實是一個binder客戶端,是在創建DisplayEventReceiver的時候通過binder方法創建的,其binder服務端實現在SurfaceFlinger進程側的EventThread.cpp。
DisplayEventReceiver::DisplayEventReceiver( ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::EventRegistrationFlags eventRegistration) { spsf(ComposerService::getComposerService()); if (sf != nullptr) { mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration); ... }}
三.SurfaceFlinger端Vsync信號消費者
首先,SurfaceFlinger的總體結構圖如下,SurfaceFlinger中存在一個Scheduler,Scheduler中存在多個VsyncDispatch。其中VSYNC-app負責所有APP的VSYNC信號的處理,本文主要講的也是這個流程中的。
其次,下圖是主流程圖中的一部分,也是本章要講的內容。
3.1 EventThread收到通知后,轉發EventThread的工作線程
是接收方法如下,轉發到mEventThread的requestNextVsync方法中。
binder::Status EventThreadConnection::requestNextVsync() { ATRACE_CALL(); mEventThread->requestNextVsync(this); return binder::Status::ok();}
我們接著看一下EventThread中的requestNextVsync方法:
void EventThread::requestNextVsync(const sp& connection) { ... if (connection->vsyncRequest == VSyncRequest::None) { connection->vsyncRequest = VSyncRequest::Single; mCondition.notify_all(); } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) { connection->vsyncRequest = VSyncRequest::Single; }}
這里的邏輯很簡單,如果當前的VSYNC狀態是None的話,釋放鎖mCondition。
這里既然notify_all,那么一定有地方wait等待鎖,而等待的地方就是threadMain方法。
3.2 threadMain方法
這個方法算是整個Vsync流程的核心,它是一個無限循環的模式,其作用是不斷的從mPendingEvents中獲取Vsync信號,然后轉交給APP端。并且在一次Vsync流程結束后,又通過VsyncSource請求下一次的Vsync信號。
threadMain方法是EventThread類初始化的時候創建的線程去執行的方法:
mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS { std::unique_locklock(mMutex); threadMain(lock); });
我們看一下精簡后的threadMain方法:
void EventThread::threadMain(std::unique_lock& lock) { DisplayEventConsumers consumers; //只要沒有退出,就一直循環 while (mState != State::Quit) { 執行以下1-7的操作 }}
總體來說,分為以下四大步驟:
1.作為消費者嘗試從mPendingEvents中獲取Vsync信號,如果獲取成功,則賦值給event。
std::optionalevent; //查看mPendingEvents中是否存在Vsync信號 if (!mPendingEvents.empty()) { event = mPendingEvents.front(); mPendingEvents.pop_front(); ... }
2.計算vsyncRequested的狀態,只要客戶端消費者的Connection保持連接,則vsyncRequested=true,并且上面步驟一獲取到event的話,則把消費者的connection加入到consumers集合中。
bool vsyncRequested = false; //獲取當前的狀態,并且判斷是否有客戶端的消費者在請求,如果有則加入到consumers集合中 auto it = mDisplayEventConnections.begin(); while (it != mDisplayEventConnections.end()) { if (const auto connection = it->promote()) { //客戶端還是處于請求的狀態 vsyncRequested |= connection->vsyncRequest != VSyncRequest::None; if (event && shouldConsumeEvent(*event, connection)) { consumers.push_back(connection); } ++it; } else { it = mDisplayEventConnections.erase(it); } }
3.如果consumers集合不為空,則進行消費。把Vsync信號分發給消費者。(具體步驟我們下一小節中講)
//如果消費者不為空,則通過dispatchEvent方法最終通知到APP一側 if (!consumers.empty()) { dispatchEvent(*event, consumers); consumers.clear(); }
4.獲取下一個狀態
//mVSyncState不會為空,則主要是根據vsyncRequested來判斷的。vsyncRequested上面計算的 State nextState; if (mVSyncState && vsyncRequested) { nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; } else { ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected"); nextState = State::Idle; }
5.進行判斷,確定是否需要請求VSYNC信號。這一塊挺有意思的,簡單理解如下:
如果當前是VSYNC狀態,下一個狀態也是VSYNC狀態,那么說明信號還沒來,所以沒必要重復發送。
如果當前是Idle狀態,下一個狀態是VSYNC狀態,那么則要進行VSYNC信號請求。
如果當前是VSYNC狀態,下一個狀態也是Idle狀態,那么說明信號已經來了,下一次的客戶端請求還沒來,所以不要進行VSYNC信號請求,則會進行取消操作。
if (mState != nextState) { if (mState == State::VSync) { mVSyncSource->setVSyncEnabled(false); } else if (nextState == State::VSync) { //如果下一個狀態還是VSync,則繼續去請求VSYNC信號 mVSyncSource->setVSyncEnabled(true); } mState = nextState; }
如何開始和結束去進行VSYNC信號獲取的獲取操作,我們第四章中講,這個主要就是消費者邏輯了。
6.如果event為空,說明mPendingEvents中已經取光了,則進入休眠操作。
反之event不為空,說明mPendingEvents中也許還存在未消費的VSYNC信號,則contine繼續消費。
//如果處理了event,那么說明此次已經拿到了Vsync信號,說明后面有可能還有,則繼續拿 if (event) { continue; }
7.進入休眠或者超時之后主動模擬信號加入到mPendingEvents中。
//說明Vsync信號已經消費完了,則進入休眠模式,等到APP側的下一次通知進行喚醒 // Wait for event or client registration/request. if (mState == State::Idle) { mCondition.wait(lock); } else { // Generate a fake VSYNC after a long timeout in case the driver stalls. When the // display is off, keep feeding clients at 60 Hz. const std::chrono::nanoseconds timeout = mState == State::SyntheticVSync ? 16ms : 1000ms; if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) { if (mState == State::VSync) { ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName); std::string debugInfo = "VsyncSource debug info:\n"; mVSyncSource->dump(debugInfo); // Log the debug info line-by-line to avoid logcat overflow auto pos = debugInfo.find("\n"); while (pos != std::string::npos) { ALOGW("%s", debugInfo.substr(0, pos).c_str()); debugInfo = debugInfo.substr(pos + 1); pos = debugInfo.find("\n"); } } LOG_FATAL_IF(!mVSyncState); const auto now = systemTime(SYSTEM_TIME_MONOTONIC); const auto deadlineTimestamp = now + timeout.count(); const auto expectedVSyncTime = deadlineTimestamp + timeout.count(); mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now, ++mVSyncState->count, expectedVSyncTime, deadlineTimestamp)); } }
3.3 dispatchEvent方法把Vsync信號分發給消費者
首先遍歷消費者,調用postEvent進行通知
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { for (const auto& consumer : consumers) { ... switch (consumer->postEvent(copy)) { } }}
然后postEvent方法中,調用sendEvent進行信號的發送
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) { ... auto size = DisplayEventReceiver::sendEvents(&mChannel, mPendingEvents.data(), mPendingEvents.size()); ...}
最終通過Socket的方法進行信號的發送,接受者就是APP側了。
ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count){ return gui::BitTube::sendObjects(dataChannel, events, count);}
四.SurfaceFlinger端生產者
上面講到通過setVSyncEnabled方法去開始或者結束獲取Vsync信號的操作。
4.1 獲取VSYNC信號
setVSyncEnabled方法如下:
void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { mCallbackRepeater->start(mWorkDuration, mReadyDuration); } else { mCallbackRepeater->stop(); } mEnabled = enable;}
對應的其實就是mCallbackRepeater的start和stop方法,其實現類是DispSyncSource.cpp中的CallbackRepeater。
我們這里看到一個成員變量mWorkDuration,這個值其實就是控制Vscyn觸發時間的。這個我們后續小節再講,這里只是知道有這個值就好了。
4.2 把時間設置給Timer定時觸發
start方法中,記錄一下傳入的workDuration時間,然后傳遞給mRegistration處理。
void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { std::lock_guard lock(mMutex); mStarted = true; mWorkDuration = workDuration; mReadyDuration = readyDuration; auto const scheduleResult = mRegistration.schedule({.workDuration = mWorkDuration.count(), .readyDuration = mReadyDuration.count(), .earliestVsync = mLastCallTime.count()}); }
mRegistration的實現類是VSyncCallbackRegistration,其中schedule方法也是交給VSyncDispatchTimerQueue來處理:
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mValidToken) { return std::nullopt; } return mDispatch.get().schedule(mToken, scheduleTiming);}
schedule方法中,進行一系列的合法判斷,最終會交給 rearmTimerSkippingUpdateFor方法處理。
然后我們就可以看到rearmTimerSkippingUpdateFor中去調用setTimer方法去設置定時觸發。
rearmTimerSkippingUpdateFor方法略,
setTimer方法如下:
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) { mIntendedWakeupTime = targetTime; mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this), mIntendedWakeupTime); mLastTimerSchedule = mTimeKeeper->now();}
則到了targetTime之后,就會執行timerCallBack方法。
4.3 生成Vsync信號加入mPendingEvents
timerCallback方法如下:
void VSyncDispatchTimerQueue::timerCallback() { struct Invocation { std::shared_ptrcallback; nsecs_t vsyncTimestamp; nsecs_t wakeupTimestamp; nsecs_t deadlineTimestamp; }; std::vectorinvocations; { std::lock_guard lock(mMutex); auto const now = mTimeKeeper->now(); mLastTimerCallback = now; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; auto const wakeupTime = callback->wakeupTime(); if (!wakeupTime) { continue; } auto const readyTime = callback->readyTime(); auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast(0)); if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) { callback->executing(); invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime, *readyTime}); } } mIntendedWakeupTime = kInvalidTime; rearmTimer(mTimeKeeper->now()); } for (auto const& invocation : invocations) { invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp, invocation.deadlineTimestamp); }
這里的核心邏輯其實就是遍歷mCallbacks,然后分別回調。
那么mCallbacks是怎么添加的呢? CallbackRepeater創建的時候,回去注冊 mRegistration,同時會傳入CallbackRepeater::callback方法作為回調,所以mCallbacks其實就是CallbackRepeater::callback。
void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { ... mCallback(vsyncTime, wakeupTime, readyTime); ... }
很明顯直接交給mCallback處理,所以我們又得看一下這個mCallback從何而來。
這個mCallback是CallbackRepeater創建時傳入的DispSyncSource::onVsyncCallback方法:
mCallbackRepeater = std::make_unique(vSyncDispatch, std::bind(&DispSyncSource::onVsyncCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), name, workDuration, readyDuration, std::chrono::steady_clock::now().time_since_epoch());
所以,最終會調用到DispSyncSource::onVsyncCallback方法:
void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { VSyncSource::Callback* callback; { std::lock_guard lock(mCallbackMutex); callback = mCallback; } ... if (callback != nullptr) { callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime}); }}
又是回調,這里很繞。這里的callback其實就是EventThread,仍然是在創建EventThread的時候設置的:
EventThread::EventThread(std::unique_ptrvsyncSource, android::frametimeline::TokenManager* tokenManager, InterceptVSyncsCallback interceptVSyncsCallback, ThrottleVsyncCallback throttleVsyncCallback, GetVsyncPeriodFunction getVsyncPeriodFunction) : mVSyncSource(std::move(vsyncSource)), mTokenManager(tokenManager), mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)), mThrottleVsyncCallback(std::move(throttleVsyncCallback)), mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)), mThreadName(mVSyncSource->getName()) { LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr, "getVsyncPeriodFunction must not be null"); mVSyncSource->setCallback(this); ...}
所以,終于可以看到盡頭了。最終其實就是調用到EventThread的onVSyncEvent方法:
void EventThread::onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) { std::lock_guardlock(mMutex); LOG_FATAL_IF(!mVSyncState); mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count, vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp)); mCondition.notify_all();}
這里我們看到,會生成一個VSync信號,加入到mPendingEvents集合中,并且發出通知,讓threadMain去獲取,從而完成了VSync信號的生產者流程。
畫了如下的surfaceFlinger結構圖,方便理解(非完整版):
五.APP層收到信號進行刷新
本章講的主要流程如下圖紅圈所示:
5.1 APP端接受流程
3.3小節中講到,SurfaceFlinger會通過BitTube的方式傳遞給APP側Vsync信號。發送vscyn信號的方法在DisplayEventReceiver.cpp中,而接收方法也在這個類當中。而具體調用方則是DisplayEventDispatcher.cpp中的dispatchVsync方法。流程如下圖所示:
5.2 dispathVsync分發流程
上面在handleEvent中,processPendingEvents獲取到了Vsync信號VsyncEventData后,交給dispatchVsync方法負責處理。
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
dispatchVsync方法的實現者是android_view_DisplayEventReceiver.cpp,如下:
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) { JNIEnv* env = AndroidRuntime::getJNIEnv(); ... jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData); env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId.value, count, javaVsyncEventData); ...}
我們可以看到,先是把VsyncEventData轉換為java可以接受的jobject對象,然后通過CallVoidMethod方法通知到java層中DisplayEventReceiver.java中的dispatchVsync方法。
5.3 java中流程流轉
首先DisplayEventReceiver中dispatchVsync方法被調用:
// Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData vsyncEventData) { onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData); }
該方法中直接調用onVsync方法,調用到Choreographer.java中FrameDisplayEventReceiver下的onVsync方法:
@Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData vsyncEventData) { try { ... if (timestampNanos > now) { timestampNanos = now; } if (mHavePendingVsync) { } else { mHavePendingVsync = true; } mTimestampNanos = timestampNanos; mFrame = frame; mLastVsyncEventData = vsyncEventData; Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
onVsync方法中,主要判斷時間。如果timestampNanos>now,則是用當前時間。所以還是以方法的調用時間為準。然后通過handle轉發到主線程中執行。
Message.obj=this,本身FrameDisplayEventReceiver又實現了Runnable接口,所以自然會執行FrameDisplayEventReceiver下的run方法:
@Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame, mLastVsyncEventData); }
這時候我們就看到,執行到了doFrame方法,而這個方法也是就是渲染流程的執行者。
5.4 如何觸發handleEvent流程?
講到這里,你或許有個疑問,上面流程中,如何執行到5.1中的handleEvent方法的呢?
主要是下圖所示的流程:
如果你斷點調試的時候,會發現下面所示的這個方法,竟然是主線程調用的:
DisplayEventReceiver.java // Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, long frameTimelineVsyncId, long frameDeadline, long frameInterval) { onVsync(timestampNanos, physicalDisplayId, frame, new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval)); }
為什么用竟然呢?因為一般來看,應該是子線程等待接受SurfaceFlinger的信號,收到了信號后交給主線程處理,如果是主線程去等待,豈不是主線程阻塞了?
這里使用looper.addFd()方法,在該方法中,用到了一個epoll_ctl的機制,即對FD文件進行監聽,當FD改變時觸發主線程的回調。如果處理完回調任務,則會進入epoll_wait的阻塞,繼續監聽。
六.擴展問題
問:高頻次請求vsync信號,會突破60FPS的限制嗎?
答:不會。
首先ViewRootImpl中做了一層處理,哪怕16ms改變了很多View的布局,最終執行到了scheduleTraversals方法時,因為有如下的判斷,所以都只會執行一次vsync信號的請求和注冊一次回調,直至收到VSYNC信號。
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } }
取消限制:
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false;}
其次,2.2小節中講到,native請求VSYNC信號時也有一次限制,等到VSYNC信號時是不會再次發送請求的。
status_t DisplayEventDispatcher::scheduleVsync() { if (!mWaitingForVsync) { ... status_t status = mReceiver.requestNextVsync(); ... mWaitingForVsync = true; ... } return OK;}
七.參考資料
https://www.jianshu.com/p/6083c590521b
https://www.jianshu.com/p/386bbb5fa29a //努比亞技術團隊文章
八.聲明
1.本文是原創,根據網上資料和閱讀ASOP的源碼之后得出的結論,如有問題,歡迎指出。
2.圖中涉及到的流程圖如果想要高清大圖或者pos格式的原圖,可以私信我。
標簽:
相關推薦:
精彩放送:
- []互惠保險什么意思
- []恒輝安防:請您參考公司于2023年4月21日年報披露的股東人數信息
- []貨車買保險去哪家
- []公積金比例怎么算
- []聯動云有保險嗎? 全球熱頭條
- []總決賽再演!浙江東陽光擊敗廣東男籃,與遼寧隊會師半決賽
- []日本文字翻譯器 日本文字翻譯_報道
- []天天快資訊:季半夏傅斯年免費閱讀
- []exands secure wifi_exands 天天看熱訊
- []今天要上班 這都可以?|速讀
- []全球快訊:驗孕棒晚上測準嗎月經不調_驗孕棒晚上測準嗎
- []焦點快播:網傳3人在四川什邡天鵝林場服毒自殺,警方:確實發現3具尸體
- []香港明年起將4月23日定為“香港全民閱讀日”
- []萊曼談拜仁戰績不佳:球員無需負主要責任,因為他們就在執行戰術 今頭條
- []100名地鐵志愿者圓滿完成“青馬”服務保障|每日精選
- []中建、湖北文旅建發聯合拿下北京共有產權房項目用地 世界最資訊
- []環球即時:奧克斯空調說明書在哪_奧克斯空調說明書
- []證監會核發首批企業債券注冊批文 涉34個企業募集資金542億
- []世界快看:全國同業最多!成都住房公積金實現5類核心業務場景數字人民幣應用全覆蓋
- []同比增長8.4%!湖南建筑業一季度實現較快增長|環球新視野
- []潤禾材料:公司是原材料供應商,終端產品生產客戶會根據自身需求,采購或定制其相關原材料
- []全球看熱訊:遼寧?。杭訌姾M庵R產權保護合作 為“走出去”企業保駕護航
- []廣東第一季度地區生產總值30178.23億 同比增長4.0%
- []世界觀點:打擊仿冒“得物包裝”案入選上海知識產權保護十大典型案例
- []中上協房地產行業委員會成立大會在杭州召開
- []住進“ICU”的神龍汽車 全面電動化轉型能否“自救”成功_熱消息
- []世界看熱訊:深特發擬發行4.7億元超短期融資券 期限90天
- []焦點熱訊:讀書之美丨重溫文學經典 影視主創把《平凡的世界》讀給你聽
- []晶科能源2023年第一季度預計凈利16億-17億同比增長299%-324% 全球市場需求旺盛_當前觀察
- []2008年以來最高!美國主權CDS飆升,市場預期違約在即? 熱消息
- []【天天報資訊】北京昌平科技園2022年度第二期6億中期票據將于5月5日付息
- []中國上城發現4個主要內部控制問題 稱已采取充分補救措施
- []天天微速訊:中辰股份:截至2023年4月20日,公司持股戶數為21328戶
- []國際實業:截至2023年4月20日,公司的股東總戶數為39,474戶
- []山西玖和恒創機電設備有限公司-快消息
- []下周(4.24-4.30)全球市場大事提醒_速看
- []中創新航江門項目產品正式下線!6月將發布新型儲能電池
- []【報資訊】唐山哪家做包皮手術好?【唐山唐誠醫院專業做包皮手術醫院】
- []信達地產:公司2022年末對外提供擔保60.61億元 降幅為4.95%-精選
- []天天熱門:安道麥A:公司在巴西的銷售業務主要以巴西雷亞爾進行結算
- []每日熱議!中際旭創:具體要看客戶的網絡架構,200G/800G光模塊可以與之配套
- []廣東宏大: 截止至2023年4月20日,公司在冊股東總戶數為30,460戶
- []熱點追蹤 | 用發展空間看公司未來|當前觀點
- []環球新消息丨青島市市南區強招引促落地 打好招商引資“組合拳”
- []蘇能股份:子公司擬投15億元建徐州泉山經開區全域光伏項目 全球熱聞
- []最新快訊!糟糕的家庭,總是喜歡在小事上消耗孩子,慢慢毀掉孩子還不自知
- []林洋能源與國家電投集團湖北電力有限公司、新源智儲能源發展(北京)有限公司簽訂戰略合作協議
- []N型電池片技術百花齊放 TOPCon產能率先放量
- []世界快消息!低碳裝機達26.07%!華電國際發布2022年ESG報告
- []要聞:浙江縉云抽蓄電站500千伏出線洞斜井段貫通
- []新型電力市場機制探索與發展趨勢
- []分布式光伏“南遷”_環球新資訊
- []全球關注:湖南寧鄉2022年儲能材料產值約440億 已集聚40多家鋰電企業
- []世界簡訊:中國電力沂水獨立儲能電站項目成功并網
- []新湖中寶注冊發行20億元中期票據 用于改善公司債務融資結構-世界頭條
- []聯創股份:截止3月底公司股東總數104,516戶
- []甘化科工股東戶數下降1.11%,戶均持股17.37萬元|天天微資訊
- []北京住總12.2億元ABN將付息 A類利率4.80%、B類利率5% 環球關注
- []僑源股份:截至2023年4月20日公司的股東總數是5635|天天通訊
- []青島西海岸一宗商住用地將于下月法拍 起拍價1.61億元
- []當前頭條:綠城38.58億元摘得西安高新區2宗居住地 為陳林村城改項目用地
- []淮南建發集團30億元私募債券項目更新至“已反饋”
- []高鐵檢修保安全 快看
- []精測電子:截至2023年4月10日,公司股東總戶數16,226戶 全球微資訊
- []今日熱門!歐洲儲能系統商業模式的演變
- []動力電池+儲能_天天最資訊
- []1.03~1.6元/Wh!11企業入圍中核1.2GW/3.2GWh儲能系統集采
- []攜程商旅國際打車服務上線;美團民宿發票服務升級 | 一周商旅動態 每日精選
- []夏邑縣汽車站時刻表_夏邑縣火車站
- []大眾子公司 PowerCo 正在加拿大建設 90 GWh 電池工廠
- []今頭條!3月儲能全動態!超100GWh項目完成簽約
- []公募基金一季度調倉思路全曝光:狂買三六零、海康威視等AI概念股_全球快資訊
- []世界要聞:探索古籍奧秘 品讀家國情懷 新區舉辦古籍科普展弘揚傳統文化
- []鄭州公共住宅建設3.8億元中票注冊文件需增加信息披露 全球速看
- []特發服務:2022年實現收入20.05億元-今日熱訊
- []順網科技:公司在AI領域較強的技術儲備,可為AIGC等人工智能應用提供算力、算法、云渲染等技術服務
- []合肥濱湖建投4.5億元中期票據將付息 利率3.5%-環球熱議
- []*ST日海:公司當前未與上述公司開展業務合作,主營業務中沒有光模塊、CPO業務-全球動態
- []焦點要聞:西安曲江新區推出1宗住宅用地 凈用地面積4萬平方米
- []遠興能源:截止2023年4月20日,公司股東人數為100,488戶
- []環球新動態:合肥:肥東撮鎮鎮大郭1片區擬征地 面積超1700畝
- []鴻博股份:公司各項業務均正常開展,公司會根據市場的需求以及自身的實際情況進行合理的業務規劃-全球熱聞
- []全球快看:孕前保健
- []中國電建2023年一季度新簽5億元以上項目55個!含9個儲能項目_精選
- []環球微資訊!光伏儲能繼續火爆,何種儲能路線最香?
- []儲能市場爆發已成共識,但項目如何盈利仍是痛點 天天報道
- []【世界時快訊】立志是什么意思?立志的相關名言有哪些?
- []“梭哈魔咒”來了?知名游資殺瘋了 歷史上僅出現4次!TMT板塊大調整 機構怎么看 動態
- []金碧輝煌是什么意思?金碧輝煌出自哪里? 獨家
- []驚喜英文怎么說?驚喜英文例句有哪些?
- []儲能招標丨7.5MW/20.127MWh!江蘇蘇州儲能電站項目EPC招標_當前看點
- []【環球播資訊】冕寧縣景點有哪些?冕寧縣景點介紹?
- []天天熱點!去臺灣怎樣辦理通行證?去臺灣辦理通行證過程是怎樣的?
- []發電方式有哪些?常見的發電形式介紹?
- []【全球新視野】長沙超300米金茂梅溪大廈塔樓核心筒封頂 預計5月底驗收交付
- []今日熱聞!凸透鏡成像實驗怎么做?凸透鏡成像的實驗步驟有哪些?
- []要聞:沈葆楨的人物評價?沈葆楨資料介紹?
- []北京朝陽孫河前葦溝地塊收到40億元報價 用地規模6.15萬平方米 微速訊
- []海螺姑娘什么梗?海螺姑娘是什么故事? 世界熱聞
- []蝦蛄怎么做好吃?蝦蛄的做法介紹?
- B站注冊資本增幅400%至5億 目前由陳睿全資持股
- 光源資本出任獨家財務顧問 沐曦集成電路10億元A輪融資宣告完成
- 巨輪智能2021年上半年營收11.24億元 期內研發費用投入增長19.05%
- 紅棗期貨尾盤拉升大漲近6% 目前紅棗市場總庫存約30萬噸
- 嘉銀金科發布2021年Q2財報 期內凈利潤達1.27億元同比增長208%
- 成都銀行2021上半年凈利33.89億元 期內實現營收同比增長17.27億元
- 汽車之家發布2021年第二季度業績 期內新能源汽車品牌收入增長238%
- 中信銀行上半年實現凈利潤290.31億元 期末不良貸款余額706.82億元
- 光伏概念掀起漲停潮交易價格創新高 全天成交額達1.29億元
- 上半年生物藥大增45% 關鍵財務指標好轉營收賬款持續下降
- 因未按時披露2021年年報 福建陽光集團收到上交所紀律處分決定書
- 南京新街口一辦公樓1.2億元二次流拍 為克莉絲汀執董朱永寧所有
- 美亞柏科:目前6G通信技術尚處于研究和探索階段,暫無具體的6G標準 熱文
- 世界快看點丨同和藥業(300636.SZ):2022年度凈利增24.10%至1.01億元 擬10派0.45元
- 全球速看:金三江:拓展海外市場和拓展新領域是我司重點經營管理工作
- 全球觀察:中國金茂成立上海興秀茂商業管理公司 注冊資本5000萬元
- 世界看熱訊:桃花姬阿膠糕的功效及食用方法 阿膠糕哪個牌子最好
- 熱頭條丨三問金風科技儲能布局
- 杭州藍然譚俊邀您共同參加第8屆動力電池回收利用高峰論壇 天天快看
- 百億私募搶籌TMT 算力基礎設施賽道火熱(附潛力股)|全球時訊
- 江蘇:新增光伏市場化并網項目均需配備儲能_快資訊
- 總投資51億元!河南濮陽鈉電池產業項目開工
- 調研丨天賜材料:鋰電池材料為主要盈利點,鋰電電解液市占率全球領先 觀熱點
- 恒榮實業拿下珠海里神前舊村改造項目地塊 二期地價款為1.14億元 環球新資訊
- 世界速遞!深圳前海一方恒融對5.01億華發優生活ABN注冊文件補充信息
- 中超綜合 | 十人海港擊退深圳 亞泰讀秒絕殺大連人
- 世界熱推薦:珠海2023第一批次擬出讓5宗住宅用地 面積合計28.63公頃
- 萬達商管股權出質東莞東城萬達廣場5億元 占后者股權比例100%
- 上海青浦區一宗超7萬平商品房用地擬拍賣 起價14.09億元
- 安寧股份:截至2023年4月20日股東人數19,167_環球訊息
- 亞聯機械IPO,經營性現金流波動明顯,去年利潤增加來自匯兌收益?_當前速遞
- 北方多地最高氣溫不足10℃,部分地區同期積雪深度破紀錄
- 活動回顧視頻 | 2023 環球旅訊數智論壇(廈門站)大咖分享合集
- 廣東省科學技術廳征集新型儲能技術攻關需求-今日熱議
- 農發行烏蘭察布市分行信貸業務開門紅“回頭看”顯成效
- 關注:織女星位于哪個星座?織女星資料介紹?
- 輪胎規格參數介紹?輪胎的類型有哪些?-全球聚看點
- 防盜網有哪些種類?防盜網種類介紹?_天天熱點評
- 環球資訊:文章中的景物描寫有什么作用?文章中的景物描的作用介紹?
- 生命樹是什么?生命樹出自哪里?
- 全球熱議:沒有廢除,也不是虛設!“深圳二手房指導價謝幕”傳聞一日游
- 每日視訊:湖北能源:公司年度長協煤正在逐步簽訂和落實中,公司省內煤電長協覆蓋率現已達到接近80%
- 快看:踩腳襪是什么?踩腳襪有什么特點?
- 每日觀點:深圳光明鳳凰碧眼片區規劃出爐 擬建綜合體育中心
- 成人高考專升本好考嗎?考試科目有哪些?
- 世界速看:軍嫂有什么優待政策?軍嫂可以享受的優待政策介紹?
- 嶧城區峨山鎮金山小學:讀書讓我們飛得更遠
- 深圳龍崗5.2億掛牌4宗產業用地 其中3宗用于寶龍生物藥二期建設
- 星巴克杯子怎么買?購買星巴克杯子的具體操作步驟介紹?
- 今日熱搜:等腰直角三角形長什么樣?等腰直角三角形有哪些性質?
- 2023福耀玻璃工業集團股份有限公司校園招聘(鄭州有崗)_環球快看
- 阿德巴約:樂福首發幫助了球隊 他不需得很多分但能做正確的事情-今熱點
- 觀天下!遠洋集團完成20億元PPN產品兌付
- 江蘇:一季度社會消費品零售總額同比增7.8%至11706.8億元
- 建發房產擬使用10億中票的募資用于項目建設及并購股權 全球觀熱點
- 環球快資訊:關于二手房交易,你了解多少?
- 賽摩智能: 公司目前暫無大語言模型方面的應用 全球速看料
- 2023徐匯區零售消費券使用時間+使用范圍_世界觀速訊
- 氫能運輸分析與展望
- “應退盡退”生態加速形成 常態化退市格局日趨明朗
- 韓國豪擲1000億“押注”固態電池
- 直連Colab,支持20種編程語言:谷歌版ChatGPT代碼水平反殺了?
- 云投集團完成發行13億短期融資券 票面利率7.22%
- 天津北辰科技5億中期票據即將付息 利率7.50%
- 上海財政局回應市民:將開展家庭唯一住房出售環節增值稅問題調研
- 焦點快播:每周金選:隆基綠能歸母凈利潤147.79億元同比增加62.66%;寒武紀云端產品線業務收入大幅增長;海上風機龍頭明陽智能保持高速增長;高純晶硅龍頭通威股份歸母凈利潤最高增長231%;協鑫集成歸母凈利潤或翻倍;
- 環球即時看!國美零售:委任開元信德為新核數師 信永中和退出
- 【BT金融分析師】秦淮數據被指增長放緩,分析師稱起碼“私有化危機”消失了 世界報資訊
- 環球快消息!央行金中夏:引導金融機構加大對民營和小微外貿企業的支持力度
- 【BT金融分析師】華潤電力前景向好,分析師稱未來中國人均用電量增長空間不少
- 建研設計:公司建立有自己的數據存儲檔案庫,主要服務于建筑設計與咨詢主營業務
- 日推生化樂高moc分享--@bregmanicle的生化戰士作品”Lambda V3“
- 環球消息!高價值貨運箱不翼而飛,加拿大機場上演“黃金大劫案”
- 【天天熱聞】鄉村閱讀空間——滋養“文化秧苗”茁壯成長
- 【環球播資訊】關于召開《電化學儲能電站能量管理系統技術規范》等四項團體標準第一次工作會議的通知
- 儲能EPC環比降14.5%!大規模集采競爭白熱化
- 五一云南旅游預訂暴漲400%!潑水節帶動旅游熱,游客:潑起水來都看不到人
- 寧德時代再投40億!
- 恒運儲能公司和用電與照明公司簽署戰略合作協議-世界球精選
- 年輕人為何成旅游特種兵:努力工作并希望快樂 最新快訊
- 天天實時:臨洮縣成功簽約投資40億元的共享儲能項目
- 2023德國iF獎揭曉,海信視像再獲4項國際設計大獎
- 今日快看!2023年中牟五一限行規定
- 怡合達:截至2023年4月20日,公司股東總數10098 環球短訊
- 北京市住宅產業化集團:工廠造房不是夢
- 福州首批集中供地掛牌,10宗地總起拍價16.81億元 當前速看
- 山西:一季度全省商品房銷售面積439.8萬平方米_全球觀焦點
- 深圳:一季度住戶存款增加2148.00億元
- 當前時訊:天邦食品:截止至2023年04月20日,公司股東總戶數112,258戶
- 重倉房地產行業 廣州銀行嘗到了苦頭
- 山西:一季度全省地區生產總值為5824.33億元,同比上漲5.0%_熱文
- 晶澳科技:根據已披露的2022年年報,公司沒有商譽,不存在未減值商譽 全球最資訊
- 全球熱點評!酒店,需學會與周邊居民保持“熱戀”
- 研究機構預測到2030年英國累計部署的電池儲能項目裝機規?;驅⑦_24GW|快看點
- 今日最新!天奈科技去年凈利潤增長43%,擬30億投建正極材料項目
- 全球今頭條!國內首個氫能交易平臺啟動
- 華發股份發布新一代優+產品體系5.0
- 特斯拉吞下降價“苦果”
- 總投資360億元!愛旭股份30GW電池+30GW組件項目簽約濟南
- 海航控股:穩中求進,打好效益翻身仗
- 外交部副部長孫衛東就韓國領導人涉臺灣問題錯誤言論向韓國駐華大使提出嚴正交涉_每日觀點
- 天天百事通!遠洋控股集團:從未有過涉及接觸債權人討論成立債委會的任何情形
- 環球熱訊:天孚通信:截至2023年04月20日收盤,公司股東人數為23635(合并信用賬戶)
- 福州第一批10宗地塊定檔5月18日 起始總價16.81億元
- 焦點短訊!東吳蘇園產業REIT一季度收入約7251萬 可供分配金額約3118萬
- 【新視野】因未及時披露2021年年報 景瑞地產及負責人受到上交所通報批評
- 今日報丨思創醫惠第一大股東存變更可能 受損投資者能否挽回權益?
- 因未按時披露2021年年報 上海世茂建設及有關責任人被通報批評
- 成都印發政策 “真金白銀”支持外貿轉型發展 熱議