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上实现您想要的function,使用Java进行操作将是更容易理解的路径,因为一些可用的示例完全符合您的要求。 看看:

  • 为Android实施推送通知/ Google Cloud Messaging
  • 使用基于云的警报服务的Android *客户端应用程序
  • Github Gist – GCMIntentService.java

要连接Java JNI和Delphi,您可以按照详细步骤进行操作,从而实现应用程序前端和后端之间的顺畅通信 。

如果您决定重复使用某些代码,请记住给予作者适当的信誉。

我让GCM与Delphi合作, 我制作了一个样本组件来处理注册和接收GCM消息。

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

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

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

首先,您需要一个修改过的classes.dex文件。 您可以通过在归档文件中运行bat文件Java dir来创建它,也可以使用我已编译的文件(也包含在归档中)。

你必须将新的classes.dex添加到你的Android部署并UNCHECK embarcadero:

classes.dex部署

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

 < %uses-permission%>  

并在android:theme="%theme%">

        

在您的应用程序中,声明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; 

将您的GCM项目编号放入SenderID。 要使用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; gcm: JGoogleCloudMessaging; begin if FAlreadyRegistered then result := true else begin if CheckPlayServicesSupport then begin gcm := GetGCMInstance; p := TJavaObjectArray.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) 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"?>     < %uses-permission%>                      

并且测试应用程序的完整源代码(它将发送和接收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); } } 

而HERE是源代码的zip存档。

如果您无法正常工作,可能是您在GCM控制台中未配置的内容。

以下是GCM控制台所需的内容:

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

项目编号

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

API ID

我认为创建Java桥梁可能是一个好主意。 看看这篇关于如何做的post。 在这里,您可以find用Java实现客户端GCM的教程。

我很高兴看到delphi正在发展并适应当前的需求。 这篇post让我好奇,所以我环顾四周,所以我遇到了这些资源:

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

我希望这会对某人有所帮助。