Евгений (KeeperWorld)
-
Постов
2 -
Зарегистрирован
-
Посещение
-
Победитель дней
2
Сообщения, опубликованные Евгений (KeeperWorld)
-
-
В 06.02.2019 в 14:48, Евгений Корепов сказал:
procedure TForm1.ClearListViewAndCancelAsynchronousRequests();
Var I : Integer;
begin
for I := 0 to FAsyncResultList.Count - 1 do
if FAsyncResultList.Items <> Nil then
if Not FAsyncResultList.Items.IsCompleted then
FAsyncResultList.Items.Cancel;
FListViewUpdating:=True;
for I := ListView1.Items.Count - 1 downto 0 do
ListView1.Items.Delete(I);
FListViewUpdating:=False;
end;
Евгений, спасибо громадное за код!!! Красиво и лаконично!
Но под Rio всё равно крэшится с ошибкой, если приложение закрывать раньше, чем успевают загрузиться фотки:
Project Test21.exe raised exception class ENetHTTPClientException with message 'Error receiving data: (12017) Операция отменена'.
Поправил вот так:
procedure TForm1.ClearListViewAndCancelAsynchronousRequests();
var
I: Integer;
begin
FListViewUpdating := True; // Запрещаем продолжать загружать фотки (если ещё не успели загрузиться все)
while FAsyncResultList.Count > 0 do // Дожидаемся окончания выполнения всех IAsyncResult.Cancel, несмотря на асинхронность
begin
for I := FAsyncResultList.Count - 1 downto 0 do
if Assigned(FAsyncResultList.Items) and (not FAsyncResultList.Items.IsCompleted) then
FAsyncResultList.Items.Cancel
else
FAsyncResultList.Delete(I); // Заодно удаляем элементы (раннее не удалялись - утечка памяти)
end;
ListView1.Items.Clear;
FListViewUpdating := False;
end;=====
Тоже, кстати, пару раз поймал ошибку в TMonitor и в TDictionary.
Выяснил, что возникает из-за обращения к элементам списка в LoadImage, когда их уже нет. Пофиксил так (отмечено синим):
procedure TForm1.LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage);
var
K: Integer; // Анонимная процедура захватывает локальную переменную, а не обращается к AItem, которой уже может не быть в момент _окончания_ скачивания фотки
AAsyncResult: IAsyncResult;
begin
if Not Assigned(AItem) or Not Assigned(AListItemImage) then
Exit;
if AItem.Data['ImageState'].AsInteger <> ListViewItemImageEmpty then
Exit;
if AItem.Data['ImageURL'].AsString.IsEmpty then
Exit;
AItem.Data['ImageState'] := ListViewItemImageLoading;
K := AItem.Index; // Запоминаем индекс в локальную K, которая уйдёт в анонимку (время жизни K > времени жизни анонимки)
FAsyncResultList.Items[K] := FHTTPClient.BeginGet(
procedure (const ASyncResult: IAsyncResult)
var
AHTTPResponse: IHTTPResponse;
begin
if ASyncResult.IsCancelled then
Exit;
try
AHTTPResponse := THTTPClient.EndAsyncHTTP(ASyncResult);
if Not Assigned(AHTTPResponse) then
Exit;
if AHTTPResponse.StatusCode <> 200 then
Exit;
except
Exit;
end;TThread.Synchronize(Nil,
procedure
begin
if FListViewUpdating then // Выходим, так как внутри анонимной процедуры AItem или AListItemImage - не сброшены в nil, хотя их уже может и не быть
Exit; // Кстати, наверное, правильнее было бы вместо проверки FListViewUpdating использовать и/или условие: if ASyncResult.IsCancelled then Exit; ?
if Not Assigned(AItem) or Not Assigned(AListItemImage) then
Exit;
AListItemImage.BeginUpdate;
AListItemImage.Bitmap := TBitmap.Create;
AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream);
AListItemImage.EndUpdate;
AItem.Data['ImageState'] := ListViewItemImageLoaded;
FAsyncResultList.Items[K] := nil; // Наверное, это присвоение лучше вытащить наверх, перед проверкой всех условий? Ведь фотка скачалась успешно... Или не надо?
end
);
end,
AItem.Data['ImageURL'].AsString
);
end;====
Прогонял много раз, клацал по кнопке один и много раз и закрывал сразу приложение, ошибки пока больше не появлялись... Тьфу-тьфу-тьфу....
Отображение картинок в ListView
в TListView
Опубликовано · Изменено пользователем Евгений (KeeperWorld)
Да там всё Евгений Корепов сделал уже. Я только три копейки своих добавил...
Вот конечный код: