本文讲述的是在混栈开发模式下的flutter图片加载内存优化,如果你的项目是一个纯净的flutter工程,就是不属于以原生接入flutter的方式,那么这篇文章对你也有一定的指导意义。
如果你的项目是纯净的flutter,那么优化的方向可以考虑有一下几种优化方式:
如果,你使用的是混栈开发模式,就是所谓的在原生的基础上接入flutter,那么在成功接入flutter之后,你肯定会碰到这样一个困扰,就是flutter这边的图片加载如何利用原生那边已经缓存好的图片数据。因为如果不利用的话,比如同样一张图片,在原生层加载了一次,然后,在flutter这边的业务,假如也需要加载同样一张图,而且是相同尺寸,那将会占用两份内存,这个开销是很不划算的,那么如何解决,请继续本文阅读。首先先看一个效果,图的上半部分是利用原生ImageView加载图片,可以看到内存快找中找不到Image这个class,flutter整体占用内存也比原生要低一些。
所以,做到这一步,下面利用原生已经缓存好的图片就不是什么难事了,众所周知,原生图片缓存框架不要太多太好用,Android中有比较著名的Glide,iOS中的有SDWebImage,有了我们上面的成果,我们就可以复用原生已有的图片内存了。
首先,我们了解到flutter为我们提供了一个PlatformView,在Android端叫做AndroidView,在iOS端叫做UIKitView。这个PlatformView就是flutter官方为了解决flutter层去使用原生空间而提供的一个控件。所以,我们只需要做一个插件去封装原生的ImageView即可。
当然,我们需要注意的是,我要实现我们的目的,flutter层必须告知原生层图片加载所需要的信息:
通过PlatformView Android端的文档,我们了解到:
The Android view object is created using aPlatformViewFactory. Plugins can register platform view factories withPlatformViewRegistry#registerViewFactory.
插件可以使用下面的方式注册:
public static void registerWith(Registrar registrar) {
registrar.platformViewRegistry().registerViewFactory("imageView", ImageViewFactory(registrar.messenger()));
}
因此,我们就想,可以通过封装插件的方式,来提供出这样一个PlatformView,这样,flutter层就可以做到一套代码来使用双平台原生加载了。
所以,且看Android这边的实现方式。首先,我们需要创建一个插件工程,创建的方法
flutter create --org com.example --template=plugin -i objc -a java flutter_image_view
关于插件详细的教程可以参考官方文章介绍;
public class FlutterImageViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
public FlutterImageViewFactory(BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
}
@Override
public PlatformView create(Context context, int id, Object o) {
return new FlutterImageView(context, messenger, id);
}
}
create方法的返回就是我们原生提供给 Flutter的PlatFormView的实现,看看具体实现代码
public class FlutterImageView implements PlatformView, MethodChannel.MethodCallHandler {
private final ImageView imageView;
private final MethodChannel methodChannel;
private Context context;
FlutterImageView(Context context, BinaryMessenger messenger, int id) {
imageView = new ImageView(context);
this.context = context;
methodChannel = new MethodChannel(messenger, "com.tencent.igame/flutter_image_view_" + id);
methodChannel.setMethodCallHandler(this);
}
@Override
public View getView() {
return imageView;
}
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
switch (methodCall.method) {
case "setUrl":
setUrl(methodCall, result);
break;
case "setSize":
setSize(methodCall, result);
default:
result.notImplemented();
}
}
private void setUrl(MethodCall methodCall, MethodChannel.Result result) {
String url = (String) methodCall.arguments;
//这里使用glide加载图片
Glide.with(context).load(url).centerCrop().into(imageView);
result.success(null);
}
@SuppressWarnings("ConstantConditions")
private void setSize(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.argument("width") != null && methodCall.argument("height") != null) {
int width = methodCall.argument("width");
int height = methodCall.argument("height");
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(width, height);
imageView.setLayoutParams(layoutParams);
}
result.success(null);
}
@Override
public void dispose() {
}
}
通过messenger,我们可以将flutter发送的消息传递到原生这边,可以看到setUrl这里,我们使用了Glide来加载图片了。那么,我们不禁要问,这个messenger是怎么从flutter那边传递到原生这边的,实际上,我们创建插件工程的时候,在.android目录,和.ios目录早就留好了接口了,我们只需要通过registrar.platformViewRegistry().registerViewFactory即可
public class TipFlutterImageViewPlugin implements MethodCallHandler {
/**
* Plugin registration.
*/
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "tip_flutter_image_view");
channel.setMethodCallHandler(new TipFlutterImageViewPlugin());
registrar
.platformViewRegistry()
.registerViewFactory(
"com.tencent.igame/flutter_image_view", new FlutterImageViewFactory(registrar.messenger()));
}
@Override
public void onMethodCall(MethodCall call, Result result) {
}
}
然后整个过程就OK了。
可以看到,正如PlatFormView文档所的那样,使用原生view嵌入到flutter代价是有点昂贵到,从原生切回flutter图片展示是秒显示,而从flutter切回原生有延时,但是我们获得的收益是利用了原生图片加载框架中缓存的图片(当然是原生那边已经加载过同样一张图的情况下),以时间换空间,该插件使用在较少图片加载的页面,如果页面中图片较多,可以考虑使用外接纹理Texture方案。
如果需要使用本插件,可以看到代码,传送门
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。