前言
BootAnimation就是安卓系統(tǒng)的開機(jī)動畫仅孩,估計(jì)網(wǎng)上面對BootAnimation的源碼解讀已經(jīng)一大堆了矢空,但是我想借BootAnimation分析以及和應(yīng)用的對比來讓讀者好好理解一個(gè)應(yīng)用的本質(zhì)戴甩。
bootanimation在哪里
bootanimation的源碼在frameworks/base/cmd/bootanimation目錄下舍败,是c++寫的燕鸽,最后編譯成一個(gè)可執(zhí)行程序bootanimation是在手機(jī)system/bin目錄下兄世。
對照應(yīng)用:
我們編譯出來的是一個(gè)APK的壓縮包,一般是dex加一些資源啊研,放在我們手機(jī)system目錄或者data目錄御滩。
bootanimation的啟動
bootanimation會在android開機(jī)啟動的時(shí)候執(zhí)行init.rc然后執(zhí)行以下指令,然后就會找到bootanimation的可執(zhí)行程序并運(yùn)行党远。
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
writepid /dev/stune/top-app/tasks
然后就會運(yùn)行bootanimation_main.cpp中main方法削解,這是應(yīng)該大家在剛開始學(xué)c語言或者java語言的hello world的時(shí)候都清楚,暫時(shí)先不用代碼細(xì)節(jié)沟娱,我們后面慢慢分析氛驮。
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
bool noBootAnimation = bootAnimationDisabled();
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
waitForSurfaceFlinger();
// create the boot animation object
sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
}
ALOGV("Boot animation exit");
return 0;
}
對照應(yīng)用:
其實(shí)一個(gè)應(yīng)用的啟動過程和上述有點(diǎn)類似,說白了就是Zygote進(jìn)程加載應(yīng)用的dex文件济似,然后執(zhí)行ActivityThread.java的中main方法矫废,有點(diǎn)長,有興趣的可以仔細(xì)看看
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
bootanimation的main方法
雖然main方法的代碼不多砰蠢,但是值得思考的問題有很多蓖扑,以下是我對這邊所有代碼注解
int main()
{
//設(shè)置進(jìn)程的優(yōu)先級
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
//判斷是否disable BootAnimation
bool noBootAnimation = bootAnimationDisabled();
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
//初始化Binder服務(wù)
sp<ProcessState> proc(ProcessState::self());
//啟動Binder線程池
ProcessState::self()->startThreadPool();
//等待SurfaceFlinger服務(wù)結(jié)束
waitForSurfaceFlinger();
//創(chuàng)建開機(jī)動畫的對象,其實(shí)BootAnimation這個(gè)對象會另外運(yùn)行一個(gè)線程
// create the boot animation object
sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
ALOGV("Boot animation set up. Joining pool.");
//將當(dāng)前線程加入Binder線程池避免退出台舱。
IPCThreadState::self()->joinThreadPool();
}
ALOGV("Boot animation exit");
return 0;
}
細(xì)節(jié)1:
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
這兩行代碼律杠,會初始化bootanimation的Binder服務(wù),為什么要Binder服務(wù)竞惋,因?yàn)殚_機(jī)動畫需要SurfaceFlinger的支持柜去,SurfaceFlinger就是一個(gè)Binder服務(wù)。
對照應(yīng)用:
應(yīng)用除了需要SurfaceFlinger拆宛,還需要AMS,PMS,WMS等大量的Binder服務(wù)嗓奢,所以也需要初始化Binder服務(wù),但是我們發(fā)現(xiàn)ActivityThread.java的main方法中并沒有Binder服務(wù)的初始化胰挑,其實(shí)應(yīng)用的Binder服務(wù)的初始化在onZygoteInit的時(shí)候已經(jīng)完成了蔓罚。
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
細(xì)節(jié)2:
大家會發(fā)現(xiàn)bootanimation的最后一步是IPCThreadState::self()->joinThreadPool()椿肩,這個(gè)的意思是將當(dāng)前的線程也就是main方法所在的線程加入Binder的線程池瞻颂,并block住豺谈。為什么要這樣子做,這樣子是保證當(dāng)前進(jìn)程不退出贡这。
對照應(yīng)用:
其實(shí)在ActivityThread.java的main方法中也有類似的操作就是Looper.loop()茬末,也是為了避免主線程也就是UI線程退出。
小結(jié):
其實(shí)開機(jī)動畫也好盖矫,我們自己寫的應(yīng)用也好丽惭,本質(zhì)上主線程就是一個(gè)永遠(yuǎn)沒有返回或者結(jié)束的main方法。
bootanimation的界面繪制
先看如下代碼辈双,簡單總結(jié)一下就是通過Binder調(diào)用從SurfaceFlinger獲取了一塊Surface责掏,并將Surface和OpenGL繪制引擎進(jìn)行綁定,因?yàn)殚_機(jī)動畫是通過OpenGL繪制的湃望。
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
//獲得屏幕
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
//獲得屏幕信息
DisplayInfo dinfo;
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
if (status)
return -1;
//創(chuàng)建SurfaceControl
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
//設(shè)置Layer
SurfaceComposerClient::Transaction t;
t.setLayer(control, 0x40000000)
.apply();
//獲得對應(yīng)的Surface
sp<Surface> s = control->getSurface();
//初始化opengl egl
// initialize opengl and egl
const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_NONE
};
EGLint w, h;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
//獲得一塊屏幕
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
//0,0就是代表NULL,正常情況下這個(gè)兩個(gè)是返回换衬,最大,最小的版本
eglInitialize(display, 0, 0);
//選擇我們自己需要的配置
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
//創(chuàng)建一個(gè)Window证芭,注意這個(gè)時(shí)間創(chuàng)建一個(gè)傳入了一個(gè)EGLNativeWindowType,建立起OPENGL和Surface之間的關(guān)系
surface = eglCreateWindowSurface(display, config, s.get(), NULL);
//創(chuàng)建統(tǒng)一的上下文
context = eglCreateContext(display, config, NULL, NULL);
//獲取surface的寬度
eglQuerySurface(display, surface, EGL_WIDTH, &w);
//獲取surface的高度
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
//將這個(gè)上下文和當(dāng)前創(chuàng)建的屏幕和Surface綁定
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
return NO_INIT;
......省略部分代碼
return NO_ERROR;
}
對照應(yīng)用:
一般應(yīng)用都是基于Activity開發(fā)的瞳浦,其實(shí)Activity就是幫你隱藏了太多像SurfaceFlinger申請Surface,并在Surface使用Canvas進(jìn)行繪制废士。
申請Surface
/frameworks/base/core/java/android/view/ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...省略部分代碼
//mSurface需要通過relayout方法叫潦,才能變成可用的Surface
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurface);
return relayoutResult;
}
通過Surface獲得Canvas對象然后傳遞給view進(jìn)行ondraw方法的繪制
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// Draw with software renderer.
final Canvas canvas;
....省略代碼
// 通過前面申請的Surface鎖定以后,獲得Canvas對象
canvas = mSurface.lockCanvas(dirty);
....省略代碼
// 將Canvas對象傳遞給View進(jìn)行draw的方法的回調(diào)官硝,也就是當(dāng)前的activity開始界面繪制
mView.draw(canvas);
....省略代碼
return true;
}
總結(jié)
對BootAnimation和應(yīng)用進(jìn)行比較矗蕊,我更多的希望讀者可以透過現(xiàn)象看本質(zhì),其實(shí)開機(jī)動畫和應(yīng)用非常類似氢架。只不過應(yīng)用的啟動需要Zygote進(jìn)程傻咖,AMS,PMS达箍,WMS的支持没龙,分裝了好多對Binder初始化,Surface的申請缎玫,繪制硬纤,而開機(jī)動畫啟動比較快,這個(gè)時(shí)候Zygote進(jìn)程赃磨,AMS筝家,PMS,WMS都還沒有準(zhǔn)備好邻辉,只能依靠SurfaceFlinger和Binder的最原始的接口來顯示UI溪王。
進(jìn)一步思考
BootAnimation不支持觸摸事件腮鞍,應(yīng)用支持觸摸事件,我們能否讓BootAnimation也支持觸摸事件莹菱,應(yīng)用的觸摸事件的本質(zhì)是什么 移国?