改造 2 - 动态 URL

Posted

技术标签:

【中文标题】改造 2 - 动态 URL【英文标题】:Retrofit 2 - Dynamic URL 【发布时间】:2015-12-10 03:10:06 【问题描述】:

使用 Retrofit 2,您可以在服务方法的注释中设置完整的 URL,例如:

public interface APIService 
  @GET("http://api.mysite.com/user/list")
  Call<Users> getUsers();

但是,在我的应用程序中,我的 web 服务的 URL 在编译时是未知的,应用程序在下载的文件中检索它们,所以我想知道如何使用带有完整动态 URL 的 Retrofit 2。

我尝试设置完整路径,例如:

public interface APIService 
  @GET("fullUrl")
  Call<Users> getUsers(@Path("fullUrl") fullUrl);


new Retrofit.Builder()
  .baseUrl("http://api.mysite.com/")
  .build()
  .create(APIService.class)
  .getUsers("http://api.mysite.com/user/list"); // this url should be dynamic
  .execute();

但是在这里,Retrofit 并没有看到路径实际上是一个完整的 URL,并且正在尝试下载 http://api.mysite.com/http%3A%2F%2Fapi.mysite.com%2Fuser%2Flist

任何关于我如何使用 Retrofit 和这样的动态 url 的提示?

谢谢

【问题讨论】:

对于未来的搜索者,有一个关于使用 Retrofit 的动态 URL 的指南:futurestud.io/tutorials/… 同样的问题,但在这个线程中得到更好的答案***.com/questions/53211672/… 【参考方案1】:

我认为您以错误的方式使用它。这是changelog的摘录:

新增:@Url 参数注释允许为端点传递完整的 URL。

所以你的界面应该是这样的:

public interface APIService 
    @GET
    Call<Users> getUsers(@Url String url);

【讨论】:

@pdegand59 这是全新的 :) 见 changelog 虽然端点的 url 是 fullurl,但为什么需要 baseurl? 如果我使用这个@Url 注释。那我们应该在 .baseUrl() 中写什么呢?? 你应该写下你的主网址。 @Url 参数无论如何都会覆盖它。 我尝试像上面那样添加 URL,但是我通常为改造构建器设置的 baseUrl 覆盖了我在参数中指定的完整 url,因此它不是 dynamic.com,而是被转换为 @ 987654324@【参考方案2】:

我只想替换部分 url,通过这个解决方案,我不必传递整个 url,只需传递动态部分:

public interface APIService 

  @GET("users/user_id/playlists")
  Call<List<Playlist> getUserPlaylists(@Path(value = "user_id", encoded = true) String userId);

【讨论】:

帮了我很多...谢谢 投了赞成票。使用 retrofit2 suspend fun getUserPlaylists(@Path("id") id: String): YourOwnResponse 对我有很大帮助。谢谢【参考方案3】:

您可以在 @Path 注释上使用 encoded 标志:

public interface APIService 
  @GET("fullUrl")
  Call<Users> getUsers(@Path(value = "fullUrl", encoded = true) String fullUrl);

这将防止将/ 替换为%2F。 但是,它不会让您免于将 ? 替换为 %3F,因此您仍然无法传递动态查询字符串。

【讨论】:

这不是错字。 以及如何将 %3F 替换为 ? 对于?,您无法手动处理。您应该像 @Query("page") number: Int 一样使用 @Query 来传递查询参数。您也可以添加多个参数 这拯救了我的一天! 此外,您可以使用更多字段:@GET("fullUrl/id") fun getData(@Path(value = "fullUrl", encoded = true) fullUrl: String, @Path(value = "id", encoded = true) id: String): Call?【参考方案4】:

从 Retrofit 2.0.0-beta2 开始,如果您有服务从该 URL 响应 JSON: http://myhost/mypath

以下操作无效:

public interface ClientService 
    @GET("")
    Call<List<Client>> getClientList();


Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://myhost/mypath")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

ClientService service = retrofit.create(ClientService.class);

Response<List<Client>> response = service.getClientList().execute();

但这没关系:

public interface ClientService 
    @GET
    Call<List<Client>> getClientList(@Url String anEmptyString);


Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://myhost/mypath")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

ClientService service = retrofit.create(ClientService.class);

Response<List<Client>> response = service.getClientList("").execute();

【讨论】:

【参考方案5】:

你可以用这个:

@GET("group/id/users")

Call&lt;List&lt;User&gt;&gt; groupList(@Path("id") int groupId, @Query("sort") String sort);

有关更多信息,请参阅文档https://square.github.io/retrofit/

【讨论】:

【参考方案6】:

Retrofit (MVVM) 中带有 Get 和 Post 方法的动态 URL

改造服务界面:

public interface NetworkAPIServices 

@POST()
Observable<JsonElement> executXYZServiceAPI(@Url String url,@Body AuthTokenRequestModel param);


@GET
Observable<JsonElement> executeInserInfo(@Url String url);

MVVM 服务类:

   public Observable<JsonElement> executXYZServiceAPI(ModelObject object) 
    return networkAPIServices.authenticateAPI("url",
            object);


 public Observable<JsonElement> executeInserInfo(String ID) 
    return networkAPIServices.getBank(DynamicAPIPath.mergeUrlPath("url"+ID)));

和改造客户端类

 @Provides
@Singleton
@Inject
@Named("provideRetrofit2")
Retrofit provideRetrofit(@Named("provideRetrofit2") Gson gson, @Named("provideRetrofit2") OkHttpClient okHttpClient) 


   builder = new Retrofit.Builder();
    if (BaseApplication.getInstance().getApplicationMode() == ApplicationMode.DEVELOPMENT) 
        builder.baseUrl(NetworkURLs.BASE_URL_UAT);
     else 
        builder.baseUrl(NetworkURLs.BASE_URL_PRODUCTION);
    


    builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
    builder.client(okHttpClient);
    builder.addConverterFactory(GsonConverterFactory.create(gson));


    return builder.build();

例如 这是网址:https://gethelp.wildapricot.com/en/articles/549-changing-your

基本网址:https://gethelp.wildapricot.com

剩余的@Url:/en/articles/549-changeing-your(这是你在复古服务类中传递的)

【讨论】:

【参考方案7】:

第一步

  Please define a method in Api interface like:-
 @FormUrlEncoded
 @POST()
 Call<RootLoginModel> getForgotPassword(
        @Url String apiname,
        @Field(ParameterConstants.email_id) String username
 );

第二步 要获得最佳实践,请为改造实例定义一个类:-

  public class ApiRequest 
       static Retrofit retrofit = null;



public static Retrofit getClient() 
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
            .addInterceptor(logging)
            .connectTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build();

    if (retrofit==null) 
        retrofit = new Retrofit.Builder()
                .baseUrl(URLConstants.base_url)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    
    return retrofit;

第三步 在您的活动中定义:-

  final APIService request =ApiRequest.getClient().create(APIService.class);
  Call<RootLoginModel> call = request.getForgotPassword("dynamic api 
  name",strEmailid);

【讨论】:

【参考方案8】:

RetrofitHelper 用 kotlin 编写的库,可以让您使用几行代码进行 API 调用,并且您可以在每次调用中使用不同的 URL、标头和参数

在您的应用程序类中添加多个 URL,如下所示:

class Application : Application() 

    override fun onCreate() 
    super.onCreate()

        retrofitClient = RetrofitClient.instance
                    //api url
                .setBaseUrl("https://reqres.in/")
                    //you can set multiple urls
        //                .setUrl("example","http://ngrok.io/api/")
                    //set timeouts
                .setConnectionTimeout(4)
                .setReadingTimeout(15)
                    //enable cache
                .enableCaching(this)
                    //add Headers
                .addHeader("Content-Type", "application/json")
                .addHeader("client", "android")
                .addHeader("language", Locale.getDefault().language)
                .addHeader("os", android.os.Build.VERSION.RELEASE)
            

        companion object 
        lateinit var retrofitClient: RetrofitClient

        
      

然后在通话中使用您需要的 URL:

retrofitClient.Get<GetResponseModel>()
            //set base url
            .setBaseUrlKey("example")
            //set path
            .setPath("api/users/2")
            //set url params Key-Value or HashMap
            .setUrlParams("KEY","Value")
            .setResponseHandler(GetResponseModel::class.java,
                object : ResponseHandler<GetResponseModel>() 
                    override fun onSuccess(response: Response<GetResponseModel>) 
                        super.onSuccess(response)
                        //handle response
                    
                ).run(this)

欲了解更多信息,请参阅documentation

【讨论】:

【参考方案9】:

如果您已经设置了代码,并且不想对不同的接口进行更改,请使用link 中描述的解决方案。 重点是 changeApiBaseUrl 方法更新 URL 并重新创建 Retrofit 构建器。

public class ServiceGenerator   
public static String apiBaseUrl = "http://futurestud.io/api";
private static Retrofit retrofit;

private static Retrofit.Builder builder =
        new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(apiBaseUrl);

private static OkHttpClient.Builder httpClient =
        new OkHttpClient.Builder();

// No need to instantiate this class.
private ServiceGenerator() 


public static void changeApiBaseUrl(String newApiBaseUrl) 
    apiBaseUrl = newApiBaseUrl;

    builder = new Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(apiBaseUrl);


public static <S> S createService(Class<S> serviceClass, AccessToken token) 
    String authToken = token.getTokenType().concat(token.getAccessToken());
    return createService(serviceClass, authToken);


// more methods
// ...

您可以按如下方式使用它:

public class DynamicBaseUrlActivity extends AppCompatActivity 

public static final String TAG = "CallInstances";
private Callback<ResponseBody> downloadCallback;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_file_upload);

    downloadCallback = new Callback<ResponseBody>() 
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) 
            Log.d(TAG, "server contacted at: " + call.request().url());
        

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) 
            Log.d(TAG, "call failed against the url: " + call.request().url());
        
    ;

    // first request
    FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);
    Call<ResponseBody> originalCall = downloadService.downloadFileWithFixedUrl();
    originalCall.enqueue(downloadCallback);

    // change base url
    ServiceGenerator.changeApiBaseUrl("http://development.futurestud.io/api");

    // new request against new base url
    FileDownloadService newDownloadService = ServiceGenerator.create(FileDownloadService.class);
    Call<ResponseBody> newCall = newDownloadService.downloadFileWithFixedUrl();
    newCall.enqueue(downloadCallback);
    

【讨论】:

【参考方案10】:
step -*1 

movie_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:focusable="true"

    android:orientation="horizontal"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/row_padding_vertical"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingBottom="@dimen/row_padding_vertical">

    <ImageView
        android:id="@+id/ivImage"
        android:layout_
        android:layout_
        android:layout_marginRight="10dp"
        android:src="@mipmap/ic_launcher" />

    <LinearLayout
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_
            android:layout_
            android:layout_alignParentTop="true"
            android:text="Hello"
            android:textColor="@color/title"
            android:textSize="16dp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/genre"
            android:layout_
            android:layout_
            android:layout_below="@id/title"
            android:text="realName" />
    </LinearLayout>

    <TextView
        android:id="@+id/year"
        android:layout_
        android:layout_
        android:layout_alignParentRight="true"
        android:text="Team"
        android:textColor="@color/year" />

</LinearLayout>                                                                                                            

Api.java                                                                                  
import org.json.JSONObject;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;

public interface  Api 

    String BASE_URL = "https://simplifiedcoding.net/demos/";

    @GET("marvel")
    Call<List<Hero>> getHeroes();

    @FormUrlEncoded
    @POST("/login")
    public void login(@Field("username") String username, @Field("password") String password, Callback<List<Hero>> callback);




MoviesAdapter.java                                                                                       import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.squareup.picasso.Picasso;

import java.util.List;

public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> 

    private List<Hero> moviesList;

    Context context;


    public class MyViewHolder extends RecyclerView.ViewHolder 
        public TextView title, year, genre;
        public ImageView ivImage;

        public MyViewHolder(View view) 
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            genre = (TextView) view.findViewById(R.id.genre);
            year = (TextView) view.findViewById(R.id.year);
            ivImage =  view.findViewById(R.id.ivImage);
            ivImage.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 

                    Toast.makeText(context, "-" + moviesList.get(getAdapterPosition()).getName(), Toast.LENGTH_SHORT).show();
                
            );
        
    


    public MoviesAdapter(List<Hero> moviesList,Context context) 
        this.moviesList = moviesList;
        this.context = context;
    

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.movie_list_row, parent, false);

        return new MyViewHolder(itemView);
    

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) 
        Hero movie = moviesList.get(position);
        holder.title.setText(movie.getName());
        holder.genre.setText(movie.getRealname());
        holder.year.setText(movie.getTeam());

        Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(holder.ivImage);
    

    @Override
    public int getItemCount() 
        return moviesList.size();
    
                                                                                                                                                        main activity                                                                                                               import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity 

    private List<Hero> movieList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MoviesAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView=findViewById(R.id.recycler_view);
        mAdapter = new MoviesAdapter(movieList,MainActivity.this);
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(mAdapter);
        //calling the method to display the heroes
        getHeroes();

    


    private void getHeroes() 
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ApiInterface.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create()) //Here we are using the GsonConverterFactory to directly convert json data to object
                .build();

        ApiInterface api = retrofit.create(ApiInterface.class);

        Call<List<Hero>> call = api.getHeroes();

        call.enqueue(new Callback<List<Hero>>() 
            @Override
            public void onResponse(Call<List<Hero>> call, Response<List<Hero>> response) 
                List<Hero> heroList = response.body();

                //Creating an String array for the ListView
                String[] heroes = new String[heroList.size()];

                //looping through all the heroes and inserting the names inside the string array
                for (int i = 0; i < heroList.size(); i++) 
                    //heroes[i] = heroList.get(i).getName();
                    movieList.add(new Hero( heroList.get(i).getName(), heroList.get(i).getRealname(), heroList.get(i).getTeam()));
                
                mAdapter.notifyDataSetChanged();

            

            @Override
            public void onFailure(Call<List<Hero>> call, Throwable t) 
                Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
            
        );
    


Hero.java
package com.example.owner.apipractice;
public class Hero 

    private String name;
    private String realname;
    private String team;

    public Hero(String name, String realname, String team) 
        this.name = name;
        this.realname = realname;
        this.team = team;
    

    private String firstappearance;
    private String createdby;
    private String publisher;
    private String imageurl;
    private String bio;


    public Hero(String name, String realname, String team, String firstappearance, String createdby, String publisher, String imageurl, String bio) 
        this.name = name;
        this.realname = realname;
        this.team = team;
        this.firstappearance = firstappearance;
        this.createdby = createdby;
        this.publisher = publisher;
        this.imageurl = imageurl;
        this.bio = bio;
    

    public String getName() 
        return name;
    

    public String getRealname() 
        return realname;
    

    public String getTeam() 
        return team;
    

    public String getFirstappearance() 
        return firstappearance;
    

    public String getCreatedby() 
        return createdby;
    

    public String getPublisher() 
        return publisher;
    

    public String getImageurl() 
        return imageurl;
    

    public String getBio() 
        return bio;
    

【讨论】:

请解释一下你的答案

以上是关于改造 2 - 动态 URL的主要内容,如果未能解决你的问题,请参考以下文章

[Layui]后台大布局tab风格动态菜单-无限层

改造中的静态标题和动态标题问题

未找到改造注释。 (参数#1)

[Layui]后台大布局tab风格动态菜单-无限层

如何使用改造 Android 调用具有动态参数的 api?

第十九章 动态URL权限控制——《跟我学Shiro》