OkHttp在安卓中的使用?
之前一直用xutils,使用起來還算方便,但是好像官方不建議用了,所以就想換一套網路請求框架,看到大家對OkHttp十分看好,就去github上看了一番,發現非同步的callback還是在子線程里,不能像Xutils一樣方便的使用,因為對多線程不太熟悉,用Handler的post()每次都要new Runnable ,並不是太方便,不知道怎麼用比較好,想看看大家都是怎麼用的,希望各位大神給點兒意見。謝了
2015/11/16
-----------------------------------------------------------------------------------------------------------------lsxiao/ZhihuDailyRRD · GitHub2015/11/6 15:20
-----------------------------------------------------------------------------------------------------------------
忙得很,抽空規範了下注釋,小改了下布局,把5.0的視差嵌套滾動弄上去了,加上了日報的配圖。2015/11/4 00:33
-----------------------------------------------------------------------------------------------------------------一個RRD的例子基本上寫好了,只有兩個簡單的,查看當日新聞列表,和查看詳細新聞功能(沒有主題日報)。再改改就放GitHub調試了下Api,已經可以正常工作了,開始擼日報列表了。
知乎上我就不寫了,編輯工具太難用了,不適合貼大量代碼。
2015/11/2 11:29
-----------------------------------------------------------------------------------------------------------------感謝Max安珀的回復OkHttp是Square開源Android版Http客戶端。除了OkHttp,Android自帶HttpURLConnection和HttpClient。而Retrofit則在Http客戶端外繼續封裝了一層,讓你從伺服器獲取數據更加方便,所以說二者不矛盾。
Retrofit 2.0後默認使用的是OkHttp,2.0之前是可選的。
2015/10/31 21:14 更新由上而下,最新的編輯在最上面,所以你可能需要從下往上看了。
-----------------------------------------------------------------------------------------------------------------前兩周左右,團隊開了新項目,而且我們團隊自己的框架正好迭代了新版本,集成了基於事件流的響應式編程框架RxJava。
所以我們Android組開了一個會,會上組長提出了這個項目要用RxJava配合Retrofit和Dagger2開發的要求。
真不巧,我所處的項目小組是第一個吃螃蟹的人(雖然Android組很大,但是項目組就只有4個人),而且我才入職三個月,第一次聽到Rx還是聽導師隨口說起的。
雖然我在讀大學的時候見過Dagger這幾個字母,但是我根本沒摸過它,我大學也就是用過ButterKnife的水平,更別說Dagger2了。
當時我就感覺很碉啊,簡直就是碉堡了,因為又有新的Android技術可以讓我折騰了,果然沒有來錯地方啊!
RxJava國內高質量的文章還是有的。
比如這一篇:
『匠心寫作』裡面的『給 Android 開發者的 RxJava 詳解』,是目前就職於Flipboard的『扔物線』寫的,通俗易懂。
但目前Dagger2在國內的中文資料還是很少,網上搜來搜去也就那幾篇,而且質量不高,感覺寫的人語文都學得不怎麼好。
所以我們組長親自動手,寫好了RxJava+Retrofit+Dagger2的配合使用文檔,並且弄好了項目的架構設計(簡直就是良心)。
當項目遇上了RxJava+Retrofit+Dagger2的巧妙結合,根本讓你欲罷不能,個中好處要用過才知道。
注【我不會講解Dagger2+RxJava+Retrofit它們分別的詳細使用方法,很占篇幅】
那麼我先舉個從伺服器獲取用戶數據的例子,看看如何用RxJava+Retrofit+Dagger2的方式來寫:
基本方式使用Retrofit和RxJava//User Service
Service service = new ServiceImpl();
//創建Observable對象
Observable&
new Observable.OnSubscribe&
@Override
public void call(Subscriber& super User&> subscriber) {
try {
User user =service.getUser("username","password")
subscriber.onNext(user);
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
});
//綁定訂閱者 上面的代碼在ServiceImpl類的getUser方法裡面使用了Retrofit獲取伺服器數據。 獲取數據是在RxAndroid提供的IO線程進行的,顯示數據是在UI線程進行的。 這就是基本的RxJava的用法了。
observable.subscribeOn(Schedulers.io())//在IO線程進行網路請求,獲取數據
.observeOn(AndroidSchedulers.mainThread())//在UI線程操作數據
.subscribe(new Action1&
@Override
public void call(User user) {
//將userInfo數據顯示到UI上
}
});
現在我們來看看上面的代碼有什麼缺點:
(1). service實例可以是單例模式,我們不需要在每處需要請求的地方都去重新實例化一個新的對象。
所以我們只需要實例化一次service對象,然後通過Dagger2把這個對象注入給一個全局變數(比如BaseActivity的成員變數)
這樣我們可以在任何地方獲取到它。
(2).還有創建Observable對象太麻煩了,其實Retrofit支持直接返回Observable類型的對象。
--------------------------------------------------------------------------------------------------------------下面開始修改我們的代碼:getService()//取得全局Service
.getUser("username","password")//調用返回Observable類型對象的方法
.subscribeOn(Schedulers.io())//在IO線程進行網路請求,獲取數據
.observeOn(AndroidSchedulers.mainThread())//在UI線程操作數據
.subscribe(new Action1&
@Override
public void call(User user) {
//將userInfo數據顯示到UI上
}
});
代碼是不是一下子減少了很多,之前的實例化Service和新建Observable的代碼都分散到了Dagger2的組件和模塊中,代碼的耦合度降低。
但是我們的Service可能不止一個啊,這個時候你可以繼續抽象一層DataLayer層出來。
通過一個DataLayer對象可以獲取所有的Service,這些Service都是單例的,且通過Dagger2注入。
我們可以在Application裡面初始化DataLayer對象,用單例模式,然後注入給BaseActivity,BaseFragment等等基類的成員變數。
這樣你在Fragment/Activity裡面直接調用
getDataLayer().getUserService().getUser().subscribeOn.....有沒有覺得這種鏈式的調用方式很方便,很清晰,有一種碉堡的感覺?!
依賴關係圖:
2015/10/31 18:19
--------------------------------------------------------------------------------------------------------------昨天很忙,沒時間講Rx+Retrofit+Dagger2怎麼配合開發的,今天先回家,吃了飯就開更。2015/10/30
--------------------------------------------------------------------------------------------------------------Retrofit支持返回Observale對象,這意味著我們可以使用RxAndroid(RxJava)來處理網路請求的問題。在RxAndroid提供的IO線程裡面進行網路數據的請求,在主線程進行數據的顯示。
正好我們項目也是這麼做的,同時我們還使用了Dagger2來進行解耦操作。
今天有點忙,下午再來說說怎麼用Dagger2和Retrofit以及Rx很好的配合開發。
---------------------說好的更新分界線-----------------------在Android應用開發中,我們不可避免的需要網路操作,尤其是在與伺服器頻繁的交互過程中,更是需要大量的重複編碼:HttpURLConnection,Thread, AsyncTask, Service等,十分複雜又容易出錯。在2013年Google I/O大會上推出了網路通信框架——Volley。它可以極大簡化HTTP通信,載入網路圖片的操作,並且在小數據量的頻繁網路交互中性能表現良好。不過,它不適合進行大文件的上傳下載。基於上述原因,在最近的項目中我學習使用了Volley網路庫,然而Volley在使用中也存在一些問題:比如,Volley自帶的JsonRequest只支持JSONObject,而如果想要使用Gson,還需要自己定製Request。另外,默認情況下,Volley在Froyo使用Apache Http stack作為其傳輸層,而在Gingerbread及之後的版本上使用HttpURLConnection stack作為傳輸層。很自然我們想到,能不能將口碑十分好的OkHttp替換為Volley的傳輸層呢。最後,很多時候我們需要一些小文件的上傳操作(如用戶頭像,批量上傳一些小圖片),能不能使用Volley完成這個任務呢。所以,這裡描述一種結合使用Volley+OkHttp+Gson進行快速網路開發的方法,並給出一些網路介面封裝的例子。-----------------下文代碼較多預警------------------首先,要將OkHttp替換為Volley的傳輸層,需要實現一個OkHttpStack類,然後在實例化Volley的RequestQueue時將之替換即可:
RequestQueue mQueue = Volley.newRequestQueue(this, new OkHttpStack(new OkHttpClient()));
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.Map;
import me.zq.youjoin.utils.LogUtils;
import me.zq.youjoin.utils.StringUtils;
public class PostObjectRequest&
private ResponseListener listener;
private Gson gson;
private Type clazz;
private Map&
public PostObjectRequest(String url, Map&
ResponseListener listener){
super(Method.POST, url, listener);
this.listener = listener;
this.clazz = type;
this.params = params;
this.gson = new Gson();
}
@Override
protected Response&
try{
T result;
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
LogUtils.d("hehe", jsonString);
result = gson.fromJson(StringUtils.FixJsonString(jsonString), clazz);
return Response.success(result,
HttpHeaderParser.parseCacheHeaders(response));
}catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response){
listener.onResponse(response);
}
@Override
protected Map&
return params;
}
}
import android.util.Log;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import me.zq.youjoin.model.ImageInfo;
import me.zq.youjoin.utils.StringUtils;
public class PostUploadRequest&
/**
* 正確數據的時候回掉用
*/
private ResponseListener mListener ;
/*請求 數據通過參數的形式傳入*/
private List&
private static final String BOUNDARY = "-----ZZQ----MZZ-----"; //數據分隔線
private static final String MULTIPART_FORM_DATA = "multipart/form-data";//使用表單數據方法提交
private static final String TAG = "hehe_upload_request";
private static final String PARAM = "uploadedfile";//圖片的參數,&為了上傳多張圖片,在下面的封裝中使用uploadedfile[index]格式作為圖片參數&
private Map&
private Gson gson;
private Type clazz;
public PostUploadRequest(String url, List&
Map&
Type type, ResponseListener listener) {
super(Method.POST, url, listener);
this.mListener = listener ;
this.params = params;
this.gson = new Gson();
this.clazz = type;
setShouldCache(false);
mListItem = listItem ;
//設置請求的響應事件,因為文件上傳需要較長的時間,所以在這裡加大了,設為5秒
setRetryPolicy(new DefaultRetryPolicy(5000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
/**
* 這裡開始解析數據
* @param response Response from the network
* @return
*/
@Override
protected Response&
try {
String mString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
Log.v(TAG, "mString = " + mString);
T result = gson.fromJson(StringUtils.FixJsonString(mString), clazz);
return Response.success(result,
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
/**
* 回調正確的數據
* @param response The parsed response returned by
*/
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
//重寫getBody()方法,封裝Post包
@Override
public byte[] getBody() throws AuthFailureError {
if (mListItem == null||mListItem.size() == 0){
return super.getBody() ;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
if(!params.isEmpty()) {
StringBuilder sbParams = new StringBuilder();
for (Map.Entry&
Map.Entry entry = (Map.Entry) o;
/*第一行*/
//`"--" + BOUNDARY + "
"`
sbParams.append("--" + BOUNDARY);
sbParams.append("
");
/*第二行*/
//Content-Disposition: form-data; name="參數的名稱"; + 參數 + "
"
sbParams.append("Content-Disposition: form-data;");
sbParams.append(" name="");
sbParams.append((String) entry.getKey());
sbParams.append(""");
sbParams.append("
");
sbParams.append("
");
sbParams.append((String) entry.getValue());
sbParams.append("
");
}
try {
bos.write(sbParams.toString().getBytes("utf-8"));
// bos.write("
".getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
int N = mListItem.size() ; 為了方便調用,封裝Response介面如下: public interface ResponseListener& }
ImageInfo imageInfo ;
for (int i = 0; i &< N ;i++){
imageInfo = mListItem.get(i) ;
StringBuilder sb= new StringBuilder() ;
/*第一行*/
//`"--" + BOUNDARY + "
"`
sb.append("--"+BOUNDARY);
sb.append("
") ;
/*第二行*/
//Content-Disposition: form-data; name="參數的名稱"; filename="上傳的文件名" + "
"
sb.append("Content-Disposition: form-data;");
sb.append(" name="");
sb.append(PARAM + "[" + Integer.toString(i) + "]"); //為了上傳多張圖片,使用uploadedfile[index]格式作為圖片參數
sb.append(""") ;
sb.append("; filename="") ;
sb.append(imageInfo.getFileName()) ;
sb.append(""");
sb.append("
") ;
/*第三行*/
//Content-Type: 文件的 mime 類型 + "
"
sb.append("Content-Type: ");
sb.append(imageInfo.getMime()) ;
sb.append("
") ;
/*第四行*/
//"
"
sb.append("
") ;
try {
bos.write(sb.toString().getBytes("utf-8"));
/*第五行*/
//文件的二進位數據 + "
"
bos.write(imageInfo.getValue());
bos.write("
".getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
/*結尾行*/
//`"--" + BOUNDARY + "--" + "
"`
String endLine = "--" + BOUNDARY + "--" + "
" ;
try {
bos.write(endLine.getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
// Log.v(TAG,"imageInfo =
"+bos.toString()) ;
return bos.toByteArray();
}
//Content-Type: multipart/form-data; boundary=----------8888888888888
@Override
public String getBodyContentType() {
return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;
}
// @Override //注意一旦重寫getBody()方法,則使用此方法添加參數無效。
// protected Map&
// return params;
// }
}
import com.android.volley.Response;
/**
* 網路管理類,封裝網路操作介面
*/
public class NetworkManager {
/**
* 網路介面相關常量
*/
public static final String USER_USERNAME = "user_name";
public static final String USER_PASSWORD = "user_password";
public static final String USER_EMAIL = "user_email";
public static final String USER_ID = "user_id";
public static final String USER_WORK = "user_work";
public static final String USER_BIRTH = "user_birth";
public static final String USER_SIGN = "user_sign";
public static final String USER_LOCATION = "user_location";
public static final String USER_SEX = "user_sex";
public static final String TWEETS_CONTNET = "tweets_content";
public static final String FRIEND_ID = "friend_id";
public static final String SEND_USERID = "send_userid";
public static final String RECEIVE_USERID = "receive_userid";
public static final String MESSAGE_CONTENT = "message_content";
/**
* 伺服器介面URL
*/
public static final String BASE_API_URL = "http://192.168.0.103:8088/youjoin-server/controllers/";
public static final String API_SIGN_IN = BASE_API_URL + "signin.php";
public static final String API_SIGN_UP = BASE_API_URL + "signup.php";
public static final String API_UPDATE_USERINFO = BASE_API_URL + "update_userinfo.php";
public static final String API_SEND_TWEET = BASE_API_URL + "send_tweet.php";
public static final String API_REQUEST_USERINFO = BASE_API_URL + "request_userinfo.php";
public static final String API_ADD_FRIEND = BASE_API_URL + "add_friend.php";
public static final String API_SEND_MESSAGE = BASE_API_URL + "send_message.php";
public static final String API_RECEIVE_MESSAGE = BASE_API_URL + "receive_message.php";
public static final String API_COMMENT_TWEET = BASE_API_URL + "comment_tweet.php";
public static final String API_UPVOTE_TWEET = BASE_API_URL + "upvote_tweet.php";
private static RequestQueue mRequestQueue ;
/**
* 發送動態介面
* @param listener ResponseListener
*/
public static void postSendTweet(String content, List&
ResponseListener listener){
Map&
params.put(USER_ID, YouJoinApplication.getCurrUser().getId());
params.put(TWEETS_CONTNET, content);
Request request = new PostUploadRequest(API_SEND_TWEET, images, params,
new TypeToken&
NetworkManager.getRequestQueue().add(request);
}
/**
* 發送私信介面
* @param receiveUserId 接收方用戶id
* @param content 私信內容
* @param listener ResponseListener
*/
public static void postSendMessage(String receiveUserId, String content,
ResponseListener listener){
Map&
params.put(SEND_USERID, YouJoinApplication.getCurrUser().getId());
params.put(RECEIVE_USERID, receiveUserId);
params.put(MESSAGE_CONTENT, content);
Request request = new PostObjectRequest(
API_SEND_MESSAGE,
params, new TypeToken&
listener);
NetworkManager.getRequestQueue().add(request);
}
/**
* 添加好友介面
* @param friendUserId 要添加的好友id
* @param listener ResponseListener
*/
public static void postAddFriend(String friendUserId,
ResponseListener listener){
Map&
params.put(USER_ID, YouJoinApplication.getCurrUser().getId());
params.put(FRIEND_ID, friendUserId);
Request request = new PostObjectRequest(
API_ADD_FRIEND,
params, new TypeToken&
listener);
NetworkManager.getRequestQueue().add(request);
}
/**
* 個人資料請求(下載)介面
* @param userId 要獲取的用戶id
* @param listener ResponseListener
*/
public static void postRequestUserInfo(String userId, ResponseListener listener){
Map&
params.put(USER_ID, userId);
Request request = new PostObjectRequest(
API_REQUEST_USERINFO,
params,new TypeToken&
listener);
NetworkManager.getRequestQueue().add(request);
}
/**
* 個人資料更新(上傳)介面
* @param userInfo 用戶實體類
* @param picPath 頭像的本地路徑
* @param listener ResponseListener
*/
public static void postUpdateUserInfo(UserInfo userInfo, String picPath, ResponseListener listener){
if(userInfo.getId() == null) return;
List&
imageList.add(new ImageInfo(picPath));
Map&
params.put(USER_ID, userInfo.getId());
params.put(USER_WORK, userInfo.getWork());
params.put(USER_LOCATION, userInfo.getLocation());
params.put(USER_SEX, userInfo.getSex());
params.put(USER_BIRTH, userInfo.getBirth());
params.put(USER_SIGN, userInfo.getUsersign());
Request request = new PostUploadRequest(API_UPDATE_USERINFO, imageList, params,
new TypeToken&
NetworkManager.getRequestQueue().add(request);
}
/**
* 登陸介面
* @param username 登錄用戶名
* @param password 登陸密碼
* @param listener ResponseListener
*/
public static void postSignIn(String username, String password,
ResponseListener listener){
Map&
param.put(USER_USERNAME, username);
param.put(USER_PASSWORD, Md5Utils.getMd5(password));
Request request = new PostObjectRequest(
API_SIGN_IN,
param,
new TypeToken&
listener);
NetworkManager.getRequestQueue().add(request);
}
/**
* 註冊介面
* @param username 註冊用戶名
* @param password 註冊密碼
* @param email 註冊郵箱
* @param listener ResponseListener
*/
public static void postSignUp(String username, String password, String email,
ResponseListener listener){
Map&
param.put(USER_USERNAME, username);
param.put(USER_PASSWORD, Md5Utils.getMd5(password));
param.put(USER_EMAIL, email);
Request request = new PostObjectRequest(
API_SIGN_UP,
param,
new TypeToken&
listener);
NetworkManager.getRequestQueue().add(request);
}
/**初始化Volley 使用OkHttpStack
* @param context 用作初始化Volley RequestQueue
*/
public static synchronized void initialize(Context context){
if (mRequestQueue == null){
synchronized (NetworkManager.class){
if (mRequestQueue == null){
mRequestQueue =
Volley.newRequestQueue(context, new OkHttpStack(new OkHttpClient()));
}
}
}
mRequestQueue.start();
}
/**獲取RequestQueue實例
* @return 返回RequestQueue實例
*/
public static RequestQueue getRequestQueue(){
if (mRequestQueue == null)
throw new RuntimeException("請先初始化mRequestQueue") ;
return mRequestQueue ;
}
}
import android.app.Application;
import android.content.Context;
import me.zq.youjoin.model.UserInfo;
import me.zq.youjoin.network.NetworkManager;
public class YouJoinApplication extends Application {
public static float sScale;
public static int sHeightPix;
private static Context context;
private static UserInfo currUser;
@Override
public void onCreate(){
super.onCreate();
context = getApplicationContext();
NetworkManager.initialize(context);
sScale = getResources().getDisplayMetrics().density;
sHeightPix = getResources().getDisplayMetrics().heightPixels;
}
public static Context getAppContext(){
return context;
}
public static UserInfo getCurrUser() {
return currUser;
}
public static void setCurrUser(UserInfo currUser) {
YouJoinApplication.currUser = currUser;
}
}
參考資料:
OkHttpStack.java -- from GitHubAndroid Networking I: OkHttp, Volley and GsonAndroid Volley完全解析(三),定製自己的RequestAndroid volley 解析(三)之文件上傳篇Android OkHttp完全解析 是時候來了解OkHttp了
---------------------之前的回答分界線-----------------------
目前在開發一個項目,用的是Volley+OkHttp+Gson,感覺比之前用HttpURLConnection手擼爽到不知道哪裡去了,有空整理一下發個demo上來 ps:其他很多答案提到的Retrofit2,RxJava,Dagger2勾起了我極大的興趣,這周研究研究~Volley + OkHttp作為網路請求框架,用起來棒棒噠
建議直接選用Retrofit2,
可以選擇結合RxJava,再可以選擇結合RetroLambdaRetrofit2中默認內置了OkHttp來處理你的網路請求(當然也能換)
給兩篇文章題主可以參考下
Retrofit 2 主要講從Retrofit1.9升級到2.0的過程中會遇到的問題和區別,同時用一系列文章介紹了Retrofit的使用,題主可以結合著看,了解一下曾經的1.9,同時再看看現在的2.0。
給 Android 開發者的 RxJava 詳解 如果題主有興趣Retrofit結合RxJava,再可以參考這片文章,講解的非常詳細,即便不用也值得一看。文中也大致介紹了Retrofit和RxJava配合起來的效果。
Retrofit 這是官方文檔可以嘗試一下volley!我們我們現在用的volley和fastjson封裝起來的,感覺還不錯!
可以用Retrofit
https://git.oschina.net/icecooly/FastHttpClient推薦使用這個 封裝了一下 使用起來簡單一點
據說這是最完美當然model層okhttp不難,建議看完api和帖子後,看看源碼有很多可以學習到的東西
可以考慮volley擴展okhttp,
Retrofit2+RxJava+Dagger2+Data Binding
可以自己進行二次封裝,參考Android 基於OkHttp的UI層回調封裝
SPDY和緩存響應、GZIP壓縮這塊自己不斷研究代碼才是。學而時習之不亦說乎
推薦閱讀:
※你遇到過哪些高質量的Android面試?
※為什麼要學習 Android 開發?
※Android Studio編譯慢、卡死和狂佔內存怎麼破?
※為什麼就算配置很高的 Android 手機玩遊戲感覺畫面也沒有 iPhone 流暢,而且觸屏感覺比較遲鈍?
TAG:Android開發 |