[Android]TIdHTTP and TThread

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

[Android]TIdHTTP and TThread

Postby Lena » Thu Dec 15, 2016 2:51 am

Hi.
I plan to have 5-100 lines in the ListBox with pictures from remote host. Please help me to move this code into a separate TThread in C++ Builder Berlin.
Code: Select all
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  std::unique_ptr<TMemoryStream> WelcomeINI(new TMemoryStream());
 //test for 5 images:
  for (int i = 1; i <= 5; i++)
  {
      String NameOfImage = IntToStr(i) + ".png";
      String URLLink = L"http://welcome.um.la/myimg/" + NameOfImage;
      try
      {
      IdHTTP1->Get(URLLink, WelcomeINI.get());
      WelcomeINI->Position = 0;
      }
      catch(...)
         {
          WelcomeINI->LoadFromFile("72x72.png"); //image by default
          WelcomeINI->Position = 0;
         }


     ListBox1->BeginUpdate();
     TListBoxItem *ListBoxItem;
     TListBoxGroupHeader *ListBoxGroupHeader;
     ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
     ListBoxGroupHeader->Text = L"Custom header";
     ListBox1->AddObject(ListBoxGroupHeader);

     ListBoxItem = new TListBoxItem(ListBox1);
     ListBoxItem->StyleLookup = L"listboxitemleftdetail";
     ListBoxItem->Height = 72; //image heigth
     ListBoxItem->TextSettings->WordWrap = true;
     ListBoxItem->Text = L"Custom text";
     ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
     ListBox1->AddObject(ListBoxItem);
      WelcomeINI->Clear();
     ListBox1->EndUpdate();
  }


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

void __fastcall TForm1::IdHTTP1WorkEnd(TObject *ASender, TWorkMode AWorkMode)
{
 //TAniIndicator
 AniIndicator1->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::IdHTTP1WorkBegin(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax)

{
 AniIndicator1->Enabled = true;
}
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby rlebeau » Thu Dec 15, 2016 12:34 pm

Lena wrote:I plan to have 5-100 lines in the ListBox with pictures from remote host. Please help me to move this code into a separate TThread in C++ Builder Berlin.


What is the actual problem you are having? Have you ever worked with TThread or TTask before? I've written examples for this kind of scenario before, for example this one (sorry, it is in Delphi, but it can be translated to C++).

I can tell you right now, though, that FireMonkey's TBitmap class DOES NOT work correctly outside of the main UI thread. There are many open bug tickets about that. So, you could certainly download the images in a background thread, but you would end up doing a lot of synchronizing with the UI thread in order to load them into the UI.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1450
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TIdHTTP and TThread

Postby Lena » Fri Dec 16, 2016 2:19 am

What is the actual problem you are having?


TIdHTTP, TBitmap with TThread this is a very difficult subject for me.
Thank You for Your code but for me very difficult translates for C++. I did not find in the Help example in C++.

that FireMonkey's TBitmap class DOES NOT work correctly


I read on a forum that can be used TBitmapSurface. However, there is no example in C ++. :(
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby rlebeau » Wed Dec 21, 2016 5:07 pm

Try something like this:

Code: Select all
int ThreadsRunning = 0;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TListBoxItem *ListBoxItem;
    TListBoxGroupHeader *ListBoxGroupHeader;

    AniIndicator1->Enabled = true;

    ListBox1->BeginUpdate();
    try
    {
        //test for 5 images:
        for (int i = 1; i <= 5; i++)
        {
            ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
            ListBoxGroupHeader->Text = L"Custom header";
            ListBox1->AddObject(ListBoxGroupHeader);

            ListBoxItem = new TListBoxItem(ListBox1);
            ListBoxItem->StyleLookup = L"listboxitemleftdetail";
            ListBoxItem->Height = 72; //image heigth
            ListBoxItem->TextSettings->WordWrap = true;
            ListBoxItem->Text = L"Custom text";
            ListBox1->AddObject(ListBoxItem);

            String URLLink = L"http://welcome.um.la/myimg/" + IntToStr(i) + ".png";
            TThread *thread = TThread::CreateAnonymousThread(
              [URLLink, ListBoxItem]()
              {
                  std::unique_ptr<TMemoryStream> WelcomeINI(new TMemoryStream);
                  std::unique_ptr<TIdHTTP> HTTP(new TIdHTTP);

                  try
                  {
                      HTTP->Get(URLLink, WelcomeINI.get());
                  }
                  catch(...)
                  {
                      WelcomeINI->LoadFromFile("72x72.png"); //image by default
                  }

                  WelcomeINI->Position = 0;
                  ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
              }
            );
            thread->OnTerminate = &ThreadTerminated;
            thread->Start();

            ++ThreadsRunning;
        }
    }
    __finally
    {
        ListBox1->EndUpdate();
    }
}

void __fastcall TForm1::ThreadTerminated(TObject *Sender)
{
    if (--ThreadsRunning <= 0)
        AniIndicator1->Enabled = false;
}
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1450
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TIdHTTP and TThread

Postby Lena » Sun Dec 25, 2016 2:47 am

Thank You very much!

I have a problem compiling:
WelcomeINI->Position = 0;//here
[bccaarm Error] UnitHTTP.cpp(60): 'this' cannot be implicitly captured in this context
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby Lena » Sun Dec 25, 2016 2:52 am

I try:
Code: Select all
TThread *thread = TThread::CreateAnonymousThread([URLLink, ListBoxItem, this]()


Now missing error. I try to move on. :)
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby Lena » Sun Dec 25, 2016 3:09 am

I have 5 images on hosting:
http://welcome.um.la/myimg/1.png
http://welcome.um.la/myimg/2.png
****
I try on WIN64:
Code: Select all
TListBoxItem *ListBoxItem;
    TListBoxGroupHeader *ListBoxGroupHeader;

    AniIndicator1->Enabled = true;

    ListBox1->BeginUpdate();
    try
    {
        //test for 5 images:
        for (int i = 1; i <= 5; i++)
        {
            ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
            ListBoxGroupHeader->Text = L"Custom header";
            ListBox1->AddObject(ListBoxGroupHeader);

            ListBoxItem = new TListBoxItem(ListBox1);
            ListBoxItem->StyleLookup = L"listboxitemleftdetail";
            ListBoxItem->Height = 72; //image heigth
            ListBoxItem->TextSettings->WordWrap = true;
            ListBoxItem->Text = L"Custom text";
            ListBox1->AddObject(ListBoxItem);

         String URLLink = L"http://welcome.um.la/myimg/" + IntToStr(i) + ".png";
         TThread *thread = TThread::CreateAnonymousThread(
         [URLLink, ListBoxItem, this]()
           {
                  std::unique_ptr<TMemoryStream> WelcomeINI(new TMemoryStream);
                  std::unique_ptr<TIdHTTP> HTTP(new TIdHTTP);

                  try
                  {
                 HTTP->Get(URLLink, WelcomeINI.get());
              }
                  catch(...)
                  {
                      WelcomeINI->LoadFromFile("72x72.png"); //image by default
              }

              WelcomeINI->Position = 0;
              ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
           }
         );
         thread->OnTerminate = &ThreadTerminated;
            thread->Start();

            ++ThreadsRunning;
        }
    }
    __finally
    {
        ListBox1->EndUpdate();
   }


Strange problems with pictures in ListBox. Please tell me how to fix?
Attachments
pic.jpg
pic.jpg (58.25 KiB) Viewed 7002 times
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby Lena » Thu Dec 29, 2016 8:02 am

Trying to add the line of code did not help:
****
ListBox1->AddObject(ListBoxItem);
ListBoxItem->ApplyStyleLookup();//<- here
***

:(
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby Lena » Fri Dec 30, 2016 8:19 am

Hi.
Set StylesData should solve the problem.
As I understand it is necessary to replace the line:
ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
something like this:
ListBoxItem->StylesData["ItemData.Bitmap"] = TValue::From<TMemoryStream>(WelcomeINI.get());
but this is not the correct code.
Please show me how to set StylesData a picture from a TMemoryStream.
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby rlebeau » Tue Jan 03, 2017 5:26 pm

Lena wrote:I have a problem compiling:
WelcomeINI->Position = 0;//here
[bccaarm Error] UnitHTTP.cpp(60): 'this' cannot be implicitly captured in this context


That does not make any sense, as nothing inside the lambda I gave you uses the 'this' pointer at all, so it should not need to be captured. What code is on line 60 exactly?
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1450
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TIdHTTP and TThread

Postby rlebeau » Tue Jan 03, 2017 5:30 pm

Lena wrote:Set StylesData should solve the problem.
As I understand it is necessary to replace the line:
ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());
something like this:
ListBoxItem->StylesData["ItemData.Bitmap"] = TValue::From<TMemoryStream>(WelcomeINI.get());
but this is not the correct code.
Please show me how to set StylesData a picture from a TMemoryStream.


StylesData[] returns a TValue, so I would expect it to return a TBitmap object pointer in this situation:

Code: Select all
ListBoxItem->StylesData["ItemData.Bitmap"].AsType<TBitmap*>()->LoadFromStream(WelcomeINI.get());


However, based on this article:

Working with StylesData in your FireUI applications

It seems you should be using ListBoxItem->FindStyleResource() instead of ListBoxItem->StyledData[] directly.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1450
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TIdHTTP and TThread

Postby Lena » Wed Jan 04, 2017 1:49 am

What code is on line 60 exactly?


I try:
Code: Select all
TThread *thread = TThread::CreateAnonymousThread([URLLink, ListBoxItem]()
//***
catch(...)
{
WelcomeINI->LoadFromFile("44x44.png"); //image by default
}
WelcomeINI->Position = 0;//<- Failed


[bccaarm Error] UnitHTTP.cpp(60): 'this' cannot be implicitly captured in this context
Failed


I try:
TThread *thread = TThread::CreateAnonymousThread([URLLink, ListBoxItem, this]()
Now missing error. C++ Builder 10.1 Up2.

When press button error on line.
ListBoxItem->StylesData["ItemData.Bitmap"].AsType<TBitmap*>()->LoadFromStream(WelcomeINI.get());
First chance exception at $00000000018485B8. Exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'.
Process Project1.exe (4624)

Before Android I try in WIN64.
Maybe we have to change TListBox to TListView?
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby rlebeau » Wed Jan 04, 2017 2:12 pm

Lena wrote:I try:
Code: Select all
TThread *thread = TThread::CreateAnonymousThread([URLLink, ListBoxItem]()
//***
catch(...)
{
WelcomeINI->LoadFromFile("44x44.png"); //image by default
}
WelcomeINI->Position = 0;//<- Failed


[bccaarm Error] UnitHTTP.cpp(60): 'this' cannot be implicitly captured in this context
Failed



This sounds like a compiler error to me. As I said, the lambda I gave you does not use the 'this' pointer at all, so I don't know why it is complaining about capturing the 'this' pointer.

Lena wrote:When press button error on line.
ListBoxItem->StylesData["ItemData.Bitmap"].AsType<TBitmap*>()->LoadFromStream(WelcomeINI.get());
First chance exception at $00000000018485B8. Exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'.
Process Project1.exe (4624)


Did you make sure StylesData[] is not returning an empty TValue?

Code: Select all
TValue value = ListBoxItem->StylesData["ItemData.Bitmap"];
if (!value.IsEmpty)
{
    TBitmap *bitmap = value.AsType<TBitmap*>();
    bitmap->LoadFromStream(WelcomeINI.get());
}


Which line actually crashes? Is it the conversion of the TValue to a TBitmap* pointer, or the call to LoadFromStream(). This is basic debugging.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1450
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TIdHTTP and TThread

Postby Lena » Thu Jan 05, 2017 4:12 am

Thank You!

Now I see a very strange behavior. All my code now:
Code: Select all
//Try in WIN64 for test
/*
my images here:
http://welcome.um.la/myimg/1.png
http://welcome.um.la/myimg/2.png
***
*/
#include <fmx.h>
#pragma hdrstop
#include <memory>
#include "UnitHTTP.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;

int ThreadsRunning = 0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

   TListBoxItem *ListBoxItem;
   TListBoxGroupHeader *ListBoxGroupHeader;

   AniIndicator1->Enabled = true;

   ListBox1->BeginUpdate();
   try
   {
      //test for 5 images:
      for (int i = 1; i <= 5; i++)
      {
         ListBoxGroupHeader = new TListBoxGroupHeader(ListBox1);
         ListBoxGroupHeader->Text = L"Custom header";
         ListBox1->AddObject(ListBoxGroupHeader);

         ListBoxItem = new TListBoxItem(ListBox1);
         ListBoxItem->StyleLookup = L"listboxitemleftdetail";
         ListBoxItem->Height = 44; //image heigth
         ListBoxItem->TextSettings->Trimming = TTextTrimming::None;
         ListBoxItem->TextSettings->WordWrap = true;
         ListBoxItem->Text = L"Custom text";
         ListBox1->AddObject(ListBoxItem);
         ListBoxItem->ApplyStyleLookup();

         String URLLink = L"http://welcome.um.la/myimg/" + IntToStr(i) + ".png";
         TThread *thread = TThread::CreateAnonymousThread(
         [URLLink, ListBoxItem, this]()
           {
              std::unique_ptr<TMemoryStream> WelcomeINI(new TMemoryStream);
              std::unique_ptr<TIdHTTP> HTTP(new TIdHTTP);

                  try
              {
                 HTTP->Get(URLLink, WelcomeINI.get());
              }
              catch(...)
              {
                 WelcomeINI->LoadFromFile("44x44.png"); //image by default
              }

              WelcomeINI->Position = 0;

              TValue value = ListBoxItem->StylesData["ItemData.Bitmap"];
              if (!value.IsEmpty)
               {
                  TBitmap *bitmap = value.AsType<TBitmap*>();
                  bitmap->LoadFromStream(WelcomeINI.get());
               }

              //ListBoxItem->StylesData["ItemData.Bitmap"].AsType<TBitmap*>()->LoadFromStream(WelcomeINI.get());
              //ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());

           }
         );
         thread->OnTerminate = &ThreadTerminated;
         thread->Start();

         ++ThreadsRunning;
      }
   }
    __finally
   {
      ListBox1->EndUpdate();
   }

}
//---------------------------------------------------------------------------
void __fastcall TForm1::ThreadTerminated(TObject *Sender)
{
    if (--ThreadsRunning <= 0)
      AniIndicator1->Enabled = false;
}


I set a breakpoint on line and I see an empty bitmap:
TBitmap *bitmap = value.AsType<TBitmap*>(); //breakpoint does not work

I change the code:
Code: Select all
TValue value = ListBoxItem->StylesData["ItemData.Bitmap"];
              if (!value.IsEmpty)
               {
                  TBitmap *bitmap = value.AsType<TBitmap*>();
                  bitmap->LoadFromStream(WelcomeINI.get());
               }


To:
ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());


After that I see the pictures on the ListBox but only after scrolling.

Probably FMX ListBox has a bug with the cache, and probably should be used instead of a ListBox to TListView...
I do not see any other way. :(
I wanted to deal with the work ListBox but it's not working.
Lena
BCBJ Master
BCBJ Master
 
Posts: 522
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TIdHTTP and TThread

Postby rlebeau » Thu Jan 05, 2017 12:29 pm

Lena wrote:Now I see a very strange behavior. All my code now:


Your anonymous thread is not synchronizing with the main UI thread when accessing the ListBoxItem. You MUST synchronize. Not only because it is the right thing to do in general (you can't access UI controls across thread boundaries, especially on Android), but also because FireMonkey's TBitmap class is not thread-safe, it can only be used safely in the main UI thread (there are numerous bug reports in Quality Portal about this issue).

Lena wrote:I set a breakpoint on line and I see an empty bitmap:
TBitmap *bitmap = value.AsType<TBitmap*>(); //breakpoint does not work

I change the code:
Code: Select all
TValue value = ListBoxItem->StylesData["ItemData.Bitmap"];
if (!value.IsEmpty)
   {
      TBitmap *bitmap = value.AsType<TBitmap*>();
      bitmap->LoadFromStream(WelcomeINI.get());
   }


To:
Code: Select all
ListBoxItem->ItemData->Bitmap->LoadFromStream(WelcomeINI.get());


After that I see the pictures on the ListBox but only after scrolling.


That is likely related to you accessing the ListBoxItem and Bitmap from outside of the main UI thread. Sync with the main UI thread when loading the Bitmap, and see if it redraws correctly. If not, try calling ListBoxItem->InvalidateRect() or ListBoxItem->Repaint() after loading the bitmap.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1450
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Next

Return to Technical

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 14 guests

cron