我們都知道,很多app的應用都離不開對網絡的請求,OkHttp是一個很好的移動網絡請求框架。使用起來非常方便,但是如果每使用一次網絡請求就要去new 一個client,不僅代碼冗長多餘,而且沒有必要浪費資源,所以我們今天要講的就是將OkHttp進行簡單的封裝,作爲一個工具類來調用,也體現了java三要素之一:封裝。
在上一篇裏,已經說過如何添加依賴和網絡權限,這裏就不贅述。Android目前不允許在主線程裏對網絡進行請求,因爲會阻塞UI線程,用戶體驗非常不好,所以,所有的網絡請求都應該在子線程裏進行,這裏也就涉及到多線程,我們將以單例的形式來做網絡請求,讓整個應用只有一個OkhttpClient。節省資源開銷。
我們寫的是一個工具類OkHttpUtil,所以誰使用這個工具類,就將獲取到的網絡數據回調給誰。所以,先寫一個回調接口,包含兩個方法java代碼
//創建接口,回調給調用者
interface ResultCallback{
void onError(Request request,Exception e);
void onResponse(Response response) throws IOException;
}
接着我們創建OkHttpUtil的實例,我們使用單例模式,網絡請求最好使用單例模式。將其構造方法私有化,然後提供一個靜態的方法返回OkHttpUtil對象。這裏提一下,單例模式最好寫兩個判定語句,這裏就不細說了,屬於java設計模式範疇。
private volatile static OkHttpUtil okHttpUtil;//會被多線程使用,所以使用關鍵字volatile
private OkHttpClient client;
private Handler mHandler;
//私有化構造方法
private OkHttpUtil(Context context){
File sdcache = context.getExternalCacheDir();
int cacheSize = 10 * 1024 *1024;//設置緩存大小
OkHttpClient.Builder builder= new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20,TimeUnit.SECONDS)
.readTimeout(20,TimeUnit.SECONDS)
.cache(new Cache(sdcache.getAbsoluteFile(),cacheSize));//設置緩存的路徑
client = builder.build();
mHandler = new Handler();
}
//單例模式,全局得到一個OkHttpUtil對象
public static OkHttpUtil getInstance(Context context){
if (okHttpUtil == null){
synchronized (OkHttpUtil.class){
if (okHttpUtil == null){
okHttpUtil = new OkHttpUtil(context);
}
}
}
return okHttpUtil;
}
在其私有的構造方法中,我們得到了一個OkHttpClient 和handler,他們分別是用來發起網絡請求和在線程中傳遞數據。handler是一個很神奇的東東,傳遞數據就靠它了。我們剛剛說網絡請求在子線程中進行的,而Android是不允許在子線程中更新UI的,那我們如何將數據更新到UI線程呢?其實就是handler幫我們完成的。
我們再寫兩個私有的方法,來處理如何將得到的網絡數據傳遞出去,一個是當請求成功後的方法,一個是請求失敗後的方法
/**當請求失敗時,都會調用這個方法
* @param call
* @param e
* @param callback
*/
private void sendFailedCallback(final Call call, final IOException e, final ResultCallback callback){
mHandler.post(new Runnable() {
@Override
public void run() {
Log.i("main","當前線程:"+Thread.currentThread().getName());
if (callback != null){
callback.onError(call.request(),e);
}
}
});
}
/**請求成功調用該方法
* @param response 返回的數據
* @param callback 回調的接口
*/
private void sendSuccessCallback(final Response response, final ResultCallback callback){
mHandler.post(new Runnable() {
@Override
public void run() {
Log.i("main","當前線程:"+Thread.currentThread().getName());
if (callback != null){
try {
callback.onResponse(response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
這裏我們使用handler.post()來傳遞數據,這裏看起來好像開啓了一個新的線程,但是,我通過控制檯獲取當前線程的名稱,竟然發現這個handler工作在主線程,也就是說,我們可以直接在回調裏面更新UI了。
前期工作都準備就緒,我們可以寫請求方法了,一個是get請求,一個是post請求(提交表單)
/**get異步請求
* @param url
* @param callback
*/
public void getAsynHttp(String url, final ResultCallback callback){
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailedCallback(call,e,callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
sendSuccessCallback(response,callback);
}
});
}
/**提交表單數據
* @param url
* @param map
* @param callback
*/
public void postForm(String url, Map<String,String > map, final ResultCallback callback){
FormBody.Builder form = new FormBody.Builder();//表單對象,包含以input開始的對象,以html表單爲主
if (map != null && !map.isEmpty()){
//遍歷Map集合
for(Map.Entry<String ,String> entry : map.entrySet()){
form.add(entry.getKey(),entry.getValue());
}
RequestBody body = form.build();
Request request = new Request.Builder().url(url).post(body).build();//採用post提交數據
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailedCallback(call,e,callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()&&response != null){
sendSuccessCallback(response,callback);
}
}
});
}
}
其實,封裝使用的知識點不是很多,弄清楚單例模式,接口回調,也就差不多了,當然,我們還可以添加很多方法,讓這個工具類更充實,大家可以根據自己的實際情況添加。在最後我會貼上工具類的源碼,以及具體使用例子
OkHttpUtil源碼
public class OkHttpUtil {
private volatile static OkHttpUtil okHttpUtil;//會被多線程使用,所以使用關鍵字volatile
private OkHttpClient client;
private Handler mHandler;
//私有化構造方法
private OkHttpUtil(Context context){
File sdcache = context.getExternalCacheDir();
int cacheSize = 10 * 1024 *1024;//設置緩存大小
OkHttpClient.Builder builder= new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20,TimeUnit.SECONDS)
.readTimeout(20,TimeUnit.SECONDS)
.cache(new Cache(sdcache.getAbsoluteFile(),cacheSize));//設置緩存的路徑
client = builder.build();
mHandler = new Handler();
}
//單例模式,全局得到一個OkHttpUtil對象
public static OkHttpUtil getInstance(Context context){
if (okHttpUtil == null){
synchronized (OkHttpUtil.class){
if (okHttpUtil == null){
okHttpUtil = new OkHttpUtil(context);
}
}
}
return okHttpUtil;
}
/**get異步請求
* @param url
* @param callback
*/
public void getAsynHttp(String url, final ResultCallback callback){
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailedCallback(call,e,callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
sendSuccessCallback(response,callback);
}
});
}
/**提交表單數據
* @param url
* @param map
* @param callback
*/
public void postForm(String url, Map<String,String > map, final ResultCallback callback){
FormBody.Builder form = new FormBody.Builder();//表單對象,包含以input開始的對象,以html表單爲主
if (map != null && !map.isEmpty()){
//遍歷Map集合
for(Map.Entry<String ,String> entry : map.entrySet()){
form.add(entry.getKey(),entry.getValue());
}
RequestBody body = form.build();
Request request = new Request.Builder().url(url).post(body).build();//採用post提交數據
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailedCallback(call,e,callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()&&response != null){
sendSuccessCallback(response,callback);
}
}
});
}
}
/**當請求失敗時,都會調用這個方法
* @param call
* @param e
* @param callback
*/
private void sendFailedCallback(final Call call, final IOException e, final ResultCallback callback){
mHandler.post(new Runnable() {
@Override
public void run() {
Log.i("main","當前線程:"+Thread.currentThread().getName());
if (callback != null){
callback.onError(call.request(),e);
}
}
});
}
/**請求成功調用該方法
* @param response 返回的數據
* @param callback 回調的接口
*/
private void sendSuccessCallback(final Response response, final ResultCallback callback){
mHandler.post(new Runnable() {
@Override
public void run() {
Log.i("main","當前線程:"+Thread.currentThread().getName());
if (callback != null){
try {
callback.onResponse(response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
//創建接口,回調給調用者
interface ResultCallback{
void onError(Request request,Exception e);
void onResponse(Response response) throws IOException;
}
}
在MainActivity中,我們添加一個按鈕,這些代碼就不一一講解了,然後再按鈕裏發送請求
public class MainActivity extends AppCompatActivity {
private utl_btn;
private OkHttpUtil httpUtil;
private String path = "xxxxxxxxxxx";
private String path_url = "xxxxxxxxxxx";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
utl_btn = (Button) findViewById(R.id.btn3);
httpUtil = OkHttpUtil.getInstance(MainActivity.this);
utl_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
HashMap<String,String> map = new HashMap();
map.put("xxx","yyy");
map.put("zzz","aaa");
httpUtil.postForm(path_url, map, new OkHttpUtil.ResultCallback() {
@Override
public void onError(Request request, Exception e) {
}
@Override
public void onResponse(Response response) throws IOException {
Log.i("main","response:"+response.body().string());
}
});
}
});
}
}
好了,其實如果網絡請求不是很複雜,我們完全可以自己封裝一個,沒有必要使用開源庫,當然,GitHub上有很多優秀的開源庫,值得我們學習和借鑑,如果比較懶的話,可是直接添加依賴,這裏推薦一個OkHttpFinal