Skip to content

Replace AMapProvider's reflection with direct SDK calls #51

Description

@parawanderer

AMapProvider talks to the AMap SDK entirely through reflection — every class looked up by string, every method by name:

Class<?> markerOptionsClass = Class.forName("com.amap.api.maps.model.MarkerOptions");
Object markerOptions = markerOptionsClass.newInstance();

Class<?> latLngClass = Class.forName("com.amap.api.maps.model.LatLng");
Object latLng = latLngClass.getConstructor(double.class, double.class)
        .newInstance(gcj02[0], gcj02[1]);

java.lang.reflect.Method positionMethod = markerOptionsClass.getMethod("position", latLngClass);
positionMethod.invoke(markerOptions, latLng);

The map object itself is held as private Object aMap;, and markers and polylines as Map<String, Object>.

Nothing requires this

The usual reasons for reflection do not apply. The SDK is a plain compile-classpath dependency:

// app/build.gradle.kts:287
implementation(libs.amap.map3d)   // com.amap.api:3dmap:9.8.3

It is not compileOnly, there is no AMap-free product flavour, and no build path excludes it. import com.amap.api.maps.AMap would compile today. Most likely the class was written before the dependency was wired up and never converted back — no criticism of the original work, which is otherwise a clean implementation of IMapProvider.

Why it is worth changing

It will break under R8, silently. isMinifyEnabled = false for release right now, and proguard-rules.pro has no AMap keep rules. The moment minification is turned on, R8 renames the classes and methods this file looks up by name, and every lookup fails at runtime while the build stays green. Direct references would be traced and kept automatically. So the reflection is not neutral here — it is actively less safe than the straightforward version, in the one configuration the project is likely to want eventually.

Failures are runtime-only and swallowed. Each block ends in catch (Exception e) { Log.e(...); }, so an SDK upgrade that renames or re-signatures a method produces a log line and a map with no markers, rather than a compile error. On a codebase where nobody involved can run AMap, a compile error is the only feedback channel that actually reaches us.

It cannot be reviewed. A typo in "com.amap.api.maps.model.MarkerOptions" is indistinguishable from a correct string to any reader or tool. There is no autocomplete, no go-to-definition, no refactoring support, and no type checking on any argument passed through invoke.

Some of it is already deprecated. Class.newInstance() has been deprecated since Java 9 in favour of getDeclaredConstructor().newInstance(), because it propagates checked exceptions the compiler cannot see.

Proposal

Replace the reflection with direct SDK calls, keeping IMapProvider exactly as it is, so MapsActivity and HistoryViewActivity are untouched:

  • private Object aMap → private AMap aMap
  • Map<String, Object> markers → Map<String, Marker> markers
  • Class.forName(...) / getMethod(...) / invoke(...) → ordinary calls
  • keep the coordinate conversion (CoordinateConverter.wgs84ToGcj02) and the privacy-compliance calls exactly as they are

Mechanical, and it should shrink the file substantially.

The catch

Neither the maintainer nor Claude can test this. Verifying AMap needs a mainland-Chinese developer account with real-name verification, plus an API key bound to the package name and signing fingerprint. So the change compiles and reviews, but the runtime behaviour is unverified by us.

That argues for doing it as its own small PR that touches nothing else, and for asking @SadGare to confirm the map still works afterwards — the same arrangement as the fork merge in #49. A compile error is worth more here than anywhere else in the codebase precisely because nobody on this side can exercise the code path.

Related: the marker draw-order support added in #49 had to go in reflectively for the same reason, with a try/catch so a rename degrades to an unordered marker rather than no marker at all. That workaround disappears with this change.


Issue co-authored by Claude Code.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    @appIssues regarding the OpenTagViewer Android appenhancementNew feature or requestgood first issueGood for newcomers

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions