Delphiへの移植を考えているAndroidアプリがありますが、GCMとのインターフェース方法がわかりません。GCMBaseIntentServiceを=で実行する必要があると考えています。 Javaそしてdelphi共有オブジェクトとのインターフェース?
または、Delphi Xe5 Androidアプリでプッシュ通知を行う方法を探しています。
JNIを使用してDelphiとJavaをインターフェースします。JNIを使用すると、双方向で呼び出すことができます:Java toDelphiまたはDelphito Java。したがって、Delphiを拡張できます。 Javaクラス のアプリケーション。
Androidで必要なものを実装するには、Javaで実行する方が簡単です。いくつかの利用可能な例は、あなたが考えていることを正確に実行します。ご覧ください。
Java JNIとDelphi)をインターフェースするために、詳細な手順に従うことができます。これにより、アプリケーションのフロントエンドとバックエンドの間で スムーズな通信 が可能になります。
コードの一部を再利用する場合は、作成者に適切なクレジットを与えることを忘れないでください。
GCMをDelphiで動作させ、 サンプルコンポーネントを作成しました GCMメッセージの登録と受信を処理しました。
[〜#〜] note [〜#〜]:これは単なる大まかなテストコードであり、実際のアプリケーションでは使用していません(まだ)。自由に修正および改善してください。バグを見つけた場合は、投稿してください。
ブライアンロングと彼の記事 Androidサービス に感謝します。
GCM送信者ID(gcmコンソールからのプロジェクト番号)とGCM API ID(GCMコンソールでサーバーアプリケーションのキーを作成)を取得します。これらが必要になります(下の写真を参照)。
まず、変更されたclasses.dexファイルが必要です。これは、batファイルJava dirをアーカイブで実行することによって作成できます。または、私がすでにコンパイルしたもの(アーカイブにも含まれています)を使用することもできます。
新しいclasses.dexをAndroidデプロイメント)に[〜#〜]追加[〜#〜]する必要がありますおよび[〜#〜]チェックを外す[〜#〜]エンバカデロの1つ:
次に、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;
次に、フォームでTGCMNotificationタイプの変数と、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プロジェクト番号を入力します。 APPをGCMに登録するには、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;
次に、必要なユニットを投稿します。アプリケーションと同じ場所に保存します(または、すべてを [〜#〜] here [〜#〜] からダウンロードします)。
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) -->
そして、テストアプリケーションの完全なソース(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);
}
}
それを機能させるのに問題がある場合は、GCMコンソールで正しく構成されていない可能性があります。
GCMコンソールに必要なものは次のとおりです。
プロジェクト番号(GCMに登録するときにこれを使用し、DoRegisterを呼び出す前にTGCMNotification.SenderIDに配置します)。
API IDこれを使用して、登録済みのデバイスにメッセージを送信します。
デルフィが進化し、現在のニーズに適応しているのを見てうれしいです。投稿は私に好奇心をそそったので、私は少し周りを見回したので、これらのリソースに出くわしました:
エンバカデロに関するフォーラム投稿 GCMとデルファイ側の間の通信の問題を解決するために datasnap を使用することをお勧めします。
これが誰かの助けになることを願っています。