topic subscribetotopic studio how google ejemplo create java android delphi cloud google-cloud-messaging
Y AQUÍ está el archivo zip con la fuente.AQUÍhice un componente de muestra

java - subscribetotopic - Google Cloud Messaging en Delphi XE5?



how to use google cloud messaging (4)

Tengo una aplicación para Android que estoy pensando en transferir a Delphi, pero no veo una manera de interactuar con GCM. Estoy pensando que posiblemente tendría que ejecutar el GCMBaseIntentService en java y la interfaz con el objeto compartido delphi?

Alternativamente, estoy buscando una forma de hacer notificaciones push en una aplicación de Android Delphi Xe5.


Creo que crear un puente a Java podría ser una buena idea. Echa un vistazo a this publicación sobre cómo hacerlo. Y here puede encontrar un tutorial para implementar GCM del lado del cliente en Java.


Me complace ver que Delphi está evolucionando y adaptándose a las necesidades actuales. La publicación me hizo curioso, así que busqué un poco, así que encontré estos recursos:

datasnap foro en embarcadero que recomienda el uso de datasnap para resolver el problema de comunicación entre GCM y Delphi Side.

Espero que esto ayude a alguien.


Obtuve GCM trabajando con Delphi e hice un componente de muestra para encargarme de registrar y recibir los mensajes de GCM.

NOTA : Este es solo un código de prueba aproximado, no lo estoy usando en ninguna aplicación real (todavía). Por favor, no dude en modificar y mejorar, y si encuentra errores, por favor publique nuevamente.

Muchas gracias a Brian Long y su artículo sobre los servicios de Android .

Obtenga su ID de remitente de GCM (es su número de proyecto desde la consola de gcm) y su ID de API de GCM (cree una clave para la aplicación de servidor en la consola de GCM), los necesitará (vea las imágenes en la parte inferior).

Antes que nada, necesitas un archivo class.dex modificado. Puede crear esto ejecutando el archivo bat de Java en el archivo, o puede usar el que ya compilé (también incluido en el archivo).

Tienes que AGREGAR las nuevas classes.dex a tu Despliegue Android y DESACTIVAR el embarcadero uno:

Luego, debe editar su AndroidManifest.template.xml y agregarlo justo después de <%uses-permission%> :

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

y justo después de 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>

En su aplicación, declare la unidad gcmnotification:

uses gcmnotification;

y luego en tu formulario declaras una variable del tipo TGCMNotification y un procedimiento que vincularás al evento 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;

Coloque en SenderID su número de proyecto de GCM. Para registrar su aplicación con GCM, llame a DoRegister:

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

Si el DoRegister devuelve verdadero (registrado con éxito), gcmn.RegistrationID tendrá la identificación única que necesita para enviar mensajes a este dispositivo.

Y recibirás mensajes en el procedimiento del evento:

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

.. y eso es TODO lo que necesitas para Recibir. Genial, ¿eh? :-)

Para enviar, simplemente use 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;

A continuación, voy a publicar las unidades que necesita, simplemente guárdelas en el mismo lugar con su aplicación (o simplemente descargue todo desde AQUÍ ).

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.

Aquí está el AndroidManifest.template.xml modificado

<?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) -->

Y la fuente completa para la aplicación de prueba (enviará y recibirá un mensaje de 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.

El GCMReceiver.java que necesita compilar y agregarlo a classes.dex es:

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); } }

Y AQUÍ está el archivo zip con la fuente.

Si tiene problemas para hacer que funcione, es probable que algo no esté configurado en su consola de GCM.

Esto es lo que necesita de su consola de GCM:

Número de proyecto (lo usa cuando se registra con GCM, lo coloca en TGCMNotification.SenderID antes de llamar a DoRegister).

API ID lo usará para enviar mensajes a los dispositivos registrados.


Usted interconecta Java con Delphi usando JNI. El JNI le permite llamar en ambas direcciones: Java a Delphi o Delphi a Java. Para que pueda ampliar sus aplicaciones Delphi con clases de Java .

Para implementar lo que desea en Android, hacerlo en Java será la ruta más fácil de seguir, ya que algunos ejemplos disponibles hacen exactamente lo que usted tiene en mente. Sólo echar un vistazo:

Para conectar Java JNI y Delphi, puede seguir los pasos detallados, lo que permite una comunicación fluida entre el front-end y el back-end de su aplicación.

Si decide volver a utilizar parte del código, recuerde dar el crédito apropiado a los autores.