System designTroubleshooting

Android Studio import module fails with UnsupportedOperationException

Android Studio's Import Module throws UnsupportedOperationException, an IDE bug. Skip the dialog: add the SDK as a Gradle dependency with its native .so files.

LumaSpec Vision9 min read Share on LinkedIn

On this page 10 sections
  1. 01Read the trace: the dialog crashed, not the SDK
  2. 02Why Import Module fails on a vision SDK
  3. 03Route 1: make the Java folder a Gradle library module
  4. 04Route 2: declare a prebuilt AAR or JAR
  5. 05Package the native .so libraries
  6. 06Work the causes in order
  7. 07The smoke test that proves both halves
  8. 08Keep it from coming back
  9. 09Next step: the capture side
  10. 10Questions

You point File → New → Import Module at the vision SDK's Java folder, click through the defaults, and Android Studio answers with java.lang.UnsupportedOperationException and a wall of com.android.tools.idea frames. The SDK is fine and your project is fine: the crash comes from the IDE's legacy import path hitting a module Gradle never modelled. This page reads the trace to that conclusion, then wires the SDK in the way current tooling expects — an included Gradle project or a declared AAR/JAR dependency — packages the native .so libraries, and ends with a smoke test that proves both halves load before you write any image code.

Read the trace: the dialog crashed, not the SDK

Every frame belongs to the IDE — com.android.tools.idea and com.intellij — not to your code and not to the SDK. The top frames tell the whole story:

java.lang.UnsupportedOperationException
    at com.android.tools.idea.gradle.structure.model.empty.PsEmptyModule.getConfigurations(PsEmptyModule.kt:53)
    at com.android.tools.idea.gradle.structure.model.PsModule.getConfigurations$default(PsModule.kt:88)
    at com.android.tools.idea.gradle.structure.dependencies.AddModuleDependencyDialog.createDependencyScopesPanel(AddModuleDependencyDialog.java:96)

PsEmptyModule is the placeholder model Android Studio keeps for a module it knows by name but holds no Gradle data for. AddModuleDependencyDialog asked that placeholder for its dependency configurations, and the placeholder throws UnsupportedOperationException by design. So the wizard registered a module, Gradle produced no model for it, and the Project Structure dialog crashed on the empty entry. Nothing about the SDK or your machine is broken; reinstalling either changes nothing.

Quick test: open the folder the wizard created in the Project view. A real Gradle module carries its own build.gradle. If the folder is bare sources, or the Gradle Sync output shows red, the IDE is holding exactly the empty module the trace describes. The crash also reproduces on current IDE lines — the build string in one matching report reads AI-221.6008.13.2211.9477386 — so updating Android Studio will not fix it.

Why Import Module fails on a vision SDK

The import wizard is a relic of the pre-Gradle tooling. Tutorials written for the 2.4.11.0 OpenCV Android SDK tell you to import OpenCV-android-sdk/sdk/java because that folder was laid out for the old importer, and it carries no Gradle build of its own. The wizard copies the files and registers a module name anyway; on a Gradle-only IDE that leaves a module with nothing behind it, and Project Structure trips over it.

Current projects need none of this. Dependencies live in two text files — settings.gradle and the app module's build.gradle — and Project Structure is a viewer for what Gradle has already synced. So the decision is simple: stop using the import wizard for SDKs entirely, and declare the dependency with one of the two routes below, chosen by what your SDK ships.

Route 1: make the Java folder a Gradle library module

This is the route for an SDK folder with no build.gradle — the 2.4.11.0 archive among them.

  1. Register the folder in settings.gradle, wherever the archive sits. Here it lives inside the project's libs folder:
// settings.gradle
include ':opencv'
project(':opencv').projectDir = new File(settingsDir, 'libs/OpenCV-android-sdk/sdk/java')

opencv and the libs path are my illustrative names; include and project(...) are Gradle's own syntax, and a module may live anywhere projectDir points.

  1. Give the folder a minimal library build script — old-layout folders have none, which is exactly why the wizard left an empty module:
// libs/OpenCV-android-sdk/sdk/java/build.gradle
apply plugin: 'com.android.library'

Current 4.x archives ship the sdk folder as a ready Gradle module, so for those the file already exists and this step disappears.

  1. Depend on it from the app module:
// app/build.gradle
dependencies {
    implementation project(':opencv')
}
  1. Sync. From here the Sync output lists real, fixable Gradle problems — a missing namespace line, an old plugin declaration — instead of a UI crash. The Gradle migration walkthrough covers those edits step by step.

Route 2: declare a prebuilt AAR or JAR

When the SDK ships a prebuilt AAR or JAR, skip source inclusion: drop the archive into the app module's libs folder and declare it.

// app/build.gradle
dependencies {
    implementation files('libs/vision-sdk.aar')
}

vision-sdk.aar is an illustrative file name; use the shipped archive. The 2.4.11.0 package contains no AAR, so Route 1 is the only option there.

Package the native .so libraries

The Java half of the SDK is wrappers only: every call drops into native code through JNI. A build with the jar but without the packaged libraries compiles clean and dies on the first call with java.lang.UnsatisfiedLinkError. Copy each ABI folder from the SDK's native directory — for the OpenCV archive that is sdk/native/libs — into src/main/jniLibs, so the APK carries them:

app/src/main/jniLibs/
├── arm64-v8a/
│   └── libopencv_java.so
├── armeabi-v7a/
│   └── libopencv_java.so
└── x86_64/
    └── libopencv_java.so

The tree shows the 2.4-line layout: one native library per ABI. Decide the folders to ship:

ABI folder Ship it for
arm64-v8a every current phone
armeabi-v7a 32-bit phones you still support
x86_64 emulator runs on a PC

Then match the folders to what your release actually contains. A 2.4-era archive has no arm64-v8a folder, and a 64-bit-only phone cannot load its 32-bit libraries — no packaging trick fixes that, and the durable fix is a current SDK release.

Load the libraries once, before the first native call. The OpenCV package ships a loader for exactly this: OpenCVLoader.initDebug() loads the libraries you packaged and returns true when they are up. On other SDKs, call the vendor's init or load method once at startup.

Work the causes in order

If a missing dependency or a crash still shows up after the routes above, test in this order:

Likely cause Test Fix
The wizard-created module has no build.gradle Open the module folder in the Project view Delete the folder, remove its include line from settings.gradle, apply Route 1 or 2
Gradle sync fails on the SDK's old script Red errors in the Sync output Fix the namespace and plugin lines; follow the Gradle migration walkthrough
The module is included twice Two include lines for one folder in settings.gradle Keep one line and sync
The wizard was pointed at the SDK root or the native folder The created module lists no sources Re-include the Java folder, OpenCV-android-sdk/sdk/java

None of these is fixed by reinstalling the IDE, reinstalling the SDK, or rebooting. Until the dependency is declared in Gradle files, Project Structure keeps meeting an empty module and keeps throwing at PsEmptyModule.kt:53.

The smoke test that proves both halves

Run this once from app startup or a throwaway activity. loaded and probe are illustrative variable names; OpenCVLoader, Mat and CvType are the SDK's documented classes, and Log is the platform logger.

boolean loaded = OpenCVLoader.initDebug();      // true when the packaged .so files load
Log.i("VisionSmoke", "native libraries loaded: " + loaded);
Mat probe = new Mat(4, 4, CvType.CV_8UC1);      // 4 x 4 pixels, 8-bit unsigned, one channel
Log.i("VisionSmoke", "probe Mat is " + probe.rows() + " x " + probe.cols());
probe.release();

Expected logcat output:

VisionSmoke: native libraries loaded: true
VisionSmoke: probe Mat is 4 x 4

If the first line prints false, or throws UnsatisfiedLinkError, go back to the jniLibs section: the libraries are missing or the ABI set does not match the device. If both lines print, the import crash is solved for good and your next errors are ordinary API mistakes — the most common being the Mat(Mat, Rect) trim call that resolves to the wrong Rect import, covered in the Mat(Mat, Rect) import fix. If you build the SDK from source instead of consuming the archive, the CMake Java/Ant build errors guide is the companion read.

Keep it from coming back

  • Declare every dependency in Gradle files, and treat File → New → Import Module as unavailable.
  • Pin one SDK version across every module of the app; the one-library-version pattern shows how mixed versions break builds in confusing ways.
  • Keep the Java and native halves from the same release. The JNI entry points the jar calls are named after that release's classes, so a mismatched pair fails at load time.
  • When a later upgrade breaks the build, work from the SDK build errors after a library upgrade checklist instead of guessing at dependency changes.

Next step: the capture side

The app module now owns the SDK as an ordinary dependency, and Project Structure goes back to being a viewer. If this Android front end will drive a vision cell, pick the capture hardware as deliberately as you picked the SDK: browse the GigE area scan cameras or the smart cameras and vision sensors, filter by interface and resolution in the camera finder, or start the system configurator at the camera step. More systems-level setup and troubleshooting guides sit on the systems topic page, and when a build still fights you, the contact page is the way to reach us.

Questions

Why does Import Module crash with UnsupportedOperationException instead of an error message?

The wizard copied the SDK's files and registered a module name, but the folder has no buildable Gradle build, so the IDE holds a module with no Gradle model. Project Structure reads every module through that model, gets the empty placeholder, and its getConfigurations() call throws UnsupportedOperationException by design. It is a dialog crash, not a build failure: nothing you install or reinstall changes it until the dependency is declared in Gradle files.

Can I skip build.gradle and just drop the SDK's jar into the libs folder?

Dropping a jar into libs does nothing on its own; a file in a folder is not a dependency. The jar must still be declared, typically with implementation files('libs/name.jar'), or the APK will not include its classes and every import stays red. The native .so libraries are a separate step: copy each ABI folder into src/main/jniLibs so they are packaged, then call the loader once. Both halves, one declared dependency, one load call.

Which ABI folders should I ship, and what if the SDK has no arm64-v8a?

Ship arm64-v8a for every current phone, add armeabi-v7a only if you still target 32-bit devices, and add x86_64 for emulator runs. Check the folders the SDK actually ships: a release from the 32-bit era has no arm64-v8a directory, and a 64-bit-only phone cannot load those libraries at all — the app throws UnsatisfiedLinkError no matter how they are packaged. The durable fix there is moving to a current SDK release.

Does the same fix apply to a vendor camera SDK that is not OpenCV?

Yes. Any SDK that ships a Java library plus native .so files follows the same three moves: include its Gradle project or declare its AAR or jar as an implementation dependency, package each ABI folder under src/main/jniLibs, and call the vendor's init or load method once before the first native call. The wizard crash also behaves the same, because the empty-module exception comes from the IDE's import path, not from the library you are adding.

How do I clean up the half-imported module the wizard left behind?

Delete the module folder the wizard created, open settings.gradle and remove the include line it added, then sync. If Project Structure still lists the phantom module after a clean sync, close and reopen the project; a stale entry is IDE cache, and the Invalidate Caches action on the File menu resets it. Leaving the empty module in place invites the same PsEmptyModule crash the next time you open Project Structure.

Should the app use initAsync or initDebug to load OpenCV?

In the 2.4-line package, initAsync() delegates library loading to the separate OpenCV Manager app; initDebug() loads the libraries packaged inside your own APK. For an inspection app you deploy on a fixed device, initDebug() is the predictable choice: no second application to install, no runtime download, and the smoke test proves exactly the libraries you shipped. Call whichever loader you choose once at startup, before the first Mat, and log its result.