程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi Android程序啟動過程,delphiandroid

Delphi Android程序啟動過程,delphiandroid

編輯:Delphi

Delphi Android程序啟動過程,delphiandroid


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消息循環中處理調用

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

然後程序進入工程文件的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;
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程序開啟運行到顯示基本完成。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved