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 ,如需转载请自行联系原作者