frameworks/base/core/java/android/view/ViewRootImpl.java
[ViewRootImpl.java–>ViewRootImpl.ViewRootImpl()]
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
......
/**
创建Choreographer
*/
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
}
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java–>Choreographer.getInstance()]
/**
* Gets the choreographer for the calling thread. Must be called from
* a thread that already has a {@link android.os.Looper} associated with it.
*
* @return The choreographer for this thread.
* @throws IllegalStateException if the thread does not have a looper.
*/
public static Choreographer getInstance() {
return sThreadInstance.get();
}
```
[Choreographer.java-->Choreographer.sThreadInstance()]
```
// Thread local storage for the choreographer.
private static final ThreadLocal sThreadInstance =
new ThreadLocal() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper);
}
};
[Choreographer.java–>Choreographer.Choreographer()]
private Choreographer(Looper looper) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : 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();
}
}
1.mLooper
2.mHandler
3.mDisplayEventReceiver
4.mCallbackQueues
mLooper = looper –> looper = Looper.myLooper()
/frameworks/base/core/java/android/os/Looper.java
[Looper.java–>Looper.myLooper()]
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
/**
返回和当前线程绑定的Looper
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
mHandler = new FrameHandler(looper)
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java–>Choreographer.Choreographer().FrameHandler()]
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;
}
}
}
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java–>FrameDisplayEventReceiver]
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper) {
super(looper);
}
}
frameworks/base/core/java/android/view/DisplayEventReceiver.java
[DisplayEventReceiver.java–>DisplayEventReceiver()]
/**
* 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();
mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue);
mCloseGuard.open("dispose");
}
/frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
[android_view_DisplayEventReceiver.cpp–>nativeInit()]
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject messageQueueObj) {
/**
1. 获取messageQueue
*/
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
/**
2. 创建一个NativeDisplayEventReceiver
*/
sp receiver = new NativeDisplayEventReceiver(env,
receiverWeak, messageQueue);
/**
3. 调用NativeDisplayEventReceiver的initialize函数
*/
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 object
/**
4. 返回一个NativeDisplayEventReceiver
*/
return reinterpret_cast(receiver.get());
}
[android_view_DisplayEventReceiver.cpp–>NativeDisplayEventReceiver]
class NativeDisplayEventReceiver : public DisplayEventDispatcher {
public:
NativeDisplayEventReceiver(JNIEnv* env,
jobject receiverWeak, const sp& messageQueue);
void dispose();
protected:
virtual ~NativeDisplayEventReceiver();
private:
jobject mReceiverWeakGlobal;
sp mMessageQueue;
/**
NativeDisplayEventReceiver中有DisplayEventReceiver,则在创建NativeDisplayEventReceiver
对象时调用DisplayEventReceiver的构造方法
*/
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
};
/frameworks/native/libs/gui/DisplayEventReceiver.cpp
[DisplayEventReceiver.cpp–>DisplayEventReceiver()]
DisplayEventReceiver::DisplayEventReceiver() {
/**
1. 创建ISurfaceComposer
*/
sp sf(ComposerService::getComposerService());
if (sf != NULL) {
/**
2. 调用ISurfaceComposer的createDisplayEventConnection()函数创建EventConnection
*/
mEventConnection = sf->createDisplayEventConnection();
if (mEventConnection != NULL) {
/**
3. 调用getDataChannel函数,mDataChannel对应的是BitTube
*/
mDataChannel = mEventConnection->getDataChannel();
}
}
}
/frameworks/native/include/private/gui/ComposerService.h
[ComposerService.h]
class ComposerService : public Singleton
{
sp mComposerService;
sp mDeathObserver;
Mutex mLock;
ComposerService();
void connectLocked();
void composerServiceDied();
friend class Singleton;
public:
// Get a connection to the Composer Service. This will block until
// a connection is established.
static sp getComposerService();
};
/frameworks/native/libs/gui/SurfaceComposerClient.cpp
[SurfaceComposerClient.cpp]
/**
ComposerService单例模式,在/system/core/include/utils/Singleton.h中有定义
*/
ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
/**
ComposerService构造函数,调用connectLocked()函数
*/
ComposerService::ComposerService()
: Singleton() {
Mutex::Autolock _l(mLock);
connectLocked();
}
/**
获取mComposerService,即SurfaceFlinger服务的连接
*/
void ComposerService::connectLocked() {
const String16 name("SurfaceFlinger");
/**
>sp mComposerService;
getService将mComposerService实例化成一个BpSurfaceComposer对象,定义在ISurfaceComposer.cpp中
其中这里的BpSurfaceComposer已经获得了SurfaceFlinger的service的在binder驱动中的handle值
mComposerService是一个 BpSurfaceComposer(new BpBinder(handle))对象
>getService是从ServiceManager进程中查找"SurfaceFlinger"的Binder对应的handle值,
返回的out型参数mComposerService对应的就是"SurfaceFlinger" binder 服务端的客户端Binder对象,通过这个
mComposerService就可以和"SurfaceFlinger"进行通信
*/
while (getService(name, &mComposerService) != NO_ERROR) {
usleep(250000);
}
assert(mComposerService != NULL);
// Create the death listener.
class DeathObserver : public IBinder::DeathRecipient {
ComposerService& mComposerService;
virtual void binderDied(const wp& who) {
ALOGW("ComposerService remote (surfaceflinger) died [%p]",
who.unsafe_get());
mComposerService.composerServiceDied();
}
public:
DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
};
mDeathObserver = new DeathObserver(*const_cast(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
}
/*static*/ sp ComposerService::getComposerService() {
/**
static方法获取单例模式下的BpSurfaceComposer(new BpBinder(handle))对象
*/
ComposerService& instance = ComposerService::getInstance();
Mutex::Autolock _l(instance.mLock);
if (instance.mComposerService == NULL) {
ComposerService::getInstance().connectLocked();
assert(instance.mComposerService != NULL);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
}
void ComposerService::composerServiceDied()
{
Mutex::Autolock _l(mLock);
mComposerService = NULL;
mDeathObserver = NULL;
}
/frameworks/native/include/binder/IServiceManager.h
[IServiceManager.h–>IServiceManager::getService()]
template
status_t getService(const String16& name, sp* outService)
{
/**
defaultServiceManager返回的进程范围内唯一的BpServiceManager,即
sm = new BpServiceManager(new BpBinder(0));
*/
const sp sm = defaultServiceManager();
if (sm != NULL) {
/**
sm->getService(name): 会调用BpServiceManager中的getService函数,
经过binder驱动程序和service_manager守护进程进行通信,得到service名
称为name的service的handle值,返回的是一个BpBinder(handle)
*/
/**
interface_cast(new BpBinder(handle))会创建一个BpINTERFACE对象,
*/
*outService = interface_cast(sm->getService(name));
if ((*outService) != NULL) return NO_ERROR;
}
return NAME_NOT_FOUND;
}
sm返回的是一个BpServiceManager对象,该对象是一个Binder的客户端,也就是Service_Manager
服务守护进程的Bp客户端,相当于是BpServiceManager(new BpBinder(0)),根据前面的服务名称
”SurfaceFlinger”,最终outService返回的是一个BpSurfaceComposer对象
/frameworks/native/services/surfaceflinger/SurfaceFlinger.h
[SurfaceFlinger.h]
/**
SurfaceFlinger是BnSurfaceComposer的实现者,因此ComposerService::getComposerService()获取的服务,即是SurfaceFlinger
*/
class SurfaceFlinger : public BnSurfaceComposer,
private IBinder::DeathRecipient,
private HWComposer::EventHandler
{
ComposerService::getComposerService()返回的是一个与SurfaceFlinger进程建立Binder进程通信的客户端,服务端就SurfaceFlinger对象本身
mEventConnection = sf->createDisplayEventConnection();
/frameworks/native/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
[SurfaceFlinger_hwc1.cpp–>SurfaceFlinger_hwc1::createDisplayEventConnection()]
/**
返回DisplayEventReceiver构造函数中的mEventConnection是一个BpDisplayEventConnection对象
*/
sp SurfaceFlinger::createDisplayEventConnection() {
return mEventThread->createEventConnection();
}
/frameworks/native/services/surfaceflinger/EventThread.cpp
[EventThread.cpp–>EventThread::createEventConnection()]
sp EventThread::createEventConnection() const {
return new Connection(const_cast(this));
}
[EventThread.cpp–>EventThread::Connection::Connection()]
/**
onnection的构造函数会创建一个BitTube对象,BitTube对象中包含一对互联的socket,
一端发送另一端就能收到。并将BitTube对象存储在EventThread::Connection.mChannel里面
*/
EventThread::Connection::Connection(
const sp& eventThread)
: count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
frameworks/native/libs/gui/BitTube.cpp
[BitTube.cpp–>BitTube::BitTube()]
static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
BitTube::BitTube()
: mSendFd(-1), mReceiveFd(-1)
{
init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
}
[BitTube.cpp–>BitTube::init()]
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
// sine we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
mReceiveFd = sockets[0];
mSendFd = sockets[1];
} else {
mReceiveFd = -errno;
ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
}
}
/frameworks/native/services/surfaceflinger/EventThread.h
[EventThread.h–>EventThread]
class EventThread : public Thread, private VSyncSource::Callback {
/**
Connection是一个BnDisplayEventConnection对象
*/
class Connection : public BnDisplayEventConnection {
public:
Connection(const sp& eventThread);
status_t postEvent(const DisplayEventReceiver::Event& event);
// count >= 1 : continuous event. count is the vsync rate
// count == 0 : one-shot event that has not fired
// count ==-1 : one-shot event that fired this round / disabled
int32_t count;
private:
virtual ~Connection();
virtual void onFirstRef();
virtual sp getDataChannel() const;
virtual void setVsyncRate(uint32_t count);
virtual void requestNextVsync(); // asynchronous
sp const mEventThread;
/**
mChannel是一个BitTube
*/
sp const mChannel;
};
mDataChannel= mEventConnection->getDataChannel();
BpDisplayEventConnection.getDataChannel() //IDisplayEventConnection.cpp –>BnDisplayEventConnection.onTransact //IDisplayEventConnection.cpp
–>EventThread::Connection.getDataChannel() //EventThread.cpp
/frameworks/native/services/surfaceflinger/EventThread.cpp
sp<BitTube> EventThread::Connection::getDataChannel() const {
return mChannel;
}
[BnDisplayEventConnection.onTransact //IDisplayEventConnection.cpp]
status_t BnDisplayEventConnection::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case GET_DATA_CHANNEL: {
/**
调用Connection.getDataChannel()函数返回前面创建的BitTube对象:
*/
CHECK_INTERFACE(IDisplayEventConnection, data, reply);
sp channel(getDataChannel());
channel->writeToParcel(reply);
return NO_ERROR;
}
case SET_VSYNC_RATE: {
CHECK_INTERFACE(IDisplayEventConnection, data, reply);
setVsyncRate(data.readUint32());
return NO_ERROR;
}
case REQUEST_NEXT_VSYNC: {
CHECK_INTERFACE(IDisplayEventConnection, data, reply);
requestNextVsync();
return NO_ERROR;
}
}
return BBinder::onTransact(code, data, reply, flags);
}
/frameworks/native/libs/gui/BitTube.cpp
[BitTube.cpp–>BitTube::writeToParcel()]
status_t BitTube::writeToParcel(Parcel* reply) const
{
if (mReceiveFd < 0)
return -EINVAL;
status_t result = reply->writeDupFileDescriptor(mReceiveFd);
close(mReceiveFd);
mReceiveFd = -1;
return result;
}
[BpDisplayEventConnection.getDataChannel() //IDisplayEventConnection.cpp]
virtual sp<BitTube> getDataChannel() const
{
Parcel data, reply;
data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
remote()->transact(GET_DATA_CHANNEL, data, &reply);
return new BitTube(reply);
}
frameworks/native/libs/gui/BitTube.cpp
[BitTube.cpp–>BitTube::BitTube(const Parcel& data)]
/**
构造函数从Parcel中读取前面在SurfaceFlinger进程中写入的socket接收端的描述符
*/
BitTube::BitTube(const Parcel& data)
: mSendFd(-1), mReceiveFd(-1)
{
mReceiveFd = dup(data.readFileDescriptor());
if (mReceiveFd < 0) {
mReceiveFd = -errno;
ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
strerror(-mReceiveFd));
}
}
native层的NativeDisplayEventReceiver对象已经创建结束,这时候客户端进程已经有了一个和SurfaceFlinger服务端相连的socket接收端描述符
/frameworks/base/libs/androidfw/DisplayEventDispatcher.cpp
[DisplayEventDispatcher.cpp–>DisplayEventDispatcher::initialize()]
status_t DisplayEventDispatcher::initialize() {
status_t result = mReceiver.initCheck();
if (result) {
ALOGW("Failed to initialize display event receiver, status=%d", result);
return result;
}
/**
参数mReceiver.getFd()返回的是在创建NativeDisplayEventReceiver时从SurfaceFlinger服务端接收回来的socket接收端描述符
*/
int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
this, NULL);
if (rc < 0) {
return UNKNOWN_ERROR;
}
return OK;
}
/system/core/libutils/Looper.cpp
[Looper.cpp–>Looper::addFd()]
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
/**
将上面传进来的NativeDisplayEventReceiver对象封装成一个SimpleLooperCallback对象
*/
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}
int Looper::addFd(int fd, int ident, int events, const sp& callback, void* data) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
events, callback.get(), data);
#endif
if (!callback.get()) {
if (! mAllowNonCallbacks) {
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
return -1;
}
if (ident < 0) {
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
return -1;
}
} else {
ident = POLL_CALLBACK;
}
{ // acquire lock
AutoMutex _l(mLock);
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
/**
1. 创建一个struct epoll_event结构体对象,将对应的内存全部用清0,并作对应的初始化
*/
struct epoll_event eventItem;
request.initEventItem(&eventItem);
/**
2. 查询通过addFd方法已经添加到epoll中监听的文件描述符
*/
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
/**
查询不到的话,则调用epoll_ctl方法设置EPOLL_CTL_ADD属性将对应的文件描述符添加到epoll监听的描述符中
*/
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
return -1;
}
mRequests.add(fd, request);
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
if (errno == ENOENT) {
// Tolerate ENOENT because it means that an older file descriptor was
// closed before its callback was unregistered and meanwhile a new
// file descriptor with the same number has been created and is now
// being registered for the first time. This error may occur naturally
// when a callback has the side-effect of closing the file descriptor
// before returning and unregistering itself. Callback sequence number
// checks further ensure that the race is benign.
//
// Unfortunately due to kernel limitations we need to rebuild the epoll
// set from scratch because it may contain an old file handle that we are
// now unable to remove since its file descriptor is no longer valid.
// No such problem would have occurred if we were using the poll system
// call instead, but that approach carries others disadvantages.
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
"being recycled, falling back on EPOLL_CTL_ADD: %s",
this, strerror(errno));
#endif
epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error modifying or adding epoll events for fd %d: %s",
fd, strerror(errno));
return -1;
}
scheduleEpollRebuildLocked();
} else {
ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
return -1;
}
}
mRequests.replaceValueAt(requestIndex, request);
}
} // release lock
return 1;
}
addFd传入的参数EVENT_INPUT,说明当前应用线程的native层的Looper对象中的epoll机制已经开始监听来自于SurfaceFlinger服务端socket端的写入事件
/frameworks/base/core/java/android/view/Choreographer.java
[Choreographer.java]
/**
* 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;
/**
* Callback type: Commit callback. Handles post-draw operations for the frame.
* Runs after traversal completes. The {@link #getFrameTime() frame time} reported
* during this callback may be updated to reflect delays that occurred while
* traversals were in progress in case heavy layout operations caused some frames
* to be skipped. The frame time reported during this callback provides a better
* estimate of the start time of the frame in which animations (and other updates
* to the view hierarchy state) actually took effect.
* @hide
*/
public static final int CALLBACK_COMMIT = 3;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
[Choreographer.java–>CallbackQueue]
private final class CallbackQueue {
private CallbackRecord mHead;
public boolean hasDueCallbacksLocked(long now) {
return mHead != null && mHead.dueTime <= now;
}
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 = next;
return callbacks;
}
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;
}
public void removeCallbacksLocked(Object action, Object token) {
CallbackRecord predecessor = null;
for (CallbackRecord callback = mHead; callback != null;) {
final CallbackRecord next = callback.next;
if ((action == null || callback.action == action)
&& (token == null || callback.token == token)) {
if (predecessor != null) {
predecessor.next = next;
} else {
mHead = next;
}
recycleCallbackLocked(callback);
} else {
predecessor = callback;
}
callback = next;
}
}
}
[Choreographer.java–>CallbackRecord]
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}