改造 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);
【讨论】:
帮了我很多...谢谢 投了赞成票。使用 retrofit2suspend 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<List<User>> 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的主要内容,如果未能解决你的问题,请参考以下文章