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單元中。這個代碼如下:
<a></a>
可見在前面又注釋,寫了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的這個線程入口函數
然後是
這些代碼将在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;
然後我們看這個執行的代碼
實際上他是通過跨平台服務獲得目前運作程式的服務平台,也就是FMX.PlatForm.Android中的TPlatformAndroid,此類繼承了IFMXApplicationService,并且在前面已經通過RegisterCorePlatformServices注冊了TPlatformAndroid,是以這裡實際上調用的就是TPlatformAndroid的Run過程,這個Run實際上就是開始跑Android的消息循環處理了。代碼很短,實際上就是調用了InternalProcessMessages來進行内部消息循環處理
首先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;
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;
PaintRects這個則開始比對FMXForm的PaintRects函數來進行界面資訊繪制。于是一個Android程式開啟運作到顯示基本完成。
本文轉自 不得閑 部落格園部落格,原文連結:http://www.cnblogs.com/DxSoft/p/4460236.html ,如需轉載請自行聯系原作者