1. 程式人生 > >Android Volley解析(一)之GET、POST請求篇

Android Volley解析(一)之GET、POST請求篇

一、 Volley 的地位

自2013年Google I/O 大會上,Google 推出 Volley 之後,一直到至今,由於其使用簡單、程式碼輕量、通訊速度快、併發量大等特點,倍受開發者們的青睞。
先看兩張圖,讓圖片告訴我們 Volley 的用處;
第一張 Volley 的經典圖
這裡寫圖片描述
通過上圖,我們可以發現 Volley適合網路通訊頻繁操作,並能同時實現多個網路通訊。
第二張圖
這裡寫圖片描述
我們在以前在 ListView 的 item 中如果有網路請求,一般都是通過Task 非同步任務來完成,並在完成之後通知 Adapter 更新資料。而Volley 不需要這麼麻煩,因為裡面已經為我們封裝好了處理的執行緒,網路請求,快取的獲取,資料的回掉都是對應不同的執行緒。

二、Volley使用步驟及基本分析

volley 的使用遵循以下四步:
1、獲取請求隊裡RequestQueue
RequestQueue mRequestQueue = Vollay.newRequestQueue(Context context) ;
2、啟動請求佇列
mRequestQueue.start();
以上這兩步通常也歸為一步
3、獲取請求Request
Request mRequest = new ObjectRequest(…) ;
ObjectRequest需要根據自己請求返回的資料來定製,繼承之抽象類Request,Vollay 已經為我們實現了 StringRequest、JsonArrayRequest、JsonObjectRequest、ImageRequest請求;
4、把請求新增到請求佇列中
mRequestQueue.add(mRequest);
說明:在一個專案中,請求佇列不需要出現多個,一般整個專案中共用同一個mRequestQueue,因為請求佇列啟動的時候會做以下事情

    /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
    //結束佇列中所有的執行緒
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        //初始化快取處理執行緒
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        //啟動快取執行緒
mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. //啟動網路請求處理執行緒,預設為5個,可以自己設定 size for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); //儲存網路請求執行緒 mDispatchers[i] = networkDispatcher; //啟動網路請求處理執行緒 networkDispatcher.start(); } }

啟動一個快取mCacheDispatcher執行緒,用來讀取快取資料,啟動若干個網路請求mDispatchers執行緒,用來實現網路通訊。
mCacheDispatcher執行緒的 run 方法

    @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // Make a blocking call to initialize the cache.
        //初始化快取
        mCache.initialize();
        //迴圈獲取快取請求
        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                //從快取佇列中獲取快取請求,如果沒有快取請求,這個方法會阻塞在這裡
                final Request request = mCacheQueue.take();
                //列印 log 資訊
                request.addMarker("cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                //如果請求終止了,結束本次迴圈
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // Attempt to retrieve this item from cache.
               //獲取快取資料,如果沒有,把請求加入到網路請求的佇列中
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    Log.i("CacheDispatcher", "沒有快取資料:" + request.getUrl());
                    mNetworkQueue.put(request);
                    continue;
                }

                // If it is completely expired, just send it to the network.
                //判斷快取是否已經過期,如果過期,把請求加入到網路請求的佇列中,直接請求網路獲取資料
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    Log.i("CacheDispatcher", "快取資料過期:" + request.getUrl());
                    mNetworkQueue.put(request);
                    continue;
                }

                // We have a cache hit; parse its data for delivery back to the request.
                // 已經獲取到了有效的快取資料,回撥給 request 的parseNetworkResponse,需要自己根據需求來解析資料
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");
                //判斷快取是否需要重新整理
                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    Log.i("CacheDispatcher", "獲取快取資料:" + request.getUrl());
                    mDelivery.postResponse(request, response);
                } else {
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }

mDispatchers執行緒的 run 方法

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request request;
        while (true) {
            try {
                // Take a request from the queue.
                //獲取網路請求,當佇列中為空的時候,阻塞
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                // Tag the request (if API >= 14)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                }

                // Perform the network request.
                //網路請求的基本操作(核心操作),從網路中獲取資料
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                //判斷是否需要快取,如果需要則快取。
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back.
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }

這兩個執行緒處理型別基本相同,都是採用迴圈的方法,在佇列中獲取請求,有請求則執行相應的請求,沒有則阻塞在下面兩行程式碼中

//阻塞執行緒的執行
//快取執行緒阻塞的地方
final Request request = mCacheQueue.take();
//網路請求阻塞的地方
request = mQueue.take();

所以我們一般只需要根據不同的介面,例項化不同的請求 Request,往佇列中新增 即可,它首先判斷請求是否需要快取,如果不需要,直接新增到網路請求的佇列中,結束下面的操作,如果需要快取,則把請求新增到快取佇列中,具體看程式碼。

    public Request add(Request request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        //判斷請求是否需要快取,如果不需要,直接新增到網路請求的佇列中,結束下面的操作,如果需要快取,則把請求新增到快取佇列中
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request>();
                }

                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert 'null' queue for this cacheKey, indicating there is now a request in
                // flight.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

所以如果需要快取的話,一開始會從mCacheQueue.take()會得到執行,當不符合要求的時候,請求會新增到真正的網路請求佇列中,以下是不符合要求的程式碼

                //沒有快取
                if (entry == null) {
                    request.addMarker("cache-miss");
                    Log.i("CacheDispatcher", "沒有快取資料:" + request.getUrl());
                    mNetworkQueue.put(request);
                    continue;
                }

                // If it is completely expired, just send it to the network.
                //快取已過期
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    Log.i("CacheDispatcher", "快取資料過期:" + request.getUrl());
                    mNetworkQueue.put(request);
                    continue;
                }

如果快取不符合要求,網路執行緒終止阻塞得到執行;
我們一般習慣用法是在 Application 中全域性初始化RequestQueue mRequestQueue,並啟動它,讓整個應用都能獲取到。具體運用將會在下面用到。

三、Volley 實戰 GET 請求和 POST 請求

/**
 * Created by gyzhong on 15/3/3.
 */
public class TestBean {
    @Expose
    private int id ;
    @Expose
    private String name ;
    @Expose
    private int download ;
    @Expose
    private int version ;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getDownload() {
        return download;
    }

    public void setDownload(int download) {
        this.download = download;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
}

1、GET 請求
第一步:在 Application 中初始化RequestQueue,

    //初始化請求佇列
    private void initRequestQueue(){
        //初始化 volley
        VolleyUtil.initialize(mContext);
    }
/**
 * Created by gyzhong on 15/3/1.
 */
public class VolleyUtil {

    private static RequestQueue mRequestQueue ;

    public static void initialize(Context context){
        if (mRequestQueue == null){
            synchronized (VolleyUtil.class){
                if (mRequestQueue == null){
                    mRequestQueue = Volley.newRequestQueue(context) ;
                }
            }
        }
        mRequestQueue.start();
    }

    public static RequestQueue getRequestQueue(){
        if (mRequestQueue == null)
            throw new RuntimeException("請先初始化mRequestQueue") ;
        return mRequestQueue ;
    }
}

第二步:定製 Request
先來分析介面所返回的資料,我們看到是一條 json 資料,雖然 Volley 中已經為我們定製好了JsonObjectRequest請求,但我們知道,在資料具體顯示的時候,是需要把 json 資料轉化為物件進行處理,所以這裡我們可以定製通用的物件請求。如何定製呢?
先看StringRequest的實現程式碼

//繼承Request<String>,String 為請求解析之後的資料
public class StringRequest extends Request<String> {
    //正確資料回撥介面
    private final Listener<String> mListener;
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    //回撥解析之後的資料
    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

    //解析資料,把網路請求,或者中快取中獲取的資料,解析成 String
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

通過上面程式碼可知,StringRequest繼承了 Request 並實現了兩個抽象方法parseNetworkResponse()和 deliverResponse(),這兩個方法很好理解,parseNetworkResponse()把獲取到的資料解析成我們所定義的資料型別;deliverResponse()把所解析的資料通過回撥介面回撥給展示處。
為了簡化回撥介面,這裡把錯誤回撥Response.ErrorListener 和正確的資料回撥Response.Listener合併成一個ResponseListener

/**
 * Created by gyzhong on 15/3/1.
 * 簡化回撥介面
 */
public interface ResponseListener<T> extends Response.ErrorListener,Response.Listener<T> {
}

根據 StringRequest,如法炮製

/**
 * Created by gyzhong on 15/3/1.
 */
public class GetObjectRequest<T> extends Request<T> {

    /**
     * 正確資料的時候回掉用
     */
    private ResponseListener mListener ;
    /*用來解析 json 用的*/
    private Gson mGson ;
    /*在用 gson 解析 json 資料的時候,需要用到這個引數*/
    private Type mClazz ;

    public GetObjectRequest(String url,Type type, ResponseListener listener) {
        super(Method.GET, url, listener);
        this.mListener = listener ;
        mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() ;
        mClazz = type ;
    }

    /**
     * 這裡開始解析資料
     * @param response Response from the network
     * @return
     */
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            T result ;
            String jsonString =
                    new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            result = mGson.fromJson(jsonString,mClazz) ;
            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);
    }
}

以上程式碼中在例項化 Gson 的時候用到的是mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation(),主要是用於過濾欄位用的.如果有疑問的同學可以參考我前面寫的一篇文章Gson 過濾欄位的幾種方法
第三步:獲取 request

Request request = new GetObjectRequest(url,new TypeToken<TestBean>(){}.getType(),listener) ;

1、url -> http://www.minongbang.com/test.php?test=minongbang
2、new TypeToken(){}.getType() ->為 gson 解析 json 資料所要的 type
3、listener -> 為我們定義的ResponseListener回撥介面
第四步:新增請求到佇列中

VolleyUtil.getRequestQueue().add(request) ;

所以,此介面的程式碼即為

    /**
     * Minong 測試資料get網路請求介面
     * @param value 要搜尋的關鍵字
     * @param listener 回撥介面,包含錯誤回撥和正確的資料回撥
     */
    public static void getObjectMiNongApi(String value,ResponseListener listener){
        String url ;
        try {
            url = Constant.MinongHost +"?test="+ URLEncoder.encode(value, "utf-8") ;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            url = Constant.MinongHost +"?test="+ URLEncoder.encode(value) ;
        }
        Request request = new GetObjectRequest(url,new TypeToken<TestBean>(){}.getType(),listener) ;
        VolleyUtil.getRequestQueue().add(request) ;
    }

第五步:程式碼測試

public class GetRequestActivity extends ActionBarActivity {

    /*資料顯示的View*/
    private TextView mIdTxt,mNameTxt,mDownloadTxt,mLogoTxt,mVersionTxt ;
    /*彈出等待對話方塊*/
    private ProgressDialog mDialog ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_get);
        mIdTxt = (TextView) findViewById(R.id.id_id) ;
        mNameTxt = (TextView) findViewById(R.id.id_name) ;
        mDownloadTxt = (TextView) findViewById(R.id.id_download) ;
        mLogoTxt = (TextView) findViewById(R.id.id_logo) ;
        mVersionTxt = (TextView) findViewById(R.id.id_version) ;
        mDialog = new ProgressDialog(this) ;
        mDialog.setMessage("get請求中...");
        mDialog.show();
        /*請求網路獲取資料*/
        MiNongApi.getObjectMiNongApi("minongbang",new ResponseListener<TestBean>() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mDialog.dismiss();
            }

            @Override
            public void onResponse(TestBean response) {
                mDialog.dismiss();
                /*顯示資料*/
                mIdTxt.setText(response.getId()+"");
                mNameTxt.setText(response.getName());
                mDownloadTxt.setText(response.getDownload()+"");
                mLogoTxt.setText(response.getLogo());
                mVersionTxt.setText(response.getVersion()+"");
            }
        });
    }

}

測試效果圖如下:
這裡寫圖片描述 這裡寫圖片描述
可以看到和我們在瀏覽器中請求的資料一模一樣!

2、POST請求
因為在講 get 請求的時候花了很大篇幅講原理,所以在 post 請求的時候,需要注意的東西相對來說比較少, 不管是 get 請求還是 post 請求,實現步驟是不會變。 這裡post 請求,我們也是用http://www.minongbang.com/test.php?test=minongbang這個 api 來測試!
在前面我們已經講到了,在同一個應用中共用同一個 RequestQueue,所以第一步可以省略,因為我們已經實現過了。這裡直接到定製Request,我們在學習網路程式設計的時候就已經知道,用 GET方式請求,請求的資料是直接跟在 URL的後面用”?”去分開了,如果有多個數據則用”&”分開。而 POST則把資料直接封裝在HTTP的包體中,兩者各有優缺點,自己衡量著用。
因為 api 介面還是同一個,所以返回的資料型別肯定是一樣的,在解析資料的時候就可以和 GetObjectRequest 複用,所以 PostObjectRequest 的實現可以通過繼承GetObjectRequest的方式,也可以直接拷貝一份出來,為了更好的區分,我這裡就直接拷貝一份,然後再稍加修改。

/**
 * Created by gyzhong on 15/3/1.
 */
public class PostObjectRequest<T> extends Request<T> {

    /**
     * 正確資料的時候回掉用
     */
    private ResponseListener mListener ;
    /*用來解析 json 用的*/
    private Gson mGson ;
    /*在用 gson 解析 json 資料的時候,需要用到這個引數*/
    private Type mClazz ;
    /*請求 資料通過引數的形式傳入*/
    private Map<String,String> mParams;
    //需要傳入引數,並且請求方式不能再為 get,改為 post
    public PostObjectRequest(String url, Map<String,String> params,Type type, ResponseListener listener) {
        super(Method.POST, url, listener);
        this.mListener = listener ;
        mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() ;
        mClazz = type ;
        setShouldCache(false);
        mParams = params ;
    }

    /**
     * 這裡開始解析資料
     * @param response Response from the network
     * @return
     */
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            T result ;
            String jsonString =
                    new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            Log.v("zgy", "====jsonString===" + jsonString);
            result = mGson.fromJson(jsonString,mClazz) ;
            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);
    }

//關鍵程式碼就在這裡,在 Volley 的網路操作中,如果判斷請求方式為 Post 則會通過此方法來獲取 param,所以在這裡返回我們需要的引數,
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return mParams;
    }
}

再來看看 api 介面怎麼實現,

    /*
    * *Minong 測試資料post網路請求介面
    * @param value 測試資料
    * @param listener 回撥介面,包含錯誤回撥和正確的資料回撥
    */
    public static void postObjectMinongApi(String value,ResponseListener listener){
        Map<String,String> param = new HashMap<String,String>() ;
        param.put("test",value) ;
        Request request = new PostObjectRequest(Constant.MinongHost,param,new TypeToken<TestBean>(){}.getType(),listener);
        VolleyUtil.getRequestQueue().add(request) ;
    }

跟 get 請求還是很相似的,只是在例項化 Request 的時候多傳入了一個param引數,並且 url 不能再是包含請求資料的 url。
介面 api測試程式碼

public class PostRequestActivity extends ActionBarActivity {

    /*資料顯示的View*/
    private TextView mIdTxt,mNameTxt,mDownloadTxt,mLogoTxt,mVersionTxt ;
    /*彈出等待對話方塊*/
    private ProgressDialog mDialog ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_get);
        mIdTxt = (TextView) findViewById(R.id.id_id) ;
        mNameTxt = (TextView) findViewById(R.id.id_name) ;
        mDownloadTxt = (TextView) findViewById(R.id.id_download) ;
        mLogoTxt = (TextView) findViewById(R.id.id_logo) ;
        mVersionTxt = (TextView) findViewById(R.id.id_version) ;
        mDialog = new ProgressDialog(this) ;
        mDialog.setMessage("post請求中...");
        mDialog.show();
        /*請求網路獲取資料*/
        MiNongApi.postObjectMinongApi("minongbang",new ResponseListener<TestBean>() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mDialog.dismiss();
            }

            @Override
            public void onResponse(TestBean response) {
                mDialog.dismiss();
                /*顯示資料*/
                mIdTxt.setText(response.getId()+"");
                mNameTxt.setText(response.getName());
                mDownloadTxt.setText(response.getDownload()+"");
                mLogoTxt.setText(response.getLogo());
                mVersionTxt.setText(response.getVersion()+"");
            }
        });
    }
}

測試資料顯示跟 get 請求完全相同;ok,以上就是 Volley GET請求和 POST請求的全部內容!接下來又到了總結的時候

四、總結

1、volley 適用於輕量高併發的網路請求,這裡補充一個知識點,因為 Volley 請求網路的資料全部儲存在記憶體中,所以 volley 不適合請求較大的資料,比如下載檔案,下載大圖片等。

2、volley 的使用遵循四個步驟
a、RequestQueue mRequestQueue = Vollay.newRequestQueue(Context context) ;
b、mRequestQueue.start()
c、Request mRequest = new ObjectRequst(…)
d、mRequestQueue.add(mRequest)

3、同一個程式中最好共用一個 RequestQueue。

4、可以根據介面的放回資料型別定製任意的 Request,volley 已經預設為我們實現了 StringRequest、JsonArrayRequest、JsonObjectRequest、ImageRequest四個請求型別。

最後如果覺得有用請繼續關注我的 blog,我將會在下篇 blog 中講解,Volley解析(二)之表單提交篇