要直接在 HTTP 级别访问 Google Books API 或任何其他 REST API,如果您愿意编写异步代码,可以使用 Volley,或者对于更简单的同步请求,可以使用 OkHttp。还有Android Asynchronous Http Client。
但更好的是,您可以使用 Feign 或 Retrofit 抽象出 HTTP 级别的实现细节,并在自动生成的实现之上提供流畅的类型安全 API。 Retrofit 是 the most used network library in Android,但 Feign 在更广泛的 Java 生态系统中使用得更多。
这是一个使用 Feign for Google Books API 的示例,Retrofit 非常相似。
API 接口,实现由 Feign 自动生成:
public interface GoogleBooksApi {
@RequestLine("GET /books/v1/volumes")
Results findBookByISBN(@QueryMap Map<String, Object> queryParameters);
}
API 客户端代码:
public class BookLookupService {
public Book fetchBookByISBN(String isbn) throws BookLookupException {
final GoogleBooksApi googleBooksApi = connect();
final Map<String, Object> queryParameters = new HashMap<>();
queryParameters.put("q", "isbn:" + isbn);
final Results apiResponse = googleBooksApi.findBookByISBN(queryParameters);
if (apiResponse == null || apiResponse.getTotalItems() < 1) {
throw new BookLookupException("No books found for ISBN " + isbn);
}
final List<Result> results = apiResponse.getItems();
if (results == null || results.size() < 1) {
throw new BookLookupException("Invalid items list for ISBN " + isbn);
}
final Book book = results.get(0).getBook();
return book;
}
private static GoogleBooksApi connect() {
return Feign.builder()
.decoder(new GsonDecoder())
.logger(new Logger.ErrorLogger())
.logLevel(Logger.Level.BASIC)
.target(GoogleBooksApi.class, "https://www.googleapis.com");
}
}
实体模拟 API 响应结构:
public class Results {
int totalItems;
List<Result> items;
public int getTotalItems() {
return totalItems;
}
public List<Result> getItems() {
return items;
}
}
public class Result {
// the JSON field is named volumeInfo
Book volumeInfo;
public Book getBook() {
return volumeInfo;
}
}
public class Book {
private String title;
private List<String> authors;
public String getTitle() {
return title;
}
public List<String> getAuthors() {
return authors;
}
}
最后同样重要的是,测试:
@RunWith(AndroidJUnit4.class)
public class BookLookupServiceAndroidTest {
private BookLookupService bookLookupService = new BookLookupService();
@Test
public void whenMultipleLookupResultsThenReturnsFirst() throws Exception {
assertThat(bookLookupService.fetchBookByISBN("9780321356680").getTitle(),
is(equalTo("Effective Java, 2nd Edition")));
}
}
请注意,您需要将代码包装在AsyncTask 中以使其异步,因为主线程上不允许网络请求。 AsyncTask 应该更新 onPostExecute() 中的 UI。
这是一个例子:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
final Button fetchBookButton = (Button) findViewById(R.id.FetchBookButton);
fetchBookButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { new FetchBookTask().execute(getISBN()); }
});
}
private String getISBN() {
final EditText isbnField = (EditText) findViewById(R.id.BookIsbnField);
return isbnField.getText().toString();
}
private void showMessage(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
class FetchBookTask extends AsyncTask<String, Void, Book> {
@Override
protected Book doInBackground(String... params) {
final String isbn = params[0];
try {
return new BookLookupService().fetchBookByISBN(isbn);
} catch (Exception e) {
Log.e("fetchBookByISBN", e.toString());
return null;
}
}
@Override
protected void onPostExecute(Book book) {
if (book != null) {
showMessage("Got book: " + book.getTitle());
} else {
showMessage("Failed to fetch book");
}
}
}
}