Android訓(xùn)練課程(Android Training) - 使用Volley傳輸網(wǎng)絡(luò)數(shù)據(jù)(Transmitting Network Data Using Volley)

使用Volley傳輸網(wǎng)絡(luò)數(shù)據(jù)(Transmitting Network Data Using Volley)

Volley 是一個 HTTP 庫,它使得在Android應(yīng)用程序中操作網(wǎng)絡(luò)更容易,是重要的,更多快速的。Volley 屬于“開放源代碼項目”。.

Volley 提供了下列好處:

  • 自動化的網(wǎng)絡(luò)請求調(diào)度安排。
  • 多并發(fā)的網(wǎng)絡(luò)連接。
  • 對標(biāo)準(zhǔn)HTTP 透明化的硬盤和內(nèi)存 響應(yīng)緩存。 cache coherence.
  • 支持請求的優(yōu)先級。
  • 支持終止請求的 API. 你可以終止一個單獨的請求,或者終止一些范圍內(nèi)的,或者一定請求周期段的請求。
  • 輕松的定制化,比如重試和回退。
  • 強順序,它使得在網(wǎng)絡(luò)操作時,更容易的正確處理UI和提取數(shù)據(jù)的異步。
  • 調(diào)試和跟蹤工具。.
    Volley擅長的RPC類型(遠(yuǎn)程過程調(diào)用)的操作過去常常應(yīng)用于填充UI,例如提取一頁的搜索結(jié)果作為結(jié)構(gòu)化數(shù)據(jù)。它更容易和其他協(xié)議整合,和出色的支持原始字符串,圖片和JSON。它為你想要的特性提供內(nèi)建的支持,Volley 將你從樣板的代碼中解放處理,使得你將注意力集中在你的業(yè)務(wù)細(xì)節(jié)。

Volley 不適合用于 大文件的下載 或者流操作,因為Volley在解析過程中會持有所有的響應(yīng)內(nèi)容在內(nèi)存中。如果要大文件下載操作,考慮是使用其他替代,比如DownloadManager。

核心的Volley包開放在AOSP工程下的 frameworks/volley,并且包含了主要的請求調(diào)度通道,類似于公共應(yīng)用事業(yè),在Volley "toolbox."是有效的。最簡單的添加Volley到你的項目中的方式是 克隆Volley倉庫并且做為你項目中的library項目:

  1. 使用Git克隆Volley倉庫,在你的命令提示行下輸入下面的內(nèi)容:
    git clone https://android.googlesource.com/platform/frameworks/volley
  2. 導(dǎo)入下載的源代碼到你的項目中,并且作為你的library項目 (如果你使用 Eclipse,更多描述請閱讀 Managing Projects from Eclipse with ADT,) 或者編譯成一個 .jar 文件.

課程

  • 發(fā)送一個簡單請求 (Sending a Simple Request)
    學(xué)習(xí)如何使用Volley的默認(rèn)行為發(fā)送簡單請求,和如何去終止一個請求。
  • 設(shè)置請求隊列(Setting Up a RequestQueue)
    學(xué)習(xí)如何設(shè)置一個請求隊列,和如何使用一個單例模式來創(chuàng)建一個和你的App的生命周期一致的請求隊列 .
  • 構(gòu)造一個標(biāo)準(zhǔn)請求(Making a Standard Request)
    學(xué)習(xí)如何使用Volley的out-of-the-box請求類型(比如原始字符串,圖片,JSON)發(fā)送一個請求。
  • 實現(xiàn)自定義的請求(Implementing a Custom Request)
    學(xué)習(xí)如何實現(xiàn)自定義請求。

發(fā)送一個簡單請求(Sending a Simple Request)

在一個較高的水平,你使用Volley創(chuàng)建一個請求隊列并且傳入一個 請求對象 作為參數(shù)。請求隊列負(fù)責(zé)管理工作線程來 啟動網(wǎng)絡(luò)操作,讀取和寫入到緩存,和解析響應(yīng)。請求執(zhí)行解析原始響應(yīng),Volley小心的分發(fā)解析的響應(yīng)傳送到主線程。

這節(jié)課描述了如何使用Volley.newRequestQueue 這個便利的方法來發(fā)送一個請求。它為你配置了一個請求隊列。你可以通過學(xué)習(xí)下一課,“設(shè)置一個請求隊列( Setting Up a RequestQueue)”的內(nèi)容掌握如何配置請求隊列。

這節(jié)課也描述了如何添加一個請求到消息隊列,和終止一個請求。

添加網(wǎng)絡(luò)訪問權(quán)限(Add the INTERNET Permission)

要使用 Volley, 你必須在你的manifest文件中添加 android.permission.INTERNET 權(quán)限. 沒有這個,你的app將不能訪問網(wǎng)絡(luò)。

使用 newRequestQueue

Volley 提供了一個便利的方法 Volley.newRequestQueue 配置一個消息隊列,使用默認(rèn)值,和啟動隊列,例如:

final TextView mTextView = (TextView) findViewById(R.id.text);
...

// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "+ response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});
// Add the request to the RequestQueue.
queue.add(stringRequest);

Volley 總是傳遞那些解析后的響應(yīng)到主線程。運行在主線程的好處是非常便利的使用收到的數(shù)據(jù)去通知UI控件,就像 你可以在你的響應(yīng)handler里自由的直接修改UI控件,但是類庫提供的語義格外的重要,尤其是關(guān)聯(lián)到取消請求時。

閱讀 Setting Up a RequestQueue 章節(jié)可以獲得更多 設(shè)置請求隊列的內(nèi)容,它可以用來替代Volley.newRequestQueue便利的方法。

發(fā)送一個請求(Send a Request)

要發(fā)送一個請求,你可以簡單的構(gòu)造一個請求,并使用add() 方法添加到請求隊列,像上面描述的那樣.一旦你添加了請求,它被通過管道移動,獲得服務(wù),和獲得原始響應(yīng)和傳遞。

當(dāng)你調(diào)用了add() 方法,Volley啟動一個緩存處理線程和一個網(wǎng)絡(luò)分發(fā)線程池。當(dāng)你添加請求到隊列中,它被緩存線程獲拾取和分類: 如果請求可以從緩存中服務(wù),緩存中的原始響應(yīng)內(nèi)容被在緩存進(jìn)程中解析,并且解析后的響應(yīng)內(nèi)容被傳遞到主線程。如果請求無法從緩存中服務(wù),它將被放置在網(wǎng)絡(luò)隊列中。第一個活動的網(wǎng)絡(luò)線程從隊里中拿到它,處理HTTP傳輸,在工作線程中解析響應(yīng)的內(nèi)容,寫入響應(yīng)內(nèi)容到緩存,并且發(fā)送解析后的響應(yīng)傳遞到主線程中。

注意哪些 昂貴的操作,比如阻塞I/O,和解析/解碼,都是在工作線程中完成的。你可以在任何線程中添加請求,但是響應(yīng)總是被傳遞到主線程中。

圖表 1 插圖說明一個請求的生命周期:



圖 1. 請求的生命周期.

中斷一個請求(Cancel a Request)

要中斷一個請求, 在你的請求對象上 調(diào)用 cancel()方法.一旦被中斷后,Volley會確保 你的響應(yīng)處理器 絕對不被調(diào)用。實際意義是你可在你的activity中的onStop()方法中中斷你的等待中的請求,而且你不會被迫亂丟你的請求處理器,比如檢查getActivity() == null ,onSaveInstanceState() 方法是否已經(jīng)被調(diào)用,或者其他自衛(wèi)性的樣板代碼。

要獲得這樣行為的好處,典型情況下你不得不追蹤所有 “飛行中的(in-flight)”請求,以使得在合適的時機去終止它。這有一個更簡單的方法: 你可以為每一個請求關(guān)聯(lián)一個 標(biāo)簽對象。你可以使用這個標(biāo)簽來提供可被中斷請求的范圍。比如,你可以使用 Activity對象 標(biāo)記你所有的請求,并且在 onStop() 時調(diào)用 requestQueue.cancelAll(this) 。同樣的,你可以 在一個ViewPager選項卡中,使用他們各自的 選項卡對象 標(biāo)記 它們自己的所有的 縮略圖 請求,并在切換時觸發(fā)終止操作,以確保 新的選項卡對象不會被 其他選項卡的請求 所持有。

下面是一個使用 字符串值作為標(biāo)簽 的示例:

1.定義你的標(biāo)簽并且添加到你的請求上。

public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue;  // Assume this exists.

// Set the tag on the request.
stringRequest.setTag(TAG);

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);

2.在你的 activity的 onStop() 方法中, 終止所有標(biāo)記過這個標(biāo)簽的請求。

@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

當(dāng)調(diào)用終止請求時要非常小心。如果你 依賴 你的響應(yīng)處理器,以變動一個狀態(tài)或者踢開一些步驟,你需要記得這些。再次強調(diào),在終止后相應(yīng)處理絕不會被調(diào)用。

設(shè)置一個請求隊列(Setting Up a RequestQueue)

上節(jié)課展示了如何使用 Volley.newRequestQueue 這個便利的方法來設(shè)置一個請求隊列,以獲得Volley提供的默認(rèn)行為的好處。這節(jié)課教你通過明確的幾個步驟來創(chuàng)建一個請求隊列,使得你可以定制它。

這節(jié)課也描述一個推薦的實踐方式,使用單例模式創(chuàng)建一個請求隊列,這個請求隊列會持續(xù)整個App的生命周期。

設(shè)置一個網(wǎng)絡(luò)和緩存(Set Up a Network and Cache)

一個請求隊列要完成它自己的工作需要兩樣?xùn)|西: 一個 network(網(wǎng)絡(luò)) 對象處理請求的傳輸,和一個 cache(緩存)對象來處理緩存。在Volley 工具盒 中已經(jīng)有了里那兩個標(biāo)準(zhǔn)的可用的實現(xiàn): DiskBasedCache提供了一個 “每響應(yīng)單文件(one-file-per-response)” 的緩存并在內(nèi)存中建立索引; BasicNetwork對象提供了以 你自己選擇的AndroidHttpClient 或 HttpURLConnection 對象 為基礎(chǔ)的網(wǎng)絡(luò)傳輸。

BasicNetwork 是Volley的默認(rèn) network(網(wǎng)絡(luò)) 實現(xiàn)。一個 BasicNetwork 對象必須先被 使用HTTP客戶端 來初始化后才能連接網(wǎng)絡(luò)。比較有代表性的是AndroidHttpClient or HttpURLConnection:

  • 在Android API level 9 (Gingerbread)以下的應(yīng)用中,使用 AndroidHttpClient 。早于 Gingerbread, HttpURLConnection 是不可靠的. 更多請閱讀 Android's HTTP Clients.

  • 在Android API level 9 (Gingerbread)及更高版本,使用 HttpURLConnection .
    要創(chuàng)建一個可運行在Android全版本的應(yīng)用,你可用檢查Android硬件設(shè)備上運行的Android系統(tǒng)的版本號,以做出選擇是HTTP 客戶端,比如:

      HttpStack stack;
      ...
      // If the device is running a version >= Gingerbread...
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
          // ...use HttpURLConnection for stack.
      } else {
          // ...use AndroidHttpClient for stack.
      }
      Network network = new BasicNetwork(stack);
    

這個代碼片段展示了設(shè)置請求隊列的步驟:

    RequestQueue mRequestQueue;
    
    // Instantiate the cache
    Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
    
    // Set up the network to use HttpURLConnection as the HTTP client.
    Network network = new BasicNetwork(new HurlStack());
    
    // Instantiate the RequestQueue with the cache and network.
    mRequestQueue = new RequestQueue(cache, network);
    
    // Start the queue
    mRequestQueue.start();
    
    String url ="http://www.myurl.com";
    
    // Formulate the request and handle the response.
    StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            // Do something with the response
        }
    },
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // Handle error
        }
    });
    
    // Add the request to the RequestQueue.
    mRequestQueue.add(stringRequest);
    ...

如果你僅僅需要構(gòu)建單次的請求,并且不想離開線程池的范圍,你可用創(chuàng)建在任何地方創(chuàng)建請求隊列,和在收到響應(yīng)或者錯誤后調(diào)用stop()方法,使用Volley.newRequestQueue()的方法可參閱 Sending a Simple Request。但是更多一般的使用情形是 使用單例模式創(chuàng)建請求隊列并且讓它和你的應(yīng)用的生命周期一致,更多描述在下一章節(jié)。

使用單例模式(Use a Singleton Pattern)

如果你的應(yīng)用需要經(jīng)常訪問網(wǎng)絡(luò),那么配置一個單例模式的請求隊列并保持在app的整個生命周期的方式是非常有效率的。你可以有多種方式這樣實現(xiàn)。推薦的方式是實現(xiàn)一個單例類來封裝請求隊列和其他的Volley功能方法/函數(shù)。其他的實現(xiàn)方式比如實現(xiàn) Applicaton的子類并在Application.onCreate()方法中配置請求隊列,這樣的方式現(xiàn)在是被勸阻的;一個靜態(tài)的單例能夠以模塊化的方式提供同樣的功能。

一個關(guān)鍵概念是請求隊列必須使用Application的context對象來初始化,而不時 Activity的context.這樣確保請求隊列會持續(xù)在整個app的生命周期,而在activity的context的實現(xiàn)會在activity被重新創(chuàng)建時被創(chuàng)建多次(比如,當(dāng)用戶旋轉(zhuǎn)了屏幕就會重新創(chuàng)建activity)。

下面是一個實現(xiàn)了單例模式的類的示例,它提供了請求隊列和圖片下載器(ImageLoader)的功能:

    private static MySingleton mInstance;
        private RequestQueue mRequestQueue;
        private ImageLoader mImageLoader;
        private static Context mCtx;
    
        private MySingleton(Context context) {
            mCtx = context;
            mRequestQueue = getRequestQueue();
    
            mImageLoader = new ImageLoader(mRequestQueue,
                    new ImageLoader.ImageCache() {
                private final LruCache<String, Bitmap>
                        cache = new LruCache<String, Bitmap>(20);
    
                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }
    
                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
        }
    
        public static synchronized MySingleton getInstance(Context context) {
            if (mInstance == null) {
                mInstance = new MySingleton(context);
            }
            return mInstance;
        }
    
        public RequestQueue getRequestQueue() {
            if (mRequestQueue == null) {
                // getApplicationContext() is key, it keeps you from leaking the
                // Activity or BroadcastReceiver if someone passes one in.
                mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
            }
            return mRequestQueue;
        }
    
        public <T> void addToRequestQueue(Request<T> req) {
            getRequestQueue().add(req);
        }
    
        public ImageLoader getImageLoader() {
            return mImageLoader;
        }
    }

下面是使用單例類處理請求隊列操作的示例:

// Get a RequestQueue
RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
    getRequestQueue();
...

// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest);

構(gòu)建一個標(biāo)準(zhǔn)請求(Making a Standard Request)

這節(jié)課描述了如何使用Volley提供的一般請求類型:

  • StringRequest. 指定一個網(wǎng)址(URL)和在相應(yīng)里收到一個原始字符串. 在 Setting Up a Request Queue 有示例.
  • ImageRequest. 指定一個網(wǎng)址(URL)和在響應(yīng)中收到圖片.
  • JsonObjectRequest 和 JsonArrayRequest (都是 JsonRequest的子類). 指定一個網(wǎng)址(URL)和獲得一個JSON 對象或者JSON數(shù)組.
    如果你期望的響應(yīng)是上面這些中的一種,你可能不再需要實現(xiàn)自定義的請求。這節(jié)課描述了如何使用這些標(biāo)準(zhǔn)請求類型。更多關(guān)于自定義請求的內(nèi)容請閱讀Implementing a Custom Request.

請求一個圖片(Request an Image)

Volley 提供了下面的類用來請求圖片,這些類在它們彼此層次的頂層,提供不同程度的處理圖片的支持:

ImageRequest(圖片請求)— 一個封裝的請求,用于通過指定的URL獲得一個圖片和在對圖片解碼完畢后回調(diào)。它還提供了便利的特性比如指定一個將要調(diào)整到的尺寸。它非常有利的是,Volley的線程調(diào)度確保了昂貴的圖片操作(比如解碼,改變大小)等自動的在工作線程中執(zhí)行。
ImageLoader(圖片下載器)— 一個輔助類,處理通過多個遠(yuǎn)程圖片地址加載和緩存圖片。ImageLoader是一個處理大量圖片請求的控制臺,比如在一個ListView中放置多個縮略圖。ImageLoader 提供了一個內(nèi)存緩存,以在Volley一般的緩存之上,對于防止閃爍這是非常重要的。這提供了一個可能性去實現(xiàn)一個緩存的擊中,而不會在主線程阻塞或者發(fā)生延遲,如果使用 硬盤I/O 是不可能做到的。ImageLoader同時也做了響應(yīng)合并,沒有它,幾乎每個響應(yīng)處理器都會將圖片顯示在一個視圖View上和導(dǎo)致每個圖片發(fā)生一次布局(layout)操作。合并使得 同時傳遞多個響應(yīng) 成為可能,它提升了性能。
NetworkImageView— 以 ImageLoader為基礎(chǔ)和 有效的代替 ImageView 在一些狀況下,比如當(dāng)你的圖片通過一個網(wǎng)址在網(wǎng)絡(luò)中被提取。NetworkImageView也管理著 在一個NetworkImageView被從視圖層級中分離時終止等待中的請求。

使用 ImageRequest (Use ImageRequest)

下面是一個使用ImageRequest的示例。它通過一個URL獲得圖片并在應(yīng)用中顯示。注意這里使用單例模式和請求隊里進(jìn)行的交互。 (更多關(guān)于這個話題的討論請閱讀 Setting Up a RequestQueue):

    ImageView mImageView;
    String url = "http://i.imgur.com/7spzG.png";
    mImageView = (ImageView) findViewById(R.id.myImage);
    ...
    
    // Retrieves an image specified by the URL, displays it in the UI.
    ImageRequest request = new ImageRequest(url,
        new Response.Listener() {
            @Override
            public void onResponse(Bitmap bitmap) {
                mImageView.setImageBitmap(bitmap);
            }
        }, 0, 0, null,
        new Response.ErrorListener() {
            public void onErrorResponse(VolleyError error) {
                mImageView.setImageResource(R.drawable.image_load_error);
            }
        });
    // Access the RequestQueue through your singleton class.
    MySingleton.getInstance(this).addToRequestQueue(request);

使用 ImageLoader 和 NetworkImageView(Use ImageLoader and NetworkImageView)

你可以使用 ImageLoader 和 NetworkImageView 協(xié)調(diào)有效的管理大量圖片的顯示, 比如在 ListView中.在你的布局 XML 文件中, 使用 NetworkImageView和使用 ImageView 非常相似, 例如:

<com.android.volley.toolbox.NetworkImageView
        android:id="@+id/networkImageView"
        android:layout_width="150dp"
        android:layout_height="170dp"
        android:layout_centerHorizontal="true" />

你可以使用 ImageLoader 通過它自己來顯示一個圖片,例如:

ImageLoader mImageLoader;
ImageView mImageView;
// The URL for the image that is being loaded.
private static final String IMAGE_URL =
    "http://developer.android.com/images/training/system-ui.png";
...
mImageView = (ImageView) findViewById(R.id.regularImageView);

// Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
         R.drawable.def_image, R.drawable.err_image));

然而, 如果你的操作僅僅是顯示圖片在 ImageView中的話,那么 NetworkImageView 也可以做到這些你想做到的,例如:

    ImageLoader mImageLoader;
    NetworkImageView mNetworkImageView;
    private static final String IMAGE_URL =
        "http://developer.android.com/images/training/system-ui.png";
    ...
    
    // Get the NetworkImageView that will display the image.
    mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);
    
    // Get the ImageLoader through your singleton class.
    mImageLoader = MySingleton.getInstance(this).getImageLoader();
    
    // Set the URL of the image that should be loaded into this view, and
    // specify the ImageLoader that will be used to make the request.
    mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);

上面這些代碼片段演示了通過一個單例類訪問請求隊列(RequestQueue)和圖片下載(ImageLoader)的方式,更多描述見Setting Up a RequestQueue。這樣的方式確保了你創(chuàng)建這些類的單個實例并且持續(xù)整個App的生命周期。非常重要的原因是對于ImageLoader(這個幫助類處理讀取和緩存圖片)來說,內(nèi)存緩存的主要功能是做到了在翻轉(zhuǎn)屏幕時不閃爍。使用一個單例模式允許位圖緩存比activity活得長。如果你在Activity中創(chuàng)建了一個ImageLoader,這個ImageLoader將跟隨activity,每次用戶翻轉(zhuǎn)設(shè)備時都會重新創(chuàng)建而發(fā)生屏幕閃爍。

LRU緩存示例(Example LRU cache)

Volley工具盒提供了基于DiskBasedCache類的標(biāo)準(zhǔn)緩存的實現(xiàn)。這個類緩存一個文件直接到硬盤上的指定文件夾。但是要使用ImageDownloader,你需要提供一個自定義的內(nèi)存 LRU位圖緩存并需要實現(xiàn)ImageLoader.ImageCache接口。你可以使用單例類設(shè)置你的緩存;更多討論請閱讀 Setting Up a RequestQueue.

下面的LruBitmapCache類是一個實現(xiàn)的示例。它繼承自 LruCache 并實現(xiàn)了 ImageLoader.ImageCache interface接口:

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import com.android.volley.toolbox.ImageLoader.ImageCache;

public class LruBitmapCache extends LruCache<String, Bitmap>
        implements ImageCache {

    public LruBitmapCache(int maxSize) {
        super(maxSize);
    }

    public LruBitmapCache(Context ctx) {
        this(getCacheSize(ctx));
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }

    // Returns a cache size equal to approximately three screens worth of images.
    public static int getCacheSize(Context ctx) {
        final DisplayMetrics displayMetrics = ctx.getResources().
                getDisplayMetrics();
        final int screenWidth = displayMetrics.widthPixels;
        final int screenHeight = displayMetrics.heightPixels;
        // 4 bytes per pixel
        final int screenBytes = screenWidth * screenHeight * 4;

        return screenBytes * 3;
    }
}

下面演示了如何實例化一個ImageLoader和使用緩存:

RequestQueue mRequestQueue; // assume this exists.
ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(
            LruBitmapCache.getCacheSize()));

請求JSON (Request JSON)

Volley 為JSON請求提供了下面的類:

  • JsonArrayRequest — 通過一個指定的URL,發(fā)送請求到獲得一個JSONArray (JSON數(shù)組)響應(yīng)體。
  • JsonObjectRequest — 通過一個指定的URL,發(fā)送請求到獲得一個 JSONObject (JSON對象)響應(yīng)體,它允許一個可選的JSONObject對象作為參數(shù)通過作為請求體的一部分被傳送。
    這些類都是基于一般基礎(chǔ)類JsonRequest的。你可以使用他們就像其他類型的請求一樣,比如,下面的示例演示了提取一個JSON feed和以文本的形式在UI顯示它。

Both classes are based on the common base class JsonRequest. You use them following the same basic pattern you use for other types of requests. For example, this snippet fetches a JSON feed提要 and displays it as text in the UI:

TextView mTxtDisplay;
ImageView mImageView;
mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);
String url = "http://my-json-feed";

JsonObjectRequest jsObjRequest = new JsonObjectRequest
        (Request.Method.GET, url, null, new Response.Listener() {

    @Override
    public void onResponse(JSONObject response) {
        mTxtDisplay.setText("Response: " + response.toString());
    }
}, new Response.ErrorListener() {

    @Override
    public void onErrorResponse(VolleyError error) {
        // TODO Auto-generated method stub

    }
});

// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);

要獲得一個基于Gson的實習(xí)自定義JSON請求的示例,請閱讀下一課, Implementing a Custom Request.

實現(xiàn)一個自定義請求(Implementing a Custom Request)

這節(jié)課描述了如何實現(xiàn)你的自定義請求類型,這些類型是沒有被包含在 Volley支持的 out-of-the-box 類型的。

寫一個自定義請求(Write a Custom Request)

在工具盒中,有很多請求是可以 準(zhǔn)備-即用 的;如果你的響應(yīng)是一個字符串,圖片,或者JSON,或許你不再需要去實現(xiàn)一個自定義請求。

在那些你想要去實現(xiàn)自定義請求的情形中,這些時你需要做的:

  • 繼承 Request<T> 類, 在這里 <T>表示你的請求期望解析到的類型。如果你的解析后的響應(yīng)是一個字符串,例如,創(chuàng)建你的自定義請求時要繼承Request<String>。詳情見更多Volley工具盒中的類 StringRequest 和 ImageRequest 繼承自 Request<T>的示例。
    *Implement the abstract methods parseNetworkResponse() and deliverResponse(), described in more detail below.

解析網(wǎng)絡(luò)請求(parseNetworkResponse)

一個響應(yīng)封裝了一個解析后的響應(yīng)用于傳輸,為了一個指定的類型(比如字符串,圖片,或者JSON)。 下面是一個實現(xiàn) parseNetworkResponse() 的示例:

    @Override
    protected Response<T> parseNetworkResponse(
            NetworkResponse response) {
        try {
            String json = new String(response.data,
            HttpHeaderParser.parseCharset(response.headers));
        return Response.success(gson.fromJson(json, clazz),
        HttpHeaderParser.parseCacheHeaders(response));
        }
        // handle errors
    ...
    }

注意下面:

  • parseNetworkResponse() 有一個參數(shù) NetworkResponse, 它包含了 裝載了 字節(jié)數(shù)組 byte[] 的響應(yīng),HTTP狀態(tài)碼,和響應(yīng)頭。
  • 你的實現(xiàn)必須返回一個 Response<T>, 它包含了你的類型化的(強類型的)響應(yīng)對象和緩存元數(shù)據(jù),或者在解析失敗時的錯誤

如果你的協(xié)議中包含了非標(biāo)準(zhǔn)的語義,你可以構(gòu)造一個你自己的 Cache.Entry,但是大多數(shù)的請求像下面這樣是好用的:

return Response.success(myDecodedObject,
        HttpHeaderParser.parseCacheHeaders(response));

Volley 在一個工作現(xiàn)場中調(diào)用 parseNetworkResponse(). 這確保了昂貴的解析操作比如對一個JPEG圖像進(jìn)行解碼到一個位圖對象中,不會阻塞UI線程。

傳輸響應(yīng) (deliverResponse)

Volley 使用你的 parseNetworkResponse()中返回的對象到主線程進(jìn)行回調(diào)。許多請求在這里調(diào)用回調(diào)接口,例如:

protected void deliverResponse(T response) {
        listener.onResponse(response);

GSON請求示例:GsonRequest(Example: GsonRequest)

Gson 是一個類庫,使用反射的方法作用于互相轉(zhuǎn)換Java對象和JSON 。你可以定義一個和那些JSON的鍵名稱具有相同名稱的字段的Java對象。將類的對象傳遞給GSON,而GSON將會為你自動填充這些字段的值。下面是一個使用GSON解析的Volley請求的完整的實現(xiàn):

public class GsonRequest<T> extends Request<T> {
    private final Gson gson = new Gson();
    private final Class<T> clazz;
    private final Map<String, String> headers;
    private final Listener<T> listener;

    /**
     * Make a GET request and return a parsed object from JSON.
     *
     * @param url URL of the request to make
     * @param clazz Relevant class object, for Gson's reflection
     * @param headers Map of request headers
     */
    public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
            Listener<T> listener, ErrorListener errorListener) {
        super(Method.GET, url, errorListener);
        this.clazz = clazz;
        this.headers = headers;
        this.listener = listener;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }

    @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(
                    response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            return Response.success(
                    gson.fromJson(json, clazz),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }
}

Volley 提供了 準(zhǔn)備-即用 的 JsonArrayRequest 和 JsonArrayObject 類, 如果你更加喜歡這種方式. 請閱讀 Using Standard Request Types 獲得更多信息。

(全文完結(jié))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容