Delphi Android程序啓動過程

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;
var
android_app: Pandroid_app;
PipeDescriptors: TPipeDescriptors;
attr: pthread_attr_t;
thread: pthread_t;
begin
android_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 then
begin
android_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 do
pthread_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;
type
TMainFunction = procedure;
var
DlsymPointer: Pointer;
EntryPoint: TMainFunction;
begin
DlsymPointer := dlsym(RTLD_DEFAULT, '_NativeMain');
if DlsymPointer <> nil then
begin
EntryPoint := TMainFunction(DlsymPointer);
EntryPoint;
end;
end;
 
var
android_app: Pandroid_app;
looper: PALooper;
begin
android_app := Pandroid_app(param);
android_app^.config := AConfiguration_new;//建立應用程序config
AConfiguration_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 then
project main code, which will enter application main loop. This call will block until the loop ends, which is
typically 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;
var
cmd: ShortInt;
begin
Result := -1;
if __read(android_app^.msgread, @cmd, sizeof(cmd)) = SizeOf(cmd) then
begin
case cmd of
APP_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; //準備執行命令狀態
begin
case cmd of
APP_CMD_INPUT_CHANGED:
begin
pthread_mutex_lock(android_app^.mutex);
if android_app^.inputQueue <> nil then
AInputQueue_detachLooper(android_app^.inputQueue);
android_app^.inputQueue := android_app^.pendingInputQueue;
if android_app^.inputQueue <> nil then
AInputQueue_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:
begin
pthread_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:
begin
pthread_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;
begin
case cmd of
APP_CMD_TERM_WINDOW:
begin
pthread_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:
begin
pthread_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 applications
from recovering saved state. FireMonkey recovers saved state usually after APP_CMD_INIT_WINDOW, which happens
much later after CMD_RESUME. }
{ free_saved_state(android_app) };
end;
end;
 
procedure process_cmd(app: Pandroid_app; source: Pandroid_poll_source); cdecl;
var
cmd: ShortInt;
begin
cmd := 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消息循環中處理調用java

其實這些代碼中不少地方都是有註釋的。仔細看看就明白了。
 

而後程序進入工程文件的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中android

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;
var
AppService: IFMXApplicationService;
begin
{$IFNDEF ANDROID}
AddExitProc(DoneApplication);
{$ENDIF}
FRunning := True;
try
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationService, AppService) then
AppService.Run;
finally
FRunning := 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" is
kept in uses list, in order to export ANativeActivity_onCreate callback. }
app_dummy;
 
repeat
InternalProcessMessages;
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;
    end
PEventPollSource^.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程序開啓運行到顯示基本完成。
相關文章
相關標籤/搜索