Delphi Android程式啟動過程

來源:互聯網
上載者:User

標籤:

Delphi的Android程式是原生的程式,也就是NativeActivity。那麼就需要先看一下NativeActivity的原理,在AndroidManifest.xml檔案裡面指定入口activity為nativeactivity,這樣應用程式一啟動,java虛擬機器這邊就開一個主線程,主線程建立一個活動,就是nativeactivity,這個nativeactivity在建立的過程中就會去應用程式的.so動態連結程式庫中尋找一個函數:__ANativeActivity_onCreate(ANativeActivity, void* size_t),然後調用這個函數,這個函數就是C++代碼中的真正的入口函數,在這個入口函數裡面做寫什麼事情呢,請參考ndk裡面的Native_app_glue。它是這樣來實現的:對這個傳進來的ANativeActivity, 設定這個activity的各種是事件的回呼函數:activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;設定完了之後就調用:
activity->instance = android_app_create(activity, savedState, savedStateSize);這個在Delphi的Androidapi.AppGlue單元中實現,這個就是Delphi下的Android NDK一些對應的簡單封裝。Delphi中在這個單元中匯出了一個ANativeActivity_onCreate就是前面介紹的__ANativeActivity_onCreate,這個函數就相當於是Delphi的Android運行程式的入口函數,在這個函數中Delphi儲存了一個DelphiActivity,用來儲存這個Activity結構。Android的入口位置不再是Delphi的工程檔案位置的Begin  end之間的代碼。這個函數被打包到Lib+工程名.So檔案中,然後作為一個到處函數,程式啟動並執行時候會載入這個So動態庫,然後載入ANativeActivity_onCreate執行Android入口。此程序呼叫android_app_create,同樣在Androidapi.AppGlue單元中。這個代碼如下:
// --------------------------------------------------------------------// Native activity interaction (called from main thread)// -------------------------------------------------------------------- function android_app_create(activity: PANativeActivity; savedState: Pointer; savedStateSize: size_t): Pandroid_app;varandroid_app: Pandroid_app;PipeDescriptors: TPipeDescriptors;attr: pthread_attr_t;thread: pthread_t;beginandroid_app := Pandroid_app(__malloc(SizeOf(TAndroid_app)));FillChar(android_app^, SizeOf(TAndroid_app), 0);android_app^.activity := activity; pthread_mutex_init(android_app^.mutex, nil);pthread_cond_init(android_app^.cond, nil);if savedState <> nil thenbeginandroid_app^.savedState := __malloc(savedStateSize);android_app^.savedStateSize := savedStateSize;Move(PByte(savedState)^, PByte(android_app^.savedState)^, savedStateSize);end; pipe(PipeDescriptors);android_app^.msgread := PipeDescriptors.ReadDes;android_app^.msgwrite := PipeDescriptors.WriteDes; pthread_attr_init(attr);pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);pthread_create(thread, attr, @android_app_entry, android_app); pthread_mutex_lock(android_app^.mutex);while android_app^.running = 0 dopthread_cond_wait(android_app^.cond, android_app^.mutex);pthread_mutex_unlock(android_app^.mutex); Result := android_app;end;
可見在前面又注釋,寫了Native activity interaction (called from main thread),說明這個是主線程調用的。程式首先先建立了一個android_app結構體,然後設定 app的activity。pthread_mutex_init(android_app^.mutex, nil);//建立一個線程同步對象 mutex互斥體,pthread_cond_init(android_app^.cond, nil);//建立一個線程通訊的對象。用於主線程(UI線程)和我們的線程通訊。然後檢查看看android系統之前是否已經為我們的應用程式儲存過狀態。有的話直接恢複就好了。另外比較重要的是android應用程式的螢幕方向變化的話,activity也要從建立立!!!!!然後建立兩個管道對象,一個讓線程用來讀取訊息,一個用來寫入訊息pipe(PipeDescriptors);android_app^.msgread  := PipeDescriptors.ReadDes; //讀取訊息android_app^.msgwrite := PipeDescriptors.WriteDes; //寫然後建立線程,運行程式碼pthread_attr_init(attr);pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);pthread_create(thread, attr, @android_app_entry, android_app);//建立線程,線程入口為android_app_entry,入口的參數為android_app//開始等待線程運行起來pthread_mutex_lock(android_app^.mutex);  while android_app^.running = 0 do    pthread_cond_wait(android_app^.cond, android_app^.mutex);  pthread_mutex_unlock(android_app^.mutex);運行完了之後返回android_app結構,這樣就相當於我們的這個activity的oncreate完成了。然後看一下android_app_entry的這個線程入口函數
function android_app_entry(param: Pointer): Pointer; cdecl; // Delphi: init system unit and RTL.procedure SystemEntry;typeTMainFunction = procedure;varDlsymPointer: Pointer;EntryPoint: TMainFunction;beginDlsymPointer := dlsym(RTLD_DEFAULT, ‘_NativeMain‘);if DlsymPointer <> nil thenbeginEntryPoint := TMainFunction(DlsymPointer);EntryPoint;end;end; varandroid_app: Pandroid_app;looper: PALooper;beginandroid_app := Pandroid_app(param);android_app^.config := AConfiguration_new;//建立應用程式configAConfiguration_fromAssetManager(android_app^.config, android_app^.activity^.assetManager);  //從主線程擷取訊息用android_app^.cmdPollSource.id := LOOPER_ID_MAIN;android_app^.cmdPollSource.app := android_app;android_app^.cmdPollSource.process := @process_cmd;//設定處理cmd的命令的函數android_app^.inputPollSource.id := LOOPER_ID_INPUT;android_app^.inputPollSource.app := android_app;android_app^.inputPollSource.process := @process_input;//輸入事件處理的函數  //建立一個looper訊息迴圈,用來抓取訊息looper := ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);ALooper_addFd(looper, android_app^.msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, nil, @android_app^.cmdPollSource);android_app^.looper := looper; pthread_mutex_lock(android_app^.mutex);android_app^.running := 1; //設定,讓線程從等待開啟運行中退出,也就是從android_app_create中退出pthread_cond_broadcast(android_app^.cond);pthread_mutex_unlock(android_app^.mutex); { Delphi: this will initialize System and any RTL related functions, call unit initialization sections and thenproject main code, which will enter application main loop. This call will block until the loop ends, which istypically signalled by android_app^.destroyRequested. }SystemEntry;//這裡才是調用了工程檔案的Begin End之間的代碼,是Delphi的一個封裝,實際上,在Android Native中調用的是android_main(android_app);然後再這裡執行一些訊息的處理,等待程式運行結束{ This place would be ideal to call unit finalization, class destructors and so on. }// Halt; android_app_destroy(android_app);//銷毀android_app退出線程。這裡實際上才是Android程式的終結 Result := nil;end;

 

然後是
function android_app_read_cmd(android_app: Pandroid_app): ShortInt; cdecl;varcmd: ShortInt;beginResult := -1;if __read(android_app^.msgread, @cmd, sizeof(cmd)) = SizeOf(cmd) thenbegincase cmd ofAPP_CMD_SAVE_STATE:free_saved_state(android_app);//釋放當前的儲存狀態end;Result := cmd;end;end; procedure android_app_pre_exec_cmd(android_app: Pandroid_app; cmd: ShortInt); cdecl; //準備執行命令狀態begincase cmd ofAPP_CMD_INPUT_CHANGED:beginpthread_mutex_lock(android_app^.mutex);if android_app^.inputQueue <> nil thenAInputQueue_detachLooper(android_app^.inputQueue);android_app^.inputQueue := android_app^.pendingInputQueue;if android_app^.inputQueue <> nil thenAInputQueue_attachLooper(android_app^.inputQueue, android_app^.looper, LOOPER_ID_INPUT, nil,@android_app^.inputPollSource);pthread_cond_broadcast(android_app^.cond);pthread_mutex_unlock(android_app^.mutex);end; APP_CMD_INIT_WINDOW:beginpthread_mutex_lock(android_app^.mutex);android_app^.window := android_app^.pendingWindow;pthread_cond_broadcast(android_app^.cond);pthread_mutex_unlock(android_app^.mutex);end; APP_CMD_TERM_WINDOW:pthread_cond_broadcast(android_app^.cond); APP_CMD_RESUME, APP_CMD_START, APP_CMD_PAUSE, APP_CMD_STOP:beginpthread_mutex_lock(android_app^.mutex);android_app^.activityState := cmd;pthread_cond_broadcast(android_app^.cond);pthread_mutex_unlock(android_app^.mutex);end; APP_CMD_CONFIG_CHANGED:AConfiguration_fromAssetManager(android_app^.config, android_app^.activity^.assetManager); APP_CMD_DESTROY:android_app^.destroyRequested := 1;end;end; procedure android_app_post_exec_cmd(android_app: Pandroid_app; cmd: ShortInt); cdecl;begincase cmd ofAPP_CMD_TERM_WINDOW:beginpthread_mutex_lock(android_app^.mutex);android_app^.window := nil;pthread_cond_broadcast(android_app^.cond);pthread_mutex_unlock(android_app^.mutex);end; APP_CMD_SAVE_STATE:beginpthread_mutex_lock(android_app^.mutex);android_app^.stateSaved := 1;pthread_cond_broadcast(android_app^.cond);pthread_mutex_unlock(android_app^.mutex);end; APP_CMD_RESUME:{ Delphi: It is unclear why this line is necessary in original AppGlue, but it prevents FireMonkey applicationsfrom recovering saved state. FireMonkey recovers saved state usually after APP_CMD_INIT_WINDOW, which happensmuch later after CMD_RESUME. }{ free_saved_state(android_app) };end;end; procedure process_cmd(app: Pandroid_app; source: Pandroid_poll_source); cdecl;varcmd: ShortInt;begincmd := android_app_read_cmd(app);//先讀取命令android_app_pre_exec_cmd(app, cmd);//準備命令if Assigned(app^.onAppCmd) then //程式內部的指令處理app^.onAppCmd(app, cmd);android_app_post_exec_cmd(app, cmd);//執行處理end;

這些代碼將在Android訊息迴圈中處理調用

其實這些代碼中很多地方都是有注釋的。仔細看看就明白了。 

然後程式進入工程檔案的Begin End之間,先進入SysInit.pas單元的_InitExe,然後會調用GetThisModuleHandle,這個會調用dlopen(Info.dli_fname, RTLD_LAZY)就是相當於載入Windows的DLL,會先載入(Lib+程式名.so)獲得當前控制代碼,然後dlClose關閉,最後程式以這個工程的庫控制代碼作為程式的Hinstance,也就是說我們的好多資源應該會都打包到這個so中去,最後如果是Android環境,會調用_StartExe,來啟動程式,_StartExe中會調用InitUnits來初始化一些單元,這裡就會調用程式所引用到的各個單元的Initialization中的內容,在這個過程中會初始化FMX.PlatForm這個跨平台單元的TPlatformServices類庫,本庫是跨平台單元服務管理,然後就會調用FMX.PlatForm下的initialization,裡面的RegisterCorePlatformServices會根據選擇的平台來判定到底調用哪個平台下的RegisterCorePlatformServices,這個判定通過編譯預先處理指令在Implemention下的Use中

uses{$IFDEF IOS}  FMX.Platform.iOS,{$ELSE}{$IFDEF MACOS}  FMX.Platform.Mac,{$ENDIF MACOS}{$ENDIF IOS}{$IFDEF MSWINDOWS}  FMX.Platform.Win,{$ENDIF}{$IFDEF ANDROID}  FMX.Platform.Android,{$ENDIF},在ANdroid下,就會調用FMX.PlatForm.Android中的RegisterCorePlatformService來註冊核心平台服務,然后里面有if Assigned(PlatformAndroid.FAndroidApp) then  begin    PlatformAndroid.FAndroidApp^.onAppCmd := @HandleAndroidCmd;    PlatformAndroid.FAndroidApp^.onInputEvent := @HandleAndroidInputEvent;  end;這樣就將前面所說的Android的訊息和輸入事件轉到FMX平台的HandleAndroidCmd和HandleAndroidInputEvent函數中然後HandleApplicationEvent中就會和訊息管理器TMessageManager結合對訊息進行處理。把這些單元中的一些初始化過程都搞完了,就執行到工程檔案中的Begin ENd之間去處理代碼了,最後執行了Application.Run;然後我們看這個執行的代碼
procedure TApplication.Run;varAppService: IFMXApplicationService;begin{$IFNDEF ANDROID}AddExitProc(DoneApplication);{$ENDIF}FRunning := True;tryif TPlatformServices.Current.SupportsPlatformService(IFMXApplicationService, AppService) thenAppService.Run;finallyFRunning := False;end;end;

 

實際上他是通過跨平台服務獲得當前運行程式的服務平台,也就是FMX.PlatForm.Android中的TPlatformAndroid,此類繼承了IFMXApplicationService,並且在前面已經通過RegisterCorePlatformServices註冊了TPlatformAndroid,所以這裡實際上調用的就是TPlatformAndroid的Run過程,這個Run實際上就是開始跑Android的訊息迴圈處理了。代碼很短,實際上就是調用了InternalProcessMessages來進行內部訊息迴圈處理
procedure TPlatformAndroid.Run;begin{ Although calling this routine is not really necessary, but it is a way to ensure that "Androidapi.AppGlue.pas" iskept in uses list, in order to export ANativeActivity_onCreate callback. }app_dummy; repeatInternalProcessMessages;until FAndroidApp^.destroyRequested <> 0;end;

 

首先InternalProcessMessages中有EventPollValue := ALooper_pollAll(GetAndUpdateTimeout, nil, nil, PPointer(@PEventPollSource));先通過這個獲得訊息資訊,如果獲得到了就有if (PEventPollSource <> nil) and Assigned(PEventPollSource^.process) then    begin      PEventPollSource^.process(FAndroidApp, PEventPollSource);      if EventPollValue = LOOPER_ID_MAIN then        HasEvents := True;    endPEventPollSource^.process(FAndroidApp, PEventPollSource);這句會調用之前設定初始化的Process_Cmd或者Process_Input這樣,就將訊息和實際關聯起來了。至於顯示Android的介面,則會通過TWindowManager.Current.RenderIfNeeds來判定是否需要渲染介面資訊,如果需要則會調用TWindowManager.Render來進行介面渲染,最後Render函數中procedure RenderNormalWindows;  var    I: Integer;    PaintControl: IPaintControl;  begin    for I := FWindows.Count - 1 downto 0 do      if FWindows[I].Form.Visible and (not FWindows[I].RequiresComposition) and Supports(FWindows[I].Form,        IPaintControl, PaintControl) then      begin        PaintControl.PaintRects([TRectF.Create(0, 0, FContentRect.Width, FContentRect.Height)]);        Break;      end;  end;PaintRects這個則開始匹配FMXForm的PaintRects函數來進行介面資訊繪製。於是一個Android程式開啟運行到顯示基本完成。 http://blog.csdn.net/suiyunonghen/article/details/45365529

Delphi Android程式啟動過程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.