使用Volley+OkHttp+Gson加速Android网络开发

在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()));


OkHttpStack类的代码如下(来自GitHub,详见文后参考资料部分,这里为了方便网络不佳的读者贴出来):

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Circle Internet Financial
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.toolbox.HttpStack;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Protocol;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;


import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;


import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;




/**
 * OkHttp backed {@link com.android.volley.toolbox.HttpStack HttpStack} that does not
 * use okhttp-urlconnection
 */
public class OkHttpStack implements HttpStack {


    private final OkHttpClient mClient;


    public OkHttpStack(OkHttpClient client) {
        this.mClient = client;
    }


    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {


        OkHttpClient client = mClient.clone();
        int timeoutMs = request.getTimeoutMs();
        client.setConnectTimeout(timeoutMs, TimeUnit.MILLISECONDS);
        client.setReadTimeout(timeoutMs, TimeUnit.MILLISECONDS);
        client.setWriteTimeout(timeoutMs, TimeUnit.MILLISECONDS);


        com.squareup.okhttp.Request.Builder okHttpRequestBuilder = new com.squareup.okhttp.Request.Builder();
        okHttpRequestBuilder.url(request.getUrl());


        Map<String, String> headers = request.getHeaders();
        for (final String name : headers.keySet()) {
            okHttpRequestBuilder.addHeader(name, headers.get(name));
        }
        for (final String name : additionalHeaders.keySet()) {
            okHttpRequestBuilder.addHeader(name, additionalHeaders.get(name));
        }


        setConnectionParametersForRequest(okHttpRequestBuilder, request);


        com.squareup.okhttp.Request okHttpRequest = okHttpRequestBuilder.build();
        Call okHttpCall = client.newCall(okHttpRequest);
        Response okHttpResponse = okHttpCall.execute();


        StatusLine responseStatus = new BasicStatusLine(parseProtocol(okHttpResponse.protocol()), okHttpResponse.code(), okHttpResponse.message());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
        response.setEntity(entityFromOkHttpResponse(okHttpResponse));


        Headers responseHeaders = okHttpResponse.headers();
        for (int i = 0, len = responseHeaders.size(); i < len; i++) {
            final String name = responseHeaders.name(i), value = responseHeaders.value(i);
            if (name != null) {
                response.addHeader(new BasicHeader(name, value));
            }
        }


        return response;
    }


    private static HttpEntity entityFromOkHttpResponse(Response r) throws IOException {
        BasicHttpEntity entity = new BasicHttpEntity();
        ResponseBody body = r.body();


        entity.setContent(body.byteStream());
        entity.setContentLength(body.contentLength());
        entity.setContentEncoding(r.header("Content-Encoding"));


        if (body.contentType() != null) {
            entity.setContentType(body.contentType().type());
        }
        return entity;
    }


    @SuppressWarnings("deprecation")
    private static void setConnectionParametersForRequest(com.squareup.okhttp.Request.Builder builder, Request<?> request)
            throws IOException, AuthFailureError {
        switch (request.getMethod()) {
            case Request.Method.DEPRECATED_GET_OR_POST:
                // Ensure backwards compatibility.  Volley assumes a request with a null body is a GET.
                byte[] postBody = request.getPostBody();
                if (postBody != null) {
                    builder.post(RequestBody.create(MediaType.parse(request.getPostBodyContentType()), postBody));
                }
                break;
            case Request.Method.GET:
                builder.get();
                break;
            case Request.Method.DELETE:
                builder.delete();
                break;
            case Request.Method.POST:
                builder.post(createRequestBody(request));
                break;
            case Request.Method.PUT:
                builder.put(createRequestBody(request));
                break;
            case Request.Method.HEAD:
                builder.head();
                break;
            case Request.Method.OPTIONS:
                builder.method("OPTIONS", null);
                break;
            case Request.Method.TRACE:
                builder.method("TRACE", null);
                break;
            case Request.Method.PATCH:
                builder.patch(createRequestBody(request));
                break;
            default:
                throw new IllegalStateException("Unknown method type.");
        }
    }


    private static ProtocolVersion parseProtocol(final Protocol p) {
        switch (p) {
            case HTTP_1_0:
                return new ProtocolVersion("HTTP", 1, 0);
            case HTTP_1_1:
                return new ProtocolVersion("HTTP", 1, 1);
            case SPDY_3:
                return new ProtocolVersion("SPDY", 3, 1);
            case HTTP_2:
                return new ProtocolVersion("HTTP", 2, 0);
        }


        throw new IllegalAccessError("Unkwown protocol");
    }


    private static RequestBody createRequestBody(Request r) throws AuthFailureError {
        final byte[] body = r.getBody();
        if (body == null) return null;


        return RequestBody.create(MediaType.parse(r.getBodyContentType()), body);
    }
}


然后,我们需要实现使用Gson的Request类:

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<T> extends Request<T> {


    private ResponseListener listener;
    private Gson gson;
    private Type clazz;
    private Map<String, String> params;


    public PostObjectRequest(String url, Map<String, String> params, Type type,
                             ResponseListener listener){
        super(Method.POST, url, listener);
        this.listener = listener;
        this.clazz = type;
        this.params = params;
        this.gson = new Gson();
    }


    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse 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<String, String> getParams() throws AuthFailureError{
        return params;
    }


}



最后,我们来实现图片上传部分。由于我们希望在上传图片的时候添加一些参数,例如用户id等,导致这部分的实现比较复杂。虽然我们可以将这些信息存储在图片文件名中实现传递,但这总归是取巧的办法。比较正规一点的方法还是研究网页表单提交时的Post请求的格式,然后自己构造一个合法的Post请求。我们可以使用Chorme的开发者工具来抓取Post请求,研究其格式,下图为我抓取的一个带图片和参数的表单Post请求:


从图中可以看出,我们只需要重写getBody()方法,逐行仿照Post格式写入我们自己的图片和参数,就可实现参数+图片的上传。多张图片的上传原理相同。另外,图片要注意在上传之前进行压缩处理。另外,为了方便服务器解析多张图片,将图片参数设置为uploadedfile[index]格式。代码如下:

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<T> extends Request<T> {

    /**
     * 正确数据的时候回掉用
     */
    private ResponseListener mListener ;
    /*请求 数据通过参数的形式传入*/
    private List<ImageInfo> mListItem ;

    private static final String BOUNDARY = "--------------520-13-14"; //数据分隔线
    private static final String MULTIPART_FORM_DATA = "multipart/form-data";//使用表单数据方法提交
    private static final String TAG = "hehe_upload_request";
    private static final String PARAM = "uploadedfile";//图片的参数,<span style="font-family: Arial, Helvetica, sans-serif;">为了上传多张图片,在下面的封装中使用uploadedfile[index]格式作为图片参数</span>


    private Map<String, String> params;
    private Gson gson;
    private Type clazz;

    public PostUploadRequest(String url, List<ImageInfo> listItem,
                             Map<String, String> params,
                             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<T> parseNetworkResponse(NetworkResponse 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<String, String> o : params.entrySet()) {
                Map.Entry entry = (Map.Entry) o;

                /*第一行*/
                //`"--" + BOUNDARY + "\r\n"`
                sbParams.append("--" + BOUNDARY);
                sbParams.append("\r\n");
                /*第二行*/
                //Content-Disposition: form-data; name="参数的名称"; + 参数 + "\r\n"
                sbParams.append("Content-Disposition: form-data;");
                sbParams.append(" name=\"");
                sbParams.append((String) entry.getKey());
                sbParams.append("\"");
                sbParams.append("\r\n");
                sbParams.append("\r\n");
                sbParams.append((String) entry.getValue());
                sbParams.append("\r\n");
            }
            try {
                bos.write(sbParams.toString().getBytes("utf-8"));
//                bos.write("\r\n".getBytes("utf-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        int N = mListItem.size() ;
        ImageInfo imageInfo ;
        for (int i = 0; i < N ;i++){
            imageInfo = mListItem.get(i) ;
            StringBuilder sb= new StringBuilder() ;
            /*第一行*/
            //`"--" + BOUNDARY + "\r\n"`
            sb.append("--"+BOUNDARY);
            sb.append("\r\n") ;
            /*第二行*/
            //Content-Disposition: form-data; name="参数的名称"; filename="上传的文件名" + "\r\n"
            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("\r\n") ;
            /*第三行*/
            //Content-Type: 文件的 mime 类型 + "\r\n"
            sb.append("Content-Type: ");
            sb.append(imageInfo.getMime()) ;
            sb.append("\r\n") ;
            /*第四行*/
            //"\r\n"
            sb.append("\r\n") ;
            try {
                bos.write(sb.toString().getBytes("utf-8"));
                /*第五行*/
                //文件的二进制数据 + "\r\n"
                bos.write(imageInfo.getValue());
                bos.write("\r\n".getBytes("utf-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        /*结尾行*/
        //`"--" + BOUNDARY + "--" + "\r\n"`
        String endLine = "--" + BOUNDARY + "--" + "\r\n" ;
        try {
            bos.write(endLine.getBytes("utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
       // Log.v(TAG,"imageInfo =\n"+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<String, String> getParams() throws AuthFailureError{
//        return params;
//    }
}

为了方便调用,封装Response接口如下:

import com.android.volley.Response;

public interface ResponseListener<T> extends Response.ErrorListener,Response.Listener<T> {

}

至此,对Volley+OkHttp+Gson的封装就完成了。在使用时,我们可以在自定义Application中初始化Queue,再封装一个专门的NetworkManager类来提供所有的网络访问操作,比如,在我最近的项目中是这样用的:


/**
 * 网络管理类,封装网络操作接口
 */
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<ImageInfo> images,
                                     ResponseListener listener){
        Map<String, String> params = new HashMap<>();
        params.put(USER_ID, YouJoinApplication.getCurrUser().getId());
        params.put(TWEETS_CONTNET, content);
        Request request = new PostUploadRequest(API_SEND_TWEET, images, params,
                new TypeToken<UpdateUserInfoResult>(){}.getType(), listener);
        NetworkManager.getRequestQueue().add(request);
    }

    /**
     * 发送私信接口
     * @param receiveUserId 接收方用户id
     * @param content 私信内容
     * @param listener ResponseListener
     */
    public static void postSendMessage(String receiveUserId, String content,
                                       ResponseListener listener){
        Map<String, String> params = new HashMap<>();
        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<ResultInfo>(){}.getType(),
                listener);
        NetworkManager.getRequestQueue().add(request);
    }

    /**
     * 添加好友接口
     * @param friendUserId 要添加的好友id
     * @param listener ResponseListener
     */
    public static void postAddFriend(String friendUserId,
                                     ResponseListener listener){
        Map<String, String> params = new HashMap<>();
        params.put(USER_ID, YouJoinApplication.getCurrUser().getId());
        params.put(FRIEND_ID, friendUserId);
        Request request = new PostObjectRequest(
                API_ADD_FRIEND,
                params, new TypeToken<ResultInfo>(){}.getType(),
                listener);
        NetworkManager.getRequestQueue().add(request);
    }

    /**
     * 个人资料请求(下载)接口
     * @param userId   要获取的用户id
     * @param listener ResponseListener
     */
    public static void postRequestUserInfo(String userId, ResponseListener listener){
        Map<String, String> params = new HashMap<>();
        params.put(USER_ID, userId);
        Request request = new PostObjectRequest(
                API_REQUEST_USERINFO,
                params,new TypeToken<UserInfo>(){}.getType(),
                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<ImageInfo> imageList = new ArrayList<>();
        imageList.add(new ImageInfo(picPath));
        Map<String, String> params = new HashMap<>();
        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<UpdateUserInfoResult>(){}.getType(), listener);
        NetworkManager.getRequestQueue().add(request);
    }

    /**
     * 登陆接口
     * @param username 登录用户名
     * @param password 登陆密码
     * @param listener ResponseListener
     */
    public static void postSignIn(String username, String password,
                                  ResponseListener listener){
        Map<String, String> param = new HashMap<>();
        param.put(USER_USERNAME, username);
        param.put(USER_PASSWORD, Md5Utils.getMd5(password));
        Request request = new PostObjectRequest(
                API_SIGN_IN,
                param,
                new TypeToken<UserInfo>(){}.getType(),
                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<String, String> param = new HashMap<>();
        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<UserInfo>(){}.getType(),
                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 GitHub
Android Networking I: OkHttp, Volley and Gson
Android Volley完全解析(三),定制自己的Request
Android volley 解析(三)之文件上传篇
Android OkHttp完全解析 是时候来了解OkHttp了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值