web-dev-qa-db-ja.com

Delphi XE5のGoogleクラウドメッセージング?

Delphiへの移植を考えているAndroidアプリがありますが、GCMとのインターフェース方法がわかりません。GCMBaseIntentServiceを=で実行する必要があると考えています。 Javaそしてdelphi共有オブジェクトとのインターフェース?

または、Delphi Xe5 Androidアプリでプッシュ通知を行う方法を探しています。

25
FerretDriver

JNIを使​​用してDelphiとJavaをインターフェースします。JNIを使​​用すると、双方向で呼び出すことができます:Java toDelphiまたはDelphito Java。したがって、Delphiを拡張できます。 Javaクラス のアプリケーション。

Androidで必要なものを実装するには、Javaで実行する方が簡単です。いくつかの利用可能な例は、あなたが考えていることを正確に実行します。ご覧ください。

Java JNIとDelphi)をインターフェースするために、詳細な手順に従うことができます。これにより、アプリケーションのフロントエンドとバックエンドの間で スムーズな通信 が可能になります。

コードの一部を再利用する場合は、作成者に適切なクレジットを与えることを忘れないでください。

20
Avanz

GCMをDelphiで動作させ、 サンプルコンポーネントを作成しました GCMメッセージの登録と受信を処理しました。

[〜#〜] note [〜#〜]:これは単なる大まかなテストコードであり、実際のアプリケーションでは使用していません(まだ)。自由に修正および改善してください。バグを見つけた場合は、投稿してください。

ブライアンロングと彼の記事 Androidサービス に感謝します。

GCM送信者ID(gcmコンソールからのプロジェクト番号)とGCM API ID(GCMコンソールでサーバーアプリケーションのキーを作成)を取得します。これらが必要になります(下の写真を参照)。

まず、変更されたclasses.dexファイルが必要です。これは、batファイルJava dirをアーカイブで実行することによって作成できます。または、私がすでにコンパイルしたもの(アーカイブにも含まれています)を使用することもできます。

新しいclasses.dexをAndroidデプロイメント)に[〜#〜]追加[〜#〜]する必要がありますおよび[〜#〜]チェックを外す[〜#〜]エンバカデロの1つ:

classes.dex deployment

次に、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);
    }
}

そしてここにソースのあるZipアーカイブがあります。

それを機能させるのに問題がある場合は、GCMコンソールで正しく構成されていない可能性があります。

GCMコンソールに必要なものは次のとおりです。

プロジェクト番号(GCMに登録するときにこれを使用し、DoRegisterを呼び出す前にTGCMNotification.SenderIDに配置します)。

Project number

API IDこれを使用して、登録済みのデバイスにメッセージを送信します。

API ID

16
ioan ghip

デルフィが進化し、現在のニーズに適応しているのを見てうれしいです。投稿は私に好奇心をそそったので、私は少し周りを見回したので、これらのリソースに出くわしました:

エンバカデロに関するフォーラム投稿 GCMとデルファイ側の間の通信の問題を解決するために datasnap を使用することをお勧めします。

これが誰かの助けになることを願っています。

0
Olimpiu POP

Javaへのブリッジを作成するのは良い考えかもしれません。それを行う方法については this の投稿をご覧ください。そして here = Javaでクライアント側GCMを実装するためのチュートリアルを見つけることができます。

0
Ilya Gazman