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%!湖南建筑业一季度实现较快增长|环球新视野
- []润禾材料:公司是原材料供应商,终端产品生产客户会根据自身需求,采购或定制其相关原材料
- []全球看热讯:辽宁省:加强海外知识产权保护合作 为“走出去”企业保驾护航
- []广东第一季度地区生产总值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福耀玻璃工业集团股份有限公司校园招聘(郑州有岗)_环球快看
- 阿德巴约:乐福首发帮助了球队 他不需得很多分但能做正确的事情-今热点
- 杭州临平区全域放宽限购:外地户籍只需1个月社保可购房
- 观天下!远洋集团完成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年年报 上海世茂建设及有关责任人被通报批评
- 成都印发政策 “真金白银”支持外贸转型发展 热议