1. new AudioTrack和set分析
Native的AudioTrack代碼在AudioTrack.cpp中。這一節,分析它的構造函數和set調用。
**AudioTrack.cpp**
~~~
AudioTrack::AudioTrack()//我們使用無參構造函數
:mStatus(NO_INIT)
{
//把狀態初始化成NO_INIT。Android的很多類都采用了這種狀態控制
}
~~~
再看看set調用,這個函數有很多內容。
**AudioTrack.cpp**
~~~
/*
還記得我們傳入的參數嗎?
streamType=STREAM_MUSIC,sampleRate=8000,format=PCM_16
channels=2,frameCount由計算得來,可以假設一個值,例如1024,不影響分析。
flags=0,cbf=audiocallback, user為cbf的參數,notificationFrames=0
因為是流模式,所以sharedBuffer=0。threadCanCallJava 為true
*/
status_t AudioTrack::set(int streamType,uint32_t sampleRate,int format,
int channels,int frameCount,uint32_t flags,callback_t cbf,void* user,
int notificationFrames,const sp<IMemory>& sharedBuffer,
boolthreadCanCallJava)
{
//前面有一些判斷,都是和AudioSystem有關的,以后再分析
......
/*
audio_io_handle_t是一個int類型,通過typedef定義,這個值的來歷非常復雜,
涉及AudioFlinger和AudioPolicyService, 后邊的分析試將其解釋清楚。
這個值主要被AudioFlinger使用,用來表示內部的工作線程索引號。AudioFlinger會根據
情況創建幾個工作線程,下面的AudioSystem::getOutput會根據流類型等其他參數最終選
取一個合適的工作線程,并返回它在AF中的索引號。
而AudioTrack一般使用混音線程(Mixer Thread)
*/
audio_io_handle_toutput = AudioSystem::getOutput(
(AudioSystem::stream_type)streamType,
sampleRate,format, channels,
(AudioSystem::output_flags)flags);
//調用creatTrack
status_t status = createTrack(streamType, sampleRate, format,channelCount,
frameCount,flags, sharedBuffer, output);
//cbf是JNI層傳入的回調函數audioCallback,如果用戶設置了回調函數,則啟動一個線程
if (cbf!= 0) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
}
returnNO_ERROR;
}
~~~
再看createTrack函數:
**AudioTrack.cpp**
~~~
status_t AudioTrack::createTrack(intstreamType,uint32_t sampleRate,
int format,int channelCount,int frameCount, uint32_t flags,
const sp<IMemory>& sharedBuffer, audio_io_handle_t output)
{
status_tstatus;
/*
得到AudioFlinger的Binder代理端BpAudioFlinger。
關于這部分內容,我們已經很熟悉了,以后的講解會跨過Binder,直接分析Bn端的實現
*/
constsp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
/*
向AudioFinger發送createTrack請求。注意其中的幾個參數,
在STREAM模式下sharedBuffer為空
output為AudioSystem::getOutput得到一個值,代表AF中的線程索引號
該函數返回IAudioTrack(實際類型是BpAudioTrack)對象,后續AF和AT的交互就是
圍繞IAudioTrack進行的
*/
sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),
streamType,sampleRate,format,channelCount,frameCount,
((uint16_t)flags) << 16,sharedBuffer,output,&status);
/*
在STREAM模式下,沒有在AT端創建共享內存,但前面提到了AT和AF的數據交互是
通過共享內存完成的,這塊共享內存最終由AF的createTrack創建。我們以后分析AF時
再做介紹。下面這個調用會取出AF創建的共享內存
*/
sp<IMemory> cblk = track->getCblk();
mAudioTrack.clear();//sp的clear
mAudioTrack= track;
mCblkMemory.clear();
mCblkMemory= cblk;//cblk是control block的簡寫
/*
IMemory的pointer在此處將返回共享內存的首地址,類型為void*,
static_cast直接把這個void*類型轉成audio_track_cblk_t,表明這塊內存的首部中存在
audio_track_cblk_t這個對象
*/
mCblk= static_cast<audio_track_cblk_t*>(cblk->pointer());
mCblk->out = 1;//out為1表示輸出,out為0表示輸入
mFrameCount = mCblk->frameCount;
if(sharedBuffer == 0) {
//buffers指向數據空間,它的起始位置是共享內存的首部加上audio_track_cblk_t的大小
mCblk->buffers= (char*)mCblk + sizeof(audio_track_cblk_t);
} else {
//STATIC模式下的處理
mCblk->buffers =sharedBuffer->pointer();
mCblk->stepUser(mFrameCount);//更新數據位置,后面要分析stepUser的作用
}
returnNO_ERROR;
}
~~~
(1)IAudioTrack和AT、AF的關系
上面的createTrack函數中突然冒出來一個新面孔,叫IAudioTrack。關于它和AT及AF的關系,我們用圖7-3來表示:
:-: 
圖7-3 IAudioTrack和AT、AF的關系
從圖7-3中可以發現:
- IAudioTrack是聯系AT和AF的關鍵紐帶。
至于IAudioTrack在AF端到底是什么,在分析AF時會有詳細解釋。
(2)共享內存及其Control Block
通過前面的代碼分析,我們發現IAudioTrack中有一塊共享內存,其頭部是一個audio_track_cblk_t(簡稱CB)對象,在該對象之后才是數據緩沖。這個CB對象有什么作用呢?
還記得前面提到的那個深層次思考的問題嗎?即MemoryHeapBase和MemoryBase都沒有提供同步對象,那么,AT和AF作為典型的數據生產者和消費者,如何正確協調二者生產和消費的步調呢?
Android為順應民意,便創造出了這個CB對象,其主要目的就是協調和管理AT和AF二者數據生產和消費的步伐。先來看CB都管理些什么內容。它的聲明在AudioTrackShared.h中,而定義卻在AudioTrack.cpp中。
**AudioTrackShared.h::audio_track_cblk_t聲明**
~~~
struct audio_track_cblk_t
{
Mutex lock;
Condition cv;//這是兩個同步變量,初始化的時候會設置為支持跨進程共享
/*
一塊數據緩沖同時被生產者和消費者使用,最重要的就是維護它的讀寫位置了。
下面定義的這些變量就和讀寫的位置有關,雖然它們的名字并不是那么直觀。
另外,這里提一個擴展問題,讀者可以思考一下:
volatile支持跨進程嗎?要回答這個問題需要理解volatile、CPU Cache機制和共享內存的本質
*/
volatile uint32_t user; //當前寫位置(即生產者已經寫到什么位置了)
volatile uint32_t server; //當前讀位置
/*
userBase和serverBase要和user及server結合起來用。
CB巧妙地通過上面幾個變量把一塊線性緩沖當做環形緩沖來使用,以后將單獨分析這個問題
*/
uint32_t userBase; //
uint32_t serverBase;
void* buffers; //指向數據緩沖的首地址
uint32_t frameCount;//數據緩沖的總大小,以Frame為單位
uint32_t loopStart; //設置打點播放(即設置播放的起點和終點)
uint32_t loopEnd;
int loopCount;//循環播放的次數
volatile union {
uint16_t volume[2];
uint32_t volumeLR;
}; //和音量有關系,可以不管它
uint32_t sampleRate;//采樣率
uint32_t frameSize;//一單位Frame的數據大小
uint8_t channels;//聲道數
uint8_t flowControlFlag;//控制標志,見下文分析
uint8_t out; // AudioTrack為1,AudioRecord為0
uint8_t forceReady;
uint16_t bufferTimeoutMs;
uint16_t waitTimeMs;
//下面這幾個函數很重要,后續會詳細介紹它們
uint32_t stepUser(uint32_tframeCount);//更新寫位置
bool stepServer(uint32_tframeCount);//更新讀位置
void* buffer(uint32_toffset) const;//返回可寫空間起始位置
uint32_t framesAvailable();//還剩多少空間可寫
uint32_t framesAvailable_l();
uint32_t framesReady();//是否有可讀數據
}
~~~
關于CB對象,這里要專門講解一下其中flowControlFlag的意思:
- 對于音頻輸出來說,flowControlFlag對應著underrun狀態,underrun狀態是指生產者提供數據的速度跟不上消費者使用數據的速度。這里的消費者指的是音頻輸出設備。由于音頻輸出設備采用環形緩沖方式管理,當生產者沒有及時提供新數據時,輸出設備就會循環使用緩沖中的數據,這樣就會聽到一段重復的聲音。這種現象一般被稱作“machinegun”。對于這種情況,一般的處理方法是暫停輸出,等數據準備好后再恢復輸出。
- 對于音頻輸入來說,flowControlFlag對于著overrun狀態,它的意思和underrun一樣,只是這里的生產者變成了音頻輸入設備,而消費者變成了Audio系統的AudioRecord。
* * * * *
**說明**:目前這個參數并不直接和音頻輸入輸出設備的狀態有關系。它在AT和AF中的作用必須結合具體情況,才能分析。
* * * * *
圖7-4表示CB對象和它所駐留的共享內存間的關系:
:-: 
圖7-4 共享內存和CB的關系
* * * * *
**注意**:CB實際是按照環形緩沖來處理數據讀寫的,所以user和server的真實作用還需要結合userBase和serverBase。圖7-4只是一個示意圖。
* * * * *
另外,關于CB,還有一個神秘的問題。先看下面這行代碼:
~~~
mCblk =static_cast<audio_track_cblk_t*>(cblk->pointer());
~~~
這看起來很簡單,但仔細琢磨會發現其中有一個很難解釋的問題:
- cblk->pointer返回的是共享內存的首地址,怎么把audio_track_cblk_t對象塞到這塊內存中呢?
這個問題將通過對AudioFlinger的分析,得到答案。
* * * * *
**說明**:關于audio_track_cblk_t的使用方式,后文會有詳細分析。
* * * * *
(3)數據的Push or Pull
在JNI層的代碼中可以發現,在構造AudioTrack時,傳入了一個回調函數audioCallback。由于它的存在,導致了Native的AudioTrack還將創建另一個線程AudioTrackThread。它有什么用呢?
這個線程與外界數據的輸入方式有關系,AudioTrack支持兩種數據輸入方式:
- Push方式:用戶主動調用write寫數據,這相當于數據被push到AudioTrack。MediaPlayerService一般使用這種這方式提供數據。
- Pull方式:AudioTrackThread將利用這個回調函數,以EVENT_MORE_DATA為參數主動從用戶那pull數據。ToneGenerator使用這種方式為AudioTrack提供數據。
這兩種方式都可以使用,不過回調函數除了EVENT_MORE_DATA外,還能表達其他許多意圖,這是通過回調函數的第一個參數來表明的。一起來看:
**AudioTrack.h::event_type**
~~~
enum event_type {
EVENT_MORE_DATA = 0, //表示AudioTrack需要更多數據
EVENT_UNDERRUN = 1,//這是Audio的一個術語,表示Audio硬件處于低負荷狀態
//AT可以設置打點播放,即設置播放的起點和終點,LOOP_END表示已經到達播放終點
EVENT_LOOP_END= 2,
/*
數據使用警戒通知。該值可通過setMarkerPosition ()設置。
當數據使用超過這個值時,AT會且僅通知一次,有點像WaterMarker。
這里所說的數據使用,是針對消費者AF消費的數據量而言的
*/
EVENT_MARKER = 3,
/*
數據使用進度通知。進度通知值由setPositionUpdatePeriod()設置,
例如每使用500幀通知一次
*/
EVENT_NEW_POS = 4,
EVENT_BUFFER_END = 5 //數據全部被消耗
};
~~~
請看AudioTrackThread的線程函數threadLoop。
**AudioTrack.cpp**
~~~
bool AudioTrack::AudioTrackThread::threadLoop()
{
//mReceiver就是創建該線程的AudioTrack
returnmReceiver.processAudioBuffer(this);
}
~~~
**AudioTrack.cpp**
~~~
bool AudioTrack::processAudioBuffer(constsp<AudioTrackThread>& thread)
{
BufferaudioBuffer;
uint32_t frames;
size_twrittenSize;
//處理underun的情況
if(mActive && (mCblk->framesReady() == 0)) {
if(mCblk->flowControlFlag == 0) {
mCbf(EVENT_UNDERRUN, mUserData, 0);//under run 通知
if (mCblk->server == mCblk->frameCount) {
/*
server是讀位置,frameCount是buffer中的數據總和
當讀位置等于數據總和時,表示數據都已經使用完了
*/
mCbf(EVENT_BUFFER_END, mUserData, 0);
}
mCblk->flowControlFlag = 1;
if (mSharedBuffer != 0) return false;
}
}
// 循環播放通知
while(mLoopCount > mCblk->loopCount) {
int loopCount = -1;
mLoopCount--;
if(mLoopCount >= 0) loopCount = mLoopCount;
//一次循環播放完畢,loopCount表示還剩多少次
mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
}
if(!mMarkerReached && (mMarkerPosition > 0)) {
if(mCblk->server >= mMarkerPosition) {
//如果數據使用超過警戒值,則通知用戶
mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
//只通知一次,因為該值被設為true
mMarkerReached = true;
}
}
if(mUpdatePeriod > 0) {
while (mCblk->server >= mNewPosition) {
/*
進度通知,但它不是以時間為基準,而是以幀數為基準的。
例如設置每500幀通知一次,假設消費者一次就讀了1500幀,那么這個循環會連續通知3次
*/
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
}
}
if(mSharedBuffer != 0) {
frames = 0;
} else{
frames = mRemainingFrames;
}
do {
audioBuffer.frameCount = frames;
//得到一塊可寫的緩沖
status_t err = obtainBuffer(&audioBuffer, 1);
......
//從用戶那pull數據
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
writtenSize = audioBuffer.size;
......
if(writtenSize > reqSize) writtenSize = reqSize;
//PCM8數據轉PCM16
.......
audioBuffer.size = writtenSize;
audioBuffer.frameCount = writtenSize/mCblk->frameSize;
frames -= audioBuffer.frameCount;
releaseBuffer(&audioBuffer);//寫完畢,釋放這塊緩沖
}
while(frames);
......
returntrue;
}
~~~
關于obtainBuffer和releaseBuffer,后面再分析。這里有一個問題值得思考:
- 用例會調用write函數寫數據,AudioTrackThread的回調函數也讓我們提供數據。難道我們同時在使用Push和Pull模式?
這太奇怪了!來查看這個回調函數的實現,了解一下究竟是怎么回事。該回調函數是通過set調用傳入的,對應的函數是audioCallback。
**android_media_AudioTrack.cpp**
~~~
static void audioCallback(int event, void* user,void *info) {
if(event == AudioTrack::EVENT_MORE_DATA) {
//很好,沒有提供數據,也就是說,雖然AudioTrackThread通知了EVENT_MORE_DATA,
//但是我們并沒有提供數據給它
AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
pBuff->size = 0;
}
......
~~~
懸著的心終于放下來了,還是老老實實地看Push模式下的數據輸入吧。
2. write輸入數據
write函數涉及Audio系統中最重要的關于數據如何傳輸的問題,在分析它的時候,不妨先思考一下它會怎么做。回顧一下我們已了解的信息:
- 有一塊共享內存。
- 有一個控制結構,里邊有一些支持跨進程的同步變量。
有了這些東西,write的工作方式就非常簡單了:
- 通過共享內存傳遞數據。
- 通過控制結構協調生產者和消費者的步調。
>[info] **重點強調**:帶著問題和思考來分析代碼相當于“智取”,它比一上來就直接扎入源碼的“強攻”要高明得多。希望我們能掌握這種思路和方法。
好了,現在開始分析write,看看它的實現是不是如所想的那樣。
**AudioTrack.cpp**
~~~
ssize_t AudioTrack::write(const void* buffer,size_t userSize)
{
if(mSharedBuffer != 0) return INVALID_OPERATION;
if(ssize_t(userSize) < 0) {
returnBAD_VALUE;
}
ssize_t written = 0;
constint8_t *src = (const int8_t *)buffer;
BufferaudioBuffer; // Buffer是一個輔助性的結構
do {
//以幀為單位
audioBuffer.frameCount = userSize/frameSize();
//obtainBuffer從共享內存中得到一塊空閑的數據塊
status_terr = obtainBuffer(&audioBuffer, -1);
......
size_t toWrite;
if(mFormat == AudioSystem::PCM_8_BIT &&
!(mFlags &AudioSystem::OUTPUT_FLAG_DIRECT)) {
//PCM8數據轉PCM16
}else {
//空閑數據緩沖的大小是audioBuffer.size。
//地址在audioBuffer.i8中,數據傳遞通過memcpy完成
toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, src, toWrite);
src += toWrite;
}
userSize -= toWrite;
written += toWrite;
//releaseBuffer更新寫位置,同時會觸發消費者
releaseBuffer(&audioBuffer);
}while (userSize);
returnwritten;
}
~~~
通過write函數,會發現數據的傳遞其實是很簡單的memcpy,但消費者和生產者的協調,則是通過obtainBuffer與releaseBuffer來完成的。現在來看這兩個函數。
3. obtainBuffer和releaseBuffer
這兩個函數展示了做為生產者的AT和CB對象的交互方法。先簡單看看,然后把它們之間交互的流程記錄下來,以后在CB對象的單獨分析部分,我們再來做詳細介紹。
**AudioTrack.cpp**
~~~
status_t AudioTrack::obtainBuffer(Buffer*audioBuffer, int32_t waitCount)
{
intactive;
status_t result;
audio_track_cblk_t* cblk = mCblk;
......
//①調用framesAvailable,得到當前可寫的空間大小
uint32_t framesAvail = cblk->framesAvailable();
if(framesAvail == 0) {
......
//如果沒有可寫空間,則要等待一段時間
result= cblk->cv.waitRelative(cblk->lock,milliseconds(waitTimeMs));
......
}
cblk->waitTimeMs = 0;
if(framesReq > framesAvail) {
framesReq = framesAvail;
}
//user為可寫空間起始地址
uint32_t u = cblk->user;
uint32_tbufferEnd = cblk->userBase + cblk->frameCount;
if (u+ framesReq > bufferEnd) {
framesReq = bufferEnd - u;
}
......
//②調用buffer,得到可寫空間的首地址
audioBuffer->raw = (int8_t *)cblk->buffer(u);
active= mActive;
returnactive ? status_t(NO_ERROR) : status_t(STOPPED);
}
~~~
obtainBuffer的功能,就是從CB管理的數據緩沖中得到一塊可寫空間,而releaseBuffer,則是在使用完這塊空間后更新寫指針的位置。
**AudioTrack.cpp**
~~~
void AudioTrack::releaseBuffer(Buffer*audioBuffer)
{
audio_track_cblk_t* cblk = mCblk;
cblk->stepUser(audioBuffer->frameCount);// ③調用stepUser更新寫位置
}
~~~
obtainBuffer和releaseBuffer與CB交互,一共會有三個函數調用,如下所示:
- framesAvailable判斷是否有可寫空間。
- buffer得到寫空間起始地址。
- stepUser更新寫位置。
請記住這些流程,以后在分析CB時會發現它們有重要作用。
4. delete AudioTrack
到這里,AudioTrack的使命就進入倒計時階段了。來看在它生命的最后還會做一些什么工作。
**AudioTrack.cpp**
~~~
AudioTrack::~AudioTrack()
{
if(mStatus == NO_ERROR) {
stop();//調用stop
if(mAudioTrackThread != 0) {
//通知AudioTrackThread退出
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
mAudioTrack.clear();
//將殘留在IPCThreadState 發送緩沖區的信息發送出去
IPCThreadState::self()->flushCommands();
}
}
~~~
如果不調用stop,析構函數也會先調用stop,這個做法很周到。
**AudioTrack.cpp**
~~~
void AudioTrack::stop()
{
sp<AudioTrackThread> t = mAudioTrackThread;
if (t!= 0) {
t->mLock.lock();
}
if(android_atomic_and(~1, &mActive) == 1) {
mCblk->cv.signal();
/*
mAudioTrack是IAudioTrack類型,其stop的最終處理在AudioFlinger端
*/
mAudioTrack->stop();
//清空循環播放設置
setLoop(0, 0, 0);
mMarkerReached = false;
if (mSharedBuffer != 0) {
flush();
}
if(t != 0) {
t->requestExit();//請求退出AudioTrackThread
}else {
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
}
}
if (t!= 0) {
t->mLock.unlock();
}
}
~~~
stop的工作比較簡單,就是調用IAudioTrack的stop,并且還要求退出回調線程。要重點關注IAudioTrack的stop函數,這個將做為AT和AF交互流程中的一個步驟來分析。
- 前言
- 第1章 閱讀前的準備工作
- 1.1 系統架構
- 1.1.1 Android系統架構
- 1.1.2 本書的架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.3 工具介紹
- 1.3.1 Source Insight介紹
- 1.3.2 Busybox的使用
- 1.4 本章小結
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 學習JNI的實例:MediaScanner
- 2.3 Java層的MediaScanner分析
- 2.3.1 加載JNI庫
- 2.3.2 Java的native函數和總結
- 2.4 JNI層MediaScanner的分析
- 2.4.1 注冊JNI函數
- 2.4.2 數據類型轉換
- 2.4.3 JNIEnv介紹
- 2.4.4 通過JNIEnv操作jobject
- 2.4.5 jstring介紹
- 2.4.6 JNI類型簽名介紹
- 2.4.7 垃圾回收
- 2.4.8 JNI中的異常處理
- 2.5 本章小結
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 屬性服務
- 3.3 本章小結
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 關于zygote的總結
- 4.3 SystemServer分析
- 4.3.1 SystemServer的誕生
- 4.3.2 SystemServer的重要使命
- 4.3.3 關于 SystemServer的總結
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService發送請求
- 4.4.2 有求必應之響應請求
- 4.4.3 關于zygote分裂的總結
- 4.5 拓展思考
- 4.5.1 虛擬機heapsize的限制
- 4.5.2 開機速度優化
- 4.5.3 Watchdog分析
- 4.6 本章小結
- 第5章 深入理解常見類
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初識影子對象
- 5.2.2 第二板斧--由弱生強
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 輕量級的引用計數控制類LightRefBase
- 5.2.5 題外話-三板斧的來歷
- 5.3 Thread類及常用同步類分析
- 5.3.1 一個變量引發的思考
- 5.3.2 常用同步類
- 5.4 Looper和Handler類分析
- 5.4.1 Looper類分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步關系
- 5.4.4 HandlerThread介紹
- 5.5 本章小結
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函數
- 6.2.2 獨一無二的ProcessState
- 6.2.3 時空穿越魔術-defaultServiceManager
- 6.2.4 注冊MediaPlayerService
- 6.2.5 秋風掃落葉-StartThread Pool和join Thread Pool分析
- 6.2.6 你徹底明白了嗎
- 6.3 服務總管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服務的注冊
- 6.3.3 ServiceManager存在的意義
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查詢ServiceManager
- 6.4.2 子承父業
- 6.5 拓展思考
- 6.5.1 Binder和線程的關系
- 6.5.2 有人情味的訃告
- 6.5.3 匿名Service
- 6.6 學以致用
- 6.6.1 純Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小結
- 第7章 深入理解Audio系統
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介紹
- 7.2.2 AudioTrack(Java空間)分析
- 7.2.3 AudioTrack(Native空間)分析
- 7.2.4 關于AudioTrack的總結
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的誕生
- 7.3.2 通過流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 關于AudioFlinger的總結
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的創建
- 7.4.2 重回AudioTrack
- 7.4.3 聲音路由切換實例分析
- 7.4.4 關于AudioPolicy的總結
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 題外話
- 7.6 本章小結
- 第8章 深入理解Surface系統
- 8.1 概述
- 8.2 一個Activity的顯示
- 8.2.1 Activity的創建
- 8.2.2 Activity的UI繪制
- 8.2.3 關于Activity的總結
- 8.3 初識Surface
- 8.3.1 和Surface有關的流程總結
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI層分析
- 8.3.4 Surface和畫圖
- 8.3.5 初識Surface小結
- 8.4 深入分析Surface
- 8.4.1 與Surface相關的基礎知識介紹
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface對象的創建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介紹
- 8.4.7 深入分析Surface的總結
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的誕生
- 8.5.2 SF工作線程分析
- 8.5.3 Transaction分析
- 8.5.4 關于SurfaceFlinger的總結
- 8.6 拓展思考
- 8.6.1 Surface系統的CB對象分析
- 8.6.2 ViewRoot的你問我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小結
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理與機制分析
- 9.2.1 Netlink和Uevent介紹
- 9.2.2 初識Vold
- 9.2.3 NetlinkManager模塊分析
- 9.2.4 VolumeManager模塊分析
- 9.2.5 CommandListener模塊分析
- 9.2.6 Vold實例分析
- 9.2.7 關于Vold的總結
- 9.3 Rild的原理與機制分析
- 9.3.1 初識Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 關于Rild main函數的總結
- 9.3.6 Rild實例分析
- 9.3.7 關于Rild的總結
- 9.4 拓展思考
- 9.4.1 嵌入式系統的存儲知識介紹
- 9.4.2 Rild和Phone的改進探討
- 9.5 本章小結
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模塊分析
- 10.2.2 MSS模塊分析
- 10.2.3 android.process.media媒體掃描工作的流程總結
- 10.3 MediaScanner分析
- 10.3.1 Java層分析
- 10.3.2 JNI層分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 關于MediaScanner的總結
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介紹
- 10.4.2 我問你答
- 10.5 本章小結