1. рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ
1.1ред рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрдпрд╛ рд╣реИ?
рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдЬрд╛рд╡рд╛ рдФрд░ рдПрдВрдбреНрд░реЙрдЗрдб рдХреЗ рд▓рд┐рдП рдПрдХ REST рдХреНрд▓рд╛рдЗрдВрдЯ рд╣реИред рдпрд╣ REST- рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ JSON (рдпрд╛ рдЕрдиреНрдп рд╕рдВрд░рдЪрд┐рдд рдбреЗрдЯрд╛) рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдФрд░ рд▓реЛрдб рдХрд░рдирд╛ рдЖрд╕рд╛рди рдмрдирд╛рддрд╛ рд╣реИред рд░реЗрдЯреНрд░реЛрдлрд╝рд┐рдЯ рдореЗрдВ, рдЖрдк рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдбреЗрдЯрд╛ рдХреЛ рдЕрдиреБрдХреНрд░рдорд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рд╕ рдХрдирд╡рд░реНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЖрдорддреМрд░ рдкрд░ GSON рдХрд╛ рдЙрдкрдпреЛрдЧ JSON рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрдк XML рдпрд╛ рдЕрдиреНрдп рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ HTTP рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд▓рд┐рдП OkHttp рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред
рдЖрдк рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЯреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ JSON- рдЖрдзрд╛рд░рд┐рдд рдЬрд╛рд╡рд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ: www.jsonschema2pojo.org рдпрд╣ рдореМрдЬреВрджрд╛ JSON рд╕реЗ рдЬрдЯрд┐рд▓ рдЬрд╛рд╡рд╛ рдбреЗрдЯрд╛ рд╕реНрдЯреНрд░рдХреНрдЪрд░реНрд╕ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧреА рд╣реЛ рд╕рдХрддрд╛ рд╣реИред
1.2ред рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рддреАрди рд╡рд░реНрдЧреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА:
- рдореЙрдбрд▓ рд╡рд░реНрдЧ рдЬрд┐рд╕реЗ JSON рдореЙрдбрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ
- рд╕рдВрднрд╛рд╡рд┐рдд HTTP рд╕рдВрдЪрд╛рд▓рди рдХреА рдкрд╣рдЪрд╛рди рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдЗрдВрдЯрд░рдлреЗрд╕
- Retrofit.Builder рд╡рд░реНрдЧ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдЬреЛ HTTP рдСрдкрд░реЗрд╢рди рдХреЗ рд▓рд┐рдП URL рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рдкрд░рд┐рднрд╛рд╖рд╛ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд┐рд▓реНрдбрд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдФрд░ рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред
рдкреНрд░рддреНрдпреЗрдХ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╡рд┐рдзрд┐ рд╕рдВрднрд╡ рдПрдкреАрдЖрдИ рдХреЙрд▓ рдореЗрдВ рд╕реЗ рдПрдХ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддреА рд╣реИред рдЕрдиреБрд░реЛрдз рдХреЗ рдкреНрд░рдХрд╛рд░ рдФрд░ рд░рд┐рд╢реНрддреЗрджрд╛рд░ URL рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рдореЗрдВ HTTP рдПрдиреЛрдЯреЗрд╢рди (GET, POST, рдЖрджрд┐) рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рд╡рд╛рдкрд╕реА рдореВрд▓реНрдп рдЕрдкреЗрдХреНрд╖рд┐рдд рдкрд░рд┐рдгрд╛рдо рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рде рдХреЙрд▓ рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рдкреВрд░рд╛ рдХрд░рддрд╛ рд╣реИред
@GET("users") Call<List<User>> getUsers();
URL рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдк рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрди рдмреНрд▓реЙрдХ рдФрд░ рдХреНрд╡реЗрд░реА рдкреИрд░рд╛рдореАрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд░рд┐рдкреНрд▓реЗрд╕рдореЗрдВрдЯ рдмреНрд▓реЙрдХ рдХреЛ {} рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕рдВрдмрдВрдзрд┐рдд URL рд╕реЗ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред рд╡рд┐рдзрд┐ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд▓рд┐рдП @ рдкрде рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдЗрд╕ рдкреИрд░рд╛рдореАрдЯрд░ рдХрд╛ рдорд╛рди рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрди рдмреНрд▓реЙрдХ рд╕реЗ рдмрдВрдзрд╛ рд╣реЛрддрд╛ рд╣реИред
@GET("users/{name}/commits") Call<List<Commit>> getCommitsByName(@Path("name") String name);
рд╡рд┐рдзрд┐ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд▓рд┐рдП @ рдХреНрд╡реЗрд░реА рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХреНрд╡реЗрд░реА рдкреИрд░рд╛рдореАрдЯрд░ рдЬреЛрдбрд╝реЗ рдЬрд╛рддреЗ рд╣реИрдВред рд╡реЗ URL рдХреЗ рдЕрдВрдд рдореЗрдВ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдЬреБрдбрд╝ рдЬрд╛рддреЗ рд╣реИрдВред
@GET("users") Call<User> getUserById(@Query("id") Integer id);
рд╡рд┐рдзрд┐ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд▓рд┐рдП @ рдмреЙрдбреА рдПрдиреЛрдЯреЗрд╢рди, рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдз рдирд┐рдХрд╛рдп рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЗрдЯреНрд░реЛрдлрд╝рд┐рдЯ рдХреЛ рдмрддрд╛рддрд╛ рд╣реИред
@POST("users") Call<User> postUser(@Body User user)
2. рдкреВрд░реНрд╡рд╛рдкреЗрдХреНрд╖рд╛рдПрдБ
рдирд┐рдореНрди рдЙрджрд╛рд╣рд░рдг рдЧреНрд░реЗрдб рдирд┐рд░реНрдорд╛рдг рдХреЗ рд╕рд╛рде рдЧреНрд░рд╣рдг рдЖрдИрдбреАрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред
рдпрд╣ рдЕрднреНрдпрд╛рд╕ рдорд╛рдирддрд╛ рд╣реИ рдХрд┐ рдЖрдк
рдЧреНрд░реИрдбрд▓ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реИрдВ рдФрд░
рдЧреНрд░рд╣рдг рдХреЗ рд╕рд╛рде
рдЧреНрд░реИрдбрд▓ рдХрд╛
рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВред
рдЕрдиреНрдп рд╡рд┐рдХрд╛рд╕ рд╡рд╛рддрд╛рд╡рд░рдг, рдЬреИрд╕реЗ рдХрд┐ Visual Studio Code рдпрд╛ IntelliJ, рд╡рд╣реА рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рд╕реЗ рдЖрдк рдЕрдкрдиреЗ рдкрд╕рдВрджреАрджрд╛ рдЯреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
3. рд╡реНрдпрд╛рдпрд╛рдо: рдкрд╣рд▓реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрд▓рд╛рдЗрдВрдЯ
рдЗрд╕ рдЕрднреНрдпрд╛рд╕ рдореЗрдВ, рдЖрдк рдПрдХ рд╕реНрд╡рд╕рдВрдкреВрд░реНрдг REST рдЧреНрд░рд╛рд╣рдХ рдмрдирд╛рдПрдВрдЧреЗред рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдПрдБ рдореЙрдХ рд╕рд░реНрд╡рд░ рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрдиреНрди рдХреА рдЬрд╛рддреА рд╣реИрдВред
3.1ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдирд╛ рдФрд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдирд╛
Com.vogella.retrofitgerrit рдирд╛рдо рд╕реЗ рдПрдХ рдирдпрд╛ рдЧреНрд░реЗрдбрд▓ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдПрдВред Com.vogella.retrofitgerrit рдирд╛рдо рдХреЗ src / main / java рдореЗрдВ рдПрдХ рдирдпрд╛ рдкреИрдХреЗрдЬ рдЬреЛрдбрд╝реЗрдВред
рдирд┐рдореНрди рдирд┐рд░реНрднрд░рддрд╛рдПрдБ build.gradle рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВред
// retrofit implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0' // Junit testImplementation("org.junit.jupiter:junit-jupiter-api:5.0.0") testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0") // to run JUnit 3/4 tests: testImplementation("junit:junit:4.12") testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0")
3.2ред рдПрдкреАрдЖрдИ рдФрд░ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдПрдбрд╛рдкреНрдЯрд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВ
рдЬреЗрд░рд┐рдЯ рдХреА JSON рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ, рд╣рдо рдХреЗрд╡рд▓ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рдкреНрд░рд╢реНрди рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рдкрд╣рд▓реЗ рдЬреЛрдбрд╝реЗ рдЧрдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдкреИрдХреЗрдЬ рдореЗрдВ рдирд┐рдореНрди рдбреЗрдЯрд╛ рд╡рд░реНрдЧ рдмрдирд╛рдПрдВред
package com.vogella.java.retrofitgerrit; public class Change { String subject; public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } }
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд▓рд┐рдП REST API рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВред
package com.vogella.java.retrofitgerrit; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; public interface GerritAPI { @GET("changes/") Call<List<Change>> loadChanges(@Query("q") String status); }
рдирд┐рдореНрди рдирд┐рдпрдВрддреНрд░рдХ рд╡рд░реНрдЧ рдмрдирд╛рдПрдБред рдпрд╣ рд╡рд░реНрдЧ рдПрдХ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рдмрдирд╛рддрд╛ рд╣реИ, рдЬреЗрд░рд┐рдЯ рдПрдкреАрдЖрдИ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдкрд░рд┐рдгрд╛рдо рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддрд╛ рд╣реИ (рдХрдВрд╕реЛрд▓ рдкрд░ рдХреЙрд▓ рдХреЗ рдкрд░рд┐рдгрд╛рдо рдХреЛ рдкреНрд░рд┐рдВрдЯ рдХрд░рддрд╛ рд╣реИ)ред
package com.vogella.java.retrofitgerrit; import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class Controller implements Callback<List<Change>> { static final String BASE_URL = "https://git.eclipse.org/r/"; public void start() { Gson gson = new GsonBuilder() .setLenient() .create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); GerritAPI gerritAPI = retrofit.create(GerritAPI.class); Call<List<Change>> call = gerritAPI.loadChanges("status:open"); call.enqueue(this); } @Override public void onResponse(Call<List<Change>> call, Response<List<Change>> response) { if(response.isSuccessful()) { List<Change> changesList = response.body(); changesList.forEach(change -> System.out.println(change.subject)); } else { System.out.println(response.errorBody()); } } @Override public void onFailure(Call<List<Change>> call, Throwable t) { t.printStackTrace(); } }
рдирд┐рдпрдВрддреНрд░рдХ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдореБрдЦреНрдп рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд░реНрдЧ рдмрдирд╛рдПрдВред
package com.vogella.java.retrofitgerrit; public class Main { public static void main(String[] args) { Controller controller = new Controller(); controller.start(); } }
4. рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕ рдФрд░ рдПрдбреЗрдкреНрдЯрд░
4.1ред рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕
рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрдирд╡рд░реНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдпрд╣ рдХрдиреНрд╡рд░реНрдЯрд░ рдбреЗрдЯрд╛ рдХрд╛ рдХреНрд░рдордмрджреНрдзрдХрд░рдг (рдбреА) рдХрд░рддрд╛ рд╣реИред рдХрдИ рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕ рд╡рд┐рднрд┐рдиреНрди рдХреНрд░рдорд╛рдВрдХрди рдкреНрд░рд╛рд░реВрдкреЛрдВ рдХреЗ рд▓рд┐рдП рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЙрдкрд▓рдмреНрдз рд╣реИрдВред
- JSON рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд:
- Gson: com.squareup.retrofit: рдХрдирд╡рд░реНрдЯрд░-рдЬреАрд╕рди
- рдЬреИрдХреНрд╕рди: com.squareup.retrofit: рдХрдирд╡рд░реНрдЯрд░-рдЬреИрдХрд╕рди
- рдореЛрд╢реА: com.squareup.retrofit: рдХрдирд╡рд░реНрдЯрд░-рдореЛрд╢реА
- рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдмрдлрд╝рд░реНрд╕ рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд:
- рдкреНрд░реЛрдЯреЛрдмреЙрдлрд╝: com.squareup.retrofit: рдХрдирд╡рд░реНрдЯрд░-рдкреНрд░реЛрдЯреЛрдмреЙрдлрд╝
- рддрд╛рд░: com.squareup.retrofit: рдХрдирд╡рд░реНрдЯрд░-рддрд╛рд░
- XML рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд:
- рд╕рд┐рдВрдкрд▓ XML: com.squareup.retrofit: рдХрдирд╡рд░реНрдЯрд░-рд╕рд┐рдореНрдкреНрд▓реЗрдХреНрд╕ рдПрдордПрд▓
рд╕реВрдЪреАрдмрджреНрдз рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдк рдХрдирд╡рд░реНрдЯрд░.рдлреИрдХреНрдЯрд░реА рдХреНрд▓рд╛рд╕ рдХреЛ рдмрдврд╝рд╛рдХрд░ рдЕрдиреНрдп рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдирд╛ рдЦреБрдж рдХрд╛ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВред
4.2ред рд░реЗрдЯреНрд░реЛрдкрд░рд┐рдЯ рдПрдбреЗрдкреНрдЯрд░
RxJava 2.x, Java 8, рдФрд░ Guava рдЬреИрд╕реЗ рдЕрдиреНрдп рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЗ рд╕рд╛рде рдЗрдВрдЯрд░рдлреЗрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдбрд╛рдкреНрдЯрд░реНрд╕ рдХреЗ рд╕рд╛рде рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рднреА рдмрдврд╝рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдЙрдкрд▓рдмреНрдз рдПрдбреЗрдкреНрдЯрд░ рдХрд╛ рдЕрд╡рд▓реЛрдХрди рдЬреАрдердм
рд╕реНрдХреНрд╡рд╛рдпрд░ / рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ / рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ-рдПрдбреЗрдкреНрдЯрд░ / рдореЗрдВ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, RxJava 2.x рдПрдбреЗрдкреНрдЯрд░ рдЧреНрд░реИрдбрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЬреЛрдбрд╝рд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:
compile 'com.squareup.retrofit2:adapter-rxjava2:latest.version'
рдпрд╛ рдЕрдкрд╛рдЪреЗ рдорд╛рд╡реЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░:
<dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>adapter-rxjava2</artifactId> <version>latest.version</version> </dependency>
рдПрдбреЗрдкреНрдЯрд░ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ retrofit2.Retrofit.Builder.addCallAdapterFactory (рдлрд╝реИрдХреНрдЯрд░реА) рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
рдЗрд╕ рдПрдбреЗрдкреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдЗрдВрдЯрд░рдлреЗрд╕ RxJava 2.x рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдСрдмреНрдЬрд░реНрд╡реЗрдмрд▓, рдлреНрд▓реЛрдПрдмрд▓ рдпрд╛ рд╕рд┐рдВрдЧрд▓, рдЖрджрд┐ред
@GET("users") Observable<List<User>> getUsers();
5. рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдкреНрд░рдорд╛рдгреАрдХрд░рдг
рд░реЗрдЯреНрд░реЛрдлрд╝рд┐рдЯ рдПрдкреАрдЖрдИ рдХреЙрд▓ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ рдЬрд┐рд╕реЗ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдФрд░ рдкрд╛рд╕рд╡рд░реНрдб (Http рдмреЗрд╕рд┐рдХ рдкреНрд░рдорд╛рдгреАрдХрд░рдг) рдпрд╛ API рдЯреЛрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЛ рдкреНрд░рдмрдВрдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рджреЛ рддрд░реАрдХреЗ рд╣реИрдВред рдкрд╣рд▓реА рд╡рд┐рдзрд┐ рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдиреБрд░реЛрдз рд╣реЗрдбрд░ рдХрд╛ рдкреНрд░рдмрдВрдзрди рдХрд░рдирд╛ рд╣реИред рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рд╣реИ рдХрд┐ рдЗрд╕рдХреЗ рд▓рд┐рдП рдПрдХ OkHttp рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдПред
5.1ред рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рд╕рд╛рде рдкреНрд░рдорд╛рдгреАрдХрд░рдг
рдорд╛рди рд▓реАрдЬрд┐рдП рдХрд┐ рдЖрдк рдЙрд╕ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдЖрдк рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдкреАрдЖрдИ рдкрд░рд┐рднрд╛рд╖рд╛ рдореЗрдВ рдПрдХ рдирдпрд╛ рдкреИрд░рд╛рдореАрдЯрд░ рдЬреЛрдбрд╝рдХрд░ рдРрд╕рд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
@GET("user") Call<UserDetails> getUserDetails(@Header("Authorization") String credentials)
@ рд╣реИрдбрд░ ("рдСрдерд░рд╛рдЗрдЬрд╝реЗрд╢рди") рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк рд░рд┐рдЯреНрд░реЗрдлрд╝рд┐рдЯ рдХреЛ рдСрдерд░рд╛рдЗрдЬрд╝реЗрд╢рди рд╣реЗрдбрд░ рдХреЛ рдЙрд╕ рдореВрд▓реНтАНрдп рдХреЗ рд╕рд╛рде рдЬреЛрдбрд╝рдиреЗ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддреЗ рд╣реИрдВ рдЬрд┐рд╕реЗ рдЖрдк рдкрд╛рд╕ рдХрд░ рд░рд╣реЗ рд╣реИрдВред
рдореВрд▓ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк рдЗрд╕рдХрд╛ рдЖрдзрд╛рд░ (рд╕реНрдЯреНрд░рд┐рдВрдЧ, рд╕реНрдЯреНрд░рд┐рдВрдЧ) рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде OkHttps рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓реНрд╕ рд╡рд░реНрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╡рд┐рдзрд┐ рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдФрд░ рдкрд╛рд╕рд╡рд░реНрдб рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддреА рд╣реИ рдФрд░ Http рдмреЗрд╕рд┐рдХ рд╕реНрдХреАрдо рдХреЗ рд▓рд┐рдП рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рд▓реМрдЯрд╛рддреА рд╣реИред
Credentials.basic("ausername","apassword");
рдпрджрд┐ рдЖрдк API рдЯреЛрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдФрд░ рдореВрд▓ рдпреЛрдЬрдирд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдмрд╕ рдЕрдкрдиреЗ рдЯреЛрдХрди рдХреЗ рд╕рд╛рде getUserDetails (рд╕реНрдЯреНрд░рд┐рдВрдЧ) рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд░реЗрдВред
5.2ред OkHttp рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХреЗ рд╕рд╛рде рдкреНрд░рдорд╛рдгреАрдХрд░рдгред
рдпрджрд┐ рдЖрдк рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбреЗрдЯрд╛ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддреЗ рд╣реИрдВ рддреЛ рдЙрдкрд░реЛрдХреНрдд рд╡рд┐рдзрд┐ рдХреЗрд╡рд▓ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдЬреЛрдбрд╝рддреА рд╣реИред рдпрджрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рдХреЙрд▓ рд╣реИрдВ, рддреЛ рдЖрдк рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рд╣реЛрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдкреНрд░рддреНрдпреЗрдХ рдЕрдиреБрд░реЛрдз рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдиреЗ рдФрд░ рдЕрдиреБрд░реЛрдз рд╣реЗрдбрд░ рдХреЛ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рд▓рд╛рдн рдпрд╣ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдПрдкреАрдЖрдИ рд╡рд┐рдзрд┐ рдкрд░рд┐рднрд╛рд╖рд╛ рдореЗрдВ @ рд╣реИрдбрд░ ("рдкреНрд░рд╛рдзрд┐рдХрд░рдг") рдЬреЛрдбрд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред
рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ OkHttp рдмрд┐рд▓реНрдбрд░ рдореЗрдВ okhttp3.OkHttpClient.Builder.addInterceptor (рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░) рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().addInterceptor(new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request.Builder builder = originalRequest.newBuilder().header("Authorization", Credentials.basic("aUsername", "aPassword")); Request newRequest = builder.build(); return chain.proceed(newRequest); } }).build();
OkHttp рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдП рдЧрдП рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ Retrofit2.Retrofit.Builder.client (OkHttpClient) рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .client(okHttpClient) .build();
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдкрдиреЗ рджреЗрдЦрд╛ рд╣реИ, рдпрд╣рд╛рдВ рдмреЗрд╕рд┐рдХ рдСрдерд░рд╛рдЗрдЬреЗрд╢рди рдХреЗ рд▓рд┐рдП рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓реНрд╕ рдХреНрд▓рд╛рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдлрд┐рд░, рдпрджрд┐ рдЖрдк рдПрдкреАрдЖрдИ рдЯреЛрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рд╕рд┐рд░реНрдл рдЯреЛрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред
6. рдПрдХреНрд╕рд░рд╕рд╛рдЗрдЬ: рдЬрд╛рд╡рд╛ рдореЗрдВ рд░реАрдЯреНрд░реЛрдлрд┐рдЯ рдЯреВ рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рдЧреЗрд░рд┐рдЯ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓
рдирд┐рдореНрди рдЕрдиреБрднрд╛рдЧ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдПрдХ рдиреНрдпреВрдирддрдо рдЬрд╛рд╡рд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдП рдЬреЛ рдХрд┐ Gerrit API рд╕реЗ рдЦреБрд▓реА рдкрд░рд┐рд╡рд░реНрддрди рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдкреБрдирдГ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рдкрд░рд┐рдгрд╛рдо рдХрдВрд╕реЛрд▓ рдореЗрдВ рдореБрджреНрд░рд┐рдд рд╣реЛрддреЗ рд╣реИрдВред
6.1ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдирд╛ рдФрд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдирд╛
рдпрд╣ рдЕрднреНрдпрд╛рд╕ рдорд╛рдирддрд╛ рд╣реИ рдХрд┐ рдЖрдк
рдЧреНрд░рд╣рдг рдХреЗ рд▓рд┐рдП рдЧреНрд░реИрдбрд▓ рдФрд░
рдмрд┐рд▓реНрдбрд╢рд┐рдк рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реИрдВред
Com.vogella.java.retrofitgerrit рдирд╛рдо рд╕реЗ рдПрдХ рдирдпрд╛ рдЧреНрд░реЗрдбрд▓ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдПрдВред Com.vogella.java.retrofitgerrit рдирд╛рдо рдХреЗ src / main / java рдореЗрдВ рдПрдХ рдирдпрд╛ рдкреИрдХреЗрдЬ рдЬреЛрдбрд╝реЗрдВред
рдирд┐рдореНрди рдирд┐рд░реНрднрд░рддрд╛рдПрдБ build.gradle рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВред
// retrofit implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0' // Junit testImplementation("org.junit.jupiter:junit-jupiter-api:5.0.0") testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0") // to run JUnit 3/4 tests: testImplementation("junit:junit:4.12") testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0")
6.2ред рдПрдкреАрдЖрдИ рдФрд░ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдПрдбрд╛рдкреНрдЯрд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВ
рдЬреЗрд░рд┐рдЯ рдХреА JSON рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ, рд╣рдо рдХреЗрд╡рд▓ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рдкреНрд░рд╢реНрди рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рдкрд╣рд▓реЗ рдЬреЛрдбрд╝реЗ рдЧрдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдкреИрдХреЗрдЬ рдореЗрдВ рдирд┐рдореНрди рдбреЗрдЯрд╛ рд╡рд░реНрдЧ рдмрдирд╛рдПрдВред
package com.vogella.java.retrofitgerrit; public class Change { String subject; public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } }
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд▓рд┐рдП REST API рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВред
package com.vogella.java.retrofitgerrit; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; public interface GerritAPI { @GET("changes/") Call<List<Change>> loadChanges(@Query("q") String status); }
рдирд┐рдореНрди рдирд┐рдпрдВрддреНрд░рдХ рд╡рд░реНрдЧ рдмрдирд╛рдПрдБред рдпрд╣ рд╡рд░реНрдЧ рдПрдХ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рдмрдирд╛рддрд╛ рд╣реИ, рдЬреЗрд░рд┐рдЯ рдПрдкреАрдЖрдИ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдкрд░рд┐рдгрд╛рдо рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддрд╛ рд╣реИ (рдХрдВрд╕реЛрд▓ рдкрд░ рдХреЙрд▓ рдХреЗ рдкрд░рд┐рдгрд╛рдо рдХреЛ рдкреНрд░рд┐рдВрдЯ рдХрд░рддрд╛ рд╣реИ)ред
package com.vogella.java.retrofitgerrit; import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class Controller implements Callback<List<Change>> { static final String BASE_URL = "https://git.eclipse.org/r/"; public void start() { Gson gson = new GsonBuilder() .setLenient() .create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); GerritAPI gerritAPI = retrofit.create(GerritAPI.class); Call<List<Change>> call = gerritAPI.loadChanges("status:open"); call.enqueue(this); } @Override public void onResponse(Call<List<Change>> call, Response<List<Change>> response) { if(response.isSuccessful()) { List<Change> changesList = response.body(); changesList.forEach(change -> System.out.println(change.subject)); } else { System.out.println(response.errorBody()); } } @Override public void onFailure(Call<List<Change>> call, Throwable t) { t.printStackTrace(); } }
рдирд┐рдпрдВрддреНрд░рдХ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдореБрдЦреНрдп рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд░реНрдЧ рдмрдирд╛рдПрдВред
package com.vogella.java.retrofitgerrit; public class Main { public static void main(String[] args) { Controller controller = new Controller(); controller.start(); } }
7. рдПрдХреНрд╕рд░рд╕рд╛рдЗрдЬ: рдЖрд░рдПрд╕рдПрд╕ рдлрд╝реАрдб рд╕реЗ рдПрдХреНрд╕рдПрдордПрд▓ рд░рд┐рд╕реНрдкреЙрдиреНрд╕ рдХреЛ рдХрдиреНрд╡рд░реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рдпрд╣ рдЕрдиреБрднрд╛рдЧ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рд╕рд┐рдореНрдкрд▓реЗрдХреНрд╕ XMLConverter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХреНрд╕рдПрдордПрд▓ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреИрд╕реЗ рдХрд░реЗрдВ
рдПрдХ рдиреНрдпреВрдирддрдо рдЬрд╛рд╡рд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬреЛ рд╡реЛрдЧреЗрд▓рд╛ рдЖрд░рдПрд╕рдПрд╕ рдлрд╝реАрдб (
http://vogella.com/article.rss ) рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЪреИрдирд▓ рдХрд╛ рдирд╛рдо, рд╢реАрд░реНрд╖рдХ рдФрд░ рд▓реЗрдЦ рд▓рд┐рдВрдХ рдкреНрд░рд┐рдВрдЯ рдХрд░рддрд╛ рд╣реИред
7.1ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдирд╛ рдФрд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдирд╛
рдпрд╣ рдЕрднреНрдпрд╛рд╕ рдорд╛рдирддрд╛ рд╣реИ рдХрд┐ рдЖрдк
рдЧреНрд░рд╣рдг рдХреЗ рд▓рд┐рдП рдЧреНрд░реИрдбрд▓ рдФрд░
рдмрд┐рд▓реНрдбрд╢рд┐рдк рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реИрдВред
Com.vogella.java.retrofitxml рдирд╛рдо рд╕реЗ рдПрдХ рдирдпрд╛ рдЧреНрд░реЗрдбрд▓ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдПрдВред Com.vogella.java.retrofitxml рдирд╛рдо рдХреЗ src / main / java рдореЗрдВ рдПрдХ рдирдпрд╛ рдкреИрдХреЗрдЬ рдЬреЛрдбрд╝реЗрдВред
рдирд┐рдореНрди рдирд┐рд░реНрднрд░рддрд╛рдПрдБ build.gradle рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВред
implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'
7.2ред рдПрдХ XML рджреГрд╢реНрдп рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдирд╛
рдПрдХ рдЖрд░рдПрд╕рдПрд╕ рдлрд╝реАрдб рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ:
<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <channel> <title>Eclipse and Android Information</title> <link>http://www.vogella.com</link> <description>Eclipse and Android Information</description> <language>en</language> <copyright>Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany (CC BY-NC-SA 3.0)</copyright> <pubDate>Tue, 03 May 2016 11:46:11 +0200</pubDate> <item> <title>Android user interface testing with Espresso - Tutorial</title> <description> This tutorial describes how to test Android applications with the Android Espresso testing framework.</description> <link>http://www.vogella.com/tutorials/AndroidTestingEspresso/article.html</link> <author>lars.vogel@vogella.com (Lars Vogel)</author> <guid>http://www.vogella.com/tutorials/AndroidTestingEspresso/article.html</guid> </item> <item> <title>Using the Gradle build system in the Eclipse IDE - Tutorial</title> <description>This article describes how to use the Gradle tooling in Eclipse</description> <link>http://www.vogella.com/tutorials/EclipseGradle/article.html</link> <author>lars.vogel@vogella.com (Lars Vogel)</author> <guid>http://www.vogella.com/tutorials/EclipseGradle/article.html</guid> </item> <item> <title>Unit tests with Mockito - Tutorial</title> <description>This tutorial explains testing with the Mockito framework for writting software tests.</description> <link>http://www.vogella.com/tutorials/Mockito/article.html</link> <author>lars.vogel@vogella.com (Lars Vogel)</author> <guid>http://www.vogella.com/tutorials/Mockito/article.html</guid> </item> </channel> </rss>
XML рд╣реЗрдбрд░ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЗрд╕ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╡рд┐рднрд┐рдиреНрди XML рддрддреНрд╡ рд╣реЛрддреЗ рд╣реИрдВред рдПрдХ рдЖрд░рдПрд╕рдПрд╕ рддрддреНрд╡ рдореЗрдВ рдПрдХ рдЪреИрдирд▓ рддрддреНрд╡ рд╣реЛрддрд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдЕрдиреНрдп рддрддреНрд╡ рд╣реЛрддреЗ рд╣реИрдВ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╢реАрд░реНрд╖рдХ, рд╡рд┐рд╡рд░рдг, рдкрдмрдбреЗрдЯ) рдФрд░ рдХрдИ рдЖрдЗрдЯрдо рддрддреНрд╡ (рд▓реЗрдЦ)ред
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рджреЛ рдбреЗрдЯрд╛ рдХрдХреНрд╖рд╛рдПрдВ рдмрдирд╛рдПрдБ: RSSFeed рдФрд░ рдЕрдиреБрдЪреНрдЫреЗрджред
package com.vogella.java.retrofitxml; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; @Root(name = "item", strict = false) public class Article { @Element(name = "title") private String title; @Element(name = "link") private String link; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } }
package com.vogella.java.retrofitxml; import java.util.List; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Path; import org.simpleframework.xml.Root; @Root(name="rss", strict=false) public class RSSFeed { @Element(name="title") @Path("channel") private String channelTitle; @ElementList(name="item", inline=true) @Path("channel") private List<Article> articleList; public String getChannelTitle() { return channelTitle; } public void setChannelTitle(String channelTitle) { this.channelTitle = channelTitle; } public List<Article> getArticleList() { return articleList; } public void setArticleList(List<Article> articleList) { this.articleList = articleList; } }
рдЕрдиреБрдЪреНрдЫреЗрдж рд╡рд░реНрдЧ рдПрдХ рд▓реЗрдЦ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХреЗрд╡рд▓ рд╢реАрд░реНрд╖рдХ рдФрд░ рд▓реЗрдЦ рдХреЗ рд▓рд┐рдВрдХ рдХреЛ рдмрд░рдХрд░рд╛рд░ рд░рдЦрддрд╛ рд╣реИред рдпреЗ рдПрдХрдорд╛рддреНрд░ рдРрд╕реЗ рдХреНрд╖реЗрддреНрд░ рд╣реИрдВ рдЬреЛ рд╣рдорд╛рд░реА рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВред
рдПрдиреЛрдЯреЗрд╢рди @ рд░реВрдЯ рдПрдХ рд╡рд░реНрдЧ рдХреЛ (рдбреА) рдХреНрд░рдорд╛рдВрдХрди рдХреЗ рд╡рд┐рд╖рдп рдХреЗ рд░реВрдк рдореЗрдВ рдЪрд┐рд╣реНрдирд┐рдд рдХрд░рддрд╛ рд╣реИред рд╡реИрдХрд▓реНрдкрд┐рдХ рд░реВрдк рд╕реЗ, рдЖрдк @ рд░реВрдЯ рдПрдиреЛрдЯреЗрд╢рди рдореЗрдВ рдПрдХ рдирд╛рдо рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ XML рддрддреНрд╡ рдХреЗ рдирд╛рдо рд╕реЗ рдореЗрд▓ рдЦрд╛рддрд╛ рд╣реИред рдпрджрд┐ рдХреЛрдИ рдирд╛рдо рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдирд╣реАрдВ рд╣реИ, рддреЛ рд╡рд░реНрдЧ рдирд╛рдо рдХрд╛ рдЙрдкрдпреЛрдЧ XML рддрддреНрд╡ рдХреЗ рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЪреВрдВрдХрд┐ рд╡рд░реНрдЧ рдирд╛рдо (RSSFeed) XML рддрддреНрд╡ рдирд╛рдо (rss) рд╕реЗ рдЕрд▓рдЧ рд╣реИ, рд╣рдореЗрдВ рдирд╛рдо рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
рдЬрдм рд╕рдЦреНрдд рдХреЛ рдЧрд▓рдд рдкрд░ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рд╕рдЦреНрдд рдкрд╛рд░реНрд╕рд┐рдВрдЧ рдЕрдХреНрд╖рдо рд╣реЛ рдЬрд╛рддреА рд╣реИред рдпрд╣ рдкрд╛рд░реНрд╕рд░ рдХреЛ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рдПрдХреНрд╕рдПрдордПрд▓ рддрддреНрд╡ рдпрд╛ рд╡рд┐рд╢реЗрд╖рддрд╛ рдкрд╛рдП рдЬрд╛рдиреЗ рдкрд░ рдХреЛрдИ рдЕрдкрд╡рд╛рдж рдирд╣реАрдВ рдорд┐рд▓рддрд╛ рд╣реИ, рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдореИрдкрд┐рдВрдЧ рдкреНрд░рджрд╛рди рдирд╣реАрдВ рдХреА рдЬрд╛рддреА рд╣реИред рдЪреВрдБрдХрд┐ rss рдПрд▓рд┐рдореЗрдВрдЯ рдореЗрдВ рдПрдХ рд╡рд░реНрдЬрди рд╡рд┐рд╢реЗрд╖рддрд╛ рд╣реЛрддреА рд╣реИ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рд╕рдВрдмрдВрдзрд┐рдд рдХреНрд╖реЗрддреНрд░ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ, рдпрджрд┐ рд╕рдЦреНрдд рдкреИрд░рд╛рдореАрдЯрд░ рдЧрд▓рдд рдкрд░ рд╕реЗрдЯ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рддреЛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдПрдХ рддреНрд░реБрдЯрд┐ рдЙрддреНрдкрдиреНрди рдХрд░реЗрдЧрд╛ред
@ рддрддреНрд╡ рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдПрдХ XML рддрддреНрд╡ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ, рддреЛ рдЖрдк рдЗрд╕ рдлрд╝реАрд▓реНрдб рджреНрд╡рд╛рд░рд╛ рджрд░реНрд╢рд╛рдП рдЧрдП рддрддреНрд╡ рдХрд╛ XML рдирд╛рдо рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрджрд┐ рдХреЛрдИ рдирд╛рдо рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдирд╣реАрдВ рд╣реИ, рддреЛ рдлрд╝реАрд▓реНрдб рдирд╛рдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
ArticleList рдлрд╝реАрд▓реНрдб рдХреЛ @ ElementList рдХреЗ рд╕рд╛рде рдПрдиреЛрдЯреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЗрд╕рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рдлрд╝реАрд▓реНрдб рдХрд╛ рдЙрдкрдпреЛрдЧ XML рддрддреНрд╡реЛрдВ рдХреЗ рд╕рдВрдЧреНрд░рд╣ (рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ: рд╕реВрдЪреА) рдХреЛ рдЙрд╕реА рдирд╛рдо рд╕реЗ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЬрдм рдЗрдирд▓рд╛рдЗрди рдХреЛ рд╕рд╣реА рдкрд░ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд╕рдВрдЧреНрд░рд╣ рдореЗрдВ рдЖрдЗрдЯрдо рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдЖрдЗрдЯрдо рдХреЗ рдЕрдВрджрд░ рдПрдХ рдХреЗ рдмрд╛рдж рдПрдХ рд╕реВрдЪреАрдмрджреНрдз рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдПрдХ рдордзреНрдпрд╡рд░реНрддреА рдорд╛рддрд╛-рдкрд┐рддрд╛ рдирд╣реАрдВ рд╣реИред
@ рдкрде рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк XML рдЯреНрд░реА рдХреЗ рднреАрддрд░ рдХрд┐рд╕реА XML рддрддреНрд╡ рдХреЗ рд▓рд┐рдП рдкрде рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рдЙрдкрдпреЛрдЧреА рд╣реИ рдпрджрд┐ рдЖрдк рдЬрд╛рд╡рд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд╕рд╛рде рдПрдХ рдкреВрд░реНрдг XML рдЯреНрд░реАрдб рдореЙрдбрд▓ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдЪреИрдирд▓ рдФрд░ рдХрдИ рдЖрдЗрдЯрдо-рддрддреНрд╡реЛрдВ рдХреЗ рдирд╛рдо рдХреЗ рд▓рд┐рдП, рд╣рдо рд╕реАрдзреЗ рдЪреИрдирд▓-рддрддреНрд╡ рдореЗрдВ рд╡рд┐рд╢рд┐рд╖реНрдЯ рддрддреНрд╡реЛрдВ рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
7.3ред рдПрдХ рдПрдкреАрдЖрдИ рдФрд░ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдПрдбрд╛рдкреНрдЯрд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдирд╛
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд▓рд┐рдП REST API рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВред
package com.vogella.java.retrofitxml; import retrofit2.Call; import retrofit2.http.GET; public interface VogellaAPI { @GET("article.rss") Call<RSSFeed> loadRSSFeed(); }
рдирд┐рдореНрди рдирд┐рдпрдВрддреНрд░рдХ рд╡рд░реНрдЧ рдмрдирд╛рдПрдБред рдпрд╣ рд╡рд░реНрдЧ рдПрдХ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рдмрдирд╛рддрд╛ рд╣реИ, рд╡реЛрдЧреЗрд▓рд╛ рдПрдкреАрдЖрдИ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдкрд░рд┐рдгрд╛рдо рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддрд╛ рд╣реИред
package com.vogella.java.retrofitxml; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.simplexml.SimpleXmlConverterFactory; public class Controller implements Callback<RSSFeed> { static final String BASE_URL = "http://vogella.com/"; public void start() { Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL) .addConverterFactory(SimpleXmlConverterFactory.create()).build(); VogellaAPI vogellaAPI = retrofit.create(VogellaAPI.class); Call<RSSFeed> call = vogellaAPI.loadRSSFeed(); call.enqueue(this); } @Override public void onResponse(Call<RSSFeed> call, Response<RSSFeed> response) { if (response.isSuccessful()) { RSSFeed rss = response.body(); System.out.println("Channel title: " + rss.getChannelTitle()); rss.getArticleList().forEach( article -> System.out.println("Title: " + article.getTitle() + " Link: " + article.getLink())); } else { System.out.println(response.errorBody()); } } @Override public void onFailure(Call<RSSFeed> call, Throwable t) { t.printStackTrace(); } }
рдЕрдВрддрд┐рдо рдЪрд░рдг рдирд┐рдпрдВрддреНрд░рдХ рдХреЛ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдореБрдЦреНрдп рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд░реНрдЧ рдмрдирд╛рдирд╛ рд╣реИред
package com.vogella.java.retrofitxml; public class Application { public static void main(String[] args) { Controller ctrl = new Controller(); ctrl.start(); } }
8. рд╡реНрдпрд╛рдпрд╛рдо: StackOverflow рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрд╡реЗрджрди рдкрддреНрд░ рдмрдирд╛рдирд╛
StackOverflow рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рд▓реЛрдХрдкреНрд░рд┐рдп рд╕рд╛рдЗрдЯ рд╣реИред рдпрд╣ рдПрдХ REST API рднреА рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ,
рдЬреЛ Stackoverflow API рдкреЗрдЬ рдкрд░ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдкреНрд░рд▓реЗрдЦрд┐рдд рд╣реИред
рдЗрд╕ рдЕрднреНрдпрд╛рд╕ рдореЗрдВ рдЖрдк REST рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рдЖрдк рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ StackOverflow рдЯреИрдЧ рдкреНрд░рд╢реНрдиреЛрдВ рдФрд░ рдЙрдирдХреЗ рдЙрддреНрддрд░реЛрдВ рдХреЛ рдХреНрд╡реЗрд░реА рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░реЗрдВрдЧреЗред
рд╣рдорд╛рд░реЗ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рд╣рдо рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЕрдиреБрд░реЛрдз URL рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕ URL рдХреЛ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдЦреЛрд▓реЗрдВ рдФрд░ рдЙрддреНрддрд░ рджреЗрдЦреЗрдВред
https://api.stackexchange.com/2.2/search?order=desc&sort=votes&tagged=android&site=stackoverflow
8.1ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдирд╛ рдФрд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдирд╛
Com.vogella.android.stackoverflow рдирд╛рдордХ рдПрдХ Android рдРрдк рдмрдирд╛рдПрдВред рд╢реАрд░реНрд╖ рд╕реНрддрд░ рдХреЗ рдкреИрдХреЗрдЬ рдХреЗ рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ com.vogella.android.stackoverflow рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред
рдирд┐рдореНрди рдирд┐рд░реНрднрд░рддрд╛рдПрдБ build.gradle рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВред
compile "com.android.support:recyclerview-v7:25.3.1" compile 'com.google.code.gson:gson:2.8.1'
8.2ред рдбреЗрдЯрд╛ рдореЙрдбрд▓ рдмрдирд╛рдирд╛
рд╣рдо Stackoverflow рд╕реЗ рд╕рд╡рд╛рд▓ рдФрд░ рдЬрд╡рд╛рдм рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВред рдЗрд╕ рдкреНрд░рдпреЛрдЬрди рдХреЗ рд▓рд┐рдП, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рджреЛ рдбреЗрдЯрд╛ рдХрдХреНрд╖рд╛рдПрдВ рдмрдирд╛рдПрдВред
package com.vogella.android.stackoverflow; import com.google.gson.annotations.SerializedName; public class Question { public String title; public String body; @SerializedName("question_id") public String questionId; @Override public String toString() { return(title); } }
package com.vogella.android.stackoverflow; import com.google.gson.annotations.SerializedName; public class Answer { @SerializedName("answer_id") public int answerId; @SerializedName("is_accepted") public boolean accepted; public int score; @Override public String toString() { return answerId + " - Score: " + score + " - Accepted: " + (accepted ? "Yes" : "No"); } }
8.3ред рдЧрддрд┐рд╡рд┐рдзрд┐ рдФрд░ рд▓реЗрдЖрдЙрдЯ рдмрдирд╛рдирд╛
рдЕрдкрдиреА рдЧрддрд┐рд╡рд┐рдзрд┐ рдХреЗ рд▓рд┐рдП activity_main.xml рд╕реЗрдЯ рдХрд░реЗрдВред
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="8dp" android:orientation="vertical" tools:context="com.vogella.android.stackoverflow.MainActivity"> <Spinner android:id="@+id/questions_spinner" android:layout_width="match_parent" android:layout_height="wrap_content" /> <android.support.v7.widget.RecyclerView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <Button android:id="@+id/authenticate_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="Authenticate" /> </LinearLayout>
рдПрдбреЗрдкреНрдЯрд░ рд░рд┐рд╕рд╛рдЗрдХрд▓рд░ рд╡реНрдпреВ рдХреНрд▓рд╛рд╕ рдХреЛ рдЕрдкрдиреЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдореЗрдВ RecyclerViewAdapter рдирд╛рдо рджреЗрдВред
рдПрдХ рд╕рдВрднрд╛рд╡рд┐рдд рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИред
package com.vogella.android.stackoverflow; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> { private List<Answer> data; public class ViewHolder extends RecyclerView.ViewHolder { public TextView text; public ViewHolder(View v) { super(v); text = (TextView) v.findViewById(android.R.id.text1); } } public RecyclerViewAdapter(List<Answer> data) { this.data = data; } @Override public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v; v = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_selectable_list_item, parent, false); return new ViewHolder(v); } @Override public void onBindViewHolder(RecyclerViewAdapter.ViewHolder holder, int position) { Answer answer = ((Answer) data.get(position)); holder.text.setText(answer.toString()); holder.itemView.setTag(answer.answerId); } @Override public int getItemCount() { return data.size(); } }
рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ MainActivity рдХреНрд▓рд╛рд╕ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВ:
package com.vogella.android.stackoverflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class MainActivity extends Activity implements View.OnClickListener { private String token; private Button authenticateButton; private Spinner questionsSpinner; private RecyclerView recyclerView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); questionsSpinner = (Spinner) findViewById(R.id.questions_spinner); questionsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this, "Spinner item selected", Toast.LENGTH_LONG).show(); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); authenticateButton = (Button) findViewById(R.id.authenticate_button); recyclerView = (RecyclerView) findViewById(R.id.list); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); } @Override protected void onResume() { super.onResume(); if (token != null) { authenticateButton.setEnabled(false); } } @Override public void onClick(View v) { switch (v.getId()) { case android.R.id.text1: if (token != null) {
8.4ред рдПрдХ рдирдХрд▓реА рдбреЗрдЯрд╛ рдкреНрд░рджрд╛рддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рдПрдХ рдирдХрд▓реА рдбреЗрдЯрд╛ рдкреНрд░рджрд╛рддрд╛ рдмрдирд╛рдПрдВ рдФрд░ рд╕реНрдкрд┐рдирд░ рдХреЛ рдирдХрд▓реА рд╕рд╡рд╛рд▓реЛрдВ рдХреЗ рд╕рд╛рде рднрд░реЗрдВ рдФрд░ рдирдХрд▓реА рдЙрддреНрддрд░реЛрдВ рдХреЗ рд╕рд╛рде рдкреБрдирд░реНрдирд╡реАрдиреАрдХрд░рдг рдХрд░реЗрдВ (рд╕реНрдкрд┐рдирд░ рдореЗрдВ рдЪрдпрди рдмрджрд▓рдиреЗ рдХреЗ рдмрд╛рдж)ред
package com.vogella.android.stackoverflow; import java.util.ArrayList; import java.util.List; public class FakeDataProvider { public static List<Question> getQuestions(){ List<Question> questions = new ArrayList<>(); for (int i = 0; i<10; i++) { Question question = new Question(); question.questionId = String.valueOf(i); question.title = String.valueOf(i); question.body = String.valueOf(i) + "Body"; questions.add(question); } return questions; } public static List<Answer> getAnswers(){ List<Answer> answers = new ArrayList<>(); for (int i = 0; i<10; i++) { Answer answer = new Answer(); answer.answerId = i; answer.accepted = false; answer.score = i; answers.add(answer); } return answers; } }
рдЕрдм рдЗрд╕ рдлрд░реНрдЬреА рдбреЗрдЯрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдкрд┐рдирд░ рдФрд░ рд░рд┐рд╕рд╛рдЗрдХрд▓рд╡реНрдпреВ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВред
package com.vogella.android.stackoverflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; import android.widget.Toast; import java.util.List; public class MainActivity extends Activity implements View.OnClickListener { private String token; private Button authenticateButton; private Spinner questionsSpinner; private RecyclerView recyclerView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); questionsSpinner = (Spinner) findViewById(R.id.questions_spinner); questionsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this, "Spinner item selected", Toast.LENGTH_LONG).show(); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); List<Question> questions = FakeDataProvider.getQuestions(); ArrayAdapter<Question> arrayAdapter = new ArrayAdapter<Question>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, questions); questionsSpinner.setAdapter(arrayAdapter); authenticateButton = (Button) findViewById(R.id.authenticate_button); recyclerView = (RecyclerView) findViewById(R.id.list); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); List<Answer> answers = FakeDataProvider.getAnswers(); RecyclerViewAdapter adapter = new RecyclerViewAdapter(answers); recyclerView.setAdapter(adapter); } @Override protected void onResume() { super.onResume(); if (token != null) { authenticateButton.setEnabled(false); } } @Override public void onClick(View v) { switch (v.getId()) { case android.R.id.text1: if (token != null) {
8.5ред рдЧреНрд░реЗрдб рдкреЗ рдирд┐рд░реНрднрд░рддрд╛ рдФрд░ рдЕрдиреБрдорддрд┐рдпрд╛рдБ рдЬреЛрдбрд╝рдирд╛
рдирд┐рдореНрди рдирд┐рд░реНрднрд░рддрд╛рдПрдБ build.gradle рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВред implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
рдореЗрдирд┐рдлреЗрд╕реНрдЯ рдореЗрдВ рдЗрдВрдЯрд░рдиреЗрдЯ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдЬреЛрдбрд╝реЗрдВред <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.vogella.android.stackoverflow"> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
8.6ред рдПрдХ рдПрдкреАрдЖрдИ рдФрд░ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдПрдбрд╛рдкреНрдЯрд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдирд╛
Stackoverflow API JSON рдСрдмреНрдЬреЗрдХреНрдЯ рдирд╛рдо рдХреА рд╡рд╕реНрддреБрдУрдВ рдореЗрдВ рдЙрддреНрддрд░ рдпрд╛ рдкреНрд░рд╢реНрди рд▓рдкреЗрдЯрддрд╛ рд╣реИред рдЗрд╕реЗ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП, рдирд┐рдореНрди рдбреЗрдЯрд╛ рд╡рд░реНрдЧ рдмрдирд╛рдПрдБ, рдЬрд┐рдиреНрд╣реЗрдВ ListWrapper рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред Stackoverflow рддрддреНрд╡реЛрдВ рдХреЗ рдЖрд╡рд░рдг рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдпрд╣ рд╡рд░реНрдЧ рдПрдХ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдкреИрд░рд╛рдореАрдЯрд░ рд▓реЗрддрд╛ рд╣реИ рдФрд░ рдмрд╕ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреА рд╡рд╕реНрддреБрдУрдВ рдХреА рд╕реВрдЪреА рдкреИрдХ рдХрд░рддрд╛ рд╣реИред package com.vogella.android.stackoverflow; import java.util.List; public class ListWrapper<T> { List<T> items; }
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд▓рд┐рдП REST API рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВред package com.vogella.android.stackoverflow; import java.util.List; import okhttp3.ResponseBody; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Path; import retrofit2.Call; public interface StackOverflowAPI { String BASE_URL = "https://api.stackexchange.com/"; @GET("/2.2/questions?order=desc&sort=votes&site=stackoverflow&tagged=android&filter=withbody") Call<ListWrapper<Question>> getQuestions(); @GET("/2.2/questions/{id}/answers?order=desc&sort=votes&site=stackoverflow") Call<ListWrapper<Answer>> getAnswersForQuestion(@Path("id") String questionId); }
8.7ред рдЧрддрд┐рд╡рд┐рдзрд┐ рд╕реЗрдЯрд┐рдВрдЧ
рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ MainActivity рдХреЛрдб рдмрджрд▓реЗрдВред package com.vogella.android.stackoverflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.ArrayList; import java.util.List; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends Activity implements View.OnClickListener { private StackOverflowAPI stackoverflowAPI; private String token; private Button authenticateButton; private Spinner questionsSpinner; private RecyclerView recyclerView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); questionsSpinner = (Spinner) findViewById(R.id.questions_spinner); questionsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { Question question = (Question) parent.getAdapter().getItem(position); stackoverflowAPI.getAnswersForQuestion(question.questionId).enqueue(answersCallback); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); authenticateButton = (Button) findViewById(R.id.authenticate_button); recyclerView = (RecyclerView) findViewById(R.id.list); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); createStackoverflowAPI(); stackoverflowAPI.getQuestions().enqueue(questionsCallback); } @Override protected void onResume() { super.onResume(); if (token != null) { authenticateButton.setEnabled(false); } } private void createStackoverflowAPI() { Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(StackOverflowAPI.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); stackoverflowAPI = retrofit.create(StackOverflowAPI.class); } @Override public void onClick(View v) { switch (v.getId()) { case android.R.id.text1: if (token != null) {
8.8ред рд╡реИрдХрд▓реНрдкрд┐рдХ: рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рдЫрд╡рд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛
рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рдЫрд╡рд┐ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреБрдирд░рд╛рд╡рд░реНрддреА рджреГрд╢реНрдп рдореЗрдВ рд▓рд╛рдЗрдиреЛрдВ рдХреЗ рд▓реЗрдЖрдЙрдЯ рдХреЛ рдмрджрд▓реЗрдВред рдкреНрд░рд╢реНрди рдХрд╛ рдЙрддреНрддрд░ рджреЗрдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХрд╛ рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рдЪрд┐рддреНрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рдбреЗрдЯрд╛ рдореЙрдбрд▓ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░реЗрдВред рд▓реЗрдЖрдЙрдЯ рд▓рд╛рдЗрдиреЛрдВ рдореЗрдВ рдПрдХ ImageView рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдЫрд╡рд┐ рдХреЛ рд▓реЛрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЧреНрд▓рд╛рдЗрдб рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред8.9ред рд╡реИрдХрд▓реНрдкрд┐рдХ: рд╕рдо рдФрд░ рд╡рд┐рд╖рдо рд░реЗрдЦрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рд▓реЗрдЖрдЙрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ
рд╕рдорд╛рди рдФрд░ рд╡рд┐рд╖рдо рд░реЗрдЦрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рд▓реЗрдЖрдЙрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдбреЗрдкреНрдЯрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдмрджрд▓реЗрдВредрдЗрд╕рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рдкреНрд░рдХрд╛рд░ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рд╡рд┐рднрд┐рдиреНрди рд▓реЗрдЖрдЙрдЯ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдПрдбреЗрдкреНрдЯрд░ рдореЗрдВ getItemViewType () рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред8.10ред рд╡реИрдХрд▓реНрдкрд┐рдХ: рдиреЗрдЯрд╡рд░реНрдХ рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд┐рдВрдЧ
рдпрджрд┐ рдЖрдк рдПрдХ рдиреЗрдЯрд╡рд░реНрдХ рд╡рд┐рдлрд▓рддрд╛ рдХрд╛ рдЕрдиреБрднрд╡ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдореБрдЦреНрдп рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдмрдЬрд╛рдп рдкреБрдирдГ рдЕрдиреБрд░реЛрдз рдмрдЯрди рджрд┐рдЦрд╛рдПрдВред9. рд╡реНрдпрд╛рдпрд╛рдо: Android рдкрд░ GitHub API рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рдпрд╣ рдЕрднреНрдпрд╛рд╕ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рд░рд┐рдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдВрдбреНрд░реЙрдЗрдб рдРрдк рдореЗрдВ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП рд╕рднреА GitHub рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЛ рдХреИрд╕реЗ рд╕реВрдЪреАрдмрджреНрдз рдХрд┐рдпрд╛ рдЬрд╛рдПред рдЖрдк рдбреНрд░реЙрдк-рдбрд╛рдЙрди рд╕реВрдЪреА рд╕реЗ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХрд╛ рдЪрдпрди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЪрдпрдирд┐рдд рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдЪрд░реНрдЪрд╛рдУрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВредрдлрд┐рд░ рдЖрдк рдЕрддрд┐рд░рд┐рдХреНрдд рдбреНрд░реЙрдк-рдбрд╛рдЙрди рдлрд╝реАрд▓реНрдб рд╕реЗ рдЪрд░реНрдЪрд╛ рдХрд╛ рдЪрдпрди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЙрд╕ рдкрд░ рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдкреЛрд╕реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рджрд░реНрдЬ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП DialogFragment рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛редрд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рдЬреАрдердм рдЦрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЗрд╕ рдЕрднреНрдпрд╛рд╕ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдЪреВрдВрдХрд┐ рдЗрд╕ рдЕрднреНрдпрд╛рд╕ рдХреЗ рджреМрд░рд╛рди RxJava2 рдХреЗ рд╕рд╛рде рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЗрд╕рд▓рд┐рдП RxJava2 рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдкрд░ рднреА рдзреНрдпрд╛рди рджреЗрдВ ред9.1ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗрдЯрдЕрдк
рдПрдХ рдПрдВрдбреНрд░реЙрдЗрдб рдРрдк рдмрдирд╛рдПрдВ, рдЬрд┐рд╕реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдЬреАрдердм рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред рд╢реАрд░реНрд╖ рд╕реНрддрд░ рдХреЗ рдкреИрдХреЗрдЬ рдХреЗ рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ com.vogella.android.retrofitgithub рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдФрд░ рдПрдХ рдЦрд╛рд▓реА рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ "рдкрд╢реНрдЪрдЧрд╛рдореА рд╕рдВрдЧрддрддрд╛" рдзреНрд╡рдЬ рдХреА рдЬрд╛рдБрдЪ рдХреА рдЧрдИ рд╣реИредRetrofit рдФрд░ RxJava2 CallAdapter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдмрд┐рд▓реНрдб.gradle рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдирд┐рдореНрди рдирд┐рд░реНрднрд░рддрд╛рдПрдБ рдЬреЛрдбрд╝реЗрдВ implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
рдореЗрдирд┐рдлреЗрд╕реНрдЯ рдореЗрдВ рдЗрдВрдЯрд░рдиреЗрдЯ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдЬреЛрдбрд╝реЗрдВред <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.vogella.android.retrofitgithub"> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="com.vogella.android.retrofitgithub.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
9.2ред рдПрдкреАрдЖрдИ рдкрд░рд┐рднрд╛рд╖рд╛
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рджреЛ рдбреЗрдЯрд╛ рд╡рд░реНрдЧ рдмрдирд╛рдПрдБ: GithubIssue рдФрд░ GithubRepoред package com.vogella.android.retrofitgithub; import com.google.gson.annotations.SerializedName; public class GithubIssue { String id; String title; String comments_url; @SerializedName("body") String comment; @Override public String toString() { return id + " - " + title; } }
package com.vogella.android.retrofitgithub; public class GithubRepo { String name; String owner; String url; @Override public String toString() { return(name + " " + url); } }
рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдЬрд╛рдирдХрд╛рд░реА рд╕реЗ, рдХреЗрд╡рд▓ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдирд╛рдо рдФрд░ URL рдХреЛ рдбреНрд░реЙрдк-рдбрд╛рдЙрди рд╕реВрдЪреА рдореЗрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рд╣рдо рд╕реНрд╡рд╛рдореА рдХреЛ рдбреЗрдЯрд╛ рд╡рд░реНрдЧ рдореЗрдВ рднреА рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдмрд╛рдж рдореЗрдВ рдЪрд░реНрдЪрд╛рдУрдВ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрд╡рд╛рдореА рдХреЗ рдирд╛рдо рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИредрд╣рдо рдбреНрд░реЙрдк-рдбрд╛рдЙрди рдлрд╝реАрд▓реНрдб рдореЗрдВ рдХреЗрд╡рд▓ рдЖрдИрдбреА рдФрд░ рдЪрд░реНрдЪрд╛ рдХрд╛ рд╢реАрд░реНрд╖рдХ рджрд┐рдЦрд╛рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЙрдирдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рд▓рд┐рдП рдПрдХ рдлрд╝реАрд▓реНрдб рдмрдирд╛рддреЗ рд╣реИрдВред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, Github рдХреА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЯрд┐рдкреНрдкрдгреА рдкреЛрд╕реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ URL рд╣реИ, рдЬреЛ рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ_url рдлрд╝реАрд▓реНрдб рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рд╣реИред рдмрд╛рдж рдореЗрдВ Github API рдкрд░ рдПрдХ рдирдИ рдЯрд┐рдкреНрдкрдгреА рдкреЛрд╕реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЯрд┐рдкреНрдкрдгреА рдирд╛рдордХ рдлрд╝реАрд▓реНрдб рдЬреЛрдбрд╝реЗрдВред рдЬреАрдареВрдм рдЖрдкреАрдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЯрд┐рдкреНрдкрдгреА рдХреА рд╕рд╛рдордЧреНрд░реА JSON рдЕрдиреБрд░реЛрдз рдореЗрдВ рдирд┐рдХрд╛рдп рдирд╛рдордХ рдлрд╝реАрд▓реНрдб рд╕реЗ рдмрдВрдзреА рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред рдЪреВрдВрдХрд┐ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ (рдбреА) рдЙрдирдХреЗ рдирд╛рдо рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рд╕рднреА рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЛ рдХреНрд░рдордмрджреНрдз рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдЪреВрдВрдХрд┐ рд╣рдо рд╢рд░реАрд░ рдХреЛ рдЕрдкрдиреЗ рдЬреАрдереНрдпреВрдмрдЖрдИрд╕рд╛рдЗрдбреНрд╕ рд╡рд░реНрдЧ рдореЗрдВ рдлрд╝реАрд▓реНрдб рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдо @SerializedName рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕ рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рд╕рд╛рде, рд╣рдо рдЙрд╕ рдирд╛рдо рдХреЛ рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рд╕рдХреЗ рд╕рд╛рде рдХреНрд╖реЗрддреНрд░ JSON рдХреЗ рд▓рд┐рдП рдЕрдиреБрдХреНрд░рдорд┐рдд (рдбреА) рд╣реИредрджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, GithubRepo рд╡рд░реНрдЧ рднрдВрдбрд╛рд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдЬрд╛рдирдХрд╛рд░реА рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИред рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдпрд╣рд╛рдВ рджреЗрдЦ рд░рд╣реЗ рд╣реИрдВрд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХрд╛ рдорд╛рд▓рд┐рдХ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдПрдХ рдЕрд▓рдЧ JSON рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИ, рдФрд░ рдЗрд╕рд▓рд┐рдП рдЖрдорддреМрд░ рдкрд░ рд╕реАрд░реАрдЬрд╝ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рдЬрд╛рд╡рд╛ рдХреНрд▓рд╛рд╕ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рд░реЗрдЯреНрд░реЛрдлрд╝рд┐рдЯ рдЖрдкрдХреЛ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рдХрд╛рд░ рдХреЗ рдбреАрд░рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП JSONDeserializer рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рд╣рд░ рдмрд╛рд░ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдкреНрд░рдХрд╛рд░ рдХреЗ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ deserialized рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЗрд╕ рдХрд╕реНрдЯрдо deserializer рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИредрдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдирд┐рдореНрди GithubRepoDeserialzer рд╡рд░реНрдЧ рдЬреЛрдбрд╝реЗрдВред package com.vogella.android.retrofitgithub; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import java.lang.reflect.Type; public class GithubRepoDeserializer implements JsonDeserializer<GithubRepo> { @Override public GithubRepo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { GithubRepo githubRepo = new GithubRepo(); JsonObject repoJsonObject = json.getAsJsonObject(); githubRepo.name = repoJsonObject.get("name").getAsString(); githubRepo.url = repoJsonObject.get("url").getAsString(); JsonElement ownerJsonElement = repoJsonObject.get("owner"); JsonObject ownerJsonObject = ownerJsonElement.getAsJsonObject(); githubRepo.owner = ownerJsonObject.get("login").getAsString(); return githubRepo; } }
рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд▓рд┐рдП REST API рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВ: package com.vogella.android.retrofitgithub; import java.util.List; import io.reactivex.Single; import okhttp3.ResponseBody; import retrofit2.http.Body; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Path; import retrofit2.http.Url; public interface GithubAPI { String ENDPOINT = "https://api.github.com"; @GET("user/repos?per_page=100") Single<List<GithubRepo>> getRepos(); @GET("/repos/{owner}/{repo}/issues") Single<List<GithubIssue>> getIssues(@Path("owner") String owner, @Path("repo") String repository); @POST Single<ResponseBody> postComment(@Url String url, @Body GithubIssue issue); }
рдЖрдкрдХреЗ рдкрд╛рд╕ @ Url рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рдкреНрд░рд╢реНрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдЗрд╕ рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рд╣рдо рдЗрд╕ рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП URL рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рд╣рдореЗрдВ рдЧрддрд┐рд╢реАрд▓ рд░реВрдк рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП URL рдмрджрд▓рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рд╣рдореЗрдВ GithubIssue рд╡рд░реНрдЧ рдХреЗ comments_url рдлрд╝реАрд▓реНрдб рдХреЗ рд▓рд┐рдП рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред@ рдкрде рдПрдиреЛрдЯреЗрд╢рди рдЕрдиреБрд░реЛрдз URL рдореЗрдВ рд╕рдВрдмрдВрдзрд┐рдд рд╡реИрд░рд┐рдПрдмрд▓ (рдШреБрдВрдШрд░рд╛рд▓реЗ рдмреНрд░реЗрд╕) рдХреЗ рдкреИрд░рд╛рдореАрдЯрд░ рдорд╛рди рдХреЛ рдмрд╛рдВрдзрддреЗ рд╣реИрдВред рдорд╛рд▓рд┐рдХ рдХреЛ рдФрд░ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЗ рдирд╛рдо рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рд╡рд┐рдЪрд╛рд░-рд╡рд┐рдорд░реНрд╢ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред9.3ред рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдбрд╛рдпрд▓реЙрдЧ рдмреЙрдХреНрд╕ рдмрдирд╛рдПрдВ
рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдЕрдкрдиреЗ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓реНрд╕ рдХреЛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП, DialogFragment рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓рдбрд╛рдпрд▓реЙрдЧ рдирд╛рдордХ рдирд┐рдореНрди рд╡рд░реНрдЧ рдмрдирд╛рдПрдВ, рдФрд░ рд╕рдВрд╕рд╛рдзрди рд▓реЗрдЖрдЙрдЯ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рдбрд╛рдпрд▓реЙрдЧ_рд╕реАрдбреЗрдВрд╢рд┐рдпрд▓рдПрдХреНрд╕рдПрдХреНрд╕рдПрдордПрд▓ рдирд╛рдордХ рдПрдХ рд▓реЗрдЖрдЙрдЯ рдлрд╝рд╛рдЗрд▓ рднреА рдЬреЛрдбрд╝реЗрдВредрдкрд░рд┐рдгрд╛рдо рдирд┐рдореНрди рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдХреА рддрд░рд╣ рдХреБрдЫ рджрд┐рдЦрдирд╛ рдЪрд╛рд╣рд┐рдПред
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginTop="16dp" android:text="Fill you credentials here" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="0.3" android:text="Username:" /> <EditText android:id="@+id/username_edittext" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="0.7" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="0.3" android:text="Password" /> <EditText android:id="@+id/password_edittext" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="0.7" android:inputType="textPassword" /> </LinearLayout> </LinearLayout>
package com.vogella.android.retrofitgithub; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; import android.view.View; import android.widget.EditText; import okhttp3.Credentials; public class CredentialsDialog extends DialogFragment { EditText usernameEditText; EditText passwordEditText; ICredentialsDialogListener listener; public interface ICredentialsDialogListener { void onDialogPositiveClick(String username, String password); } @Override public void onAttach(Context context) { super.onAttach(context); if (getActivity() instanceof ICredentialsDialogListener) { listener = (ICredentialsDialogListener) getActivity(); } } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_credentials, null); usernameEditText = (EditText) view.findViewById(R.id.username_edittext); passwordEditText = (EditText) view.findViewById(R.id.password_edittext); usernameEditText.setText(getArguments().getString("username")); passwordEditText.setText(getArguments().getString("password")); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) .setView(view) .setTitle("Credentials") .setNegativeButton("Cancel", null) .setPositiveButton("Continue", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (listener != null) { listener.onDialogPositiveClick(usernameEditText.getText().toString(), passwordEditText.getText().toString()); } } }); return builder.create(); } }
9.4ред рдЧрддрд┐рд╡рд┐рдзрд┐ рдмрдирд╛рдПрдБ
рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╕реЗ рдЧрддрд┐рд╡рд┐рдзрд┐_main.xml рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВред <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.vogella.android.retrofitgithub.MainActivity"> <android.support.v7.widget.Toolbar android:id="@+id/my_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <Spinner android:id="@+id/repositories_spinner" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Spinner android:id="@+id/issues_spinner" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/repositories_spinner" /> <EditText android:id="@+id/comment_edittext" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/issues_spinner" android:enabled="false" android:hint="Your comment" android:imeOptions="actionDone" android:inputType="text" android:maxLines="1" /> <Button android:id="@+id/loadRepos_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:enabled="false" android:gravity="center" android:onClick="onClick" android:text="Load user repositories" /> <Button android:id="@+id/send_comment_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/loadRepos_button" android:enabled="false" android:onClick="onClick" android:text="Send comment" /> </RelativeLayout> </LinearLayout>
рджреЛ рдмрдЯрди (рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдиреЗ рдФрд░ рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдВ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП), рджреЛ рд╕реНрдкрд┐рдирд░ (рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдФрд░ рдЪрд░реНрдЪрд╛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбреНрд░реЙрдк-рдбрд╛рдЙрди рдлрд╝реАрд▓реНрдб) рдФрд░ рдПрдбрд┐рдЯ рдЯреЗрдХреНрд╕реНрдЯ (рдЯрд┐рдкреНрдкрдгреА рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП)ред рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓рдбрд╛рдпрд▓реЙрдЧ рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдПрдВрдбреНрд░реЙрдЗрдб рдЯреВрд▓рдмрд╛рд░ рдкрд░ рдореЗрдиреВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдЗрд╕реЗ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдореЗрдиреВ рд░рд┐рд╕реЛрд░реНрд╕ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ menu_main.xml рдирд╛рдордХ рдПрдХ рдореЗрдиреВ xml рдлрд╝рд╛рдЗрд▓ рдЬреЛрдбрд╝реЗрдВ (рдпрджрд┐ рдпрд╣ рдореМрдЬреВрдж рдирд╣реАрдВ рд╣реИ рддреЛ рдПрдХ рдлрд╝реЛрд▓реНрдбрд░ рдмрдирд╛рдПрдВ)ред <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_credentials" android:title="Credentials"/> </menu>
рдЪреВрдВрдХрд┐ рд╣рдо рдЯреВрд▓рдмрд╛рд░ рд╡рд┐рдЬреЗрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдмрд╛рд░ рдХреЛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдЕрдХреНрд╖рдо рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдиреАрдЪреЗ рджрд┐рдЦрд╛рдП рдЧрдП рдЕрдиреБрд╕рд╛рд░ xml рд╢реИрд▓реА рдлрд╝рд╛рдЗрд▓ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВред <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>
рдЕрдкрдиреА рдЧрддрд┐рд╡рд┐рдзрд┐ рдХреЛрдб рдХреЛ рдирд┐рдореНрди рдореЗрдВ рдмрджрд▓реЗрдВред package com.vogella.android.retrofitgithub; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.IOException; import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; import okhttp3.Credentials; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity implements CredentialsDialog.ICredentialsDialogListener { GithubAPI githubAPI; String username; String password; Spinner repositoriesSpinner; Spinner issuesSpinner; EditText commentEditText; Button sendButton; Button loadReposButtons; private CompositeDisposable compositeDisposable = new CompositeDisposable(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.my_toolbar); setSupportActionBar(toolbar); sendButton = (Button) findViewById(R.id.send_comment_button); repositoriesSpinner = (Spinner) findViewById(R.id.repositories_spinner); repositoriesSpinner.setEnabled(false); repositoriesSpinner.setAdapter(new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, new String[]{"No repositories available"})); repositoriesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (parent.getSelectedItem() instanceof GithubRepo) { GithubRepo githubRepo = (GithubRepo) parent.getSelectedItem(); compositeDisposable.add(githubAPI.getIssues(githubRepo.owner, githubRepo.name) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(getIssuesObserver())); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); issuesSpinner = (Spinner) findViewById(R.id.issues_spinner); issuesSpinner.setEnabled(false); issuesSpinner.setAdapter(new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, new String[]{"Please select repository"})); commentEditText = (EditText) findViewById(R.id.comment_edittext); loadReposButtons = (Button) findViewById(R.id.loadRepos_button); createGithubAPI(); } @Override protected void onStop() { super.onStop(); if (compositeDisposable != null && !compositeDisposable.isDisposed()) { compositeDisposable.dispose(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_credentials: showCredentialsDialog(); return true; } return super.onOptionsItemSelected(item); } private void showCredentialsDialog() { CredentialsDialog dialog = new CredentialsDialog(); Bundle arguments = new Bundle(); arguments.putString("username", username); arguments.putString("password", password); dialog.setArguments(arguments); dialog.show(getSupportFragmentManager(), "credentialsDialog"); } private void createGithubAPI() { Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .registerTypeAdapter(GithubRepo.class, new GithubRepoDeserializer()) .create(); OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request.Builder builder = originalRequest.newBuilder().header("Authorization", Credentials.basic(username, password)); Request newRequest = builder.build(); return chain.proceed(newRequest); } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(GithubAPI.ENDPOINT) .client(okHttpClient) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); githubAPI = retrofit.create(GithubAPI.class); } public void onClick(View view) { switch (view.getId()) { case R.id.loadRepos_button: compositeDisposable.add(githubAPI.getRepos() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(getRepositoriesObserver())); break; case R.id.send_comment_button: String newComment = commentEditText.getText().toString(); if (!newComment.isEmpty()) { GithubIssue selectedItem = (GithubIssue) issuesSpinner.getSelectedItem(); selectedItem.comment = newComment; compositeDisposable.add(githubAPI.postComment(selectedItem.comments_url, selectedItem) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(getCommentObserver())); } else { Toast.makeText(MainActivity.this, "Please enter a comment", Toast.LENGTH_LONG).show(); } break; } } private DisposableSingleObserver<List<GithubRepo>> getRepositoriesObserver() { return new DisposableSingleObserver<List<GithubRepo>>() { @Override public void onSuccess(List<GithubRepo> value) { if (!value.isEmpty()) { ArrayAdapter<GithubRepo> spinnerAdapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, value); repositoriesSpinner.setAdapter(spinnerAdapter); repositoriesSpinner.setEnabled(true); } else { ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, new String[]{"User has no repositories"}); repositoriesSpinner.setAdapter(spinnerAdapter); repositoriesSpinner.setEnabled(false); } } @Override public void onError(Throwable e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "Can not load repositories", Toast.LENGTH_SHORT).show(); } }; } private DisposableSingleObserver<List<GithubIssue>> getIssuesObserver() { return new DisposableSingleObserver<List<GithubIssue>>() { @Override public void onSuccess(List<GithubIssue> value) { if (!value.isEmpty()) { ArrayAdapter<GithubIssue> spinnerAdapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, value); issuesSpinner.setEnabled(true); commentEditText.setEnabled(true); sendButton.setEnabled(true); issuesSpinner.setAdapter(spinnerAdapter); } else { ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, new String[]{"Repository has no issues"}); issuesSpinner.setEnabled(false); commentEditText.setEnabled(false); sendButton.setEnabled(false); issuesSpinner.setAdapter(spinnerAdapter); } } @Override public void onError(Throwable e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "Can not load issues", Toast.LENGTH_SHORT).show(); } }; } private DisposableSingleObserver<ResponseBody> getCommentObserver() { return new DisposableSingleObserver<ResponseBody>() { @Override public void onSuccess(ResponseBody value) { commentEditText.setText(""); Toast.makeText(MainActivity.this, "Comment created", Toast.LENGTH_LONG).show(); } @Override public void onError(Throwable e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "Can not create comment", Toast.LENGTH_SHORT).show(); } }; } @Override public void onDialogPositiveClick(String username, String password) { this.username = username; this.password = password; loadReposButtons.setEnabled(true); } }
рдпрд╣рд╛рдВ рд╣рдордиреЗ GsonBuilder рдореЗрдВ рдЯрд╛рдЗрдк рдПрдбреЗрдкреНрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рдирд┐рд░реНрдорд┐рдд GithubRepoDeserializer рдЬреЛрдбрд╝рд╛ред рдкреНрд░рддреНрдпреЗрдХ рдХреЙрд▓ рдХреЗ рд▓рд┐рдП рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП, рдПрдХ рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХреЛ OkHttpClient рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ рдерд╛ ред RxJava2 рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП API рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП, рд╣рдордиреЗ рдЕрдкрдиреЗ рдХреНрд▓рд╛рдЗрдВрдЯ рдореЗрдВ RxJava2 CallAdapter рдХреЛ рдЬреЛрдбрд╝рд╛ред10. рд╡реНрдпрд╛рдпрд╛рдо: рдПрдВрдбреНрд░реЙрдЗрдб рдкрд░ рдЯреНрд╡рд┐рдЯрд░ рд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬрд╛рдирдХрд╛рд░реА рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП OAuth рдХреЗ рд╕рд╛рде рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рдпрд╣ рдЕрднреНрдпрд╛рд╕ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рдПрдВрдбреНрд░реЙрдЗрдб рдкрд░ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЯреНрд╡рд┐рдЯрд░ рддрдХ рдХреИрд╕реЗ рдкрд╣реБрдВрдЪрд╛ рдЬрд╛рдПред рд╣рдо рдПрдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓рд┐рдЦреЗрдВрдЧреЗ рдЬреЛ рдкреНрд░рджрд╛рди рдХрд┐рдП рдЧрдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбреЗрдЯрд╛ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕ рдЕрднреНрдпрд╛рд╕ рдореЗрдВ, рд╣рдо рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдХреЗрд╡рд▓ OAuth 2 рдХреЗ рд╕рд╛рде рдЯреНрд╡рд┐рдЯрд░ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ ред рдЗрд╕ рдЕрднреНрдпрд╛рд╕ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЗ рдкрд╛рд╕ рдЯреНрд╡рд┐рдЯрд░ рдЕрдХрд╛рдЙрдВрдЯ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдкрдХреЛ рдЕрдкрдиреЗ рдЙрдкрднреЛрдХреНрддрд╛ рдХреБрдВрдЬреА рдФрд░ рдЙрдкрднреЛрдХреНрддрд╛ рд░рд╣рд╕реНрдп рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЯреНрд╡рд┐рдЯрд░ рдРрдк рдкрд░ рдЬрд╛рдиреЗ рдФрд░ рдПрдХ рдирдпрд╛ рдРрдк рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ ред рд╣рдореЗрдВ рдЕрдкрдиреЗ OAuth рдЯреЛрдХрди рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╛рдж рдореЗрдВ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред10.1ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗрдЯрдЕрдк
рд░рд┐рдЯреНрд░реЛрдлрд┐рдЯ рдЯреНрд╡рд┐рдЯрд░ рдирд╛рдордХ рдПрдХ рдПрдВрдбреНрд░реЙрдЗрдб рдРрдк рдмрдирд╛рдПрдВред рдПрдХ рд╢реАрд░реНрд╖-рд╕реНрддрд░реАрдп рдкреИрдХреЗрдЬ рдХреЗ рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ com.vogella.android.retrofittwitter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВредRetrofit рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдмрд┐рд▓реНрдб.рдЧреНрд░реЗрдб рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдирд┐рдореНрди рдкрдВрдХреНрддрд┐рдпрд╛рдБ рдЬреЛрдбрд╝реЗрдВ implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
рдореЗрдирд┐рдлреЗрд╕реНрдЯ рдореЗрдВ рдЗрдВрдЯрд░рдиреЗрдЯ рдПрдХреНрд╕реЗрд╕ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдЬреЛрдбрд╝реЗрдВред <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.vogella.android.retrofittwitter"> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
10.2ред рдПрдкреАрдЖрдИ рдкрд░рд┐рднрд╛рд╖рд╛
OAuthToken рдФрд░ UserDetails рдирд╛рдордХ рджреЛ рдбреЗрдЯрд╛ рдХрдХреНрд╖рд╛рдПрдВ рдмрдирд╛рдПрдВред package com.vogella.android.retrofittwitter; import com.google.gson.annotations.SerializedName; public class OAuthToken { @SerializedName("access_token") private String accessToken; @SerializedName("token_type") private String tokenType; public String getAccessToken() { return accessToken; } public String getTokenType() { return tokenType; } public String getAuthorization() { return getTokenType() + " " + getAccessToken(); } }
package com.vogella.android.retrofittwitter; public class UserDetails { private String name; private String location; private String description; private String url; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
OAuthToken рд╡рд░реНрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдмрд┐рдпрд░ рдХреЛ рд╕реНрдЯреЛрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬрд┐рд╕реЗ рд╣рдо рдЯреНрд╡рд┐рдЯрд░ рд╕реЗ рдЕрдиреБрд░реЛрдз рдХрд░рддреЗ рд╣реИрдВ, рдЕрдкрдиреА рдХреБрдВрдЬреА рдФрд░ рдЧреБрдкреНрдд рдХреЗ рд╕рд╛рдеред рд╣рдо Retrofit рд╕реЗ (de) рд╕реАрд░рд┐рдпрд▓рд╛рдЗрдЬрд╝ рдлрд╝реАрд▓реНрдб рдирд╛рдо рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП @ SerializedName рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВредрдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬрд╛рдирдХрд╛рд░реА рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддреЗ рд╕рдордп UserDetails рд╡рд░реНрдЧ рдХреЗрд╡рд▓ рдЯреНрд╡рд┐рдЯрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕реЗ рдХрдИ рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЛ рдмрдЪрд╛рддрд╛ рд╣реИред рд╣рдо рд╕рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбреЗрдЯрд╛ рдирд╣реАрдВ рджрд┐рдЦрд╛рддреЗ рд╣реИрдВ рдЬреЛ рдХреЗрд╡рд▓ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдирд┐рд╣рд┐рдд рдерд╛, рдХреЗрд╡рд▓ рдирд╛рдо, рд╕реНрдерд╛рди, URL рдФрд░ рд╡рд┐рд╡рд░рдгредрдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреЗ рд▓рд┐рдП REST API рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВ: package com.vogella.android.retrofittwitter; import retrofit2.Call; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Query; public interface TwitterApi { String BASE_URL = "https://api.twitter.com/"; @FormUrlEncoded @POST("oauth2/token") Call<OAuthToken> postCredentials(@Field("grant_type") String grantType); @GET("/1.1/users/show.json") Call<UserDetails> getUserDetails(@Query("screen_name") String name); }
10.3ред рдЧрддрд┐рд╡рд┐рдзрд┐ рдмрдирд╛рдПрдБ
Activity_main.xml рдлрд╝рд╛рдЗрд▓ рдФрд░ рд╕рдВрдмрдВрдзрд┐рдд MainActivity рд╡рд░реНрдЧ рдХреЛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВ: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.vogella.android.retrofittwitter.MainActivity"> <LinearLayout android:id="@+id/username_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:orientation="horizontal"> <TextView android:id="@+id/username_textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:enabled="false" android:gravity="center_vertical" android:text="Username:" /> <EditText android:id="@+id/username_edittext" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:enabled="false" android:gravity="center" android:imeOptions="actionDone" android:inputType="text" android:maxLines="1" /> </LinearLayout> <Button android:id="@+id/request_token_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:onClick="onClick" android:text="Request token" /> <Button android:id="@+id/request_user_details_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/request_token_button" android:enabled="false" android:onClick="onClick" android:text="Request user details" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/request_user_details_button" android:layout_below="@id/username_container" android:gravity="center" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="50dp"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="Name:" /> <TextView android:id="@+id/name_textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="---" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="50dp"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="Location:" /> <TextView android:id="@+id/location_textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="---" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="50dp"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="Url:" /> <TextView android:id="@+id/url_textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="---" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="50dp"> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="Description:" /> <TextView android:id="@+id/description_textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="---" /> </LinearLayout> </LinearLayout> </RelativeLayout>
package com.vogella.android.retrofittwitter; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import java.io.IOException; import okhttp3.Credentials; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity { private String credentials = Credentials.basic("aConsumerKey", "aSecret"); Button requestTokenButton; Button requestUserDetailsButton; EditText usernameEditText; TextView usernameTextView; TextView nameTextView; TextView locationTextView; TextView urlTextView; TextView descriptionTextView; TwitterApi twitterApi; OAuthToken token; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestTokenButton = (Button) findViewById(R.id.request_token_button); requestUserDetailsButton = (Button) findViewById(R.id.request_user_details_button); usernameEditText = (EditText) findViewById(R.id.username_edittext); usernameTextView = (TextView) findViewById(R.id.username_textview); nameTextView = (TextView) findViewById(R.id.name_textview); locationTextView = (TextView) findViewById(R.id.location_textview); urlTextView = (TextView) findViewById(R.id.url_textview); descriptionTextView = (TextView) findViewById(R.id.description_textview); createTwitterApi(); } private void createTwitterApi() { OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request.Builder builder = originalRequest.newBuilder().header("Authorization", token != null ? token.getAuthorization() : credentials); Request newRequest = builder.build(); return chain.proceed(newRequest); } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(TwitterApi.BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); twitterApi = retrofit.create(TwitterApi.class); } public void onClick(View view) { switch (view.getId()) { case R.id.request_token_button: twitterApi.postCredentials("client_credentials").enqueue(tokenCallback); break; case R.id.request_user_details_button: String editTextInput = usernameEditText.getText().toString(); if (!editTextInput.isEmpty()) { twitterApi.getUserDetails(editTextInput).enqueue(userDetailsCallback); } else { Toast.makeText(this, "Please provide a username", Toast.LENGTH_LONG).show(); } break; } } Callback<OAuthToken> tokenCallback = new Callback<OAuthToken>() { @Override public void onResponse(Call<OAuthToken> call, Response<OAuthToken> response) { if (response.isSuccessful()) { requestTokenButton.setEnabled(false); requestUserDetailsButton.setEnabled(true); usernameTextView.setEnabled(true); usernameEditText.setEnabled(true); token = response.body(); } else { Toast.makeText(MainActivity.this, "Failure while requesting token", Toast.LENGTH_LONG).show(); Log.d("RequestTokenCallback", "Code: " + response.code() + "Message: " + response.message()); } } @Override public void onFailure(Call<OAuthToken> call, Throwable t) { t.printStackTrace(); } }; Callback<UserDetails> userDetailsCallback = new Callback<UserDetails>() { @Override public void onResponse(Call<UserDetails> call, Response<UserDetails> response) { if (response.isSuccessful()) { UserDetails userDetails = response.body(); nameTextView.setText(userDetails.getName() == null ? "no value" : userDetails.getName()); locationTextView.setText(userDetails.getLocation() == null ? "no value" : userDetails.getLocation()); urlTextView.setText(userDetails.getUrl() == null ? "no value" : userDetails.getUrl()); descriptionTextView.setText(userDetails.getDescription().isEmpty() ? "no value" : userDetails.getDescription()); } else { Toast.makeText(MainActivity.this, "Failure while requesting user details", Toast.LENGTH_LONG).show(); Log.d("UserDetailsCallback", "Code: " + response.code() + "Message: " + response.message()); } } @Override public void onFailure(Call<UserDetails> call, Throwable t) { t.printStackTrace(); } }; }
рдмрджрд▓реЗрдВ anConsumerKey рдФрд░ рдЙрдкрднреЛрдХреНрддрд╛ рдХреБрдВрдЬреА рдФрд░ рдЧреБрдкреНрдд рдЯреНрд╡рд┐рдЯрд░ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХреЗ рд╕рд╛рдередрдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдкрд░ рднреА рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВ, рдЬрд┐рд╕реЗ рд╣рдо рдЕрдкрдиреЗ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рдореЗрдВ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред рдХреНрдпреЛрдВрдХрд┐ рд╣рдо OAuth рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рд╣рдорд╛рд░реЗ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓реНрд╕ рдкреНрд░рддреНрдпреЗрдХ рдХреЙрд▓ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ рд╣реИрдВред рдкреЛрд╕реНрдЯрдЧреНрд░реИрдбрд┐рдПрдВрдЯреНрд╕ рд╡рд┐рдзрд┐ рдХреЛ рдореВрд▓ рдЯреНрд╡рд┐рдЯрд░ рд╕реНрдХреАрдорд╛ рдореЗрдВ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓реНрд╕ (рдЙрдкрднреЛрдХреНрддрд╛ рдХреБрдВрдЬреА рдФрд░ рдЧреБрдкреНрдд) рдкреЛрд╕реНрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдирддреАрдЬрддрди, рдпрд╣ рдХреЙрд▓ рдПрдХ рд╡рд╛рд╣рдХ рдЯреЛрдХрди рд▓реМрдЯрд╛рддрд╛ рд╣реИ, рдЬреЛ рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рд╣рдорд╛рд░реЗ OAuthToken рд╡рд░реНрдЧ рдореЗрдВ deserializes, рдЬреЛ рддрдм рдЯреЛрдХрди рдлрд╝реАрд▓реНрдб рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рд╣реЛрддрд╛ рд╣реИред рдЕрдиреНрдп рдХреЛрдИ рднреА рдЕрдиреБрд░реЛрдз (рдФрд░ рдЕрдм) рдЗрд╕ рдЯреЛрдХрди рдХреЛ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рд▓рд┐рдП рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬрд╛рдирдХрд╛рд░реА рднреА рдЕрдиреБрд░реЛрдз рдХреА рд╣реИред11. рд░реЗрдЯреНрд░реЛрдлрд┐рдЯ рд░рд┐рд╕реЛрд░реНрд╕
рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдЙрдкрднреЛрдХреНрддрд╛ рдХреЗ рд╕рд╛рде рдПрдкреАрдЖрдИ рдХрд╛ рдкреБрд░рд╛рдирд╛ рд╡рд╛рдкрд╕
рдХреЗ рд╕рд╛рде рд╡рд┐рднрд╛рдЧ рдкреБрд░рд╛рдирд╛ рд╡рд╛рдкрд╕ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмреНрд▓реЙрдЧ рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ
рдкреБрд░рд╛рдирд╛ рд╡рд╛рдкрд╕ рдХреЗ рд╕рд╛рде рдПрдкреАрдЖрдИ рдХрд╛ рд╕реЗрд╡рди