7

I'm trying to consume a SOAP service in my Android app. I'm trying to follow this guide. When I try to run my app I get an error:

Error: MethodHandle.invoke and MethodHandle.invokeExact are only supported starting with Android O (--min-api 26)

I'm on Android N (SDK 25 - I can't bump to 26 like the error message indicates) and it's a brand new project just created by Android Studio.

Here's my project build.gradle file which I haven't changed:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

And here's my app build.gradle file:

apply plugin: 'com.android.application'

configurations {
    jaxb
}

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
    }
}

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

repositories {
    mavenCentral()
}

// tag::wsdl[]
task genJaxb {
    ext.sourcesDir = "${buildDir}/generatedjava/jaxb"
    ext.classesDir = "${buildDir}/classes/jaxb"
    ext.schema = "https://lite.realtime.nationalrail.co.uk/OpenLDBWS/wsdl.aspx?ver=2017-10-01"

    outputs.dir classesDir

    doLast() {
        project.ant {
            taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
                    classpath: configurations.jaxb.asPath
            mkdir(dir: sourcesDir)
            mkdir(dir: classesDir)

            xjc(destdir: sourcesDir, schema: schema,
                    package: "hello.wsdl") {
                arg(value: "-wsdl")
                produces(dir: sourcesDir, includes: "**/*.java")
            }

            javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true,
                    debugLevel: "lines,vars,source",
                    classpath: configurations.jaxb.asPath) {
                src(path: sourcesDir)
                include(name: "**/*.java")
                include(name: "*.java")
            }

            copy(todir: classesDir) {
                fileset(dir: sourcesDir, erroronmissingdir: false) {
                    exclude(name: "**/*.java")
                }
            }
        }
    }
}
// end::wsdl[]

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.example.magicmirror"
        minSdkVersion 25
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    packagingOptions {
        exclude 'META-INF/spring.tooling'
        exclude 'META-INF/spring.handlers'
        exclude 'META-INF/spring-configuration-metadata.json'
        exclude 'META-INF/additional-spring-configuration-metadata.json'
        exclude 'META-INF/spring.factories'
        exclude 'META-INF/spring.schemas'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation "org.springframework.boot:spring-boot-starter"
    implementation "org.springframework.ws:spring-ws-core"
    implementation(files(genJaxb.classesDir).builtBy(genJaxb))

    jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7"
}

From the logs the exact JAR causing this is org.springframework/spring-core/5.1.8.RELEASE.jar. Adding the compileOptions block is mentioned in other answers as a fix but it hasn't fixed this issue. How can I fix this?

jbrown
  • 7,518
  • 16
  • 69
  • 117
  • To be clear I must use SDK 25 (Android N), not 26 like the error message says. – jbrown Jul 10 '19 at 13:55
  • you can Go with these ref. Link. It might be helpful to you. [Jar File Update](https://stackoverflow.com/questions/50633710/how-to-find-cause-of-error-when-dexing-methodhandle-invoke-and-methodhandle-inv) [JAVA VErsion Configuration](https://stackoverflow.com/questions/49891730/invoke-customs-are-only-supported-starting-with-android-0-min-api-26) – Re.Thak Jul 11 '19 at 04:29
  • I've seen those. That's why I added the compileOptions. And like I've said I can't use SDK 26. So they don't help unfortunately. – jbrown Jul 11 '19 at 10:02
  • So I see you're using Spring Boot and not Spring for Android which is a bit of complicated scenario. IMHO, Spring Boot is only for Java Applications and not Android Applications as the means of bootstrapping is primarily made for pure Java Applications. However you could use Spring for Android and include the Spring WebServices library (`spring-ws-core`) and hence perform SOAP requests as such. But anything that has the term 'boot' in it is pretty much not going to work on Android. – ahasbini Jul 11 '19 at 18:11
  • I'm only using Spring Boot for the SOAP client. It doesn't look like Spring for Android has one though (unless I'm missing it). My only requirement is to create and parse SOAP requests on Android with code generation from the WSDL, so if Spring for Android can do that please let me know. – jbrown Jul 12 '19 at 07:24
  • You requirement is to consume a SOAP Web Service on Android side. So Android will be the client, while spring boot is the server. I don't know any tool about source code generation on Android. In any case in the paste I followed this link https://www.c-sharpcorner.com/UploadFile/88b6e5/how-to-call-web-service-in-android-using-soap/ – Angelo Immediata Jul 12 '19 at 10:29
  • 1
    This library might be of use to you https://android-arsenal.com/details/1/430 – Vanshaj Daga Jul 15 '19 at 05:16
  • @jbrown You should use some other library other than that – Manoj Perumarath Jul 15 '19 at 09:55

3 Answers3

3

Did you try ksoap?

https://github.com/simpligility/ksoap2-android

Looks like it should do soap, is alive and works for android.

The ksoap2-android project provides a lightweight and efficient SOAP client library for the Android platform.

Maksim Turaev
  • 4,115
  • 1
  • 29
  • 42
0

I think you just need to use a different library in order to make your SOAP calls. This is what I've used which supports versions of the SDK as far back as 14.

private void GetSomeStuff(String myterm) throws UnsupportedEncodingException {


try {

     String strStuff2 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
     "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"+
     "<soap:Header><wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">"+
     "<wsse:UsernameToken><wsse:Username>my username</wsse:Username><wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">my password</wsse:Password></wsse:UsernameToken></wsse:Security></soap:Header>"+
     "<soap:Body> <GetSomeStuff xmlns=\"https://mycompany.com.MyService\">"+
     "<term>" + myterm + "/term></GetSomeStuff></soap:Body></soap:Envelope>";


    StringBuilder stringBuilder = new StringBuilder();


    HttpPost httppost = new HttpPost("https://mycompany.com/MyService.svc");        
    StringEntity se;

    se = new StringEntity(strStuff2,HTTP.UTF_8);


    httppost.setHeader("SOAPAction", "https://mycompany.com.MyService/GetSomeStuff”);


    se.setContentType("text/xml");  
    httppost.setEntity(se);  


    HttpClient httpclient = new DefaultHttpClient();      

    HttpResponse theresponse = (HttpResponse) httpclient.execute(httppost);

    StatusLine statusLine = theresponse.getStatusLine();
    int statusCode = statusLine.getStatusCode();
    if (statusCode == 200) {
        HttpEntity entity = theresponse.getEntity();
        InputStream content = entity.getContent();
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(content));
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }

        String theXML = stringBuilder.toString();

        int start = theXML.indexOf("<GetSomeStuffResult>") + 21;
        int end = theXML.indexOf("</GetSomeStuffResult>") - 1;

        // We didn't get the response we expected
        if ((start < 0) || (end < 0) || (start==end)) {
            Log.i(“MyApp”,”Empty Response from GetSomeStuffResult");
            return;
        }

        String myData = theXML.substring(start, end);

        if (myData() > 0) {
            try {
                JSONObject jObject = new JSONObject(myData);

                String deptDesc = jObject.getString("DepartmentDescription");
                String areaCode = jObject.getString("area_code");
                String phoneNumber = jObject.getString("phone_nbr");
                String title = jObject.getString("title");

                String fullPhoneNumber = "";

                if (phoneNumber.length() == 7) {
                    fullPhoneNumber = "(" + areaCode + ") " + phoneNumber.substring(0, 3) + "-" + phoneNumber.substring(3, 7);
                } else {
                     fullPhoneNumber = "(" + areaCode + ") " + phoneNumber;
                }

                SharedPreferences appPrefs =
                        getSharedPreferences("appPreferences",MODE_PRIVATE);

                SharedPreferences.Editor prefsEditor = appPrefs.edit();
                prefsEditor.putString("Title",title);
                prefsEditor.putString("DeptDescr",deptDesc);
                prefsEditor.putString("PhoneNumber",fullPhoneNumber);

                prefsEditor.commit();   

            }
            catch (JSONException e) {
                e.printStackTrace();
            }
        }


    } else {
         Log.e(“MyApp”, "Failed to GetSomeStuff”);
    }


} catch (ClientProtocolException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
}
Michael Dougan
  • 1,698
  • 1
  • 9
  • 13
0

org.springframework/spring-core/5.1.8.RELEASE.jar won't work with minSdkVersion 25 and it's not even and Android library, but a Java library. It strictly demands minSdkVersion 26:

MethodHandle.invoke and MethodHandle.invokeExact
are only supported starting with Android O (--min-api 26)

... there are a few others linked; some require POJO generation on Desktop Java.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216