編輯:關於Android編程
在講它們之間的通信之前,我們先以MediaServer為例看看普通Server進程都在干些什麼。
int main()
{
……
// 獲得ProcessState實例
sp proc(ProcessState::self());
// 得到ServiceManager的Binder客戶端實例
sp sm = defaultServiceManager();
……
// 通過ServiceManager的Binder客戶端向系統注冊MediaPlayer服務
MediaPlayerService::instantiate();
……
// start run
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
defaultServiceManager()在上一篇中已經有介紹。
MediaPlayerService::instantiate()的實現如下,就是addService到ServiceManager,和上一篇的getService類似,故不作介紹。void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
接下來看ProcessState::self()->startThreadPool()的實現void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp t = new PoolThread(isMain);
t->run(name.string());
}
}
class PoolThread : public Thread
{
public:
PoolThread(bool isMain)
: mIsMain(isMain)
{
}
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
const bool mIsMain;
};
實際上,這個函數不過是創建了一個新的線程,然後在線程中又創建了一個IPCThreadState,並調用了joinThreadPool函數。void IPCThreadState::joinThreadPool(bool isMain)
{
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
set_sched_policy(mMyThreadId, SP_FOREGROUND);
status_t result;
do {
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
mProcess->mDriverFD, result);
abort();
}
// Let this thread exit the thread pool if it is no longer
// needed and it is not the main process thread.
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
result = executeCommand(cmd);
set_sched_policy(mMyThreadId, SP_FOREGROUND);
}
return result;
}
我們可以看到,主線程和新創建的線程,都在做一件事,talkWithDriver讀取Binder驅動,然後就executeCommand處理請求。這就是普通Server進程啟動後一直都在干的事:等待客戶端請求,處理請求,然後返回給客戶端。
既然Server進程已經准備就緒了,那麼現在就應該要輪到Client端閃亮登場,Client端將通過Binder來請求Server做一些事情。接下來看代碼:status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
status_t err = UNKNOWN_ERROR;
const sp& service(getMediaPlayerService());
if (service != 0) {
sp player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
}
err = attachNewPlayer(player);
}
return err;
}
getMediaPlayerService()之前分析過,返回一個BpMediaPlayerService,這裡問一個問題:為什麼這個BpMediaPlayerService就能和MediaPlayerService進程進行Binder通信,而不是和別的什麼Server進程?
再回顧一下代碼:/*static*/const sp答案應該在binder = sm->getService(String16("media.player"))這句話裡面,返回值binder將會作為BpMediaPlayerService構造函數的參數。下面來看getService& IMediaDeathNotifier::getMediaPlayerService() { Mutex::Autolock _l(sServiceLock); if (sMediaPlayerService == 0) { sp sm = defaultServiceManager(); sp binder; do { binder = sm->getService(String16("media.player")); if (binder != 0) { break; } ALOGW("Media player service not published, waiting..."); usleep(500000); // 0.5 s } while (true); if (sDeathNotifier == NULL) { sDeathNotifier = new DeathNotifier(); } binder->linkToDeath(sDeathNotifier); sMediaPlayerService = interface_cast (binder); } ALOGE_IF(sMediaPlayerService == 0, "no media player service!?"); return sMediaPlayerService; }
virtual spgetService(const String16& name) const { unsigned n; for (n = 0; n < 5; n++){ sp svc = checkService(name); if (svc != NULL) return svc; ALOGI("Waiting for service %s...\n", String8(name).string()); sleep(1); } return NULL; } virtual sp checkService( const String16& name) const { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); return reply.readStrongBinder(); } sp Parcel::readStrongBinder() const { sp val; unflatten_binder(ProcessState::self(), *this, &val); return val; } status_t unflatten_binder(const sp & proc, const Parcel& in, sp * out) { const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->type) { case BINDER_TYPE_BINDER: *out = static_cast (flat->cookie); return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_HANDLE: *out = proc->getStrongProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast (out->get()), *flat, in); } } return BAD_TYPE; }
unflatten_binder函數中,flat->type和flat->handle在ServiceManager中被賦值,其中flat->type的值是BINDER_TYPE_HANDLE,flat->handle的值是所查詢Service對應的Handle值,中間過程涉及到binder底層驅動的代碼,不在這裡闡述。
這樣binder = sm->getService(String16("media.player"))執行後,binder=BpBinder(Handle),其中Handle的值為所查詢Service對應的Handle值,這樣,client端和service端之間的通信就建立了。
分析完getMediaPlayerService(),建立了通信的通路,接下來正式進入通信。sp
virtual sp經過之前的分析,我們可以很容易的指導remote()返回的是BpBinder(handle), transact(CREATE,data, &reply)將數據寫入到Binder驅動,並喚醒Service進程,接下來我們就來看Server將作何反應。現在我們已經知道,Server進程一直都在讀取Binder驅動,然後executeCommand,下面就直接看executeCommand的實現。create( const sp & client, int audioSessionId) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); data.writeStrongBinder(client->asBinder()); data.writeInt32(audioSessionId); remote()->transact(CREATE, data, &reply); return interface_cast (reply.readStrongBinder()); }
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch (cmd) {
……
case BR_TRANSACTION:
{
binder_transaction_data tr;
result = mIn.read(&tr, sizeof(tr));
ALOG_ASSERT(result == NO_ERROR,
"Not enough command data for brTRANSACTION");
if (result != NO_ERROR) break;
Parcel buffer;
buffer.ipcSetDataReference(
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), freeBuffer, this);
const pid_t origPid = mCallingPid;
const uid_t origUid = mCallingUid;
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
if (gDisableBackgroundScheduling) {
if (curPrio > ANDROID_PRIORITY_NORMAL) {
// We have inherited a reduced priority from the caller, but do not
// want to run in that state in this process. The driver set our
// priority already (though not our scheduling class), so bounce
// it back to the default before invoking the transaction.
setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
}
} else {
if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
// We want to use the inherited priority from the caller.
// Ensure this thread is in the background scheduling class,
// since the driver won't modify scheduling classes for us.
// The scheduling group is reset to default by the caller
// once this method returns after the transaction is complete.
set_sched_policy(mMyThreadId, SP_BACKGROUND);
}
}
//ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
Parcel reply;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BR_TRANSACTION thr " << (void*)pthread_self()
<< " / obj " << tr.target.ptr << " / code "
<< TypeCode(tr.code) << ": " << indent << buffer
<< dedent << endl
<< "Data addr = "
<< reinterpret_cast(tr.data.ptr.buffer)
<< ", offsets addr="
<< reinterpret_cast(tr.data.ptr.offsets) << endl;
}
if (tr.target.ptr) {
sp b((BBinder*)tr.cookie);
const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
} else {
const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
}
//ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
// mCallingPid, origPid, origUid);
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
sendReply(reply, 0);
} else {
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
mCallingPid = origPid;
mCallingUid = origUid;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
<< tr.target.ptr << ": " << indent << reply << dedent << endl;
}
}
break;
……
default:
printf("*** BAD COMMAND %d received from Binder driver\n", cmd);
result = UNKNOWN_ERROR;
break;
}
if (result != NO_ERROR) {
mLastError = result;
}
return result;
}
看這裡:
if (tr.target.ptr) {
sp b((BBinder*)tr.cookie);
const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
} else {
const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
}
這裡的b實際上就是我們在addService時創建的MediaPlayerService對象,經過包括Binder驅動在內的傳輸和轉換,就成這麼個數據類型了。void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
看下面的繼承關系,
classMediaPlayerService : public BnMediaPlayerService
MediaPlayerService本身沒有實現transact方法,因此,b->transact(tr.code,buffer, &reply, tr.flags)是調用了BnMediaPlayerService的transact方法。
進入IMediaPlayerService.cpp中找到BnMediaPlayerService的transact方法,如下:status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp client =
interface_cast(data.readStrongBinder());
int audioSessionId = data.readInt32();
sp player = create(client, audioSessionId);
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
……
}
spsp到這裡為止,Server處理完了事務,接下來將處理結果返回給client,看這裡:MediaPlayerService::create(const sp & client, int audioSessionId) { pid_t pid = IPCThreadState::self()->getCallingPid(); int32_t connId = android_atomic_inc(&mNextConnId); sp c = new Client( this, pid, connId, client, audioSessionId, IPCThreadState::self()->getCallingUid()); ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid, IPCThreadState::self()->getCallingUid()); /* add by Gary. start {{----------------------------------- */ c->setScreen(mScreen); /* add by Gary. end -----------------------------------}} */ c->setSubGate(mGlobalSubGate); // 2012-03-12, add the global interfaces to control the subtitle gate wp w = c; { Mutex::Autolock lock(mLock); mClients.add(w); } return c; }
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
sendReply(reply, 0);
} else {
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
status_t err;
status_t statusBuffer;
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
if (err < NO_ERROR) return err;
return waitForResponse(NULL, NULL);
}
調用sendReply將結果寫回Binder驅動,從而得以返回client進程。通信完成。
Android中ProgressDialog的簡單示例
網上一般對進度條的示例都是如何顯示,沒有在任務結束如何關閉的文章,參考其他文章經過試驗之後把整套進度條顯示的簡單示例如下: 建立an
Android開發進階自定義控件之滑動開關實現方法【附demo源碼下載】
本文實例講述了Android開發進階自定義控件之滑動開關實現方法。分享給大家供大家參考,具體如下:自定義開關控件Android自定義控件一般有三種方式1、繼承Androi
Android init源代碼分析(2)init.rc解析
本文描述init.rc腳本解析以及執行過程,讀完本章後,讀者應能 (1) 了解init.rc解析過程 (2) 定制init.rc ini
Intellij IDEA + Android SDK + Genymotion Emulator打造最佳Android開發環境
一直使用Eclipse環境開發Android,也嘗鮮使用過Android Studio去開發,各種IDE配合Android SDK及SDK原生的Android Emula