【移动端】Flutter 获取Android AMap实例

背景

本文的背景,是因为我在开发高德地图时,需要自定义高德比例尺位置和样式;但结果查看了AMap Flutter插件和AMap SDK源码后,发现AMap无法添加自定义MyMethodCallHandler的实现类!

why?

源码

在Flutter中,高德地图的每个地图视图都是通过AMapPlatformView类生成且管理的,源码如下

java 复制代码
package com.amap.flutter.map;

import android.content.Context;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;

import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapOptions;
import com.amap.api.maps.TextureMapView;
import com.amap.flutter.map.core.MapController;
import com.amap.flutter.map.overlays.marker.MarkersController;
import com.amap.flutter.map.overlays.polygon.PolygonsController;
import com.amap.flutter.map.overlays.polyline.PolylinesController;
import com.amap.flutter.map.utils.LogUtil;

import java.util.HashMap;
import java.util.Map;

import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView;


/**
 * @author whm
 * @date 2020/10/27 5:49 PM
 * @mail hongming.whm@alibaba-inc.com
 * @since
 */
public class AMapPlatformView
        implements
        DefaultLifecycleObserver,
        ActivityPluginBinding.OnSaveInstanceStateListener,
        MethodChannel.MethodCallHandler,
        PlatformView {
    private static final String CLASS_NAME = "AMapPlatformView";
    private final MethodChannel methodChannel;

    private MapController mapController;
    private MarkersController markersController;
    private PolylinesController polylinesController;
    private PolygonsController polygonsController;

    private TextureMapView mapView;

    private boolean disposed = false;

    private final Map<String, MyMethodCallHandler> myMethodCallHandlerMap;

    AMapPlatformView(int id,
                     Context context,
                     BinaryMessenger binaryMessenger,
                     LifecycleProvider lifecycleProvider,
                     AMapOptions options) {

        methodChannel = new MethodChannel(binaryMessenger, "amap_flutter_map_" + id);
        methodChannel.setMethodCallHandler(this);
        myMethodCallHandlerMap = new HashMap<String, MyMethodCallHandler>(8);

        try {
            mapView = new TextureMapView(context, options);
            AMap amap = mapView.getMap();
            mapController = new MapController(methodChannel, mapView);
            markersController = new MarkersController(methodChannel, amap);
            polylinesController = new PolylinesController(methodChannel, amap);
            polygonsController = new PolygonsController(methodChannel, amap);
            initMyMethodCallHandlerMap();
            lifecycleProvider.getLifecycle().addObserver(this);
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "<init>", e);
        }
    }

    private void initMyMethodCallHandlerMap() {
        String[] methodIdArray = mapController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, mapController);
            }
        }

        methodIdArray = markersController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, markersController);
            }
        }

        methodIdArray = polylinesController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, polylinesController);
            }
        }

        methodIdArray = polygonsController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, polygonsController);
            }
        }
    }


    public MapController getMapController() {
        return mapController;
    }

    public MarkersController getMarkersController() {
        return markersController;
    }

    public PolylinesController getPolylinesController() {
        return polylinesController;
    }

    public PolygonsController getPolygonsController() {
        return polygonsController;
    }


    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        LogUtil.i(CLASS_NAME, "onMethodCall==>" + call.method + ", arguments==> " + call.arguments);
        String methodId = call.method;
        if (myMethodCallHandlerMap.containsKey(methodId)) {
            myMethodCallHandlerMap.get(methodId).doMethodCall(call, result);
        } else {
            LogUtil.w(CLASS_NAME, "onMethodCall, the methodId: " + call.method + ", not implemented");
            result.notImplemented();
        }
    }


    @Override
    public void onCreate(@NonNull LifecycleOwner owner) {
        LogUtil.i(CLASS_NAME, "onCreate==>");
        try {
            if (disposed) {
                return;
            }
            if (null != mapView) {
                mapView.onCreate(null);
            }
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onCreate", e);
        }
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        LogUtil.i(CLASS_NAME, "onStart==>");
    }

    @Override
    public void onResume(@NonNull LifecycleOwner owner) {
        LogUtil.i(CLASS_NAME, "onResume==>");
        try {
            if (disposed) {
                return;
            }
            if (null != mapView) {
                mapView.onResume();
            }
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onResume", e);
        }
    }

    @Override
    public void onPause(@NonNull LifecycleOwner owner) {
        LogUtil.i(CLASS_NAME, "onPause==>");
        try {
            if (disposed) {
                return;
            }
            mapView.onPause();
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onPause", e);
        }
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        LogUtil.i(CLASS_NAME, "onStop==>");
    }

    @Override
    public void onDestroy(@NonNull LifecycleOwner owner) {
        LogUtil.i(CLASS_NAME, "onDestroy==>");
        try {
            if (disposed) {
                return;
            }
            destroyMapViewIfNecessary();
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onDestroy", e);
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle bundle) {
        LogUtil.i(CLASS_NAME, "onDestroy==>");
        try {
            if (disposed) {
                return;
            }
            mapView.onSaveInstanceState(bundle);
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onSaveInstanceState", e);
        }
    }

    @Override
    public void onRestoreInstanceState(@Nullable Bundle bundle) {
        LogUtil.i(CLASS_NAME, "onDestroy==>");
        try {
            if (disposed) {
                return;
            }
            mapView.onCreate(bundle);
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onRestoreInstanceState", e);
        }
    }


    @Override
    public View getView() {
        LogUtil.i(CLASS_NAME, "getView==>");
        return mapView;
    }

    @Override
    public void dispose() {
        LogUtil.i(CLASS_NAME, "dispose==>");
        try {
            if (disposed) {
                return;
            }
            methodChannel.setMethodCallHandler(null);
            destroyMapViewIfNecessary();
            disposed = true;
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "dispose", e);
        }
    }

    private void destroyMapViewIfNecessary() {
        if (mapView == null) {
            return;
        }
        mapView.onDestroy();
    }


}

分析

通过上面的源码可以发现如下几点:

  1. 在它onMethodCall回调方法中,判断了方法名是否在myMethodCallHandlerMap中存在
java 复制代码
@Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        LogUtil.i(CLASS_NAME, "onMethodCall==>" + call.method + ", arguments==> " + call.arguments);
        String methodId = call.method;
        if (myMethodCallHandlerMap.containsKey(methodId)) {
            myMethodCallHandlerMap.get(methodId).doMethodCall(call, result);
        } else {
            LogUtil.w(CLASS_NAME, "onMethodCall, the methodId: " + call.method + ", not implemented");
            result.notImplemented();
        }
    }
  1. myMethodCallHandlerMap 是一个final属性,这也就意味着,它在构造函数中已经实例化了,且不能更改值,只能修改内容,它通过initMyMethodCallHandlerMap注册所有的Hnadler
java 复制代码
private final Map<String, MyMethodCallHandler> myMethodCallHandlerMap;

AMapPlatformView(int id,
                 Context context,
                 BinaryMessenger binaryMessenger,
                 LifecycleProvider lifecycleProvider,
                 AMapOptions options) {

    methodChannel = new MethodChannel(binaryMessenger, "amap_flutter_map_" + id);
    methodChannel.setMethodCallHandler(this);
    myMethodCallHandlerMap = new HashMap<String, MyMethodCallHandler>(8);

    try {
        mapView = new TextureMapView(context, options);
        AMap amap = mapView.getMap();
        mapController = new MapController(methodChannel, mapView);
        markersController = new MarkersController(methodChannel, amap);
        polylinesController = new PolylinesController(methodChannel, amap);
        polygonsController = new PolygonsController(methodChannel, amap);
        initMyMethodCallHandlerMap();
        lifecycleProvider.getLifecycle().addObserver(this);
    } catch (Throwable e) {
        LogUtil.e(CLASS_NAME, "<init>", e);
    }
}
  1. 通过initMyMethodCallHandlerMap方法可以看出,它只注册了mapControllermarkersControllerpolylinesControllerpolygonsController四类Handler,且没有提供可以自定义注册的方法,所以我也无能为力了
java 复制代码
private void initMyMethodCallHandlerMap() {
        String[] methodIdArray = mapController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, mapController);
            }
        }

        methodIdArray = markersController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, markersController);
            }
        }

        methodIdArray = polylinesController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, polylinesController);
            }
        }

        methodIdArray = polygonsController.getRegisterMethodIdArray();
        if (null != methodIdArray && methodIdArray.length > 0) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, polygonsController);
            }
        }
    }

解决思路

1. 通过修改initMyMethodCallHandlerMap属性值(不推荐

通过上面分析可以想到,通过修改initMyMethodCallHandlerMap属性值,自定义handler,但前提时需要拿到对应的AMapPlatformView实例,通常的方法可以通过ServiceLoader来获取,但在Android中ServiceLoader是不用生效的,Android环境中必须通过PathClassLoaderDexClassLoader来加载类,具体加载方法,可以参考其他大神的博客,我这里不做过多追述,因为我不推荐

2. 通过PlatformViewsController来获取视图(推荐

推荐这个方法是因为它可以i通过mapId直接获取,且不用过多介入到内存和进程中的交互中

  1. 创建一个自定义MethodChannel
java 复制代码
var methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "method_channel_id")
        methodChannel.setMethodCallHandler { call, result ->
            run {
                //....
            }
        }
  1. AMapPlatformView继承PlatformView,所以可以通过FlutterEngine.getPlatformViewsController().getPlatformViewById(viewId)方式获取,获取到的是TextureMapView视图,TextureMapView.getMap()可以获取AMap类,从而实现对当前地图视图进行任何操作,
kotlin 复制代码
class MainActivity : FlutterActivity() {

    var handerControllerMap = HashMap<String, IMyMethodCallHander>();


    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        IFlutterFactory.engine=flutterEngine;
    }
}
java 复制代码
/**
     * 当前缩放级别下,地图上1像素点对应的长度,单位米。
     * @param call
     * @param result
     */
    public void getScalePerPixel(MethodCall call, MethodChannel.Result result) {
        Object mapId = call.argument("mapId");
        if (null == call || null == mapId) {
            return;
        }

        try {
            View view = IFlutterFactory.engine.getPlatformViewsController().getPlatformViewById(Integer.parseInt(mapId.toString()));

            if (view != null) {
                result.success(((TextureMapView) view).getMap().getScalePerPixel());
            } else {
                result.error("ScaleControllerExecption", "获取比例尺数据失败!", "");
            }

        } catch (Exception e) {
            result.error("ScaleControllerExecption", e.getMessage(), e.getLocalizedMessage());
        }


    }
  1. viewId 其实就是mapId,可以通过AMapController.mapId获取,而AMapController可以通过AMapWidgetonMapCreated的回调方法获取
dart 复制代码
void onMapCreated(AMapController controller) {

	//连接自定义的MethodChannel
 	MethodChannel _channel=MethodChannel("method_channel_id");
	//将mapId传给android端
    double? scale=await _channel.invokeMethod<double>("scale#get",<String,dynamic>{
    "mapId":_mapController.mapId
    });

    print('-----scale---$scale');
}
相关推荐
带带老表学爬虫6 分钟前
常用 Android 反编译工具apktooldex2jarenjarifyjd-guijadx
android
Geeker556 小时前
如何在忘记密码的情况下解锁Android手机?
android·网络·macos·华为·智能手机·电脑·手机
wxx21507 小时前
【android】【adb shell】写一个shell脚本,监听进程pid变化
android·adb
心死翼未伤9 小时前
【MySQL基础篇】多表查询
android·数据结构·数据库·mysql·算法
喂_balabala9 小时前
Android手机拍照或从本地相册选取图片设置头像-高版本适配
android·开发语言
_小马快跑_10 小时前
Android | StandardCharsets.UTF_8.toString() 遇到的兼容问题记录
android
wxx215011 小时前
【Android】【多屏】多屏异显异触调试技巧总结
android
人民的石头13 小时前
Android增量更新----java版
android
Geeker5515 小时前
如何在忘记密码的情况下删除华为ID激活锁
android·运维·服务器·网络·macos·华为·智能手机
XD74297163616 小时前
【Android】ADB 使用指南
android·adb