最近工作用到 Retrofit,以前的知識有些忘記了,又去網上查,覺得挺麻煩的,就自己寫篇博客記錄一下,方便自己以後查看。這裏主要寫Retrofit的使用。
Retrofit的使用步驟。
1.添加Retrofit庫的依賴和網絡權限:
compile 'com.squareup.retrofit2:retrofit:2.2.0'// Retrofit庫
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
<uses-permission android:name="android.permission.INTERNET" />
2.創建 用於描述網絡請求 的接口
Retrofit將 Http請求 抽象成 Java接口:採用 註解 描述網絡請求參數 和配置網絡請求參數
3.創建Retrofit實例(這裏可以設置回調代碼運行的線程,默認回調代碼運行在主線程,可以自己設置運行在主線程或者子線程。)
4.將服務器返回的數據封裝成一個bean類,用於接收服務器返回的數據。
5.發送請求
請求分爲同步請求和異步請求
主要就分爲這幾部,下面來具體介紹:
1.添加Retrofit庫的依賴和網絡權限(只需要網絡權限,本文中用到了本地存儲下載的文件,所以加的存儲讀寫權限):
compile 'com.squareup.retrofit2:retrofit:2.2.0'// Retrofit庫
compile 'com.squareup.retrofit2:converter-gson:2.3.0'//返回的數據是json的,用到了gson解析
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2.創建 用於描述網絡請求 的接口,Retrofit將 Http請求 抽象成 Java接口:採用 註解 描述網絡請求參數 和配置網絡請求參數
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
/**
* 創建 用於描述網絡請求 的接口
* Retrofit將 Http請求 抽象成 Java接口:採用 註解 描述網絡請求參數 和配置網絡請求參數
*/
public interface ApiService {
//測試 get
@GET("ajax.php?a=fy&f=auto&t=auto&w=hello")
Call<ResponseInfo> testGet();
//測試 post
@POST("ajax.php?a=fy&f=auto&t=auto")
Call<ResponseInfo> testPost(@Query("w") String w);
//測試 下載文件
@Streaming
@GET
Call<ResponseBody> testDownload(@Url String fileUrl);
}
3.創建Retrofit實例(這裏可以設置回調代碼運行的線程,默認回調代碼運行在主線程,可以自己設置運行在主線程或者子線程。)
get 請求和 post請求 創建的 Retrofit實例(不指定回調運行的線程,默認運行在主線程)
// 創建Retrofit實例時需要通過Retrofit.Builder,並調用baseUrl方法設置URL。
//Retrofit2的baseUlr 必須以 /(斜線)結束,不然會拋出一個IllegalArgumentException
retrofit = new Retrofit.Builder()
.baseUrl("http://fy.iciba.com/") // 設置 網絡請求 Url
.addConverterFactory(GsonConverterFactory.create()) //設置使用Gson解析(記得加入依賴)
.build();
文件下載 的 Retrofit 實例 (指定回調運行的線程,指定運行在子線程中,因爲受到文件之後要做io流的操作)
Retrofit retrofitLoaddown = new Retrofit.Builder()
.baseUrl("http://imtt.dd.qq.com/")
//通過線程池獲取一個線程,指定callback在子線程中運行。
.callbackExecutor(Executors.newSingleThreadExecutor())
.build();
4.將服務器返回的數據封裝成一個bean類,用於接收服務器返回的數據。
/**
* 講服務器返回的數據封裝成一個bean類
*/
public class ResponseInfo {
public Content content;
public int status;
public ResponseInfo() {
}
public ResponseInfo(Content content, int status) {
this.content = content;
this.status = status;
}
@Override
public String toString() {
return "ResponseInfo{" +
"content=" + content +
", status=" + status +
'}';
}
class Content {
public String ph_am;
public String ph_am_mp3;
public String ph_en_mp3;
public String ph_tts_mp3;
public List<String> word_mean;
public Content() {
}
public Content(String ph_am, String ph_am_mp3, String ph_en_mp3, String ph_tts_mp3, List<String> word_mean) {
this.ph_am = ph_am;
this.ph_am_mp3 = ph_am_mp3;
this.ph_en_mp3 = ph_en_mp3;
this.ph_tts_mp3 = ph_tts_mp3;
this.word_mean = word_mean;
}
@Override
public String toString() {
return "Content{" +
"ph_am='" + ph_am + '\'' +
", ph_am_mp3='" + ph_am_mp3 + '\'' +
", ph_en_mp3='" + ph_en_mp3 + '\'' +
", ph_tts_mp3='" + ph_tts_mp3 + '\'' +
", word_mean=" + word_mean +
'}';
}
}
}
5.發送請求 。請求分爲同步請求和異步請求
同步請求的代碼
ApiService request = retrofit.create(ApiService.class);
Call<ResponseInfo> call = request.testGet();// 封裝請求
try {
Response<ResponseInfo> execute = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
同步請求比較簡單,我這裏主要講異步請求。
下面分別是get請求post請求和下載文件的代碼
public void testGet() {
ApiService request = retrofit.create(ApiService.class);
Call<ResponseInfo> call = request.testGet();// 封裝請求
try {
Response<ResponseInfo> execute = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
call.enqueue(new Callback<ResponseInfo>() {// 發送網絡請求(異步)
@Override
public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {//主線程中調用
ResponseInfo body = response.body();
Log.i("gao", "測試 get body = " + body + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "測試GET= " + body.content.word_mean.get(0), Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<ResponseInfo> call, Throwable t) {//主線程中調用
Log.i("gao", " testGet onFailure " + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, " testGet onFailure ", Toast.LENGTH_SHORT).show();
}
});
}
public void testPost(String string) {
ApiService request = retrofit.create(ApiService.class);
Call<ResponseInfo> call = request.testPost(string);// 封裝請求
call.enqueue(new Callback<ResponseInfo>() {// 發送網絡請求(異步)
@Override
public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {
ResponseInfo body = response.body();
Log.i("gao", "測試 post body = " + body + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "測試 post body = " + body, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<ResponseInfo> call, Throwable t) {
Log.i("gao", "testPost onFailure " + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "testPost onFailure ", Toast.LENGTH_SHORT).show();
}
});
}
public void testLoaddown(final String path, String url) {
Retrofit retrofitLoaddown = new Retrofit.Builder()
.baseUrl("http://imtt.dd.qq.com/")
//通過線程池獲取一個線程,指定callback在子線程中運行。
.callbackExecutor(Executors.newSingleThreadExecutor())
.build();
ApiService request = retrofitLoaddown.create(ApiService.class);
Call<ResponseBody> call = request.testDownload(url);//當你填進去的url是完整url的時候,設置的baseurl是無效的
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull final Response<ResponseBody> response) {
Log.i("gao", "測試 線程名字 = " + Thread.currentThread().getName());
//將Response寫入到從磁盤中,詳見下面分析 注意,這個方法是運行在子線程中的
writeResponseToDisk(path, response);
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
Log.i("gao", "testLoaddown 網絡錯誤~ " + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "testLoaddown 網絡錯誤~ ", Toast.LENGTH_SHORT).show();
}
});
}
好了,主要就是這幾部,下面在發一下代碼中的MainActivity的代碼。
import android.app.Activity;
import android.os.Environment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Executors;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Retrofit的使用步驟。
* 1.添加Retrofit庫的依賴和網絡權限:
* compile 'com.squareup.retrofit2:retrofit:2.2.0'// Retrofit庫
* compile 'com.squareup.retrofit2:converter-gson:2.3.0'
* <uses-permission android:name="android.permission.INTERNET" />
*
* 2.創建 用於描述網絡請求 的接口
* Retrofit將 Http請求 抽象成 Java接口:採用 註解 描述網絡請求參數 和配置網絡請求參數
*
* 3.創建Retrofit實例(回調默認的線程是主線程,可以自己設置運行在主線程或者子線程。)
*
* 4.將服務器返回的數據封裝成一個bean類,用於接收服務器返回的數據。
*
* 5.發送請求
* 請求分爲同步請求和異步請求
*
*
*/
public class MainActivity extends Activity implements View.OnClickListener {
Retrofit retrofit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.testGet).setOnClickListener(this);
findViewById(R.id.testPost).setOnClickListener(this);
findViewById(R.id.testLoaddown).setOnClickListener(this);
// 創建Retrofit實例時需要通過Retrofit.Builder,並調用baseUrl方法設置URL。Retrofit2的baseUlr 必須以 /(斜線)結束,不然會拋出一個IllegalArgumentException
retrofit = new Retrofit.Builder()
.baseUrl("http://fy.iciba.com/") // 設置 網絡請求 Url
.addConverterFactory(GsonConverterFactory.create()) //設置使用Gson解析(記得加入依賴)
.build();
//動態獲取權限
AppPermission.isGrantExternalRW(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.testGet:
testGet();
break;
case R.id.testPost:
testPost("hello");
break;
case R.id.testLoaddown:
if (AppPermission.isGrantExternalRW(this)) {
String url = "http://imtt.dd.qq.com/16891/89E1C87A75EB3E1221F2CDE47A60824A.apk?fsname=com.snda.wifilocating_4.2.62_3192.apk&csr=1bbd";
testLoaddown(Environment.getExternalStorageDirectory() + "/ceshi.apk", url);
}
break;
}
}
public void testGet() {
ApiService request = retrofit.create(ApiService.class);
Call<ResponseInfo> call = request.testGet();// 封裝請求
try {
Response<ResponseInfo> execute = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
call.enqueue(new Callback<ResponseInfo>() {// 發送網絡請求(異步)
@Override
public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {//主線程中調用
ResponseInfo body = response.body();
Log.i("gao", "測試 get body = " + body + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "測試GET= " + body.content.word_mean.get(0), Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<ResponseInfo> call, Throwable t) {//主線程中調用
Log.i("gao", " testGet onFailure " + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, " testGet onFailure ", Toast.LENGTH_SHORT).show();
}
});
}
public void testPost(String string) {
ApiService request = retrofit.create(ApiService.class);
Call<ResponseInfo> call = request.testPost(string);// 封裝請求
call.enqueue(new Callback<ResponseInfo>() {// 發送網絡請求(異步)
@Override
public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {
ResponseInfo body = response.body();
Log.i("gao", "測試 post body = " + body + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "測試 post body = " + body, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<ResponseInfo> call, Throwable t) {
Log.i("gao", "testPost onFailure " + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "testPost onFailure ", Toast.LENGTH_SHORT).show();
}
});
}
public void testLoaddown(final String path, String url) {
Retrofit retrofitLoaddown = new Retrofit.Builder()
.baseUrl("http://imtt.dd.qq.com/")
//通過線程池獲取一個線程,指定callback在子線程中運行。
.callbackExecutor(Executors.newSingleThreadExecutor())
.build();
ApiService request = retrofitLoaddown.create(ApiService.class);
Call<ResponseBody> call = request.testDownload(url);//當你填進去的url是完整url的時候,設置的baseurl是無效的
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull final Response<ResponseBody> response) {
Log.i("gao", "測試 線程名字 = " + Thread.currentThread().getName());
//將Response寫入到從磁盤中,詳見下面分析 注意,這個方法是運行在子線程中的
writeResponseToDisk(path, response);
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
Log.i("gao", "testLoaddown 網絡錯誤~ " + " 線程名字 = " + Thread.currentThread().getName());
Toast.makeText(MainActivity.this, "testLoaddown 網絡錯誤~ ", Toast.LENGTH_SHORT).show();
}
});
}
private void writeResponseToDisk(String path, Response<ResponseBody> response) {
//從response獲取輸入流以及總大小
writeFileFromIS(new File(path), response.body().byteStream(), response.body().contentLength());
}
private static int sBufferSize = 8192;
//將輸入流寫入文件
private void writeFileFromIS(File file, InputStream is, long totalLength) {
//創建文件
if (!file.exists()) {
if (!file.getParentFile().exists())
file.getParentFile().mkdir();
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
Log.i("gao", "創建文件錯誤~ " + " 線程名字 = " + Thread.currentThread().getName());
}
}
OutputStream os = null;
long currentLength = 0;
try {
os = new BufferedOutputStream(new FileOutputStream(file));
byte data[] = new byte[sBufferSize];
int len;
while ((len = is.read(data, 0, sBufferSize)) != -1) {
os.write(data, 0, len);
currentLength += len;
//計算當前下載進度
Log.i("gao", "下載 進度 = " + (int) (100 * currentLength / totalLength));
}
//下載完成,並返回保存的文件路徑
Log.i("gao","下載完成 file.getAbsolutePath() = "+file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
Log.i("gao","下載失敗");
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
主要的內容就是這些,需要源代碼的朋友可以下載 文中例子的 源碼
下一篇我們將 Retrofit 和Rxjava的結合:Retrofit 和 Rxjava 的簡單實用
如果對 Rxjava 不懂的朋友可以看一下上篇博客: Rxjava的使用_1