Перейти к содержанию
  • Регистрация

Евгений Корепов

Пользователи
  • Публикаций

    648
  • Зарегистрирован

  • Посещение

  • Победитель дней

    74

Весь контент Евгений Корепов

  1. Пока тестировал хелпер в боевом проекте он потихоньку оброс исрпавлениями/улучшениями: Загрузка из потока сделана через TBitmapSurface - это позволяет избежать множества глюков. LoadFromStream вынесен из Synchronize (основного потока) в поток HTTPClient - по результатам бенчмарка операция оказалась самая жручая. После исправления интерфейс перестал залипать совсем. Добавлен overload вариант с передачей в процедуру TListItemImage - для использования в TListView и корректной перерисовки подгруженных картинок через AListItemImage.Invalidate. unit BitmapAsyncLoader; interface uses FMX.Graphics, FMX.Surfaces, System.Net.HttpClient, System.Types, System.Classes, FMX.ListView.Types, FMX.ListView.Appearances; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromURLAsync(const AUrl : String); overload; procedure LoadFromURLAsync(const AUrl : String; const AListItemImage : TListItemImage); overload; end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self)then Assign(ABitmapSurface); ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String; const AListItemImage : TListItemImage); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; ABitmapSurface : TBitmapSurface; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then begin ABitmapSurface:=TBitmapSurface.Create; if TBitmapCodecManager.LoadFromStream(AHTTPResponse.ContentStream, ABitmapSurface, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then TThread.Synchronize(Nil, procedure begin if Assigned(Self) and Assigned(AListItemImage) then begin AListItemImage.BeginUpdate; Assign(ABitmapSurface); AListItemImage.Invalidate; AListItemImage.EndUpdate; end; ABitmapSurface.Free; end ) else ABitmapSurface.Free; end; end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then AHTTPClient.DisposeOf; end. Тестовый проект, на этот раз с ListView (по кнопке добавляется 100 итемов) прилагаю. BitmapAsyncLoaderListView.7z
  2. Для одного своего проекта сделал, поделюсь, вдруг кому пригодится. Тестировал под Windows и Android. Для использования просто добавьте BitmapAsyncLoader в uses, а дальше все просто: ImageControl.Bitmap.LoadFromURLAsync('https://bipbap.ru/wp-content/uploads/2017/10/0_8eb56_842bba74_XL-640x400.jpg'); Код юнита хелпера: unit BitmapAsyncLoader; interface uses FMX.Graphics, System.Net.HttpClient, System.Types, System.Classes; type TBitmapAsyncLoader = class helper for TBitmap procedure LoadFromUrlAsync(const AUrl : String); end; implementation var AHTTPClient : THTTPClient; procedure TBitmapAsyncLoader.LoadFromURLAsync(const AURL : String); begin try AHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse : IHTTPResponse; begin if Not ASyncResult.IsCompleted then exit; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); except end; if Assigned(AHTTPResponse) and (AHTTPResponse.StatusCode = 200) then TThread.Synchronize(Nil, procedure begin try Self.LoadFromStream(AHTTPResponse.ContentStream); except end; end ); end, AURL ); except end; end; initialization AHTTPClient:=THTTPClient.Create; finalization if Assigned(AHTTPClient) then begin AHTTPClient.DisposeOf; end; end. Архив с тестовым проектом прилагаю. BitmapAsyncLoader.7z
  3. Спасибо! Посмотрел diff - как и предполагал, Эмбаркадера поленилась скопипастить все коды, видимо в буфер обмена у них влезла только часть Ну как так то? Спасибо вам за проделанную работу!
  4. Одновременная работа Wifi и мобильной сети возможна. Но лично мне не удалось заставить их так работать ) Смысл в включении режима HIPRI. Вот код, может разберетесь почему не работает. Если удастся заставить работать, то напишите, будет интересно узнать где я ошибся. Если не получится, то вы можете отключать wifi на телефоне, отправлять данные по включившемуся мобильному интернету, и опять включать wifi. unit UnitFormMain; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, Androidapi.Helpers, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.JavaTypes, Androidapi.JNIBridge, Androidapi.JNI.Net, FMX.WebBrowser; type TFormMain = class(TForm) WebBrowser: TWebBrowser; procedure FormCreate(Sender: TObject); private { Private declarations } function GetConnectivityManager: JConnectivityManager; public { Public declarations } end; var FormMain: TFormMain; implementation {$R *.fmx} procedure TFormMain.FormCreate(Sender: TObject); var ConnectivityManager : JConnectivityManager; begin ConnectivityManager:=GetConnectivityManager; ConnectivityManager.setNetworkPreference(TJConnectivityManager.JavaClass.TYPE_MOBILE); ConnectivityManager.startUsingNetworkFeature(TJConnectivityManager.JavaClass.TYPE_MOBILE, StringToJString('enableHIPRI')); Sleep(3000); if ConnectivityManager.getActiveNetworkInfo.isConnected then WebBrowser.Navigate('https://whoer.net/ru'); end; function TFormMain.GetConnectivityManager: JConnectivityManager; var ConnectivityServiceNative: JObject; begin ConnectivityServiceNative := TAndroidHelper.Context.getSystemService(TJContext.JavaClass.CONNECTIVITY_SERVICE); if not Assigned(ConnectivityServiceNative) then raise Exception.Create('Could not locate Connectivity Service'); Result := TJConnectivityManager.Wrap( (ConnectivityServiceNative as ILocalObject).GetObjectID); if not Assigned(Result) then raise Exception.Create('Could not access Connectivity Manager'); end; end. По поводу второго вопроса - с весами (терминалами и т.п.) проще работать в строчном режиме. Хотя на вкус и цвет... if FIdTCPClient.IOHandler.CheckForDataOnSource(FTimeouts.ReadTimeout) then ASendingData.Value:=FIdTCPClient.IOHandler.ReadLnWait;
  5. Я еще тупее чем думал. После нажатия "Update Local File Cache" все фреймворки подсосались сами с Mac mini ))))))))))))))
  6. Как и предполагал - я слишком тупой чтоб понять их инструкцию по установке. Go to Tools -> Options -> SDK Manager à iOS Device, for each iPhoneOS device: Click on any framework (just a click: there is a bug in IDE). Click “Add a new path item” image button (top one) Set “Path on remote machine” to “/System/Library/Frameworks” or “$(SDKROOT)/System/Library/Frameworks” (same as other frameworks). Set “Framework name” to “SystemConfiguration”. Click Ok (nothing should be selected in the radio and leave the checkbox unchecked). Repeat step 3 with Framework name “AdSupport”, “StoreKit”, “Social”, “CoreData”, “CoreTelephony”, “CoreMedia”, “SystemConfiguration”, “Security”, “EventKit”, “EventKitUI”, “AVFoundation”, “iAd”, “AudioToolbox”, “CoreBluetooth”, “SafariServices”, “CoreMotion”, “MediaPlayer”, “MobileCoreServices”, “CoreVideo”, “MessageUI”, “Metal”, “IOSurface”, “ImageIO”, “CoreAudio”, “MediaToolbox”, “CoreMIDI”, “JavaScriptCore”, “ModelIO” (no need to duplicate already existing items: Delphi XE5, for example, already includes StoreKit and iAd). Click “Update Local File Cache” button. В эти папки ($(SDKROOT)/System/Library/Frameworks/...) надо что то положить? Где взять все эти фреймворки? Или я туплю?
  7. Хотя наверное куплю у них все за $100, там даже Google Analytics есть, Эмбаркадера это сделает наверное никогда. Вообще странно что Эмбаркадера кладет на такие важные вещи как монетизация приложений - сделай пользователей своих продуктов богаче и тебе перепадет больше денег за подписки и прочее. Простая как 3 копейки идея, но маркетологи Эмбы видимо еще не дочитали учебник до этого места.
  8. Спасибо за ответ! Ads Support only pack for $70.00 ? Жаба конечно душит, но думаю окупится. Какие то подводные камни обнаружились в компоненте или все норм? Почитал их инструкцию по установке (https://www.jvesoft.com/wp/configuring-ios/) нифига не понял, но вроде супер-сложного ничего нет.
  9. Получилось у вас запустить AdMob в iOS? Сейчас в Rio пробую, без подключения дополнительных фреймворков, но объявление пустое всегда и ошибка "Не удалось завершить операцию. Запас рекламных объявлений исчерпан". Пробовал и в testmode и без него. В admob приложение и банер добавлены более суток назад.
  10. Подскажите, реально ли в текущий момент сделать рекламу в iOS приложениях? Документация эмбаркадеры, вроде и современная (http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_the_AdMob_Service в примеру), но пометка внизу страниц "This page was last edited on 28 September 2015, at 15:17." дает понять что они забили на этот раздел документации 4 года назад, и с выходом Rio тупо скопипастили, чтоб было. Издевательство какое то ( P.S. В документации идет речь о iAd, но согласно Apple: About the iAd App Network Shutdown : As of December 31, 2016, the iAd App Network is no longer available.
  11. Пытался понять как работает TClearEditButton у TEdit - она очищает клавиатуру нормально. Но понять и адаптировать к TMemo не смог ))))
  12. Нашел еще более изящный способ, без исчезновения/показа клавиатуры: procedure TForm1.Button1Click(Sender: TObject); begin Memo1.SelectAll; Memo1.DeleteSelection; end;
  13. Ага, теперь нормально воспроизводится. Самый простой способ избежать этого, вот такой: procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Text:=EmptyStr; Memo1.ResetFocus; Memo1.SetFocus; end; Правда будет видно что виртуальная клавиатура исчезает, а затем опять появляется.
  14. Сделал тестовое приложение (чистый андроид 9, Rio), но не удалось воспроизвести такое поведение. Закиньте проект в котором глюк воспроизводится.
  15. Я использую для такого Indy, там все готовенькое, в том числе и TIdUDPServer со всем нужным вам функционалом (и потоками). Вот в теме немного обсуждения
  16. Не очень понятно где именно вы видите 123456, в самом контроле или в помощнике клавиатуры?
  17. К сожалению нисколько. Тут просто здравый смысл - вы приобретаете телефон хорошего качества, с нормальной гарантией и сервисом. Программа Android One гарантирует вам обновления (ежемесячные обновления и обновления OS) - это значит что устройство сможет помогать вам в разработке не один год. В последствии вы сможете продать устройство за приемлемые деньги, а не за 300 рублей (как раз по такой цене я распродал мусор Dexp купленный изначально, 4 года назад, по неопытности). Вот и все аргументы, никакого скрытого умысла. P.S. Добавлю - один из телефонов Dexp взорвался у меня прямо на столе. Чудом не сгорел рабочий кабинет, но часть рабочих бумаг погорело, слава богу все само потухло. Меня на месте в этот момент не было, телефон был подключен к компу - до этого занимался отладкой одного из приложений. Телефон превратился в оплавленный комок пластмассы. ))
  18. Это происходит в основном или с русским ребрендингом, где пытаются скрыть настоящего производителя аппарата с помощью программный заглушек. Типа Dexp и прочий мусор. Или с контрафактной китайщиной без IMEI. Тут не студия не видит, а винда не понимает что к ней подключили. Обычно решается поиском на китайских форумах подходящих дров. Просто покупайте любой Xiaomi c Android One (чистый андроид). Его вы всегда сможете использовать по назначению для себя/семьи или продать. Или покупайте любой, в нормальном магазине - если что то не заработает, вернете в течении двух недель со словами "Его цвет не походит к моим глазам" )))
  19. Это вряд ли. Да и по хуавею уже на попятную пошли, ибо без редкоземельных металлов, штатам придется вернутся к плантациям и неграм-рабам. ))))
  20. Я бы переделал и код отправки и код приема. При отправке вы выполняете лишние действия, проверяете наличие интернет, преобразуете json в лист. Отправка любым способом желательна в виде "Ключ=Значение", чтоб на стороне сервера корректно распознать данные. Вот отправка POST (добавьте в uses System.Net.Mime): function TForm1.PostURL(const aurl, json: string): string; var AHTTPClient : THTTPClient; AHttpResponse: IHttpResponse; ASource: TMultipartFormData; begin result:= ''; AHTTPClient:= THTTPClient.Create; ASource:=TMultipartFormData.Create(); ASource.AddField('request', json); try try AHttpResponse := AHTTPClient.Post(aurl, ASource); if AHttpResponse.StatusCode <> 200 then Result := AHttpResponse.StatusText else Result := AHttpResponse.ContentAsString(); except on E: exception do ShowMessage('Ошибка сети: '+E.Message); end; finally FreeAndNil(ASource); FreeAndNil(AHTTPClient); end; end; Но гораздо проще и быстрее делать GET (добавьте в uses System.NetEncoding): function TForm1.GetURL(const aurl, json: string): string; var AHTTPClient : THTTPClient; AHttpResponse: IHttpResponse; AEncodedJSON : String; begin result:= ''; AHTTPClient:= THTTPClient.Create; AEncodedJSON:=System.NetEncoding.TURLEncoding.URL.Encode(json); try try AHttpResponse := AHTTPClient.Get(aurl + '?request=' + AEncodedJSON); if AHttpResponse.StatusCode <> 200 then Result := AHttpResponse.StatusText else Result := AHttpResponse.ContentAsString(); except on E: exception do ShowMessage('Ошибка сети: '+E.Message); end; finally FreeAndNil(AHTTPClient); end; end; file_get_contents("php://input", "r") - это тоже вариант, но никогда такое не используйте. На стороне сервера, в php есть специальные глобальные переменные для получения данных $_REQUEST, $_POST и $_GET. $_POST и $_GET - для соответствующих HTTP методов. $_REQUEST - универсальный. Вот вам код php для сервера, я там описал вспомогательную функцию лога MyLog, измените путь логов на свой. <?php @ini_set("display_errors", "1"); error_reporting(E_ALL); //@ini_set("display_errors", "0"); error_reporting(0); function MyLog($Title, $Value = "", $Debug = false) { $LogFile = "/var/log/service/test001.log"; if (is_array($Title)) $Title = "\n".print_r($Title, true)."\n"; if (is_array($Value)) $Value = "\n".print_r($Value, true)."\n"; $Message = date("Y.m.d H:i:s")." ".$Title.$Value."\r\n"; if ($Debug) echo $Message; file_put_contents($LogFile, $Message, FILE_APPEND); } // Проверяем наличие параметра request if (!isset($_REQUEST["request"])) { echo "Нет параметров"; return; } // Декодируем строку из $_REQUEST["request"] в массив $RequesrArray = json_decode($_REQUEST["request"], true); // Проверяем прошло ли декодирование нормально if (!isset($RequesrArray)) { echo "Ошибочный JSON"; return; } MyLog('*************************************************************************************************************************'); MyLog('$_REQUEST:', $_REQUEST); MyLog('$_POST:', $_POST); MyLog('$_GET:', $_GET); MyLog('$RequesrArray:', $RequesrArray); // Работаем с массивом $RequesrArray $lessongrid_type = $RequesrArray['lessongrid_type']; $schedule0 = $RequesrArray['schedule'][0]; Обратите внимание json_decode($_REQUEST["request"], true); используется с вторым параметром true - при этом создается не объект (медленно), а ассоциативный массив (быстрее некуда). Вот код Delphi procedure TForm1.FormCreate(Sender: TObject); Var AURL : String; AJSON : String; begin AURL:='http://myserver.ru/api/test001.php'; AJSON:='{"lessongrid_idpartner":2,"lessongrid_type":0,"lessongrid_idpointcity":1,"lessongrid_iddircourses":1,"lessongrid_idteacher":1,"schedule":["0:00","5:00","0:00","0:00","0:00","0:00","0:00"]}'; PostURL(AURL, AJSON); GetURL(AURL, AJSON); end; А вот логи на стороне сервера: 2019.06.06 13:29:13 ************************************************************************************************************************* 2019.06.06 13:29:13 $_REQUEST: Array ( [request] => {"lessongrid_idpartner":2,"lessongrid_type":0,"lessongrid_idpointcity":1,"lessongrid_iddircourses":1,"lessongrid_idteacher":1,"schedule":["0:00","5:00","0:00","0:00","0:00","0:00","0:00"]} ) 2019.06.06 13:29:13 $_POST: Array ( [request] => {"lessongrid_idpartner":2,"lessongrid_type":0,"lessongrid_idpointcity":1,"lessongrid_iddircourses":1,"lessongrid_idteacher":1,"schedule":["0:00","5:00","0:00","0:00","0:00","0:00","0:00"]} ) 2019.06.06 13:29:13 $_GET: Array ( ) 2019.06.06 13:29:13 $RequesrArray: Array ( [lessongrid_idpartner] => 2 [lessongrid_type] => 0 [lessongrid_idpointcity] => 1 [lessongrid_iddircourses] => 1 [lessongrid_idteacher] => 1 [schedule] => Array ( [0] => 0:00 [1] => 5:00 [2] => 0:00 [3] => 0:00 [4] => 0:00 [5] => 0:00 [6] => 0:00 ) ) 2019.06.06 13:29:13 ************************************************************************************************************************* 2019.06.06 13:29:13 $_REQUEST: Array ( [request] => {"lessongrid_idpartner":2,"lessongrid_type":0,"lessongrid_idpointcity":1,"lessongrid_iddircourses":1,"lessongrid_idteacher":1,"schedule":["0:00","5:00","0:00","0:00","0:00","0:00","0:00"]} ) 2019.06.06 13:29:13 $_POST: Array ( ) 2019.06.06 13:29:13 $_GET: Array ( [request] => {"lessongrid_idpartner":2,"lessongrid_type":0,"lessongrid_idpointcity":1,"lessongrid_iddircourses":1,"lessongrid_idteacher":1,"schedule":["0:00","5:00","0:00","0:00","0:00","0:00","0:00"]} ) 2019.06.06 13:29:13 $RequesrArray: Array ( [lessongrid_idpartner] => 2 [lessongrid_type] => 0 [lessongrid_idpointcity] => 1 [lessongrid_iddircourses] => 1 [lessongrid_idteacher] => 1 [schedule] => Array ( [0] => 0:00 [1] => 5:00 [2] => 0:00 [3] => 0:00 [4] => 0:00 [5] => 0:00 [6] => 0:00 ) ) Как видите можно использовать код с $_REQUEST в обоих случаях. Вот итоговый код php <?php @ini_set("display_errors", "1"); error_reporting(E_ALL); //@ini_set("display_errors", "0"); error_reporting(0); // Проверяем наличие параметра request if (!isset($_REQUEST["request"])) { echo "Нет параметров"; return; } // Декодируем строку из $_REQUEST["request"] в массив $RequesrArray = json_decode($_REQUEST["request"], true); // Проверяем прошло ли декодирование нормально if (!isset($RequesrArray)) { echo "Ошибочный JSON"; return; } // Работаем с массивом $RequesrArray $lessongrid_type = $RequesrArray['lessongrid_type']; $schedule0 = $RequesrArray['schedule'][0];
  21. Покупайте любой телефон с андроидом посвежее (потому как в последней версии студии на андроиде < 5 вы ничего уже не запустите). И обязательно берите чистый аднроид, никаких HTC, Samsung, Xiaomi с их мега-облочками. Вот к примеру Xiaomi A1 - модель с чистым андроидом, вполне себе средний телефон, не дорого.
  22. Одно из основных требований протокола REST - отсутствия состояния. Т.е. сервер ничего не должен хранить: запрос от клиента, ответ от сервера, все. Никаких сессий и прочей мути. То что у вас на данный момент реализовано - велосипедный костыль, поэтому никакие стандартные фреймворки с этим работать не будут. P.S. Прочитал ваше сообщение внимательнее - все верно у вас работает. "НО! после любого обращения по REST, сервер создает и уничтожает сразу класс ServerMethod" - вот так и должно быть.
  23. MultiView.IsShowed http://docwiki.embarcadero.com/Libraries/Rio/en/FMX.MultiView.TCustomMultiView.IsShowed
  24. Продукты Apple не умеют корректно декодировать URL. Это исключительно ваша задача. Все другие платформы делают это автоматически. Вам нужно добавить код: uses System.NetEncoding; ... URL:='http://blabla.com/php/pay2.php?eks=' + System.NetEncoding.TURLEncoding.URL.Encode('блабла&sum=100&hash=блабла&orderid=205');
×
×
  • Создать...