ไม่สามารถสร้าง Call Adapter สำหรับตัวอย่างคลาสได้ง่าย


91

ฉันใช้ชุดติดตั้งเพิ่ม 2.0.0-beta1 กับ SimpleXml ฉันต้องการดึงทรัพยากรแบบง่าย (XML) จากบริการ REST Marshalling / Unmarshalling the Simple object ด้วย SimpleXML ทำงานได้ดี

เมื่อใช้รหัสนี้ (แปลงรูปแบบก่อน 2.0.0 โค้ด):

final Retrofit rest = new Retrofit.Builder()
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);
LOG.info(service.getSimple("572642"));

บริการ:

public interface SimpleService {

    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);

}

ฉันได้รับข้อยกเว้นนี้:

Exception in thread "main" java.lang.IllegalArgumentException: Unable to create call adapter for class example.Simple
    for method SimpleService.getSimple
    at retrofit.Utils.methodError(Utils.java:201)
    at retrofit.MethodHandler.createCallAdapter(MethodHandler.java:51)
    at retrofit.MethodHandler.create(MethodHandler.java:30)
    at retrofit.Retrofit.loadMethodHandler(Retrofit.java:138)
    at retrofit.Retrofit$1.invoke(Retrofit.java:127)
    at com.sun.proxy.$Proxy0.getSimple(Unknown Source)

ฉันขาดอะไรไป? ฉันรู้ว่าการตัดประเภทการส่งคืนด้วยCallผลงาน แต่ฉันต้องการให้บริการส่งคืนวัตถุทางธุรกิจเป็นประเภท (และทำงานในโหมดซิงค์)

อัปเดต

หลังจากเพิ่มการพึ่งพาเพิ่มเติมและ.addCallAdapterFactory(RxJavaCallAdapterFactory.create())ตามที่แนะนำโดยคำตอบอื่นฉันยังคงได้รับข้อผิดพลาดนี้:

Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for class simple.Simple. Tried:
 * retrofit.RxJavaCallAdapterFactory
 * retrofit.DefaultCallAdapter$1

ที่เกี่ยวข้อง: stackoverflow.com/questions/32617770/…
Hosam Aly

1
สำหรับผู้ที่ใช้ Coroutine check @chatlanin answer
NJ

คำตอบ:


68

คำตอบสั้น ๆ : กลับมาCall<Simple>ในอินเทอร์เฟซบริการของคุณ

ดูเหมือนว่า Retrofit 2.0 กำลังพยายามหาวิธีสร้างวัตถุพร็อกซีสำหรับอินเทอร์เฟซบริการของคุณ คาดว่าคุณจะเขียนสิ่งนี้:

public interface SimpleService {
    @GET("/simple/{id}")
    Call<Simple> getSimple(@Path("id") String id);
}

Callแต่ก็ยังคงต้องการที่จะเล่นที่ดีและมีความยืดหยุ่นเมื่อคุณไม่ต้องการที่จะกลับมาเป็น เพื่อสนับสนุนสิ่งนี้จึงมีแนวคิดของ a CallAdapterซึ่งควรรู้วิธีปรับ a Call<Simple>เป็นไฟล์Simple.

การใช้จะเป็นประโยชน์เฉพาะถ้าคุณกำลังพยายามที่จะกลับมาRxJavaCallAdapterFactoryrx.Observable<Simple>

วิธีแก้ปัญหาที่ง่ายที่สุดคือการคืนค่าCallตามที่ Retrofit คาดไว้ คุณยังสามารถเขียนCallAdapter.Factoryถ้าคุณต้องการจริงๆ


1
ใช่นี่เป็นคำตอบที่ถูกต้อง :( ฉันรู้ตั้งแต่แรกแล้วว่าCall<Simple>เป็นวิธีการทำงานอย่างไรก็ตามตามที่ระบุไว้ในคำถามของฉันการตั้งค่าของฉันคือเพียงแค่กลับมาSimple(เช่นเดียวกับในรุ่นก่อนหน้านี้) ข้อสรุปของฉันคือ : เป็นไปไม่ได้หากไม่มีรหัสพิเศษ
rmuller

ลิงก์ที่คุณให้ไว้ใช้งานCall<Simple> ไม่ได้ แพคเกจSimpleเป็นของอะไร?
อาย

ขอบคุณสำหรับโน้ต @shyam ฉันได้อัปเดตลิงก์แล้ว Simpleเป็นคลาสที่กล่าวถึงในคำถามของ OP Call<T>การเชื่อมโยงคือการ
Hosam Aly

94

ในกรณีของการKotlinและcoroutinesสถานการณ์เช่นนี้เกิดขึ้นเมื่อฉันลืมที่จะทำเครื่องหมายฟังก์ชั่นการบริการ API เป็นsuspendเมื่อผมเรียกฟังก์ชั่นนี้จากCoroutineScope(Dispatchers.IO).launch{}:

การใช้งาน:

    val apiService = RetrofitFactory.makeRetrofitService()

    CoroutineScope(Dispatchers.IO).launch {

        val response = apiService.myGetRequest()

        // process response...

    }

ApiService.kt

interface ApiService {

       @GET("/my-get-request")
       suspend fun myGetRequest(): Response<String>
}

คุณช่วยชีวิตของฉัน s2
Guilherme Ramos

ในขณะที่ใช้ Coroutines เราต้องใช้ Suspend กับฟังก์ชันของคุณ ที่ช่วยขอบคุณเพื่อน
Abubakar Rafi

มันใช้ได้กับฉัน tnx การเพิ่มการระงับเป็นวิธีแก้ข้อผิดพลาดนี้
mmdreza baqalpour

53

เพิ่มการอ้างอิง:

compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1'

สร้างอะแดปเตอร์ของคุณด้วยวิธีนี้:

Retrofit rest = new Retrofit.Builder()
    .baseUrl(endpoint)
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .build();

addCallAdapterFactory ()และaddConverterFactory ()ต้องเรียกทั้งคู่

บริการ:

public interface SimpleService {

    @GET("/simple/{id}")
    Call<Simple> getSimple(@Path("id") String id);

}

แก้ไขSimpleเป็นCall<Simple>.


7
ฉันรู้ว่าการเปลี่ยน Simple เป็น Call <Simple> จะช่วยได้ อย่างไรก็ตามฉันไม่ต้องการแนะนำประเภทCallให้กับอินเทอร์เฟซบริการ
rmuller

คุณยังสามารถใช้CompletableFutureดูstackoverflow.com/questions/32269064/…
Johannes Barop

38

ด้วย Retrofit ใหม่ (2. +) คุณต้องเพิ่ม addCallAdapterFactory ซึ่งอาจเป็นแบบธรรมดาหรือ RxJavaCallAdapterFactory (สำหรับ Observables) ฉันคิดว่าคุณสามารถเพิ่มได้มากกว่าทั้งสองอย่างด้วย จะตรวจสอบโดยอัตโนมัติว่าจะใช้อันไหน ดูตัวอย่างการทำงานด้านล่าง คุณสามารถตรวจสอบลิงค์นี้เพื่อดูรายละเอียดเพิ่มเติม

 Retrofit retrofit = new Retrofit.Builder().baseUrl(ApiConfig.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build()

1
ลิงค์เยี่ยม! จะตรวจสอบภายหลัง.
rmuller

1
ในเอกสารยังคงมี RestAdapter และไม่มีตัวอย่างวิธีการร้องขออย่างง่ายที่สุดด้วย Retrofit 2.0 ฉันได้รับข้อผิดพลาดไม่สามารถแก้ไข RxJavaCallAdapterFactory ใช้เวลาไม่กี่ชั่วโมงในการสร้างคำขอ http สำหรับ Android ที่ง่ายที่สุด ดูเหมือนว่าจะไม่ง่ายอย่างที่พวกเขาโฆษณา บางทีพวกเขาควรจัดทำเอกสารเพิ่มเติมอีกเล็กน้อย
Vlado Pandžić

ใช้ Retrofit แทน RestAdapter หากคุณใช้ Retrofit 2.0
Himanshu Virmani

5
RxJavaCallAdapterFactory ต้องการสิ่งประดิษฐ์อะแด็ปเตอร์ rxjava จาก repo เดียวกัน compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl

3
สำหรับการติดตั้งเพิ่มเติม 2 ตรวจสอบให้แน่ใจว่าคุณใช้การอ้างอิงที่ถูกต้องcom.squareup.retrofit2:adapter-rxjava:2.1.0และ com.squareup.retrofit2:converter-gson:2.1.0
George Papas

16

หากคุณต้องการใช้retrofit2และคุณไม่ต้องการคืนสินค้าเสมอไปretrofit2.Call<T>คุณต้องสร้างของคุณเองCallAdapter.Factoryซึ่งจะส่งคืนประเภทง่ายๆตามที่คุณคาดไว้ โค้ดง่ายๆมีลักษณะดังนี้:

import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

public class SynchronousCallAdapterFactory extends CallAdapter.Factory {
    public static CallAdapter.Factory create() {
        return new SynchronousCallAdapterFactory();
    }

    @Override
    public CallAdapter<Object, Object> get(final Type returnType, Annotation[] annotations, Retrofit retrofit) {
        // if returnType is retrofit2.Call, do nothing
        if (returnType.toString().contains("retrofit2.Call")) {
            return null;
        }

        return new CallAdapter<Object, Object>() {
            @Override
            public Type responseType() {
                return returnType;
            }

            @Override
            public Object adapt(Call<Object> call) {
                try {
                    return call.execute().body();
                } catch (Exception e) {
                    throw new RuntimeException(e); // do something better
                }
            }
        };
    }
}

จากนั้นลงทะเบียนอย่างง่ายSynchronousCallAdapterFactoryในชุดติดตั้งเพิ่มเติมควรแก้ปัญหาของคุณ

Retrofit rest = new Retrofit.Builder()
        .baseUrl(endpoint)
        .addConverterFactory(SimpleXmlConverterFactory.create())
        .addCallAdapterFactory(SynchronousCallAdapterFactory.create())
        .build();

retrofit2.Callหลังจากที่คุณสามารถส่งกลับชนิดง่ายโดยไม่ต้อง

public interface SimpleService {
    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);
}

ขอบคุณมาก คุณรู้หรือไม่ว่ามีวิธีสร้างอะแดปเตอร์การโทรแบบ async หรือไม่? ดังนั้นฉันจึงสามารถเรียกใช้บริการเช่นนี้: service.getSimple ("id", Adapter ใหม่ () {onFail () {} onSuccess () {}} แทนที่จะใช้ enqueue () และ callback?
markov00

"implements CallAdapter.Factory" นี่คือคลาสไม่ใช่อินเทอร์เฟซ?
ozmank

@ozmank ในเวอร์ชัน2.0.0-beta3นี้เป็นอินเทอร์เฟซตอนนี้ในเวอร์ชัน2.1.0เป็นคลาส ฉันแก้ไขโค้ดของฉันให้เป็นจริงมากขึ้น ขอบคุณ.
Mateusz Korwel

@MateuszKorwel มักจะได้รับ RuntimeException ใหม่ (e)! ฉันต้องการส่งคืน String แทน Simple
Dr.jacky

3
ขอบคุณที่โพสต์สิ่งนี้ ผมได้สร้างห้องสมุดรอบการจัดการนี้Simple getSimple()และResponse<Simple> getSimple()ร้องขอ: github.com/jaredsburrows/retrofit2-synchronous-adapter
Jared Burrows

13

เพิ่มการอ้างอิงต่อไปนี้สำหรับชุดติดตั้งเพิ่มเติม 2

 compile 'com.squareup.retrofit2:retrofit:2.1.0'

สำหรับ GSON

 compile 'com.squareup.retrofit2:converter-gson:2.1.0'

สำหรับสังเกตการณ์

compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

ในกรณีของคุณสำหรับ XML คุณจะต้องรวมการอ้างอิงต่อไปนี้

 compile 'com.squareup.retrofit2:converter-simplexml:2.1.0'

อัปเดตการเรียกใช้บริการดังต่อไปนี้

final Retrofit rest = new Retrofit.Builder()
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);

นี่เหมือนกับการตั้งค่าของฉัน
rmuller

1
อย่าลืมเพิ่มบรรทัดนี้เมื่อสร้างอะแด็ปเตอร์: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

ไม่สามารถแก้ไข: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank มัน retrofit2 ฉันได้อัปเดตคำตอบของฉันด้วย โปรดทดลองใช้
rahulrv

5

IllegalArgumentException: ไม่สามารถสร้างการเรียกใช้อะแด็ปเตอร์สำหรับคลาส java.lang.Object

คำตอบสั้น ๆ : ฉันได้แก้ไขแล้วโดยการเปลี่ยนแปลงต่อไปนี้

ext.retrofit2Version = '2.4.0' -> '2.6.0'
implementation"com.squareup.retrofit2:retrofit:$retrofit2Version"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit2Version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit2Version"

โชคดี


4

ในกรณีของฉันใช้สิ่งนี้

compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

ด้วยสิ่งนี้

new Retrofit.Builder().addCallAdapterFactory(RxJava2CallAdapterFactory.create())...

แก้ไขปัญหาเมื่อไม่มีอะไรทำงาน


2

หากคุณไม่ได้ใช้ RxJava อย่างถูกต้องการเพิ่ม RxJava เพียงเพื่อการติดตั้งเพิ่มเติม 2.5.0มีการสนับสนุนCompletableFutureในตัวซึ่งคุณสามารถใช้ได้โดยไม่ต้องเพิ่มไลบรารีหรืออะแดปเตอร์อื่น ๆ

build.gradle.kts

implementation("com.squareup.retrofit2:retrofit:2.5.0")
implementation("com.squareup.retrofit2:retrofit-converters:2.5.0")

Api.kt

interface Api {
    @GET("/resource")
    fun listCompanies(): CompletableFuture<ResourceDto>
}

การใช้งาน:

Retrofit.Builder()
   .addConverterFactory(SimpleXmlConverterFactory.create())
   .baseUrl("https://api.example.com")
   .build()
   .create(Api::class.java)

1

เพียงเพื่อทำให้ตัวอย่างการโทรชัดเจนขึ้นสำหรับผู้ที่กำลังย้ายข้อมูลไม่ได้ใช้ Rx หรือผู้ที่ต้องการการโทรแบบซิงโครนัส - การโทรจะแทนที่การตอบสนอง (wraps) เป็นหลักหมายถึง:

Response<MyObject> createRecord(...);

กลายเป็น

Call<MyObject> createRecord(...);

และไม่

Call<Response<MyObject>> createRecord(...);

(ซึ่งจะต้องใช้อะแดปเตอร์)


จากนั้นการโทรจะช่วยให้คุณยังคงใช้งานได้isSuccessfulตามที่ตอบกลับจริง คุณสามารถทำสิ่งต่างๆเช่น:

myApi.createRecord(...).execute().isSuccessful()

หรือเข้าถึงประเภทของคุณ (MyObject) เช่น:

MyObject myObj = myApi.createRecord(...).execute().body();

1
public interface SimpleService {

  @GET("/simple/{id}")
  Simple getSimple(@Path("id") String id);

}

การสื่อสารกับเครือข่ายทำได้โดยใช้เธรดแยกต่างหากดังนั้นคุณควรเปลี่ยน Simple ด้วยสิ่งนั้น

public interface SimpleService {

  @GET("/simple/{id}")
  Call<Simple> getSimple(@Path("id") String id);

}

1

ในกรณีของฉันฉันใช้

com.squareup.retrofit2:adapter-rxjava:2.5.0 //notice rxjava

แทน

com.squareup.retrofit2:adapter-rxjava2:2.5.0 //notice rxjava2

คุณควรใช้com.squareup.retrofit2:adapter-rxjava2:2.5.0เมื่อใช้io.reactivex.rxjava2


0

คุณสามารถใช้ Callback รับ Simple จากฟังก์ชัน onResponse

public class MainActivity extends Activity implements Callback<Simple> {

    protected void onCreate(Bundle savedInstanceState) {
        final Retrofit rest = new Retrofit.Builder()
                    .addConverterFactory(SimpleXmlConverterFactory.create())
                    .baseUrl(endpoint)
                    .build();
        SimpleService service = rest.create(SimpleService.class);
        Call<Simple> call = service.getSimple("572642");
        //asynchronous call
        call.enqueue(this);

        return true;
    }

    @Override
    public void onResponse(Response<Simple> response, Retrofit retrofit) {
       // response.body() has the return object(s)
    }

    @Override
    public void onFailure(Throwable t) {
        // do something
    }

}

-2

วิธีที่ฉันแก้ไขปัญหานี้คือการเพิ่มสิ่งนี้ลงในไฟล์ gradle ของแอปพลิเคชันหากไม่ได้ตั้งค่าการกำหนดค่าจะมีข้อขัดแย้งบางทีสิ่งนี้อาจได้รับการแก้ไขในไลบรารีเวอร์ชันเสถียร:

configurations {
    compile.exclude group: 'stax'
    compile.exclude group: 'xpp3'
}

dependencies {
    compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
    compile 'com.squareup.retrofit:converter-simplexml:2.0.0-beta1'
}

และสร้างอะแดปเตอร์ของคุณด้วยวิธีนี้:

  Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(endPoint)
            .addConverterFactory(SimpleXmlConverterFactory.create())
            .build();

หวังว่าจะช่วยได้!


1
นี่เป็นการกำหนดค่าเดียวกันกับในคำถามของฉัน ดังนั้นไม่ได้แก้ไขอะไร :)
rmuller
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.