久久福利_99r_国产日韩在线视频_直接看av的网站_中文欧美日韩_久久一

您的位置:首頁技術文章
文章詳情頁

分析Android Choreographer源碼

瀏覽:2日期:2022-09-17 16:55:09
目錄一、前言二、主線程運行機制的本質三、Choreographer 簡介3.1、Choreographer 的工作流程四、Choreographer 源碼分析4.1、Choreographer 的單例初始化4.2、FrameDisplayEventReceiver4.3、ChoreoGrapher 的總體流程4.4、doFrame4.4.1、設置當前 frame 的啟動時間4.4.2、順序執行callBack隊列里面的callback4.4.3、調用 c.run(frameTimeNanos) 執行回調4.5、發起繪制的請求五、源碼小結一、前言

目前大部分手機都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系統為了配合屏幕的刷新頻率,將 Vsync 的周期也設置為 16.6 ms,每個 16.6 ms , Vsync 信號喚醒 Choreographer 來做 App 的繪制操作,這就是引入 Choreographer 的主要作用。了解 Choreographer 還可以幫助 App 開發者知道程序每一幀運行的基本原理,也可以加深對 Message、Handler、Looper、MessageQueue、Measure、Layout、Draw 的理解

二、主線程運行機制的本質

在介紹 Choreographer 之前,我們先理一下 Android 主線程運行的本質,其實就是 Message 的處理過程,我們的各種操作,包括每一幀的渲染,手勢操作 ,都是通過 Message 的形式發給主線程的 MessageQueue ,MessageQueue 處理完消息繼續等下一個消息,如下圖所示

MethodTrace 圖示

分析Android Choreographer源碼

Systrace 圖示

分析Android Choreographer源碼

可以發現,每一幀時間都是固定的。所以一旦一個Message 的處理時間超過了 16.6ms 就會引起卡頓。

三、Choreographer 簡介

Choreographer 扮演 Android 渲染鏈路中承上啟下的角色

1.承上:負責接收和處理 App 的各種更新消息和回調,等到 Vsync 到來的時候統一處理。比如集中處理 Input(主要是 Input 事件的處理) 、Animation(動畫相關)、Traversal(包括 measure、layout、draw 等操作) ,判斷卡頓掉幀情況,記錄 CallBack 耗時等

2.啟下:負責請求和接收 Vsync 信號。接收 Vsync 事件回調(通過 FrameDisplayEventReceiver.onVsync );請求 Vsync(FrameDisplayEventReceiver.scheduleVsync) .

從上面可以看出來, Choreographer 擔任的是一個工具人的角色,他之所以重要,是因為通過Choreographer + SurfaceFlinger + Vsync + TripleBuffer這一套從上到下的機制,保證了 Android App 可以以一個穩定的幀率運行(目前大部分是 60fps),減少幀率波動帶來的不適感。

3.1、Choreographer 的工作流程

Choreographer 初始化SurfaceFlinger 的 appEventThread 喚醒發送 Vsync ,Choreographer 回調 FrameDisplayEventReceiver.onVsync , 進入 Choreographer 的主處理函數 doFrame

初始化 FrameHandler ,綁定 Looper 初始化 FrameDisplayEventReceiver ,與 SurfaceFlinger 建立通信用于接收和請求 Vsync 初始化 CallBackQueues

Choreographer.doFrame 計算掉幀邏輯

Choreographer.doFrame 處理 Choreographer 的第一個 callback : input

Choreographer.doFrame 處理 Choreographer 的第二個 callback : animation

Choreographer.doFrame 處理 Choreographer 的第三個 callback : insets animation

Choreographer.doFrame 處理 Choreographer 的第四個 callback : traversalChoreographer.doFrame 處理 Choreographer 的第五個 callback : commit ?traversal-draw 中 UIThread 與 RenderThread 同步數據

RenderThread 處理繪制數據,真正進行渲染

將渲染好的 Buffer swap 給 SurfaceFlinger 進行合成

四、Choreographer 源碼分析4.1、Choreographer 的單例初始化

// Thread local storage for the choreographer.private static final ThreadLocal<Choreographer> sThreadInstance =new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() {Looper looper = Looper.myLooper();if (looper == null) { throw new IllegalStateException('The current thread must have a looper!');}Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);if (looper == Looper.getMainLooper()) { mMainInstance = choreographer;}return choreographer; }};

這里采用的是ThreadLocal 來構造單例,這樣每個線程都會有一個屬于自己的 choreographer 實例。

接下去看choreographer 的構造函數

private Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mHandler = new FrameHandler(looper); // 這里可以發現只有在為 true 的時候才會使用 vsync mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; mLastFrameTimeNanos = Long.MIN_VALUE; // 每一幀的間隔是根據刷新頻率來的 mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; // 給每一種回調類型都創建了一個隊列 for (int i = 0; i <= CALLBACK_LAST; i++) {mCallbackQueues[i] = new CallbackQueue(); } // b/68769804: For low FPS experiments. setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));}

這里做了幾個初始化操作,根據Looper對象生成,Looper和線程是一對一的關系,對應上面說明里的每個線程對應一個Choreographer。

初始化FrameHandler。接收處理消息。 初始化FrameDisplayEventReceiver。FrameDisplayEventReceiver用來接收垂直同步脈沖,就是VSync信號,VSync信號是一個時間脈沖,一般為60HZ,用來控制系統同步操作,怎么同ChoreoGrapher一起工作的,將在下文介紹。 初始化mLastFrameTimeNanos(標記上一個frame的渲染時間)以及mFrameIntervalNanos(幀率,fps,一般手機上為1s/60)。 初始化CallbackQueue,callback隊列,將在下一幀開始渲染時回調。

接下去看看 FrameHandler 和 FrameDisplayEventReceiver 的結構。

private final class FrameHandler extends Handler { public FrameHandler(Looper looper) {super(looper); } @Override public void handleMessage(Message msg) {switch (msg.what) { case MSG_DO_FRAME:doFrame(System.nanoTime(), 0);break; case MSG_DO_SCHEDULE_VSYNC:doScheduleVsync();break; case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);break;} }}

看上面的代碼,就是一個簡單的Handler。處理3個類型的消息。

MSG_DO_FRAME:開始渲染下一幀的操作 MSG_DO_SCHEDULE_VSYNC:請求 Vsync 信號 MSG_DO_SCHEDULE_CALLBACK:請求執行 callback

下面再細分一下,分別詳細看一下這三個步驟是怎么實現的。

4.2、FrameDisplayEventReceiver

private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {super(looper, vsyncSource); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC // for the internal display implicitly. @Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {// Post the vsync event to the Handler.// The idea is to prevent incoming vsync events from completely starving// the message queue. If there are no messages in the queue with timestamps// earlier than the frame time, then the vsync event will be processed immediately.// Otherwise, messages that predate the vsync event will be handled first.long now = System.nanoTime();if (timestampNanos > now) { Log.w(TAG, 'Frame time is ' + ((timestampNanos - now) * 0.000001f) + ' ms in the future! Check that graphics HAL is generating vsync ' + 'timestamps using the correct timebase.'); timestampNanos = now;}if (mHavePendingVsync) { Log.w(TAG, 'Already have a pending vsync event. There should only be ' + 'one at a time.');} else { mHavePendingVsync = true;}mTimestampNanos = timestampNanos;mFrame = frame;Message msg = Message.obtain(mHandler, this);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } @Override public void run() {mHavePendingVsync = false;doFrame(mTimestampNanos, mFrame); }}

FrameDisplayEventReceiver 繼承自 DisplayEventReceiver,同時也實現了Runnable 接口,是處于 Choreographer 中的私有內部類。當接收到底層的 VSync 信號開始處理 UI 過程。VSync 信號由 SurfaceFlinger 實現并定時發送。FrameDisplayEventReceiver 收到信號后,調用 onVsync 方法組織消息發送到主線程處理。這個消息主要內容就是 run 方法里面的 doFrame 了,這里 mTimestampNanos 是信號到來的時間參數。

那么 FrameDisplayEventReceiver 是通過什么方式在 Vsync 信號到來的時候回調 onVsync 呢?答案是 FrameDisplayEventReceiver 的初始化的時候,最終通過監聽文件句柄的形式,其對應的初始化流程如下

// android/view/Choreographer.javaprivate Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; ......}// android/view/Choreographer.javapublic FrameDisplayEventReceiver(Looper looper, int vsyncSource) { super(looper, vsyncSource);}// android/view/DisplayEventReceiver.javapublic DisplayEventReceiver(Looper looper, int vsyncSource) { ...... mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, vsyncSource);}

nativeInit 后續的代碼可以自己跟一下,可以對照這篇文章和源碼,由于篇幅比較多,這里就不細說了。

簡單來說,FrameDisplayEventReceiver 的初始化過程中,通過 BitTube (本質是一個 socket pair),來傳遞和請求 Vsync 事件,當 SurfaceFlinger 收到 Vsync 事件之后,通過 appEventThread 將這個事件通過 BitTube 傳給 DisplayEventDispatcher ,DisplayEventDispatcher 通過 BitTube 的接收端監聽到 Vsync 事件之后,回調 Choreographer.FrameDisplayEventReceiver.onVsync ,觸發開始一幀的繪制。

如下圖

分析Android Choreographer源碼

4.3、ChoreoGrapher 的總體流程

FrameHandler 和 FrameDisplayEventReceiver 是怎么工作的呢?ChoreoGrapher 的總體流程圖如下圖(拷貝的圖片):

分析Android Choreographer源碼

以上是總體的流程圖:

1.PostCallBack orpostFrameCallback發起添加回調,這個FrameCallBack將在下一幀被渲染時執行。

2.AddToCallBackQueue,將 FrameCallBack 添加到回調隊列里面,等待時機執行回調。每種類型的callback按照設置的執行時間(dueTime)順序排序分別保存在一個單鏈表中。

3.判斷 FrameCallBack設定的執行時間是否在當前時間之后,若是,發送 MSG_DO_SCHEDULE_CALLBACK 消息到主線程,安排執行doScheduleCallback,安排執行CallBack。否則直接跳到第4步。

4.執行 scheduleFrameLocked,安排執行下一幀。

5.判斷上一幀是否已經執行,若未執行,當前操作直接結束。若已經執行,根據情況執行以下6、7步。

6.若使用垂直同步信號進行同步,則執行7.否則,直接跳到9。

7.若當前線程是UI線程,則通過執行scheduleVsyncLocked請求垂直同步信號。否則,送MSG_DO_SCHEDULE_VSYNC消息到主線程,安排執行doScheduleVsync,在主線程調用scheduleVsyncLocked。

8.收到垂直同步信號,調用FrameDisplayEventReceiver.onVsync(),發送消息到主線程,請求執行doFrame。

9.執行doFrame,渲染下一幀。

主要的工作在 doFrame 中,接下來我們具體看看 doFrame 函數都干了些什么。從名字看很容易理解 doFrame 函數就是開始進行下一幀的顯示工作。好了以下源代碼又來了,我們一行一行分析一下吧。

4.4、doFrame

@UnsupportedAppUsagevoid doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { // 為false, 說明還未開始if (!mFrameScheduled) { return; // no work to do}if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) { mDebugPrintNextFrameTimeDelta = false; Log.d(TAG, 'Frame time delta: ' + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + ' ms');}long intendedFrameTimeNanos = frameTimeNanos;startNanos = System.nanoTime(); // 計算當前時間與 vsync 信號的時間差final long jitterNanos = startNanos - frameTimeNanos; // 說明出現掉幀情況,注意只有 jitterNanos 大于 16.6 ms 才說明掉幀,否則只是輕微的延遲。if (jitterNanos >= mFrameIntervalNanos) { final long skippedFrames = jitterNanos / mFrameIntervalNanos; if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {Log.i(TAG, 'Skipped ' + skippedFrames + ' frames! '+ 'The application may be doing too much work on its main thread.'); }// 當發生掉幀后,需要計算被耽誤的時間。比如處理了 36.6ms, 一個周期是 16.6 ms, 相當于延遲了 3.4 ms 執行 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; if (DEBUG_JANK) {Log.d(TAG, 'Missed vsync by ' + (jitterNanos * 0.000001f) + ' ms '+ 'which is more than the frame interval of '+ (mFrameIntervalNanos * 0.000001f) + ' ms! '+ 'Skipping ' + skippedFrames + ' frames and setting frame '+ 'time to ' + (lastFrameOffset * 0.000001f) + ' ms in the past.'); }  // 修正當前幀的時間 = 開始時間 - 耽誤時間 frameTimeNanos = startNanos - lastFrameOffset;} // 當前時間小于前一幀時間,不執行操作if (frameTimeNanos < mLastFrameTimeNanos) { if (DEBUG_JANK) {Log.d(TAG, 'Frame time appears to be going backwards. May be due to a '+ 'previously skipped frame. Waiting for next vsync.'); }// 直接請求下一個 vsync 信號 scheduleVsyncLocked(); return;} // 大于 1 說明采用的默認幀數的一半,因此需要根據時間間隔來判斷是否有必要執行繪制if (mFPSDivisor > 1) { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {// 時間間隔小于指定的時間,繼續請求下一個 vsync 信號scheduleVsyncLocked();return; }} // 保存當前幀的相關信息mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);mFrameScheduled = false;mLastFrameTimeNanos = frameTimeNanos; } try { // 執行相關 callbacks Trace.traceBegin(Trace.TRACE_TAG_VIEW, 'Choreographer#doFrame');AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);mFrameInfo.markInputHandlingStart();doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);mFrameInfo.markAnimationsStart();doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);mFrameInfo.markPerformTraversalsStart();doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally {AnimationUtils.unlockAnimationClock();Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (DEBUG_FRAMES) {final long endNanos = System.nanoTime();Log.d(TAG, 'Frame ' + frame + ': Finished, took '+ (endNanos - startNanos) * 0.000001f + ' ms, latency '+ (startNanos - frameTimeNanos) * 0.000001f + ' ms.'); }}

總結起來其實主要是兩個操作:

4.4.1、設置當前 frame 的啟動時間

判斷是否跳幀,若跳幀修正當前 frame 的啟動時間到最近的 VSync 信號時間。如果沒跳幀,當前 frame 啟動時間直接設置為當前 VSync 信號時間。修正完時間后,無論當前 frame 是否跳幀,使得當前 frame 的啟動時間與 VSync 信號還是在一個節奏上的,可能延后了一到幾個周期,但是都是在下一個 vsync 信號到來才進行處理 。

如下圖所示是時間修正的一個例子,

分析Android Choreographer源碼

沒有跳幀但延遲

由于第二個 frame 執行超時,第三個 frame 實際啟動時間比第三個 VSync 信號到來時間要晚,因為這時候延時比較小,沒有超過一個時鐘周期,系統還是將 frameTimeNanos3 傳給回調,回調拿到的時間和 VSync 信號同步。

再來看看下圖:

分析Android Choreographer源碼

跳幀

由于第二個 frame執行時間超過 2 個時鐘周期,導致第三個 frame 延后執行時間大于一個時鐘周期,系統認為這時候影響較大,判定為跳幀了,將第三個 frame 的時間修正為 frameTimeNanos4,比 VSync 真正到來的時間晚了一個時鐘周期。

時間修正,既保證了doFrame操作和 VSync 保持同步節奏,又保證實際啟動時間與記錄的時間點相差不會太大,便于同步及分析。

4.4.2、順序執行callBack隊列里面的callback

然后接下來看看 doCallbacks 的執行過程:

void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) {// We use 'now' to determine when callbacks become due because it’s possible// for earlier processing phases in a frame to post callbacks that should run// in a following phase, such as an input event that causes an animation to start.final long now = System.nanoTime(); // callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);if (callbacks == null) { return;}mCallbacksRunning = true;// Update the frame time if necessary when committing the frame.// We only update the frame time if we are more than 2 frames late reaching// the commit phase. This ensures that the frame time which is observed by the// callbacks will always increase from one frame to the next and never repeat.// We never want the next frame’s starting frame time to end up being less than// or equal to the previous frame’s commit frame time. Keep in mind that the// next frame has most likely already been scheduled by now so we play it// safe by ensuring the commit time is always at least one frame behind. // commit 類型是最后執行的,如果此時發現前面處理時間過長,就會進行糾正。if (callbackType == Choreographer.CALLBACK_COMMIT) { final long jitterNanos = now - frameTimeNanos; Trace.traceCounter(Trace.TRACE_TAG_VIEW, 'jitterNanos', (int) jitterNanos); if (jitterNanos >= 2 * mFrameIntervalNanos) {final long lastFrameOffset = jitterNanos % mFrameIntervalNanos+ mFrameIntervalNanos;if (DEBUG_JANK) { Log.d(TAG, 'Commit callback delayed by ' + (jitterNanos * 0.000001f) + ' ms which is more than twice the frame interval of ' + (mFrameIntervalNanos * 0.000001f) + ' ms! ' + 'Setting frame time to ' + (lastFrameOffset * 0.000001f) + ' ms in the past.'); mDebugPrintNextFrameTimeDelta = true;}frameTimeNanos = now - lastFrameOffset;mLastFrameTimeNanos = frameTimeNanos; }} } try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);for (CallbackRecord c = callbacks; c != null; c = c.next) { if (DEBUG_FRAMES) {Log.d(TAG, 'RunCallback: type=' + callbackType+ ', action=' + c.action + ', token=' + c.token+ ', latencyMillis=' + (SystemClock.uptimeMillis() - c.dueTime)); }// 運行每一個 callback c.run(frameTimeNanos);} } finally {synchronized (mLock) { mCallbacksRunning = false; do {final CallbackRecord next = callbacks.next;recycleCallbackLocked(callbacks);callbacks = next; } while (callbacks != null);}Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}

callback的類型有以下 4 種,除了文章一開始提到的 3 種外,還有一個 CALLBACK_COMMIT。

CALLBACK_INPUT:輸入 CALLBACK_ANIMATION:動畫 CALLBACK_TRAVERSAL:遍歷,執行 measure、layout、draw CALLBACK_COMMIT:遍歷完成的提交操作,用來修正動畫啟動時間

然后看上面的源碼,分析一下每個 callback 的執行過程:

1. callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS);

得到執行時間在當前時間之前的所有 CallBack,保存在單鏈表中。每種類型的 callback 按執行時間先后順序排序分別存在一個單鏈表里面。為了保證當前 callback 執行時新 post 進來的 callback 在下一個 frame 時才被執行,這個地方 extractDueCallbacksLocked 會將需要執行的 callback 和以后執行的 callback 斷開變成兩個鏈表,新 post 進來的 callback 會被放到后面一個鏈表中。當前 frame 只會執行前一個鏈表中的 callback,保證了在執行 callback 時,如果callback中Post相同類型的callback,這些新加的 callback 將在下一個 frame 啟動后才會被執行。

2. 接下來,看一大段注釋,如果類型是 CALLBACK_COMMIT,并且當前 frame 渲染時間超過了兩個時鐘周期,則將當前提交時間修正為上一個垂直同步信號時間。為了保證下一個frame 的提交時間和當前 frame 時間相差為一且不重復。這個地方注釋挺難看懂,實際上這個地方 CALLBACK_COMMIT 是為了解決 ValueAnimator 的一個問題而引入的,主要是解決因為遍歷時間過長導致動畫時間啟動過長,時間縮短,導致跳幀,這里修正動畫第一個 frame 開始時間延后來改善,這時候才表示動畫真正啟動。為什么不直接設置當前時間而是回溯一個時鐘周期之前的時間呢?看注釋,這里如果設置為當前 frame 時間,因為動畫的第一個 frame 其實已經繪制完成,第二個 frame 這時候已經開始了,設置為當前時間會導致這兩個 frame 時間一樣,導致沖突。

如下圖所示:

分析Android Choreographer源碼

修正commit時間

比如說在第二個frame開始執行時,開始渲染動畫的第一個畫面,第二個frame執行時間超過了兩個時鐘周期,Draw操作執行結束后,這時候完成了動畫第一幀的渲染,動畫實際上還沒開始,但是時間已經過了兩個時鐘周期,后面動畫實際執行時間將會縮短一個時鐘周期。這時候系統通過修正commit時間到frameTimeNanos的上一個VSync信號時間,即完成動畫第一幀渲染之前的VSync信號到來時間,修正了動畫啟動時間,保證動畫執行時間的正確性。

4.4.3、調用 c.run(frameTimeNanos) 執行回調

private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; @UnsupportedAppUsage public void run(long frameTimeNanos) {if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos);} else { ((Runnable)action).run();} }}

CallbackRecord 的代碼如上所示。

4.5、發起繪制的請求

doFrame 的邏輯了解清楚了,但是關于發起 vsync 請求的邏輯卻沒有講。

// ViewRootImpl @UnsupportedAppUsagevoid scheduleTraversals() {  // 如果已經請求繪制了,就不會再次請求,因為多次請求,只有有一個執行就滿足要求了 if (!mTraversalScheduled) {mTraversalScheduled = true;// 同步mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// 發送一個 callback 用于在下一幀來臨時候處理mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput();} // 通知稍候繪制notifyRendererOfFramePending();pokeDrawLockIfNeeded(); }}

接著會調用postCallbackDelayed:

public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) { if (action == null) {throw new IllegalArgumentException('action must not be null'); } if (callbackType < 0 || callbackType > CALLBACK_LAST) {throw new IllegalArgumentException('callbackType is invalid'); } postCallbackDelayedInternal(callbackType, action, token, delayMillis);}

主要是做一些邏輯判斷,確保傳入的是對的。

接著又會調用postCallbackDelayedInternal,保存 callback,并發起請求下一幀。

private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) {Log.d(TAG, 'PostCallback: type=' + callbackType+ ', action=' + action + ', token=' + token+ ', delayMillis=' + delayMillis); } synchronized (mLock) {final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);// 小于當前時間,說明需要立即執行if (dueTime <= now) { scheduleFrameLocked(now);} else {  // 發送一個延遲 msg Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime);} }}

簡單來說,就是判斷當前是否有必要發起一個繪制請求,比如你發了一個 500ms 后重繪的消息,對于這個消息,會在 500ms 后在進行處理。但如果不是延遲消息,那說明需要立即處理。但是對于 view 的繪制邏輯,必須得等到下一個 vsync 到來的時候才會真正進行繪制。

接下來看看scheduleFrameLocked 的邏輯:

private void scheduleFrameLocked(long now) {if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) {if (DEBUG_FRAMES) { Log.d(TAG, 'Scheduling next frame on vsync.');}// If running on the Looper thread, then schedule the vsync immediately,// otherwise post a message to schedule the vsync from the UI thread// as soon as possible. // 如果是在一個 looper 線程中,那么直接執行請求就好if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked();} else { // 如果是在主線程,那么需要發送一個請求 vsync 的消息,并插到最前面,需要確保前一個消息處理完后在開始請求 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg);} } else {// 如果不用 vsync 信號,那么就可以直接執行,只是需要記錄每一幀的時間final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) { Log.d(TAG, 'Scheduling next frame in ' + (nextFrameTime - now) + ' ms.');}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime); }} }

這里主要是根據具體情況來判斷如何發起下一幀的繪制。對于采用 vsync 信號的主線程,會發送一個MSG_DO_SCHEDULE_VSYNC 的消息,插到最前面,確保可以最早執行。

當收到 MSG_DO_SCHEDULE_VSYNC 消息后,就會給他安排請求 vsync 信號的請求,最后會會調到 onVsync 方法。

void doScheduleVsync() { synchronized (mLock) {if (mFrameScheduled) { scheduleVsyncLocked();} }}@UnsupportedAppUsageprivate void scheduleVsyncLocked() { // 發起獲取 vsync 信號的請求 mDisplayEventReceiver.scheduleVsync();} /** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */@UnsupportedAppUsagepublic void scheduleVsync() { if (mReceiverPtr == 0) {Log.w(TAG, 'Attempted to schedule a vertical sync pulse but the display event '+ 'receiver has already been disposed.'); } else {nativeScheduleVsync(mReceiverPtr); }}

到這里,就把Choreographer 的基本原理都講完了。

五、源碼小結

Choreographer是線程單例的,而且必須要和一個 Looper 綁定,因為其內部有一個 Handler 需要和 Looper 綁定,一般是 App 主線程的 Looper 綁定。

DisplayEventReceiver是一個 abstract class,其 JNI 的代碼部分會創建一個IDisplayEventConnection 的 Vsync 監聽者對象。這樣,來自 AppEventThread 的 VSYNC 中斷信號就可以傳遞給 Choreographer 對象了。當 Vsync 信號到來時,DisplayEventReceiver 的 onVsync 函數將被調用。

DisplayEventReceiver還有一個 scheduleVsync 函數。當應用需要繪制UI時,將首先申請一次 Vsync 中斷,然后再在中斷處理的 onVsync 函數去進行繪制。

Choreographer定義了一個FrameCallbackinterface,每當 Vsync 到來時,其 doFrame 函數將被調用。這個接口對 Android Animation 的實現起了很大的幫助作用。以前都是自己控制時間,現在終于有了固定的時間中斷。

Choreographer的主要功能是,當收到 Vsync 信號時,去調用使用者通過 postCallback 設置的回調函數。目前一共定義了五種類型的回調,它們分別是:ListView的 Item 初始化(obtainsetup) 會在 input 里面也會在 animation 里面,這取決于

CALLBACK_INPUT: 處理輸入事件處理有關 CALLBACK_ANIMATION: 處理 Animation 的處理有關 CALLBACK_INSETS_ANIMATION: 處理 Insets Animation 的相關回調 CALLBACK_TRAVERSAL: 處理和 UI 等控件繪制有關 CALLBACK_COMMIT: 處理 Commit 相關回調

CALLBACK_INPUT、CALLBACK_ANIMATION會修改 view 的屬性,所以要比 CALLBACK_TRAVERSAL 先執行

最后附上一張時序圖,來回顧一下整個流程

分析Android Choreographer源碼

以上就是分析Android Choreographer源碼的詳細內容,更多關于Android Choreographer的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: 黄色片网站视频 | 日韩一区二区三区在线 | 精品乱子伦一区二区三区 | 欧美日韩久久久 | 亚洲精品免费在线观看 | 日韩成人免费av | www.久久爱.cn | 欧美日韩中文在线 | 国产精品久久免费观看spa | 青青久久av北条麻妃海外网 | 国产精品99久久久久久动医院 | 99久久久精品 | 国产视频1 | 亚洲精品一区在线观看 | 欧美一区成人 | 狠狠操夜夜操天天操 | 麻豆一区二区三区 | 国产精品一区人伦免视频播放 | 蜜桃一区二区 | 久久av综合 | www.色在线 | 91精品国产91综合久久蜜臀 | xxxx性欧美 | 久色 | 碰在线视频 | 九九精品视频在线 | 男女啪啪无遮挡 | 成人免费在线视频 | 日韩大尺度电影在线观看 | 成人网av | 美女福利视频 | chengrenzaixian | 精品欧美一区二区精品久久久 | 国产女人和拘做受在线视频 | 亚洲欧美日韩在线 | 91电影在线看 | 欧美精品免费在线观看 | 韩国精品一区 | 免费成人在线观看视频 | 国产一区二区精品 | 亚洲综合国产 | 国产欧美精品区一区二区三区 | 国产综合精品一区二区三区 | 91精品国产欧美一区二区成人 | 日本免费一区二区在线观看 | 国产高清在线精品一区二区三区 | 国产一区二区三区91 | 一区二区三区在线 | 欧 | 日韩三区 | 免费一级毛片 | av网站免费在线观看 | 国产精品视频网 | 蜜臀久久99精品久久久无需会员 | 男人天堂网站 | 综合网激情 | 精品久久久久久久久久久久久久 | 特级毛片在线 | 午夜99 | 天天操狠狠操 | 国产日韩精品视频 | 午夜精品久久久久久久星辰影院 | www.久久99 | 国产一区二区三区免费在线观看 | 午夜成人免费影院 | 精品国产伦一区二区三区观看说明 | 精精国产 | 在线观看视频一区二区 | av在线视| 视频一区在线 | 欧美久热 | 中文字幕一区二区三区日韩精品 | 一区久久久| 自拍偷拍小视频 | 午夜电影网 | 在线免费色视频 | 欧美国产在线观看 | 久久99深爱久久99精品 | 国产女人高潮视频在线观看 | 中文字幕亚洲字幕一区二区 | 成人免费视频观看视频 | 免费v片在线观看 | 日韩久久午夜一级啪啪 | 久久久久久香蕉 | 男女做爰高清无遮挡免费视频 | 婷婷国产 | 免费午夜剧场 | 九九免费视频 | 久草成人| 狠狠躁夜夜躁人人爽天天天天97 | 亚洲一区 国产 | 亚洲日韩中文字幕一区 | 欧美日韩一区不卡 | a在线观看 | 欧美精品99| 国产一级片免费观看 | 天天拍天天干天天操 | 色婷婷在线视频观看 | 亚洲精品午夜国产va久久成人 | 日韩成人av在线 | 天天夜夜操 | 欧美日韩精品综合 | 精品久久久久久久久久久久 | 久久男人天堂 | 午夜av电影院 | 精品国产乱码久久久久久1区2区 | 成人免费观看视频 | 国内精品久久久久久影视8 91一区二区在线观看 | 成人亚洲免费视频 | 男女羞羞羞视频午夜视频 | 日韩精品免费 | 在线视频中文字幕 | 欧美一级淫片免费视频黄 | 在线亚州 | 日韩欧美一区在线 | 国产一级特黄aaa大片评分 | 狠狠操狠狠操 | 久久久噜噜噜www成人网 | 国产精品久久久久无码av | 午夜三区 | 黄色小视频免费观看 | 国产精品久久久久久久久久东京 | 成人影音 | 国产黄色网址在线观看 | 国产目拍亚洲精品99久久精品 | 国产成人一区 | 午夜精品视频在线观看 | 久久99精品国产自在现线 | 99草在线视频 | 成人免费毛片嘿嘿连载视频 | 一区二区在线免费观看 | 欧美日韩一区二区三区免费视频 | 亚洲午夜精品一区二区三区 | 精品美女在线 | 在线观看亚洲一区二区三区 | 亚洲精品一区二区三区中文字幕 | 国产精品中文字幕在线播放 | 综合久久99| 成人亚洲视频 | 国产毛片毛片 | 国产成人免费视频 | 欧美视频网站 | 国产一级免费在线 | 精品少妇一区二区三区在线播放 | 亚洲一区二区 | 日韩毛片免费看 | www.久久.com | 色综合一区二区三区 | 亚洲专区中文字幕 | 久久99精品视频 | 亚洲精品永久免费 | 免费黄色在线看 | 久久三区| 高清一区二区三区视频 | 毛片入口 | 亚洲不卡视频在线 | 日韩精品久久 | 自拍亚洲 | 日韩在线视频观看 | 夜夜操天天操 | 欧美一区二区三区 | 亚洲人黄色片 | 欧美一区精品 | 国产精品乱码一区二区三区 | 国产成人精品一区二区三区视频 | 一级看片 | 国产一区二区在线免费观看 | 一区二区三区国产视频 | 国产精品久久久久久久久久 | 香蕉久久久 | 亚洲免费在线视频 | 蜜桃在线视频 | 久久综合一区二区三区 | 亚洲视频在线观看 | 一区二区三区在线 | 欧 | 久章操 | 欧美成人a∨高清免费观看 在线视频成人 | 超碰人人射 | 色噜噜一区二区 | 中文字幕一区二区三区免费视频 | 久久久综合亚洲91久久98 | 成人av一区二区三区 | 偷拍做爰吃奶视频免费看 | www.伊人网| 永久91嫩草亚洲精品人人 | 欧美性猛交一区二区三区精品 | 国产成人精品一区二区三区四区 | 亚洲小视频 | 97国产资源 | 精国产品一区二区三区四季综 | 日本黄色a级 | 伊人激情影院 | 日韩欧美一级精品久久 | 精品一区二区三区国产 | www.国产.com | 久久不卡日韩美女 | 伊人狠狠干 | 日韩视频在线免费播放 | 99精品国产热久久91蜜凸 | 国产免费拔擦拔擦8x高清在线人 | 在线小视频 | 成人在线免费视频观看 | 亚洲成人精品区 | 北条麻妃一区二区三区在线 | 日韩一区二区在线观看 | 四虎影院在线 | 99久久久国产精品 | 国产成人一区 | 欧美精品亚洲精品 | 9191在线| 中文字幕日韩欧美 | 操操操av| 美女视频黄色 | 精品国产一区二区在线 | 国产99在线 | 亚洲 | 午夜伦理影院 | 色综合中文 | 欧美一区二区三区免费观看视频 | 欧美aaaaa | 免费av电影在线观看 | 在线观看中文字幕 | 国产精品国产精品国产专区不片 | 国产野精品久久久久久久不卡 | 亚洲国产精品va在线看黑人 | 久久久久一区二区三区 | 日韩成人免费 | 欧美男人天堂 | 一区二区中文 | 狠狠躁夜夜躁人人爽天天天天97 | 久久精品亚洲精品 | 国产ts人妖另类 | 亚洲精品美女在线观看 | 日韩1区| 狠狠操综合网 | 国产精品天天干 | 亚洲免费综合 | 亚洲国产精品久久久 | 最新中文字幕在线 | 精品日韩中文字幕 | 国产视频综合 | 91网站在线播放 | 丁香婷婷综合激情五月色 | 久久国产亚洲 | 色优久久| 另类综合在线 | 成人免费小视频 | 国内精品久久久久久影视8 91一区二区在线观看 | 三级视频网站 | 精品久久久久一区二区国产 | 精品亚洲一区二区三区 | 亚洲二区视频 | 精品国产乱码久久久久久久软件 | 日本精品视频在线观看 | 欧美大片在线看免费观看 | 91精品国产欧美一区二区成人 | 国产综合精品一区二区三区 | 91性高湖久久久久久久久网站 | 亚洲一区中文字幕在线观看 | 国产成人精品一区二区三区四区 | 高清一区二区三区 | 狠狠的干| 欧美精品1区2区3区 精品国产欧美一区二区 | 亚洲精品乱码久久久久久蜜桃不爽 | 久久99精品国产麻豆婷婷洗澡 | 在线a电影 | 成人午夜免费视频 | 亚洲视频在线观看网址 | 天天碰天天操 | 国产精品久久国产精品 | 国产精品久久久久久久久免费桃花 | 天天操天天干天天爽 | 日韩欧美中文字幕在线视频 | 久久久婷婷| 成人精品国产 | 日本成人一二三区 | 亚洲成人精品一区 | 欧美午夜视频在线观看 | 欧美精品一区二区三区在线 | 91精品国产91久久久久久吃药 | 亚洲欧美国产精品专区久久 | 国产69精品99久久久久久宅男 | 久久99一区二区 | 国产亚洲精品精品国产亚洲综合 | 日韩一区二区在线免费 | 91亚洲国产| 久久资源av | 亚洲视频 欧美视频 | 欧美在线综合 | 精品视频一区二区三区 | 欧美一区二区三区免费在线观看 | 色资源| 亚洲精品在线网站 | 国产成人精品免高潮在线观看 | 成人a在线视频免费观看 | 毛片黄色 | 久久久国产视频 | 国产高清一区二区三区 | 中文字幕国产 | av在线干 | 男人的天堂在线视频 | 国产高清精品一区二区三区 | 国产精品视频免费观看 | 亚洲欧美一区二区三区在线 | 国产精品久久久久久久久久 | 免费看毛片的网站 | 午夜日韩| 6080yy精品一区二区三区 | 国产一区二区三区免费看 | 91视频一区二区三区 | 国产成人免费网站 | 91精品一区二区三区久久久久久 | a网站在线观看 | 免费久久网站 | 国产精品国产精品国产专区不片 | 亚洲精品片 | 成人免费xxx在线观看 | 免费成人在线电影 | 中文字幕二区 | 天天综合天天色 | 久久亚洲一区 | 成人欧美 | 在线看免费观看日本 | 欧美日韩中文字幕在线 | 激情五月婷婷在线 | 蜜臀视频在线观看 | 午夜精品一区二区三区在线视频 | 国产精品久久久久久久久久东京 | 国产精品美女久久久久久久久久久 | 亚洲日韩中文字幕一区 | 成人激情视频在线播放 | 精品一区二区三区四区 | 黄色国产 | 久久久av| 久久亚洲综合 | 国产日韩精品在线观看 | 中文字幕在线综合 | 91精品国产综合久久福利软件 | 古装三级在线播放 | 午夜视频网站 | av黄色在线看 | 午夜妇女aaaa区片 | 久久久久久久中文 | 成人高清视频在线观看 | 蜜臀久久99精品久久久无需会员 | 欧美日韩一区在线观看 | 夜夜av| 最新中文字幕在线资源 | h片免费 | 久久99影视| 91亚洲精品视频 | 一级毛片免费播放 | 亚洲免费在线观看视频 | 色天天综合网 | 欧美精品1区2区3区 精品国产欧美一区二区 | 久久精品网 | 伊人天天操 | 国产精品女同一区二区久久夜 | 性瘾调教校园h | 日韩中文字 | 一区二区在线看 | 日韩在线免费观看网站 | 看亚洲a级一级毛片 | 成人国产在线 | 日韩一级免费在线观看 | 亚洲午夜视频 | 亚洲一区二区中文字幕在线观看 | 色婷婷综合久久 | 欧美精品免费在线观看 | 久久国产精品免费一区二区三区 | 亚洲一二视频 | 欧美日韩在线一区二区三区 | 欧美全黄 | 先锋久久| 欧美一区二区三区免费 | 国产精品久久九九 | 久久久国产精品入口麻豆 | 国产91久久精品一区二区 | 日韩成人影院 | 久久久久久久91 | 91新视频| 日本成人片网站 | 久久国产精品无码网站 | 搞黄免费视频 | 欧美大成色www永久网站婷 | 亚洲国产精品一区 | av网站在线播放 | 日韩日韩日韩日韩日韩日韩日韩 | 女人爽到高潮aaaa电影 | 欧美日韩激情一区二区三区 | 亚洲aⅴ天堂av在线电影软件 | 久久精品国产一区 | 亚洲久久在线 | 午夜寂寞影视在线观看 | 亚洲国产成人在线 | 久久高清一区 | 欧美激情在线精品一区二区三区 | 三级黄色片在线 | 久久丁香 | 91香蕉| 久久久久久亚洲精品视频 | 亚洲精品久久久久久一区二区 | 欧美国产精品一区 | 黄色综合网 | 国产成人精品一区二区 | 超碰香蕉 | 国产噜噜噜噜噜久久久久久久久 | 国产成人av一区二区 | 午夜四虎 | 久久久久9999国产精品 | 亚洲综合色视频在线观看 | 午夜资源| 欧美一级片在线观看 | 国产成人精品久久二区二区 | 国产成人精品在线 | 精品一区二区三区免费毛片爱 | 波多野结衣中文字幕在线视频 | 亚洲视频一区二区三区四区 | 看毛片网站 | 91高清免费看 | 天天艹夜夜艹 | 久久久久久久久久久久久九 | 日韩成人免费 | 午夜精品久久久久久久 | 午夜影院18 | 日本久草| 日韩高清在线 | 天天干一干 | 国产情侣一区二区三区 | 精产国产伦理一二三区 | 在线观看日韩 | 欧美在线综合视频 | 国产精品毛片一区二区在线看 | 中文字幕在线免费看 | 久久99精品久久久久久久青青日本 | 欧美一区二区三区久久精品 | 91久久久久久久久 | 国内精品一区二区 | 日韩日韩 | 欧美a在线 | 国产精品毛片无码 | 久久少妇免费看 | 国产美女久久久 | 夜夜夜久久久 | 日韩一二三区在线观看 | 亚洲精品一区二区三区在线 | 日韩精品影院 | 麻豆视频国产 | 综合导航 | 在线观看成人小视频 | 九色自拍| chinese中国真实乱对白 | 欧美黄色片免费观看 | 亚洲免费网站 | 亚洲精品一区二区三区樱花 | www.av在线| 国精日本亚洲欧州国产中文久久 | 日韩在线不卡 | 亚洲视频中文字幕 | 一区二区三区精品 | 欧美日韩一区二区电影 | 欧美精品久久久久久久久老牛影院 | 夜夜操天天干, | 99视频免费 | 亚洲伊人久久综合 | 成人一区在线观看 | 青青伊人久久 | 一区二区不卡 | 色欧美视频 | 亚洲免费成人 | 亚洲精品免费视频 | 亚洲精品久久久一区二区三区 | 息与子猛烈交尾一区二区 | 欧美日韩高清 | 日韩视频一区二区三区 | 日韩视频免费在线观看 | 九九热这里都是精品 | 国产日韩欧美在线 | 日韩中文字幕一区二区 | ririsao久久精品一区 | 99精品欧美一区二区三区综合在线 | 91视频一88av | 一级黄色大片免费观看 | 久久综合伊人 | 国产一区二区三区高清 | 亚洲精品乱码久久久久久蜜糖图片 | 天天影视网色香欲综合网无拦截 | 亚洲aⅴ网站 | 欧美精品一区二 | 成人黄色一级片 | 久久久精品免费观看 | 日韩欧美网 | 黄色毛片在线播放 | 欧美日本亚洲 | 中文字幕在线视频免费播放 | 久久国产精品99久久久久久老狼 | 亚洲免费在线视频 | 久久99深爱久久99精品 | 中文字幕视频免费观看 | 国产精品一区二区三区在线免费观看 | 亚洲视频二区 | 欧美精品一区在线 | 亚洲影视一区 | 久久精品国产99久久久 | 午夜不卡福利视频 | 97久久精品午夜一区二区 | 久久精品1区2区 | 日本免费不卡 | 国精产品一区二区三区有限公司 | 国产日韩欧美一区二区 | 日韩在线中文字幕 | 国产视频福利在线 | 日本免费在线视频 | 美女福利视频网站 | 综合精品久久久 | 山岸逢花在线观看无删减 | 91电影在线 | 日韩看片 | 91在线精品视频 | 国产精品中文字幕在线观看 | 亚洲视频a| 一级毛片视频 | 亚洲第一av网站 | 91在线电影 | 久9久9| 成人免费xxx在线观看 | 中文字幕在线看第二 | 99国产视频 | 久久久久中文 | 成人精品一区 | 国产在线网站 | 1区2区3区视频 | 青青草国产成人av片免费 | 亚洲综合福利视频 | 国产精品视频一区二区三区 | 成人精品一区二区三区中文字幕 | 午夜成人在线视频 | 色视频在线播放 | 亚洲国产精品人人爽夜夜爽 | 色综合久久久久 | 福利在线播放 | 亚洲精品久久久久avwww潮水 | 欧美激情网址 | 中文字幕 在线观看 | 亚洲一区二区三区中文字幕 | 国产色 | 99久久久久久久久 | 国产成人午夜精品影院游乐网 | 日韩精品www | 国产欧美综合一区二区三区 | 真人一级毛片 | 玖玖精品 | 久久成人免费视频 | 超碰在线影院 | 色婷婷综合久久久中文字幕 | 久草在线资源福利站 | 日韩视频一区二区三区四区 | 日韩久久一区二区 | 亚洲国产一区二区三区四区 | 国产精品久久久久久久久久久久 | 国产一区二区视频精品 | 久久亚洲一区 | 欧美日韩在线一区二区三区 | 成人一区二区三区 | 国产国拍亚洲精品av | 99精品国自产在线 | 欧美日韩中文在线 | 久久久资源 | 五月婷婷中文 | 亚洲国产精品久久 | 成人在线视频免费观看 | 一区二区日韩 | 国产精品久久久久久久9999 | 成年人精品视频在线观看 | 国产精品久久久久久久一区探花 | 亚洲综合大片69999 | 一区日韩 | 午夜精品久久久久久久星辰影院 | 91视频在线播放视频 | 黄色影片免费在线观看 | 精品久久中文字幕 | 免费观看欧美一级 | 岛国视频| 国产欧美综合一区二区三区 | sese综合| av免费观看在线 | 综合五月激情 | 久久久久国产一区 | 久久久国产精品 | 欧美日韩中文 | 久色视频在线观看 | 国产伦精品一区二区三区四区视频 | 五月天在线婷婷 | av福利网站 | 国产黄色在线观看 | 午夜免费电影 | 日韩中文在线 | 91超碰在线观看 | 欧美第一网站 | 国产一区高清 | 青草青草久热精品视频在线观看 | 国产精品久久久久久久久久妞妞 | 91在线 | 亚洲| 成人性生交大片免费看中文带字幕 | 精品亚洲一区二区三区 | 日本久久久久久 | 欧美女优在线视频 | 国产日韩精品一区 | www.欧美日韩 | 久久蜜桃av | h在线视频 | 伊人一区| 在线中文字幕第一页 | av免费网站在线观看 | 亚洲aⅴ网站 | 国产精品视频久久 | 精品亚洲一区二区三区 |