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;
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) -->
以及测试应用程序的完整源代码(它将发送和接收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);
}
}
4条答案
按热度按时间z3yyvxxp1#
您可以使用JNI将Java与 Delphi 连接起来。JNI允许您双向调用:Java到 Delphi 或Delphi到Java。因此,您可以使用Java classes扩展 Delphi 应用程序。
为了在Android上实现你想要的东西,在Java中实现它将是更容易遵循的路径,因为一些可用的示例完全符合你的想法。你看一下:
要连接Java JNI和 Delphi ,您可以遵循详细的步骤,从而允许在应用程序的前端和后端之间使用smooth communication。
如果您决定重用某些代码,请记住对作者给予适当的信任。
c0vxltue2#
我让GCM与 Delphi 和I made a sample component一起工作,负责注册和接收GCM消息。
注意:这只是一个粗略的测试代码,我还没有在任何真实的应用中使用它。请随时修改和改进,如果您发现错误,请回帖。
非常感谢Brian Long和他关于Android Services的文章。
获取你的GCM发送者ID(从gcm控制台获取你的项目编号)和你的GCM API ID(在GCM控制台中为服务器应用程序创建一个密钥),你会需要它们(见底部的图片)。
首先,您需要修改classes.dex文件。您可以通过运行存档中的bat文件Java dir来创建它,或者您可以使用我已经编译的文件(也包含在存档中)。
你必须添加新的classes.dex到你的Android Deployment中,并取消检查embarcadero:
然后,您需要编辑AndroidManifest.template.xml并在
<%uses-permission%>
之后添加:android:theme="%theme%">
之后在应用程序中,声明gcmnotification单元:
然后在表单中声明一个TGCMMotification类型的变量和一个将链接到TGCMMotification的过程。OnReceiveGCMMotification事件:
在SenderID中输入您的GCM项目编号。要向GCM注册您的APP,请调用DoRegister:
如果DoRegister返回true(成功注册),则gcmn.RegistrationID将具有向该设备发送消息所需的唯一ID。
您将在事件过程中收到消息:
这就是你所需要的一切。很酷吧?:-)
要发送,只需使用TIdHttp:
接下来,我将发布您需要的单元,只需将它们保存在与应用程序相同的位置(或者从HERE下载整个内容)。
gcmnotification.pas
uGCMReceiver.pas
下面是修改后的AndroidManifest.template.xml
以及测试应用程序的完整源代码(它将发送和接收GCM消息):
您需要编译并将其添加到classes.dex的GCMReceiver.java是:
And HERE is the zip archive with the source.
如果您在使其工作时遇到问题,则可能是GCM控制台中的某些配置不正确。
以下是您需要从GCM控制台获得的内容:
项目编号(在GCM注册时使用,在调用DoRegister之前将其放入TGCMNotification.SenderID)。
API ID您将使用它向已注册的设备发送消息。
guz6ccqo3#
我认为建立一个通往Java的桥梁可能是一个好主意。看看this的文章如何做到这一点。在here中,你可以找到一个用Java实现客户端GCM的教程。
4ktjp1zp4#
我很高兴看到 Delphi 正在不断发展并适应当前的需求。这篇文章让我很好奇,所以我四处看了看,发现了这些资源:
forum post on embarcadero,推荐使用datasnap来解决GCM和 Delphi 端之间的通信问题。
我希望这将帮助某人。