个人博客地址 http://dandanlove.com/

前言

上一篇文章 Android的16ms和垂直同步以及三重缓存 解释了手机流畅性的问题,并在文章中提到了在Android4.1中添加的VsyncChoreographer机制,用于同Vsync机制配合,实现统一调度界面绘图。

Choreographer的构造

Choreographer是线程级别的单例,并且具有处理当前线程消息循环队列的功能。

public final class Choreographer {// Enable/disable vsync for animations and drawing.private static final boolean USE_VSYNC = SystemProperties.getBoolean("debug.choreographer.vsync", true);//单例public static Choreographer getInstance() {return sThreadInstance.get();}//每个线程一个Choreographer实例private static final ThreadLocal<Choreographer> sThreadInstance =new ThreadLocal<Choreographer>() {@Overrideprotected Choreographer initialValue() {Looper looper = Looper.myLooper();if (looper == null) {throw new IllegalStateException("The current thread must have a looper!");}return new Choreographer(looper);}};private Choreographer(Looper looper) {mLooper = looper;//创建handle对象,用于处理消息,其looper为当前的线程的消息队列mHandler = new FrameHandler(looper);//创建VSYNC的信号接受对象mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;//初始化上一次frame渲染的时间点mLastFrameTimeNanos = Long.MIN_VALUE;//计算帧率,也就是一帧所需的渲染时间,getRefreshRate是刷新率,一般是60mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());//创建消息处理队列mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];for (int i = 0; i <= CALLBACK_LAST; i++) {mCallbackQueues[i] = new CallbackQueue();}}
}

变量USE_VSYNC用于表示系统是否是用了Vsync同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的。如果系统使用了Vsync同步机制,则创建一个FrameDisplayEventReceiver对象用于请求并接收Vsync事件,最后Choreographer创建了一个大小为3的CallbackQueue队列数组,用于保存不同类型的Callback。

Choreographer的使用

注册Runnable对象

作者之前写过一篇关于ViewRootImpl的文章:ViewRootImpl的独白,我不是一个View(布局篇)里面有涉及使用Choreographer进行View的绘制,这次我们从ViewRootImpl的绘制出发来看看Choreographer的使用。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {Choreographer mChoreographer;/***部分代码省略***/public ViewRootImpl(Context context, Display display) {/***部分代码省略***/mChoreographer = Choreographer.getInstance();/***部分代码省略***/}/***部分代码省略***/void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();}}
}

注册FrameCallback对象

无论是注册Runnable还是注册FrameCallback对象最终都会调用postCallbackDelayedInternal方法往mCallbackQueues添加回调,区别在于FrameCallback的token为FRAME_CALLBACK_TOKEN,两者在回调的时候不相同。

public final class Choreographer {// All frame callbacks posted by applications have this token.private static final Object FRAME_CALLBACK_TOKEN = new Object() {public String toString() { return "FRAME_CALLBACK_TOKEN"; }};private static final class CallbackRecord {public CallbackRecord next;public long dueTime;public Object action; // Runnable or FrameCallbackpublic Object token;public void run(long frameTimeNanos) {if (token == FRAME_CALLBACK_TOKEN) {((FrameCallback)action).doFrame(frameTimeNanos);} else {((Runnable)action).run();}}}
}

Choreographer的消息处理

Choreographer接受消息

public final class Choreographer {//Input callback.  Runs first.public static final int CALLBACK_INPUT = 0;//Animation callback.  Runs before traversals.public static final int CALLBACK_ANIMATION = 1;// Traversal callback.  Handles layout and draw.  //Runs last after all other asynchronous messages have been handled.public static final int CALLBACK_TRAVERSAL = 2;private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;//长度为3(CALLBACK_LAST+1)的CallbackQueue类型的数组private final CallbackQueue[] mCallbackQueues;//发送回调事件public void postCallback(int callbackType, Runnable action, Object token) {postCallbackDelayed(callbackType, action, token, 0);}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);}private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {/***部分代码省略***/synchronized (mLock) {//从开机到现在的毫秒数(手机睡眠的时间不包括在内); final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;//添加类型为callbackType的CallbackQueue(将要执行的回调封装而成)mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);//函数执行时间if (dueTime <= now) {//立即执行scheduleFrameLocked(now);} else {//异步回调延迟执行Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);msg.arg1 = callbackType;msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, dueTime);}}}/*** @param dueTime 任务开始时间* @param action 任务* @param token 标识*/private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {CallbackRecord callback = mCallbackPool;if (callback == null) {callback = new CallbackRecord();} else {mCallbackPool = callback.next;callback.next = null;}callback.dueTime = dueTime;callback.action = action;callback.token = token;return callback;}private final class CallbackQueue {private CallbackRecord mHead;public void addCallbackLocked(long dueTime, Object action, Object token) {CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);CallbackRecord entry = mHead;//判断当前的是否不头节点if (entry == null) {mHead = callback;return;}//判断当前任务出发起始时间是不是当前所有任务的最开始时间if (dueTime < entry.dueTime) {callback.next = entry;mHead = callback;return;}//根据任务开始时间由小到大插入到链表当中while (entry.next != null) {if (dueTime < entry.next.dueTime) {callback.next = entry.next;break;}entry = entry.next;}entry.next = callback;}}
}

CallbackQueue

public final class Choreographer {/*** Callback type: Input callback.  Runs first.* @hide*/public static final int CALLBACK_INPUT = 0;/*** Callback type: Animation callback.  Runs before traversals.* @hide*/public static final int CALLBACK_ANIMATION = 1;/*** Callback type: Traversal callback.  Handles layout and draw.  Runs* after all other asynchronous messages have been handled.* @hide*/public static final int CALLBACK_TRAVERSAL = 2;
}

三种类型不同的CallbackRecord链表,按照任务触发时间由小到大排列。

FrameHandler异步处理

public final class Choreographer {private static final int MSG_DO_FRAME = 0;private static final int MSG_DO_SCHEDULE_VSYNC = 1;private static final int MSG_DO_SCHEDULE_CALLBACK = 2;private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME://刷新当前这一帧doFrame(System.nanoTime(), 0);break;case MSG_DO_SCHEDULE_VSYNC://做VSYNC的信号同步doScheduleVsync();break;case MSG_DO_SCHEDULE_CALLBACK://将当前任务加入执行队列doScheduleCallback(msg.arg1);break;}}}
}

doFrame

public final class Choreographer {void doFrame(long frameTimeNanos, int frame) {final long startNanos;synchronized (mLock) {if (!mFrameScheduled) {return; // no work to do}//当前时间startNanos = System.nanoTime();//抖动间隔final long jitterNanos = startNanos - frameTimeNanos;//抖动间隔大于屏幕刷新时间间隔(16ms)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.");}//最后一次的屏幕刷是lastFrameOffset之前开始的final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;if (DEBUG) {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;}//由于跳帧可能造成了当前展现的是之前的帧,这样需要等待下一个vsync信号if (frameTimeNanos < mLastFrameTimeNanos) {if (DEBUG) {Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "+ "previously skipped frame.  Waiting for next vsync.");}scheduleVsyncLocked();return;}//当前画面刷新的状态置falsemFrameScheduled = false;//更新最后一帧的刷新时间mLastFrameTimeNanos = frameTimeNanos;}//按照优先级策略进行画面刷新时间处理doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);if (DEBUG) {final long endNanos = System.nanoTime();Log.d(TAG, "Frame " + frame + ": Finished, took "+ (endNanos - startNanos) * 0.000001f + " ms, latency "+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");}}
}

doScheduleVsync

public final class Choreographer {//等待vsync信号void doScheduleVsync() {synchronized (mLock) {if (mFrameScheduled) {scheduleVsyncLocked();}}}//当运行在Looper线程,则立刻调度vsyncprivate void scheduleVsyncLocked() {mDisplayEventReceiver.scheduleVsync();}
}

doScheduleCallback

public final class Choreographer {// Enable/disable vsync for animations and drawing.private static final boolean USE_VSYNC = SystemProperties.getBoolean("debug.choreographer.vsync", true);private final class CallbackQueue {//判断是否有能执行的任务public boolean hasDueCallbacksLocked(long now) {return mHead != null && mHead.dueTime <= now;}/***部分代码省略***/}/***部分代码省略***///执行任务回调void doScheduleCallback(int callbackType) {synchronized (mLock) {if (!mFrameScheduled) {final long now = SystemClock.uptimeMillis();//有能执行的任务if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {scheduleFrameLocked(now);}}}}private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {mFrameScheduled = true;if (USE_VSYNC) {if (DEBUG) {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.if (isRunningOnLooperThreadLocked()) {//当运行在Looper线程,则立刻调度vsyncscheduleVsyncLocked();} else {//切换到主线程,调度vsyncMessage 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) {Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}}//检测当前的Looper线程是不是主线程private boolean isRunningOnLooperThreadLocked() {return Looper.myLooper() == mLooper;}
}
public final class Choreographer {// The display event receiver can only be accessed by the looper thread to which// it is attached.  We take care to ensure that we post message to the looper// if appropriate when interacting with the display event receiver.private final FrameDisplayEventReceiver mDisplayEventReceiver;private Choreographer(Looper looper) {/***部分代码省略***///在Choreographer的构造函数中,我们使用USE_VSYNC则会有FrameDisplayEventReceiver做为与显示器时间进行交互mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;}/***部分代码省略***/private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {//构造函数需要传入当前的looper队列  public FrameDisplayEventReceiver(Looper looper) {super(looper);}/***部分代码省略***/  }
}
public abstract class DisplayEventReceiver {private static native void nativeScheduleVsync(long receiverPtr);/*** Creates a display event receiver.** @param looper The looper to use when invoking callbacks.*/public DisplayEventReceiver(Looper looper) {if (looper == null) {throw new IllegalArgumentException("looper must not be null");}mMessageQueue = looper.getQueue();//接受数量多少等于looper中消息的多少mReceiverPtr = nativeInit(this, mMessageQueue);mCloseGuard.open("dispose");}/*** Schedules a single vertical sync pulse to be delivered when the next* display frame begins.*/public 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流程汇总

native端的消息处理

文件路径:frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp

NativeDisplayEventReceiver类结构

//NativeDisplayEventReceiver类的定义
class NativeDisplayEventReceiver : public LooperCallback {public://对象公共方法//构造函数NativeDisplayEventReceiver(JNIEnv* env,jobject receiverObj, const sp<MessageQueue>& messageQueue);status_t initialize();  //初始化方法void dispose();status_t scheduleVsync();//获取下一个VSYNC信号protected:virtual ~NativeDisplayEventReceiver();//析构函数private:jobject mReceiverObjGlobal;//java层的DisplayEventReceiver的全局引用sp<MessageQueue> mMessageQueue;//looper的消息队列DisplayEventReceiver mReceiver;//frameworks/nivate/libs/gui/DisplayEventReceiver.cppbool mWaitingForVsync;//默认为falsevirtual int handleEvent(int receiveFd, int events, void* data);bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
};//ststem/core/include/utils/Looper.h
/*** A looper callback.*/
//NativeDisplayEventReceiver的父类,用与looper中消息的回调
class LooperCallback : public virtual RefBase {protected:virtual ~LooperCallback() { }public:virtual int handleEvent(int fd, int events, void* data) = 0;
};

NativeDisplayEventReceiver初始化

//初始化native的消息队列
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,jobject messageQueueObj) {sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == NULL) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}//构造NativeDisplayEventReceiver对象sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,receiverObj, messageQueue);status_t status = receiver->initialize();if (status) {String8 message;message.appendFormat("Failed to initialize display event receiver.  status=%d", status);jniThrowRuntimeException(env, message.string());return 0;}receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get());
}
//NativeDisplayEventReceiver的构造函数
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,jobject receiverObj, const sp<MessageQueue>& messageQueue) :mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),mMessageQueue(messageQueue), mWaitingForVsync(false) {ALOGV("receiver %p ~ Initializing input event receiver.", this);
}
//receiver内部数据的初始化
status_t NativeDisplayEventReceiver::initialize() {status_t result = mReceiver.initCheck();if (result) {ALOGW("Failed to initialize display event receiver, status=%d", result);return result;}//监听mReceiver的所获取的文件句柄。int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,this, NULL);if (rc < 0) {return UNKNOWN_ERROR;}return OK;
}

NativeDisplayEventReceiver请求VSYNC的同步

//java层调用DisplayEventReceiver的scheduleVsync请求VSYNC的同步
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {sp<NativeDisplayEventReceiver> receiver =reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);status_t status = receiver->scheduleVsync();if (status) {String8 message;message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);jniThrowRuntimeException(env, message.string());}
}status_t NativeDisplayEventReceiver::scheduleVsync() {if (!mWaitingForVsync) {ALOGV("receiver %p ~ Scheduling vsync.", this);// Drain all pending events.nsecs_t vsyncTimestamp;int32_t vsyncDisplayId;uint32_t vsyncCount;processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount);//请求下一次Vsync信息处理status_t status = mReceiver.requestNextVsync();if (status) {ALOGW("Failed to request next vsync, status=%d", status);return status;}mWaitingForVsync = true;}return OK;
}//frameworks/native/libs/gui/DisplayEventReceiver.cpp
//通过IDisplayEventConnection接口来请求Vsync信号,IDisplayEventConnection实现了Binder通信框架,可以跨进程调用。
//因为Vsync信号请求进程和Vsync产生进程有可能不在同一个进程空间,因此这里就借助IDisplayEventConnection接口来实现。
status_t DisplayEventReceiver::requestNextVsync() {if (mEventConnection != NULL) {mEventConnection->requestNextVsync();return NO_ERROR;}return NO_INIT;
}

NativeDisplayEventReceiver处理消息

//NativeDisplayEventReceiver处理消息
int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {...nsecs_t vsyncTimestamp;int32_t vsyncDisplayId;uint32_t vsyncCount;//过滤出最后一次的vsyncif (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {mWaitingForVsync = false;//分发Vsync,调用到native的android/view/DisplayEventReceiver.class的dispatchVsync方法dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);}return 1;
}

DisplayEventReceiver分发VSYNC信号

public abstract class DisplayEventReceiver {/***部分代码省略***/public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {}// Called from native code.@SuppressWarnings("unused")private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {onVsync(timestampNanos, builtInDisplayId, frame);}
}private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {private boolean mHavePendingVsync;private long mTimestampNanos;private int mFrame;/***部分代码省略***/@Overridepublic void onVsync(long timestampNanos, int builtInDisplayId, int frame) {//忽略来自第二显示屏的Vsyncif (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {scheduleVsync();return;}/***部分代码省略***/mTimestampNanos = timestampNanos;mFrame = frame;//该消息的callback为当前对象FrameDisplayEventReceiverMessage msg = Message.obtain(mHandler, this);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);}@Overridepublic void run() {mHavePendingVsync = false;//DisplayEventReceiver消息处理doFrame(mTimestampNanos, mFrame);}
}

DisplayEventReceiver消息处理

参见4.2.1、doFrame介绍

Choreographer处理回调

Choreographer触发可执行任务的回调

这里为什么说可执行任务呢?因为每个任务都有自己的触发时间,Choreographer只选择它能触发的任务。

public final class Choreographer {//进行回调的标识private boolean mCallbacksRunning;/***部分代码省略***/void doCallbacks(int callbackType, long frameTimeNanos) {CallbackRecord callbacks;synchronized (mLock) {final long now = SystemClock.uptimeMillis();//找到当前能触发的回调链表callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);if (callbacks == null) {return;}mCallbacksRunning = true;}try {for (CallbackRecord c = callbacks; c != null; c = c.next) {//循环遍历,回调所有的任务c.run(frameTimeNanos);}} finally {synchronized (mLock) {mCallbacksRunning = false;do {final CallbackRecord next = callbacks.next;recycleCallbackLocked(callbacks);callbacks = next;} while (callbacks != null);}}}//回收回调任务资源private void recycleCallbackLocked(CallbackRecord callback) {callback.action = null;callback.token = null;callback.next = mCallbackPool;mCallbackPool = callback;}private final class CallbackQueue {public CallbackRecord extractDueCallbacksLocked(long now) {CallbackRecord callbacks = mHead;//当链表头部的任务触发事件都比当前时间晚,那么整个链表则没有任务需要触发if (callbacks == null || callbacks.dueTime > now) {return null;}CallbackRecord last = callbacks;CallbackRecord next = last.next;//找到当前时间之前需要触发任务链表,将该链表截断并返回while (next != null) {if (next.dueTime > now) {last.next = null;break;}last = next;next = next.next;}//mHead重置为原始链表截断的头部mHead = next;return callbacks;}}
}

处理Choreographer回调

3、Choreographer的使用部分讲述了ViewRootImpl使用Choreographer的使用,那么我们现在来看一下ViewRootImplChoreographer回调时间的处理。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {Choreographer mChoreographer;/***部分代码省略***/public ViewRootImpl(Context context, Display display) {/***部分代码省略***/mChoreographer = Choreographer.getInstance();/***部分代码省略***/}/***部分代码省略***/void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();}}final class TraversalRunnable implements Runnable {@Overridepublic void run() {//开始View的测量、布局、绘制doTraversal();}}final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
}

总结

整片文章单独看起来留下的印象不是很深刻,以前阅读过 Android的16ms和垂直同步以及三重缓存 这篇文章之后就会知道本文章是对 Android的16ms和垂直同步以及三重缓存 这篇文章其中的一些疑问进行解答。从代码的角度讲述了android的屏幕绘制部分知识。

文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

想阅读作者的更多文章,可以查看我 个人博客 和公共号:

Android系统的编舞者Choreographer相关推荐

  1. android 编舞者的使用

    android 编舞者的使用 1 编舞者Choreographer 的基本常识可以参考其他的博主,在这里主要是针对我使用的时候理解的,请取其精华即可. 使用编舞者 Choreographer 主要是在 ...

  2. Android编舞者类Choreographer小结

    Android编舞者类Choreographer小结 作用 编舞者类的作用主要是控制绘制节奏,用于发起一次vsync垂直同步信号的监听,当垂直同步信号来的时候会回调注册的Runnable或者FramC ...

  3. Choreographer ——编舞者

    Choreographer(编舞者) 官方解释 /*** Coordinates the timing of animations, input and drawing.* <p>* Th ...

  4. Android AOSP基础(三)Android系统源码的整编和单编

    本文首发于微信公众号「刘望舒」 关联系列 Android AOSP基础系列 Android系统启动系列 前言 在上一篇文章Android AOSP基础(二)AOSP 源码下载 中,我们顺利的将AOSP ...

  5. 深入剖析Android系统

    深入剖析Android系统(基于Google发布的Jelly Bean原始代码,讲述Android系统的内部静态结构关系和内部运行机制,为你呈现原汁原味的Android代码分析大餐!) 杨长刚著 IS ...

  6. Android系统开发和性能优化——查漏补缺【建议收藏】

    做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以 ...

  7. android phone驱动_一文带你掌握 Android 系统架构

    引言 本文作为Android系统架构的开篇,起到提纲挈领的作用,从系统整体架构角度概要讲解Android系统的核心技术点,带领大家初探Android系统全貌以及内部运作机制.虽然Android系统非常 ...

  8. android系统下替换so库等操作

    在工作过程中由于android 系统过大编译起来太过费时,所以需要小编来进行验证修改. 下面的是小编TV android 系统的操作. 小编替换android模块步骤 1,编译出.so文件 andro ...

  9. android 系统源码调试 局部变量值_如何方便快速的整编Android 9.0系统源码?

    点击上方"刘望舒",选择"星标" 多点在看,就是真爱! 作者 :  刘望舒  |  来源 :刘望舒的博客地址:http://liuwangshu.cn/fram ...

最新文章

  1. configure_file路径疑惑
  2. 机房布线的至高境界,美到窒息!
  3. Linq let Concat
  4. pycharm的演示模式、无干扰模式、全屏模式(presentation mode、distraction free mode、full screen mode)
  5. groovy附件存mysql_Groovy 操作mysql数据库
  6. 浦发银行计算机笔试题库,2018浦发银行校园招聘笔试试题库
  7. python读取日期_从文件中读取日期和数据(Python)
  8. java中并不是任意多个接口都可以实现多实现
  9. 蓝桥杯大赛青少年创意编程 推荐考生阅读 相关书籍的相关视频(部分)
  10. 苹果收购法国图像识别公司 技术或植入iPhone
  11. GDB使用小结- 可带参数
  12. java实现文件的复制
  13. 蓝桥杯-奇妙的数字(2015-A-3)
  14. 网站被攻击客户信息被泄露如何解决
  15. wav转mp3 c语言源码,C/C++知识点之mp3格式转wav格式 附完整C++算法实现代码
  16. 奇点云数据中台技术汇(三)| DataSimba系列之计算引擎篇
  17. 表格识别综述与相关实战
  18. esx linux 硬盘 扩容,ESX虚拟机添加新磁盘并扩容逻辑卷
  19. HDU 6578 Blank
  20. 什么是IOC和什么是AOP

热门文章

  1. 小孢子的神奇之旅——如何阅读MindSpore报错信息系列(1)
  2. python中矩阵乘以常数_python – 用numpy乘以高阶矩阵
  3. matlab卷积和互相关,互相关与卷积
  4. 【b站求职笔记】行路院-王贺 2020年12月笔记
  5. 在deepin20.6上运行ros和far planner
  6. SQL笔试题:某团数分岗笔试真题详解
  7. 关于元宇宙热的冷思考
  8. 期货开户必须具备知识点汇总
  9. 斯密斯圆图-学习笔记
  10. 人的潜能和机会总是无限的