Delphi XE5中的Google Cloud Messaging?

我有一个Android应用程序,我正在考虑移植到Delphi,但我看不到一种方式来与GCM接口。 我想我可能需要在java中运行GCMBaseIntentService并与delphi共享对象接口?

或者,我正在寻找一种方法来在Delphi Xe5 android应用程序中执行推送通知。

Solutions Collecting From Web of "Delphi XE5中的Google Cloud Messaging?"

使用JNI将Java与Delphi接口。 JNI允许你调用两个方向:Java到Delphi或Delphi到Java。 所以你可以用Java类扩展你的Delphi应用程序。

为了在Android上实现你想要的东西,在Java中执行它将是更容易遵循的途径,因为一些可用的示例完全符合你的想法。 只要看看:

  • 针对Android实施推送通知/ Google云消息传递
  • Android *客户端应用程序使用基于云的警报服务
  • Github Gist – GCMIntentService.java

为了连接Java JNI和Delphi,您可以遵循详细的步骤,从而在应用程序的前端和后端之间实现顺畅的通信 。

如果您决定重新使用一些代码,请记得给作者适当的评价。

我得到了GCM与delphi合作, 我做了一个示例组件来照顾注册和接收GCM消息。

:这只是一个粗略的testing代码,我没有在任何实际的应用程序(还)使用它。 请随意修改和改进,如果发现错误,请回复。

非常感谢Brian Long和他的关于Android服务的文章。

从GCM控制台获取您的GCM发件人ID(这是您的项目号码)和您的GCM API ID(在GCM控制台中为服务器应用程序创build一个密钥),您将需要它们(请参阅底部的图片)。

首先,你需要修改classes.dex文件。 您可以通过在存档中运行bat文件Java dir来创build此目录,也可以使用我已经编译的文件(也包含在存档中)创build此文件。

您必须将新的classes.dex添加到您的Android部署中,然后检查embarcadero:

classes.dex部署

然后,您需要编辑您的AndroidManifest.template.xml并在<%uses-permission%>之后添加:

 <%uses-permission%> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> 

android:theme="%theme%">

  <receiver android:name="com.ioan.delphi.GCMReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <category android:name="%package%" /> </intent-filter> </receiver> 

在你的申请中,申报gcmnotification单位:

 uses gcmnotification; 

然后在表单中声明一个TGCMNotificationtypes的variables和一个你将链接到TGCMNotification.OnReceiveGCMNotification事件的过程:

 type TForm8 = class(TForm) //.... private { Private declarations } public { Public declarations } gcmn: TGCMNotification; procedure OnNotification(Sender: TObject; ANotification: TGCMNotificationMessage); end; procedure TForm8.FormCreate(Sender: TObject); begin gcmn := TGCMNotification.Create(self); gcmn.OnReceiveGCMNotification := OnNotification; end; 

将SenderID放入您的GCM项目编号。 要使用GCM注册您的APP,请致电DoRegister:

 procedure TForm8.Button1Click(Sender: TObject); begin gcmn.SenderID := YOUR_GCM_SENDERID; if gcmn.DoRegister then Toast('Successfully registered with GCM.'); end; 

如果DoRegister返回true(成功注册),gcmn.RegistrationID将拥有发送消息到此设备所需的唯一ID。

你会在事件过程中收到消息:

 procedure TForm8.OnNotification(Sender: TObject; ANotification: TGCMNotificationMessage); begin Memo1.Lines.Add('Received: ' + ANotification.Body); end; 

..这就是所有你需要接收。 很酷,嗯? 🙂

发送,只需使用TIdHttp:

 procedure TForm8.Button2Click(Sender: TObject); const sendUrl = 'https://android.googleapis.com/gcm/send'; var Params: TStringList; AuthHeader: STring; idHTTP: TIDHTTP; SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL; begin idHTTP := TIDHTTP.Create(nil); try SslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil); idHTTP.IOHandler := SSLIOHandler; idHTTP.HTTPOptions := []; Params := TStringList.Create; try Params.Add('registration_id='+ gcmn.RegistrationID); Params.Values['data.message'] := 'test: ' + FormatDateTime('yy-mm-dd hh:nn:ss', Now); idHTTP.Request.Host := sendUrl; AuthHeader := 'Authorization: key=' + YOUR_API_ID; idHTTP.Request.CustomHeaders.Add(AuthHeader); IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded;charset=UTF-8'; Memo1.Lines.Add('Send result: ' + idHTTP.Post(sendUrl, Params)); finally Params.Free; end; finally FreeAndNil(idHTTP); end; end; 

接下来,我将发布你需要的单元,把它们保存在你的应用程序的相同位置(或从这里下载整个东西)。

gcmnotification.pas

 unit gcmnotification; interface {$IFDEF ANDROID} uses System.SysUtils, System.Classes, FMX.Helpers.Android, Androidapi.JNI.PlayServices, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNIBridge, Androidapi.JNI.JavaTypes; type TGCMNotificationMessageKind = (nmMESSAGE_TYPE_MESSAGE, nmMESSAGE_TYPE_DELETED, nmMESSAGE_TYPE_SEND_ERROR); { Discription of notification for Notification Center } TGCMNotificationMessage = class (TPersistent) private FKind: TGCMNotificationMessageKind; FSender: string; FWhat: integer; FBody: string; protected procedure AssignTo(Dest: TPersistent); override; public { Unique identificator for determenation notification in Notification list } property Kind: TGCMNotificationMessageKind read FKind write FKind; property Sender: string read FSender write FSender; property What: integer read FWhat write FWhat; property Body: string read FBody write FBody; constructor Create; end; TOnReceiveGCMNotification = procedure (Sender: TObject; ANotification: TGCMNotificationMessage) of object; TGCMNotification = class(TComponent) strict private { Private declarations } FRegistrationID: string; FSenderID: string; FOnReceiveGCMNotification: TOnReceiveGCMNotification; FReceiver: JBroadcastReceiver; FAlreadyRegistered: boolean; function CheckPlayServicesSupport: boolean; protected { Protected declarations } public { Public declarations } constructor Create(AOwner: TComponent); override; destructor Destroy; override; function DoRegister: boolean; function GetGCMInstance: JGoogleCloudMessaging; published { Published declarations } property SenderID: string read FSenderID write FSenderID; property RegistrationID: string read FRegistrationID write FRegistrationID; property OnReceiveGCMNotification: TOnReceiveGCMNotification read FOnReceiveGCMNotification write FOnReceiveGCMNotification; end; {$ENDIF} implementation {$IFDEF ANDROID} uses uGCMReceiver; { TGCMNotification } function TGCMNotification.CheckPlayServicesSupport: boolean; var resultCode: integer; begin resultCode := TJGooglePlayServicesUtil.JavaClass.isGooglePlayServicesAvailable(SharedActivity); result := (resultCode = TJConnectionResult.JavaClass.SUCCESS); end; constructor TGCMNotification.Create(AOwner: TComponent); var Filter: JIntentFilter; begin inherited; Filter := TJIntentFilter.Create; FReceiver := TJGCMReceiver.Create(Self); SharedActivity.registerReceiver(FReceiver, Filter); FAlreadyRegistered := false; end; destructor TGCMNotification.Destroy; begin SharedActivity.unregisterReceiver(FReceiver); FReceiver := nil; inherited; end; function TGCMNotification.DoRegister: boolean; var p: TJavaObjectArray<JString>; gcm: JGoogleCloudMessaging; begin if FAlreadyRegistered then result := true else begin if CheckPlayServicesSupport then begin gcm := GetGCMInstance; p := TJavaObjectArray<JString>.Create(1); p.Items[0] := StringToJString(FSenderID); FRegistrationID := JStringToString(gcm.register(p)); FAlreadyRegistered := (FRegistrationID <> ''); result := FAlreadyRegistered; end else result := false; end; end; function TGCMNotification.GetGCMInstance: JGoogleCloudMessaging; begin result := TJGoogleCloudMessaging.JavaClass.getInstance(SharedActivity.getApplicationContext); end; { TGCMNotificationMessage } procedure TGCMNotificationMessage.AssignTo(Dest: TPersistent); var DestNotification: TGCMNotificationMessage; begin if Dest is TGCMNotificationMessage then begin DestNotification := Dest as TGCMNotificationMessage; DestNotification.Kind := Kind; DestNotification.What := What; DestNotification.Sender := Sender; DestNotification.Body := Body; end else inherited AssignTo(Dest); end; constructor TGCMNotificationMessage.Create; begin Body := ''; end; {$ENDIF} end. 

uGCMReceiver.pas

 unit uGCMReceiver; interface {$IFDEF ANDROID} uses FMX.Types, Androidapi.JNIBridge, Androidapi.JNI.GraphicsContentViewText, gcmnotification; type JGCMReceiverClass = interface(JBroadcastReceiverClass) ['{9D967671-9CD8-483A-98C8-161071CE7B64}'] {Methods} end; [JavaSignature('com/ioan/delphi/GCMReceiver')] JGCMReceiver = interface(JBroadcastReceiver) ['{4B30D537-5221-4451-893D-7916ED11CE1F}'] {Methods} end; TJGCMReceiver = class(TJavaGenericImport<JGCMReceiverClass, JGCMReceiver>) private FOwningComponent: TGCMNotification; protected constructor _Create(AOwner: TGCMNotification); public class function Create(AOwner: TGCMNotification): JGCMReceiver; procedure OnReceive(Context: JContext; ReceivedIntent: JIntent); end; {$ENDIF} implementation {$IFDEF ANDROID} uses System.Classes, System.SysUtils, FMX.Helpers.Android, Androidapi.NativeActivity, Androidapi.JNI, Androidapi.JNI.JavaTypes, Androidapi.JNI.Os, Androidapi.JNI.PlayServices; {$REGION 'JNI setup code and callback'} var GCMReceiver: TJGCMReceiver; ARNContext: JContext; ARNReceivedIntent: JIntent; procedure GCMReceiverOnReceiveThreadSwitcher; begin Log.d('+gcmReceiverOnReceiveThreadSwitcher'); Log.d('Thread: Main: %.8x, Current: %.8x, Java:%.8d (%2:.8x)', [MainThreadID, TThread.CurrentThread.ThreadID, TJThread.JavaClass.CurrentThread.getId]); GCMReceiver.OnReceive(ARNContext,ARNReceivedIntent ); Log.d('-gcmReceiverOnReceiveThreadSwitcher'); end; //This is called from the Java activity's onReceiveNative() method procedure GCMReceiverOnReceiveNative(PEnv: PJNIEnv; This: JNIObject; JNIContext, JNIReceivedIntent: JNIObject); cdecl; begin Log.d('+gcmReceiverOnReceiveNative'); Log.d('Thread: Main: %.8x, Current: %.8x, Java:%.8d (%2:.8x)', [MainThreadID, TThread.CurrentThread.ThreadID, TJThread.JavaClass.CurrentThread.getId]); ARNContext := TJContext.Wrap(JNIContext); ARNReceivedIntent := TJIntent.Wrap(JNIReceivedIntent); Log.d('Calling Synchronize'); TThread.Synchronize(nil, GCMReceiverOnReceiveThreadSwitcher); Log.d('Synchronize is over'); Log.d('-gcmReceiverOnReceiveNative'); end; procedure RegisterDelphiNativeMethods; var PEnv: PJNIEnv; ReceiverClass: JNIClass; NativeMethod: JNINativeMethod; begin Log.d('Starting the GCMReceiver JNI stuff'); PEnv := TJNIResolver.GetJNIEnv; Log.d('Registering interop methods'); NativeMethod.Name := 'gcmReceiverOnReceiveNative'; NativeMethod.Signature := '(Landroid/content/Context;Landroid/content/Intent;)V'; NativeMethod.FnPtr := @GCMReceiverOnReceiveNative; ReceiverClass := TJNIResolver.GetJavaClassID('com.ioan.delphi.GCMReceiver'); PEnv^.RegisterNatives(PEnv, ReceiverClass, @NativeMethod, 1); PEnv^.DeleteLocalRef(PEnv, ReceiverClass); end; {$ENDREGION} { TActivityReceiver } constructor TJGCMReceiver._Create(AOwner: TGCMNotification); begin inherited; FOwningComponent := AOwner; Log.d('TJGCMReceiver._Create constructor'); end; class function TJGCMReceiver.Create(AOwner: TGCMNotification): JGCMReceiver; begin Log.d('TJGCMReceiver.Create class function'); Result := inherited Create; GCMReceiver := TJGCMReceiver._Create(AOwner); end; procedure TJGCMReceiver.OnReceive(Context: JContext; ReceivedIntent: JIntent); var extras: JBundle; gcm: JGoogleCloudMessaging; messageType: JString; noti: TGCMNotificationMessage; begin if Assigned(FOwningComponent.OnReceiveGCMNotification) then begin noti := TGCMNotificationMessage.Create; try Log.d('Received a message!'); extras := ReceivedIntent.getExtras(); gcm := FOwningComponent.GetGCMInstance; // The getMessageType() intent parameter must be the intent you received // in your BroadcastReceiver. messageType := gcm.getMessageType(ReceivedIntent); if not extras.isEmpty() then begin {* * Filter messages based on message type. Since it is likely that GCM will be * extended in the future with new message types, just ignore any message types you're * not interested in, or that you don't recognize. *} if TJGoogleCloudMessaging.JavaClass.MESSAGE_TYPE_SEND_ERROR.equals(messageType) then begin // It's an error. noti.Kind := TGCMNotificationMessageKind.nmMESSAGE_TYPE_SEND_ERROR; FOwningComponent.OnReceiveGCMNotification(Self, noti); end else if TJGoogleCloudMessaging.JavaClass.MESSAGE_TYPE_DELETED.equals(messageType) then begin // Deleted messages on the server. noti.Kind := TGCMNotificationMessageKind.nmMESSAGE_TYPE_DELETED; FOwningComponent.OnReceiveGCMNotification(Self, noti); end else if TJGoogleCloudMessaging.JavaClass.MESSAGE_TYPE_MESSAGE.equals(messageType) then begin // It's a regular GCM message, do some work. noti.Kind := TGCMNotificationMessageKind.nmMESSAGE_TYPE_MESSAGE; noti.Sender := JStringToString(extras.getString(StringToJString('sender'))); noti.What := StrToIntDef(JStringToString(extras.getString(StringToJString('what'))), 0); noti.Body := JStringToString(extras.getString(StringToJString('message'))); FOwningComponent.OnReceiveGCMNotification(Self, noti); end; end; finally noti.Free; end; end; end; initialization RegisterDelphiNativeMethods {$ENDIF} end. 

这是修改后的AndroidManifest.template.xml

 <?xml version="1.0" encoding="utf-8"?> <!-- BEGIN_INCLUDE(manifest) --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="%package%" android:versionCode="%versionCode%" android:versionName="%versionName%"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="%minSdkVersion%" /> <%uses-permission%> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <application android:persistent="%persistent%" android:restoreAnyVersion="%restoreAnyVersion%" android:label="%label%" android:installLocation="%installLocation%" android:debuggable="%debuggable%" android:largeHeap="%largeHeap%" android:icon="%icon%" android:theme="%theme%"> <receiver android:name="com.ioan.delphi.GCMReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <category android:name="%package%" /> </intent-filter> </receiver> <!-- Our activity is a subclass of the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="com.embarcadero.firemonkey.FMXNativeActivity" android:label="%activityLabel%" android:configChanges="orientation|keyboardHidden"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="%libNameValue%" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="com.embarcadero.firemonkey.notifications.FMXNotificationAlarm" /> </application> </manifest> <!-- END_INCLUDE(manifest) --> 

以及testing应用程序的完整源代码(它将发送和接收GCM消息):

 unit testgcmmain; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, FMX.Layouts, FMX.Memo, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL, gcmnotification; type TForm8 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } gcmn: TGCMNotification; procedure OnNotification(Sender: TObject; ANotification: TGCMNotificationMessage); end; const YOUR_GCM_SENDERID = '1234567890'; YOUR_API_ID = 'abc1234567890'; var Form8: TForm8; implementation {$R *.fmx} procedure TForm8.FormCreate(Sender: TObject); begin gcmn := TGCMNotification.Create(self); gcmn.OnReceiveGCMNotification := OnNotification; end; procedure TForm8.FormDestroy(Sender: TObject); begin FreeAndNil(gcmn); end; procedure TForm8.Button1Click(Sender: TObject); begin // register with the GCM gcmn.SenderID := YOUR_GCM_SENDERID; if gcmn.DoRegister then Memo1.Lines.Add('Successfully registered with GCM.'); end; procedure TForm8.OnNotification(Sender: TObject; ANotification: TGCMNotificationMessage); begin // you just received a message! if ANotification.Kind = TGCMNotificationMessageKind.nmMESSAGE_TYPE_MESSAGE then Memo1.Lines.Add('Received: ' + ANotification.Body); end; // send a message procedure TForm8.Button2Click(Sender: TObject); const sendUrl = 'https://android.googleapis.com/gcm/send'; var Params: TStringList; AuthHeader: STring; idHTTP: TIDHTTP; SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL; begin idHTTP := TIDHTTP.Create(nil); try SslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil); idHTTP.IOHandler := SSLIOHandler; idHTTP.HTTPOptions := []; Params := TStringList.Create; try Params.Add('registration_id='+ gcmn.RegistrationID); // you can send the data with a payload, in my example the server will accept // data.message = the message you want to send // data.sender = some sender info // data.what = an integer (aka "message type") // you can put any payload in the data, data.score, data.blabla... // just make sure you modify the code in my component to handle it Params.Values['data.message'] := 'test: ' + FormatDateTime('yy-mm-dd hh:nn:ss', Now); idHTTP.Request.Host := sendUrl; AuthHeader := 'Authorization: key=' + YOUR_API_ID; idHTTP.Request.CustomHeaders.Add(AuthHeader); IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded;charset=UTF-8'; Memo1.Lines.Add('Send result: ' + idHTTP.Post(sendUrl, Params)); finally Params.Free; end; finally FreeAndNil(idHTTP); end; end; end. 

您需要编译并将其添加到classes.dex的GCMReceiver.java是:

 package com.ioan.delphi; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.Context; import android.util.Log; public class GCMReceiver extends BroadcastReceiver { static final String TAG = "GCMReceiver"; public native void gcmReceiverOnReceiveNative(Context context, Intent receivedIntent); @Override public void onReceive(Context context, Intent receivedIntent) { Log.d(TAG, "onReceive"); gcmReceiverOnReceiveNative(context, receivedIntent); } } 

这里是源代码的zip文件。

如果您无法正常工作,可能是在您的GCM控制台中没有正确configuration。

这是你需要从你的GCM控制台:

项目编号 (在使用GCM注册时使用此项,在调用DoRegister之前将其放入TGCMNotification.SenderID中)。

项目编号

您将使用API ID将消息发送到注册的设备。

API ID

我认为创build与Java的桥梁可能是一个好主意。 看看这个post关于如何做到这一点。 在这里你可以find一个在Java中实现客户端GCM的教程。

我很高兴看到delphi正在发展和适应当前的需求。 这个post使我好奇,所以我环视了一下,所以我遇到了这些资源:

embarcadero的论坛post ,build议使用datasnap来解决GCM和delphi之间的通信问题。

我希望这会帮助别人。