MediaPlayerService分析
一.MediaPlayerService簡介
1.Media Service的啟動
Media程序定義:
service media /system/bin/mediaserver
class main
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc
ioprio rt 4
servicemanager 程序定義:
service servicemanager /system/bin/servicemanager
class core
user system
group system
Media中有很多Native Service(AudioFlinger MediaPlayerService CameraService AudioPolicyService等),整個Android(native或者framework)的Service都需要加入servicemanager中進行統一管理。 那麼Media Process 與ServiceManager Process是如何進行通訊的呢——Binder.通過Media Process中使用binder進行完成IPC過程,學習Binder的概念和使用方法。
2.MediaPlayer的檔案
MediaPlayer是從MediaPlayer.java開始的,應用如果想播放音樂,先要new 一個MediaPlayer,並設定其相關的引數。先列出主要的程式碼檔案,他們都在frameworks裡面:
Java層程式碼:base/media/java/android/media/MediaPlayer.java
Jni層程式碼:
base/media/jni/android_media_MediaPlayer.cpp
原生代碼:
av/media/libmedia/mediaplayer.cpp
./av/media/libmedia/IMediaPlayer.cpp
MediaPlayerService
av/media/libmediaplayerservice/MediaPlayerService.cpp
二.分析MediaPlayer的Binder機制
\frameworks\av\media\mediaserver\ main_mediaserver.cpp:
1 . ProcessState物件建立
當前程序的狀態屬性,物件建立:
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState;
return gProcess;
}
ProcessState建構函式:
ProcessState::ProcessState()
: mDriverFD(open_driver()) //開啟binder驅動裝置
, mVMStart(MAP_FAILED)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
//將binder的fd對映到當前程序虛擬空間地址中 與binder進行互動
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ,
MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
}
}
開啟binder裝置:
static int open_driver()
{
//開啟binder裝置驅動
int fd = open("/dev/binder", O_RDWR);
if (fd >= 0) {
//bidner最大支援執行緒數
size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
}
return fd;
}
ProcessState物件建立過程所做的事: 開啟/dev/binder裝置,得到binder裝置的fd; 將bidner裝置fd對映到當前程序虛擬地址空間建立互動的通道;
2. IServiceManager物件建立 sp<IServiceManager> sm = defaultServiceManager(); 為什麼需要一個IServiceManager物件呢,這個類是個抽象提供介面
class IServiceManager : public IInterface
{
virtual sp<IBinder> getService( const String16& name) const = 0;
virtual status_t addService( const String16& name,
const sp<IBinder>& service,
bool allowIsolated = false) = 0;
……
};
通過IServiceManager派生物件操作將Service加入到ServiceManager中.
defaultServiceManager()函式:
ProcessState::self()->getContextObject(NULL):得到一個IBinder物件
ProcessState::self()剛才所建立的ProcessState物件。
handle_entry是什麼呢?
struct handle_entry {
IBinder* binder;
RefBase::weakref_type* refs;
};
也就是說ProcessState 有一個表Vector<handle_entry>mHandleToObject;
表裡面的每一項儲存了一個binder,每一個binder對應一個handle。
Handle = 0是什麼,控制代碼?代表誰的控制代碼——ServiceManager在binder中的資源。從ProcessState::self()->getContextObject(NULL)得到一個 IBinder——BpBinder(0);
於是得到: gDefaultServiceManager = interface_cast<IServiceManager>(BpBinder(0));
使用interface_cast將IBinder例項轉化成IServiceManager例項。
翻譯為:
inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)
{
return IServiceManager::asInterface(obj);
}
替換成IServiceManager:
android::sp<IServiceManager> IServiceManager::asInterface(
const android::sp<android::IBinder>& obj)
{
//obj BpBinder例項
android::sp<IServiceManager> intr;
if (obj != NULL) {
//返回NULL
intr = static_cast<IServiceManager*>(
obj->queryLocalInterface(
IServiceManager::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
這裡得到IServiceManager 例項:
BpServiceManager:new BpServiceManager(new BpBinder(0));
關注BpServiceManager繼承於模板類BpInterface
BpInterface建構函式:
BpRefBase建構函式:
gDefaultServiceManager = interface_cast<IServiceManager>(BpBinder(0));實際為: gDefaultServiceManager = new BpServiceManager(new BpBinder(0));Bn代表Binder Native Bp代表Binder ProxyBpServiceManager代理的BpBinder例項 BpBinder代理的handle(0)
這個關係有些複雜,看一下類繼承結構圖:
上面這個結構看起來感覺很熟悉——Bridge模式。將Binder資料互動和功能處理橋接起來。
在Media Process 的main函式中通過:
sp<IServiceManager> sm = defaultServiceManager(); 我們得到了sm:是BpServiceManager物件。
3. MediaPlayerService加入到ServiceManager中
回到main函式中:
MediaPlayerService::instantiate();
BpServiceManager新增Service:
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
//生成資料包Parcel
Parcel data, reply;
// Write RPC headers 寫入Interface名字 得到“android.os.IServiceManager”
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
//寫入Service名字 “media.player”
data.writeString16(name);
//寫入服務
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
// remote()返回BpBinder物件
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
remote()->transact 到BpBinder中:
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
if (mAlive) {
//到當前程序IPCThreadState中 mHandle=0
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
IPCThreadState中寫入資料到Binder裝置過程
IPCThreadState::self()->transact過程:
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
//將資料轉化成binder_transaction_data 寫入到Parcel例項mOut中
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
//寫入資料
err = waitForResponse(reply);
return err;
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
while (1) {
//將資料寫入到Binder裝置中
talkWithDriver();
……
}
return err;
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
//將資料封裝成binder_write_read結構
binder_write_read bwr;
do {
//將資料寫入到所開啟的Binder裝置中
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
……
} while (err == -EINTR);
return NO_ERROR;
}
將MediaPlayerService加入到ServiceManager中,
這裡就通過BpServiceManager的AddService將資料寫入到Binder裝置傳遞給ServiceManager。
繼續Media Process過程
4. Media Process訊息循環
int main(int argc, char** argv)
{
//啟動程序的執行緒池
ProcessState::self()->startThreadPool(); //走到了這裡
//執行執行緒訊息迴圈
IPCThreadState::self()->joinThreadPool();
}
- 建立工作者執行緒
void ProcessState::startThreadPool()
{
spawnPooledThread(true);
}
void ProcessState::spawnPooledThread(bool isMain)
{
//建立PoolThread物件 並run ,非執行緒
sp<Thread> t = new PoolThread(isMain);
t->run(buf);
}
PoolThread繼承Thread
執行Thread的run函式:
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
//建立執行緒mThread _threadLoop
bool res;
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
return NO_ERROR;
}
現在有兩個執行緒:主執行緒和mThread執行緒
mThread執行緒執行:_threadLoop
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user);
do {
//呼叫子類的threadLoop
result = self->threadLoop();
……
} while(strong != 0);
return 0;
}
class PoolThread : public Thread
{
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
};
2 程序間通訊訊息迴圈過程
訊息迴圈:
void IPCThreadState::joinThreadPool(bool isMain)
{
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
status_t result;
//訊息迴圈
do {
int32_t cmd;
//從binder裝置中讀取命令
result = talkWithDriver();
if (result >= NO_ERROR) {
cmd = mIn.readInt32();
//執行命令
result = executeCommand(cmd);
}
……
} while (result != -ECONNREFUSED && result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
命令執行:
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
switch (cmd) {
case BR_DECREFS:
break;
case BR_ATTEMPT_ACQUIRE:
break;
case BR_TRANSACTION:
binder_transaction_data tr;
result = mIn.read(&tr, sizeof(tr));
if (tr.target.ptr) {
//將目標物件轉化成BBinder
sp<BBinder> b((BBinder*)tr.cookie);
//呼叫BBinder的transact 函式
const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
}
break;
……
default:
}
return result;
}
binder_transaction_data.cookie:target object cookie目標物件,這個target object是指那個呢?
在Media Process裡面有幾個Service:AudioFlinger、MediaPlayerService、CameraService等。
這個目標是這其中Service中的一個,假設目標物件為為MediaPlayerService,那為何要轉化成BBinder呢?
3 Service對命令的處理
執行緒從binder接收到訊息命令,將命令傳遞給Service處理。將目標物件轉化成BBinder,然後排程此命令;
命令從遠端傳遞到本地端進行處理,每個Service都對應BnXXX物件來處理遠端BpXXX傳來的命令。
sp<BBinder> b((BBinder*)tr.cookie); const status_t error = b->transact(tr.code, buffer, &reply, tr.flags); 這裡b代表某個Service:假設為MediaPlayerService;弄清楚執行過程,要弄清楚類繼承關係。
本地端BnMediaPlayerService訊息處理過程:真正的物件是MediaPlayerService例項。從BBinder ->transact開始傳遞:
status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
//onTransact是個virtual函式 派生類BnMediaPlayerService重寫
err = onTransact(code, data, reply, flags);
return err;
}
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
pid_t pid = data.readInt32();
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
//create是個virtual函式 派生類MediaPlayerService重寫
sp<IMediaPlayer> player = create(pid, client, audioSessionId);
//建立player寫入到資料包中 傳回
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
……
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
可以看看Client類繼承關係結構圖:
看到這個跟上面的MediaPlayerService繼承關係非常的相似,
這個結構也非常的熟悉——Adapter模式;將binder訊息互動和命令處理適配到一起。
三Client端與Service端交互
Client對Service進行使用Binder通訊,是得到一個Service BnXXX端物件的代理,在Client 為BpXXX代理,
然後使用此代理進行相關的操作. 前面在使用ServiceManager就是此種方式進行。
實際上通訊的基礎是Binder,Proxy不過是在Binder上進行了一層封裝,封裝了對binder驅動的底層操作,使具有面向物件的特性。
任意兩個程序通過Binder進行通訊,就是先得到另一個程序的binder標識,通過此binder進行資料交換。
1 新增加一個服務 看下面media Palyer類繼承結構。實現Bn端類繼承結構:
實現Bn端類繼承結構:
實現Bp端類繼承結構:
可以看到
一.MediaPlayerService簡介
1.Media Service的啟動
Media程序定義:
service media /system/bin/mediaserver
class main
user media
group a 水平 用途 環境 大學生 空間 黑板 層次 重要性 可能 1.
名字:韓梅梅
年齡:39
職業:食堂阿姨
代表的用戶在市場上的比例和重要性:5% 較重要
知識層次和能力:可能不太會熟練使用手機APP
使用本軟件的環境:食堂或其他地方
典型場景:食堂阿姨撿到一張飯卡,將此信 abstract nike 自己 this script dex java fcm cep
本文是Pig系統分析系列中的最後一篇了,主要討論怎樣擴展Pig功能。不僅介紹Pig本身提供的UDFs擴展機制,還從架構上探討Pig擴展可能性。
補充說明:前些天同事發現twitt 找到 越來越大 展示 需求分析 人的 分發 競爭 做到 推廣 一、團隊項目簡介
團隊名稱:SmartCoder
項目名稱:《一起》
二、針對 " 地圖可視化查看發布的內容 " 這一特點進行 NABCD 分析
N(Need需求)
往往用戶通過瀏覽文字信息這個模式 百度 數據庫 python 技術 兼容性 一. 前言 先說下主要的框架和主要的圖形庫的特點:(個人見解) Django:python開發的一個重量級的web框架,集成了MVC和ORM等技術,設計之初是為了使開發復雜的、數據庫驅動的網站變 使用 大學 記錄 困難 能力 排名 沒有 習慣 多少 1、姓名:李嬌嬌
2、年齡:21
3、開銷:每月500-1000,但有自己的一部分收入
4、代表的用戶在市場上的比例和重要性:該用戶代表了絕大多數的大學生,是我們軟件的典型用戶
5、使用這個軟件的典型場景:每天在宿舍裏記 案例 並發 刷新 隨機 url 對策 -- 技術 動態生成 1. 秒殺活動的技術挑戰及應對策略
1.1 對現有網站業務造成沖擊
秒殺活動具有時間短,並發訪問量大的特點,必然會對現有業務造成沖擊。對策:秒殺系統獨立部署
1.2 高並發下的應用、 一個 全局變量 加密 下使用 txt 保存 返回 每次 格式
JavaScript中的還有一個機制:cookie,則能夠達到真正全局變量的要求。 cookie是瀏覽器 提供的一種機制,它將document 對象的cookie屬性提供給Jav rip run writer fault esp 身份驗證 int 網站 table 結合六個基本質量屬性
可用性:
異常
可修改性:
1.維持語義的一致性,高內聚低耦合
2.維持現有的接口,Login依賴LoginIService接口,LoginService依賴ILog and tomat ron per vertica small resource resources start DBD = Database Designer,是Vertica數據庫優化中最主要的原生工具。
首先運行admintools工具,按下面步驟依次執行:
1.選擇 all lec 水平 next log lag 到來 delayed eas 考慮如下情況(實際一般不會做,這裏只是舉個例子):
在主線程中創建一個socket、綁定到本地端口並監聽
在主線程中創建一個epoll實例(epoll_create(2))
將監聽socket添 nbsp 項目文件 需要 管理器 TTT lease new projects build VS2013 解決方案文件結構分析
Visual Studio 的解決方案文件是一個文本文件,其中的內容不是太復雜,有些時候 Visual Studio 會把這個文件搞亂,理解一下 reac 設置 有變 相關 article class ces col cal Yii是一個基於組件、用於開發大型 Web 應用的高性能 PHP 框架。CComponent幾乎是所有類的基類,它控制著組件與事件的管理,其方法與屬性如下,私有變量$_e數據存放事件(evnet 基於 進一步 使用 sdn 能夠 疑惑 樓梯 -1 們的
進行算法設計的時候,時常有這種體會:假設已經知道一道題目能夠用動態規劃求解,那麽非常easy找到對應的動態規劃算法並實現;動態規劃算法的難度不在於實現,而在於分析和設計—— 首先你得知道這道題目須要用動態規劃來求 同學 一定的 問題: 活動 下載 相關 什麽 方便 數據庫 Need:
由本人親身體驗出發,覺得很多同學記不住老師留的作業,或者上課時間記錄了,但是老是忘記到底記錄了什麽,導致沒有半大寫作業,所以準備設計一個東西來幫助同學。
A:
首先,我最先想到的是手機,所以準備用相 ble lpad tab 環境 知識 span 場景 其他人 解決
名字
學生(註重飲食選擇,挑剔)
年齡
20
收入
無
知 工資 學生 銀行 用戶分析 使用 層次 排名 校園 手機軟件 (1)姓名:陳晨
(2)年齡:20
(3)開銷:每月1500-2000,每月有固定的兼職收入
(4)代表的用戶在市場上的比例和重要性:該用戶代表了絕大多數的大學生,是我們軟件針對的典型用戶
(5)使用這個軟件的典 elk mic als emc usb won tm4 pop iap 此文僅個人理解,不到之處望指出
提出:easyui的datagrid組件有海量分頁的內容,通過查詢數據庫的所有數據在表格進行分頁瀏覽,因為數據量多,也叫海量分頁,
網 接受 alt 快遞 png 技術分享 信用 發布任務 背景 .cn 用戶場景分析:
典型用戶:
張三:有諸多事務等抽不開身;李四:比較懶
1、背景
(1)典型用戶:張三李四
(2)用戶需求/迫切需要解決的問題:有事不能抽開身,誰能幫我個忙啊;一些小事不想自己幹,誰可 sql 格式 一段 ace 研究 軟件測試 tar 遊戲 很好 這兩天幫忙定位一個MySQL查詢很慢的問題,定位過程綜合各種方法、理論、工具,很有代表性,分享給大家作為新年禮物:)
【問題現象】
使用sphinx支持倒排索引,但sphinx從mysql查詢源數據的 相關推薦
MediaPlayerService分析
典型用戶和場景分析
Pig系統分析(8)-Pig可擴展性
團隊項目——軟件需求分析(NABCD)
flask框架+pygal+sqlit3搭建圖形化業務數據分析平臺
典型用戶分析
《大型網站技術架構:核心原理與案例分析》-- 讀書筆記 (5) :網購秒殺系統
js中cookie的使用具體分析
MVC模式在Java Web應用程序中的實例分析
Vertica DBD 分析優化設計
epoll驚群原因分析
VS 解決方案文件結構分析
YII框架分析筆記2:組件和事件行為管理
動態規劃分析總結——怎樣設計和實現動態規劃算法
團隊題目需求分析-NABCD
用戶場景分析i
軟件開發—錢多多—典型用戶分析
海量分頁的簡單分析
用戶場景分析
實戰:MySQL Sending data導致查詢很慢的問題詳細分析(轉)