Baseflow / flutter_cached_network_image

Download, cache and show images in a flutter app
https://baseflow.com
2.41k stars 641 forks source link

Is there any way to pass a HttpClient instance? #649

Open rocboronat opened 2 years ago

rocboronat commented 2 years ago

Hello! 👋

As we try to use the same HttpClient to keep keepalive connections, is there any way to pass the CachedNetworkImageProvider a HttpClient to use?

Thanks 🙇‍♂️

PascalOtto commented 2 years ago

I really would like to know this, too. I think it is kind of an essential feature.

But it looks like a http-session is created for every single image (#555). That would be absolutely horrible 😑

renefloor commented 2 years ago

I'll take you around the CacheManager files.

When creating the CacheManager you supply a config file in the constructor. The default IO config file uses a standard HttpFileService if nothing else is provided. That http fileservice only creates 1 http client if no other client is provided.

So what you can do is create a different CacheManager object that you supply a config with an HttpFileService that has your httpclient. At the moment you cannot configure this per request.

So like this:

import 'package:http/http.dart' as http;
final cacheManager = CacheManager(
  Config(
    'testCache',
    fileService: HttpFileService(
      httpClient: http.Client(),
    ),
  ),
);
Jefry-nolastname commented 2 years ago

I'll take you around the CacheManager files.

When creating the CacheManager you supply a config file in the constructor. The default IO config file uses a standard HttpFileService if nothing else is provided. That http fileservice only creates 1 http client if no other client is provided.

So what you can do is create a different CacheManager object that you supply a config with an HttpFileService that has your httpclient. At the moment you cannot configure this per request.

So like this:

import 'package:http/http.dart' as http;
final cacheManager = CacheManager(
  Config(
    'testCache',
    fileService: HttpFileService(
      httpClient: http.Client(),
    ),
  ),
);

i'm sorry, i'm still not quite sure what you meant and where to apply said code. is it in main.dart? or do i have to manually fork the plugin? i'm using self signed certificate on my client so i need to pass the httpClient to the plugin so it can retrieve it successfully, instead of handshake certificate expired.

``the code ByteData bytes = await rootBundle.load('cert/....pem'); print("bytes ${bytes.lengthInBytes}"); SecurityContext clientContext = new SecurityContext() ..setTrustedCertificatesBytes(bytes.buffer.asUint8List());

httpClient = IOClient(HttpClient(context: clientContext)); response = await httpClient.post(uri,header,.......).. ``

Jefry-nolastname commented 2 years ago

and i'm stoopid bakka......

import 'package:flutter_cache_manager/flutter_cache_manager.dart'; CachedNetworkImage( cacheManager: CacheManager( Config( 'testCache', fileService: HttpFileService( httpClient: http, ), ), ), httpHeaders: { "Authorization":"bearer ${globVar.tokenRest.token}" }, )

rocboronat commented 2 years ago

Just a piece of copypasteable code:

import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:http/http.dart' as http;

final cacheManager = CacheManager(
  Config(
    'cacheKey',
    fileService: HttpFileService(
      httpClient: http.Client(),
    ),
  ),
);

If using it as cacheManager makes CachedNetworkImage and CachedNetworkImageProvider always use the same http client, that's it 😄

If you think it's a good way to deal with it, thanks a lot and feel free to close the issue 🙇

escamoteur commented 1 month ago

how does cached_network_image know which CacheManager it should use?

escamoteur commented 1 month ago

OK, seems like you have to pass the CacheManager to every CachedNetworkImage as paramter

lukehutch commented 2 weeks ago

If you want to use the cronet or cupertino clients, they cannot be used with runWithClient, and to use the suggested solution of using Provider<Client>, you would have to pass in a new CacheManager instance every time you use CacheWithNetworkImage (because you have to get the Client implementation from the BuildContext, using context.read<Client>).

My question is: is it inefficient to reinstantiate a new CacheManager and a new HttpFileService for every new CachedNetworkImage request? Or as long as the 'cacheKey' matches every time, is the cache going to be reused between requests?

lukehutch commented 2 weeks ago

@renefloor said:

So what you can do is create a different CacheManager object that you supply a config with an HttpFileService that has your httpclient. At the moment you cannot configure this per request.

but CachedNetworkImage does take a cacheManager parameter, so why can this not be configured per request?

escamoteur commented 2 weeks ago

If you don't want to deal with context, you can use get_it instead of Provider there. We use cache_network_image and pass the cache_mamager via get_it without problems.

BTW runWithClient works most of the time for us. We only saw that JNI exception once so far. Am 27. Juni 2024, 23:16 +0100 schrieb Luke Hutchison @.***>:

If you want to use the cronet or cupertino clients, they cannot be used with runWithClient, and to use the suggested solution of using Provider, you would have to pass in a new CacheManager instance every time you use CacheWithNetworkImage (because you have to get the Client implementation from the BuildContext, using context.read). My question is: is it inefficient to reinstantiate a new CacheManager and a new HttpFileService for every new CachedNetworkImage request? Or as long as the 'cacheKey' matches every time, is the cache going to be reused between requests? — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

escamoteur commented 2 weeks ago

Why would you want to do this on every request? Am 27. Juni 2024, 23:20 +0100 schrieb Luke Hutchison @.***>:

@renefloor said:

So what you can do is create a different CacheManager object that you supply a config with an HttpFileService that has your httpclient. At the moment you cannot configure this per request. but CachedNetworkImage does take a cacheManager parameter, so why can this not be configured per request? — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

lukehutch commented 2 weeks ago

Why would you want to do this on every request?

Because see these comments re context: https://github.com/dart-lang/http/issues/1241#issuecomment-2195765525

I don't think you can just rely on getting the Client instance once, and then pre-creating a single instance of cacheManager. But I don't fully understand the context issue.

lukehutch commented 2 weeks ago

@escamoteur can you please provide full example code of what you are doing?

Why do you need to use get_it at all, if you are just creating a global variable, cacheManager? This will be lazily initialized the first time it is referenced.

escamoteur commented 2 weeks ago

Sure, that is exactly what I do, I register one Client and one Cache Manager in get_it and then I use them everywhere. I will write a blog post about this in the next days Am 28. Juni 2024, 00:05 +0100 schrieb Luke Hutchison @.***>:

Why would you want to do this on every request? Because see these comments re context: dart-lang/http#1241 (comment) I don't think you can just rely on getting the Client instance once, and then pre-creating a single instance of cacheManager. But I don't fully understand the context issue. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

HosseinYousefi commented 2 weeks ago

We only saw that JNI exception once so far.

Can I see how you're setting up the runWithClient?

escamoteur commented 2 weeks ago

Not at my computer at the moment. Do you get that exception all the time? Am 28. Juni 2024, 00:13 +0100 schrieb Hossein Yousefi @.***>:

We only saw that JNI exception once so far. Can I see how you're setting up the runWithClient? — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

HosseinYousefi commented 2 weeks ago

I don't think we should be getting the exception to begin with! I'm maintaining package:jni which is a dependency of package:cronet_http and I want to know why this happens.

escamoteur commented 2 weeks ago

I just read a comment of one of our users who said since we added cronnet the app doesn't start anymore. He mentioned that he uses an Android without play services. My hunch is that cronnet might not be available on such systems. Will try tomorrow Am 28. Juni 2024, 00:18 +0100 schrieb Hossein Yousefi @.***>:

I don't think we should be getting the exception to begin with! I'm maintaining package:jni which is a dependency of package:cronet_http and I want to know why this happens. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

HosseinYousefi commented 2 weeks ago

@escamoteur No worries. Btw are you coming tomorrow to the GDE conf in Berlin? I have a talk there, we can meet and talk about it there!

escamoteur commented 2 weeks ago

Unfortunately not, I m on Tenerife over the summer working from here. Am 28. Juni 2024, 00:21 +0100 schrieb Hossein Yousefi @.***>:

@escamoteur No worries. Btw are you coming tomorrow to the GDE conf in Berlin? I have a talk there, we can meet and talk about it there! — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

lukehutch commented 2 weeks ago

I just read a comment of one of our users who said since we added cronnet the app doesn't start anymore.

@escamoteur was that when you were using runWithClient, or did you get the report after you switched to using get_it?

escamoteur commented 2 weeks ago

ok, I could just reproduce this, it has nothing to do with runWithClient if you use an Android without play services cronnet seems not be available

lukehutch commented 2 weeks ago

ok, I could just reproduce this, it has nothing to do with runWithClient if you use an Android without play services cronnet seems not be available

What is the exact failure, and is there a way to detect that this is going to fail? I want to make my app robust to this situation...

Also, why on earth would cronet require Google Play Services? It is just the Chromium HTTP client library... @HosseinYousefi

escamoteur commented 2 weeks ago

check https://github.com/dart-lang/http/issues/1241#issuecomment-2196475295

I agreee, I too I'm surprised that it seems that cronnet is part of the Google play service part of Android