Перейти к содержанию
Fire Monkey от А до Я

[Статья] PHP сервер для рассылки Push на Android и iOS


Рекомендуемые сообщения

В статья я максимально подробно попытался описать работу с PHP скриптом для рассылки Push сообщений из любой программы вне зависимости от платформы. Затрагивается вопроса от экспорта скриптов до кода программы: отправка и получение Push, регистрация устройств.

 

Надеюсь кому-нибудь пригодится. Буду рад комментариям, обоснованной критике и доработкам.

 

Ссылка на комментарий

чорт, я всю неделю провел, раскапывая эту тему, и то до конца не достал ))

а надо было просто немного подождать ))))

огромное спасибо за статью

Ссылка на комментарий
  • 4 недели спустя...

Добрый день.Статья хорошая,все подробно написано и в принципе понятно.Решил попробовать,в PHP не силен.Все сделал,регистрация устройства проходит,в таблицу заносится.Но при попытке отправки ругается вот на что:

function SendAndroid($tokens, $text, $config)
	{
		# Создаём поток для отправки с использование API ключа
		$sender = new /CodeMonkeysRu/GCM/Sender/ ($config['gcm']['apikey']);
		# Создаём сообщение для указаных токенов
		$message = new /CodeMonkeysRu/GCM/Message/ ($tokens (array("message" => $text))); 

На строке:

$sender = new /CodeMonkeysRu/GCM/Sender/ ($config['gcm']['apikey']); 

Выдает ошибку:

 

Parse error: syntax error, unexpected '/' in /public_html/push.php on line 235

 

Я вижу что ему не нравится символ '/'.Но никак не могу понять в чем подвох.

 

Изменено пользователем dante333
Ссылка на комментарий

Странно, у меня код всё несколько другой:

 

# Создаём поток для отправки с использование API ключа
$sender = new \CodeMonkeysRu\GCM\Sender($config['gcm']['apikey']);
# Создаём сообщение для указаных токенов
$message = new \CodeMonkeysRu\GCM\Message($tokens, array("message" => $text));
Ссылка на комментарий

Спасибо,теперь ошибок нет.Теперь загвоздка в другом.Я проверял регистрацию устройств через браузер,скопировал ссылку из статьи,прописал токен и id устроиства,все отлично.В БД все записывается.Теперь набросал в Delphi приложение,опять же по статье.Все скомпилировалось и установилось без ошибок,но при запуске регистрация не проходит.Манифест правил,разрешения проставил.У меня уже есть приложение с пушами через kinvey,оно работает,пуши приходят.Но хотелось бы уйти от kinvey,собственно ради этого и затеял все.

 

UPD.Пока писал,приложение компилилось еще раз и о чудо,все заработало.Спасибо за помощь.

Изменено пользователем dante333
Ссылка на комментарий

Спасибо,теперь ошибок нет.Теперь загвоздка в другом.Я проверял регистрацию устройств через браузер,скопировал ссылку из статьи,прописал токен и id устроиства,все отлично.В БД все записывается.Теперь набросал в Delphi приложение,опять же по статье.Все скомпилировалось и установилось без ошибок,но при запуске регистрация не проходит.Манифест правил,разрешения проставил.У меня уже есть приложение с пушами через kinvey,оно работает,пуши приходят.Но хотелось бы уйти от kinvey,собственно ради этого и затеял все.

 

UPD.Пока писал,приложение компилилось еще раз и о чудо,все заработало.Спасибо за помощь.

Собственно, от кинвей и парсе на XE10 пришлось уйти, т.к. запустить их на iOS их на новой версии не вышло. 

Ссылка на комментарий
  • 1 год спустя...

Подскажите, пожалуйста, в чем может быть проблема?

На строке:

APushService->AppProps[TPushService_TAppPropNames_GCMAppID] = FAndroidServerKey;

Ошибка: segmentation fault 11

 

TForm1 *Form1;
const String FAndroidServerKey = L"875940064719";

void __fastcall TForm1::FormShow(TObject *Sender)
{
  APushService = nullptr;
  AServiceConnection = nullptr;

  String ADeviceID = "";
  String ADeviceToken = "";
  #if defined(__ANDROID__)
  APushService = TPushServiceManager::Instance->GetServiceByName(TPushService_TServiceNames_GCM);
  APushService->AppProps[TPushService_TAppPropNames_GCMAppID] = FAndroidServerKey;
  #endif
  #if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
  APushService = TPushServiceManager::Instance->GetServiceByName(TPushService_TServiceNames_APS);
  #endif

  if (APushService)
    {
		AServiceConnection = new TPushServiceConnection(APushService);
		AServiceConnection->OnChange = &OnServiceConnectionChange;
		AServiceConnection->OnReceiveNotification = &OnReceiveNotificationEvent;
		AServiceConnection->Active = true;

		ADeviceID  = APushService->DeviceIDValue[TPushService_TDeviceIDNames_DeviceID];
		ADeviceToken = APushService->DeviceTokenValue[TPushService_TDeviceTokenNames_DeviceToken];

		if(ADeviceID != "" && ADeviceToken != "")
		  {
		   RegisterDevice(ADeviceID, ADeviceToken); //global.hpp
		  }
	}

}

Весь код:


    void __fastcall OnReceiveNotificationEvent(TObject *Sender, TPushServiceNotification* const ANotification);
    void __fastcall OnServiceConnectionChange(TObject *Sender, TPushService::TChanges AChange);


private:	// User declarations
     TPushService * APushService;
	 TPushServiceConnection * AServiceConnection;

//cpp
void __fastcall TForm1::FormShow(TObject *Sender)
{
  APushService = nullptr;
  AServiceConnection = nullptr;

  String ADeviceID = "";
  String ADeviceToken = "";
  #if defined(__ANDROID__)
  APushService = TPushServiceManager::Instance->GetServiceByName(TPushService_TServiceNames_GCM);
  APushService->AppProps[TPushService_TAppPropNames_GCMAppID] = FAndroidServerKey;
  #endif
  #if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
  APushService = TPushServiceManager::Instance->GetServiceByName(TPushService_TServiceNames_APS);
  #endif

  if (APushService)
    {
		AServiceConnection = new TPushServiceConnection(APushService);
		AServiceConnection->OnChange = &OnServiceConnectionChange;
		AServiceConnection->OnReceiveNotification = &OnReceiveNotificationEvent;
		AServiceConnection->Active = true;

		ADeviceID  = APushService->DeviceIDValue[TPushService_TDeviceIDNames_DeviceID];
		ADeviceToken = APushService->DeviceTokenValue[TPushService_TDeviceTokenNames_DeviceToken];

		if(ADeviceID != "" && ADeviceToken != "")
		  {
		   RegisterDevice(ADeviceID, ADeviceToken); //global.hpp
		  }
	}

}
//---------------------------------------------------------------------------

void __fastcall TForm1::OnServiceConnectionChange(TObject *Sender, TPushService::TChanges AChange)
{
	if (AChange.Contains(TPushService::TChange::DeviceToken) && (AServiceConnection))
    {
        String ADeviceID = "";
        String ADeviceToken = "";
		ADeviceID = APushService->DeviceIDValue[TPushService_TDeviceIDNames_DeviceID];
		ADeviceToken = APushService->DeviceTokenValue[TPushService_TDeviceTokenNames_DeviceToken];

		if(ADeviceID != "" && ADeviceToken != "")
		  {
		   RegisterDevice(ADeviceID, ADeviceToken); //global.hpp
		  }

	}

}
//---------------------------------------------------------------------------

void __fastcall TForm1::OnReceiveNotificationEvent(TObject *Sender, TPushServiceNotification* const ANotification)
{
    //ShowMessage(ANotification->Json->ToString());

	String MessageText = "";
	//Получаем текст сообщения в зависимости ль платформы
	#if defined(__ANDROID__)
	MessageText = ANotification->DataObject->GetValue("message")->Value();
	#endif
	#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
	MessageText = ANotification->DataObject->GetValue("alert")->Value();
	#endif
	if(MessageText != "")
		ShowNotification(MessageText, 0);

}

 

Ссылка на комментарий
  • Модераторы

незнаю как на плюсах будет, в uses добавлены?

System.PushNotification
{$IFDEF ANDROID}, FMX.PushNotification.Android {$ENDIF}
{$IFDEF IOS}, FMX.PushNotification.iOS{$ENDIF}

 

Ссылка на комментарий
//h
#include <System.PushNotification.hpp>

//cpp
#if defined(__ANDROID__)
#include <FMX.PushNotification.Android.hpp>
#endif
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
#include <FMX.PushNotification.IOS.hpp>
#endif

segmentation fault 11

и попадаем в файл System.PushNotification на строки

procedure TPushService.SetAppProp(const AName, AValue: string);
begin
  FAppProps.AddOrSetValue(AName, AValue);
end;

Что можно еще посоветовать?

Спасибо.

 

Ссылка на комментарий
1 час назад, Равиль Зарипов (ZuBy) сказал:

READ_PHONE_STATE вроде нужно

Добавила. Видимо еще какая-то настройка мною упущена. APushService = 0х0...

 

 

null1.jpg

Изменено пользователем Kitty
Ссылка на комментарий

Вроде тут похожая проблема:

APushService is always nil

только не поняла, а что же надо сделать?...

Мой манифест в общей папке проекта 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%"
        android:installLocation="%installLocation%">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="%targetSdkVersion%" />
<%uses-permission%>

    <uses-feature android:glEsVersion="0x00020000" android:required="True"/>
    <application android:persistent="%persistent%" 
        android:restoreAnyVersion="%restoreAnyVersion%" 
        android:label="%label%" 
        android:debuggable="%debuggable%" 
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%">

<%application-meta-data%>
		<%services%>
        <!-- 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|keyboard|keyboardHidden|screenSize"
                android:launchMode="singleTask">
            <!-- 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>
        <%activity%>
		<service android:name="com.embarcadero.gcm.notifications.GCMIntentService" /> 
        <%receivers%>
		
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

А такой манифест AndroidManifest.xml в паке релиз:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="ua.com.mikros.Promo"
        android:versionCode="1"
        android:versionName="1.0.0"
        android:installLocation="auto">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <permission android:name="ua.com.mikros.Promo.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="ua.com.mikros.Promo.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />


    <uses-feature android:glEsVersion="0x00020000" android:required="True"/>
    <application android:persistent="False" 
        android:restoreAnyVersion="False" 
        android:label="Promo" 
        android:debuggable="True" 
        android:largeHeap="False"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme"
        android:hardwareAccelerated="true">


		
        <!-- 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="Promo"
                android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
                android:launchMode="singleTask">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name"
                android:value="Promo" />
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter> 
        </activity>
        
		<service android:name="com.embarcadero.gcm.notifications.GCMIntentService" /> 
        <receiver android:name="com.embarcadero.rtl.notifications.NotificationAlarm" />
<receiver android:exported="true" android:name="com.embarcadero.gcm.notifications.GCMNotification" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="ua.com.mikros.Promo" />
</intent-filter>
</receiver>

		
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

 

Ссылка на комментарий
2 часа назад, Kitty сказал:

Вроде тут похожая проблема:

APushService is always nil

только не поняла, а что же надо сделать?...

Мой манифест в общей папке проекта 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%"
        android:installLocation="%installLocation%">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="%targetSdkVersion%" />
<%uses-permission%>

    <uses-feature android:glEsVersion="0x00020000" android:required="True"/>
    <application android:persistent="%persistent%" 
        android:restoreAnyVersion="%restoreAnyVersion%" 
        android:label="%label%" 
        android:debuggable="%debuggable%" 
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%">

<%application-meta-data%>
		<%services%>
        <!-- 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|keyboard|keyboardHidden|screenSize"
                android:launchMode="singleTask">
            <!-- 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>
        <%activity%>
		<service android:name="com.embarcadero.gcm.notifications.GCMIntentService" /> 
        <%receivers%>
		
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

А такой манифест AndroidManifest.xml в паке релиз:


<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="ua.com.mikros.Promo"
        android:versionCode="1"
        android:versionName="1.0.0"
        android:installLocation="auto">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <permission android:name="ua.com.mikros.Promo.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="ua.com.mikros.Promo.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />


    <uses-feature android:glEsVersion="0x00020000" android:required="True"/>
    <application android:persistent="False" 
        android:restoreAnyVersion="False" 
        android:label="Promo" 
        android:debuggable="True" 
        android:largeHeap="False"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme"
        android:hardwareAccelerated="true">


		
        <!-- 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="Promo"
                android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
                android:launchMode="singleTask">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name"
                android:value="Promo" />
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter> 
        </activity>
        
		<service android:name="com.embarcadero.gcm.notifications.GCMIntentService" /> 
        <receiver android:name="com.embarcadero.rtl.notifications.NotificationAlarm" />
<receiver android:exported="true" android:name="com.embarcadero.gcm.notifications.GCMNotification" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="ua.com.mikros.Promo" />
</intent-filter>
</receiver>

		
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

 

В чем различия?

Ссылка на комментарий
  • 2 недели спустя...

Проблема с билдером решена и он может теперь работать с пушами. Источник: как исправить баг

У меня вопрос по статье. Вот в статье есть код:

const
  // Доменное имя сайта 
  DOMAIN: string = '193.106.248.115';

implementation


procedure RegisterDevice(DeviceID : string; DeviceToken : string);
var
  // Данные для передачи скрипту
  postdata: TStringList;
  // Подключение для передачи данных
  httpconnect : TIdHTTP;
begin

  try
    // Создаём подключение
    httpconnect := TIdHTTP.Create;
    // Указываем данные для отправки
    postdata := TStringList.Create;
    postdata.Add('action=register-device');
    postdata.Add('did=' + DeviceID);
    postdata.Add('token=' + DeviceToken);
    {$ifdef ANDROID}
      postdata.Add('platform=android');
    {$else}
      postdata.Add('platform=ios');
    {$endif}

    // Отправляем запрос
    httpconnect.Post(DOMAIN + 'push.php', postdata);
  finally
    // Отключаемся и освобождаем память
    httpconnect.Disconnect;
    httpconnect.DisposeOf;
  end;
end;

Два вопроса:

У меня, к примеру, весь PHP сервер скопирован в корень 193.106.248.115 в папку Push_Server.

1. Мне надо объявить константу так? ->

const
  // Доменное имя сайта
  DOMAIN: string = '193.106.248.115/Push_Server';

или как?

2. Не знаю паскаль. Почему нет освобождения памяти для postdata?

postdata := TStringList.Create;

 

 

 

Ссылка на комментарий

Скажите вот так будет правильно?

procedure RegisterDevice(DeviceID : string; DeviceToken : string);
var
  // Данные для передачи скрипту
  postdata: TStringList;
  // Подключение для передачи данных
  httpconnect : TIdHTTP;
begin

  try
    // Создаём подключение
    httpconnect := TIdHTTP.Create;
    // Указываем данные для отправки
    postdata := TStringList.Create;
    postdata.Add('action=register-device');
    postdata.Add('did=' + DeviceID);
    postdata.Add('token=' + DeviceToken);
    {$ifdef ANDROID}
      postdata.Add('platform=android');
    {$else}
      postdata.Add('platform=ios');
    {$endif}

    // Отправляем запрос
    httpconnect.Post(DOMAIN + 'push.php', postdata);
  finally
    // Отключаемся и освобождаем память
    httpconnect.Disconnect;
    httpconnect.DisposeOf;
    IF Assigned( postdata  ) THEN BEGIN
      postdata.DisposeOf;
      postdata := Nil;
   END; 

  end;
end;

Просто в С++ я использую при объявлении умный указатель unique_ptr и меня не волнует освобождение памяти т.к. этот указатель сам все делает и за всем следит когда освобождать правильно память.

И остаеться вопрос, как в моем случае правильно объявить

const
// Доменное имя сайта
DOMAIN: string = ?

Спасибо.

Изменено пользователем Kitty
Ссылка на комментарий

Не используйте TIdHTTP, с ним будут проблемы. Post тоже не обязательно, вот простой работающий код :

Var AHTTPClient : THTTPClient;
	HTTPQuery : String;
begin
	HTTPQuery:=APIURL+'?DeviceID='+HDevicePushParams.DeviceID+'&DeviceToken='+HDevicePushParams.DeviceToken;
	AHTTPClient:=THTTPClient.Create;
	AHTTPClient.Get(HTTPQuery);
	AHTTPClient.Free;
end;
	

Вместо AHTTPClient.Get(HTTPQuery), можно использовать асинхронный запрос AHTTPClient.BeginGet(HTTPQuery), тогда приложение не будет записать при недоступности сервера или проблемах с связью.

Изменено пользователем Евгений Корепов
Ссылка на комментарий

Я пытаюсь адаптировать статью этого топика. Т.е в статье присутствует .pas файл с нужным функционалом. Как этот файл будут выглядеть в окончательном виде с THTTPClient?

Ссылка на комментарий
В 09.04.2017 в 16:27, Kitty сказал:

const
  // Доменное имя сайта
  DOMAIN: string = '193.106.248.115/Push_Server';

Не знаю правильно ли указывать папку вместе с именем сайта, но обычно папки разделяют так

const
  // Доменное имя сайта
  DOMAIN = '193.106.248.115\Push_Server';

Изменено пользователем GASCHE
Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...