diff options
Diffstat (limited to 'docs/html/training')
-rw-r--r-- | docs/html/training/basics/network-ops/connecting.jd | 1 | ||||
-rw-r--r-- | docs/html/training/basics/network-ops/index.jd | 9 | ||||
-rw-r--r-- | docs/html/training/contacts-provider/retrieve-names.jd | 12 | ||||
-rw-r--r-- | docs/html/training/training_toc.cs | 29 | ||||
-rw-r--r-- | docs/html/training/volley/index.jd | 133 | ||||
-rw-r--r-- | docs/html/training/volley/request-custom.jd | 163 | ||||
-rw-r--r-- | docs/html/training/volley/request.jd | 281 | ||||
-rw-r--r-- | docs/html/training/volley/requestqueue.jd | 204 | ||||
-rw-r--r-- | docs/html/training/volley/simple.jd | 169 |
9 files changed, 996 insertions, 5 deletions
diff --git a/docs/html/training/basics/network-ops/connecting.jd b/docs/html/training/basics/network-ops/connecting.jd index 50a9e1b..1452ded 100644 --- a/docs/html/training/basics/network-ops/connecting.jd +++ b/docs/html/training/basics/network-ops/connecting.jd @@ -25,6 +25,7 @@ next.link=managing.html <h2>You should also read</h2> <ul> + <li><a href="{@docRoot}training/volley/index.html">Transmitting Network Data Using Volley</a></li> <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li> <li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li> <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li> diff --git a/docs/html/training/basics/network-ops/index.jd b/docs/html/training/basics/network-ops/index.jd index 89ab539..1f6493f 100644 --- a/docs/html/training/basics/network-ops/index.jd +++ b/docs/html/training/basics/network-ops/index.jd @@ -24,6 +24,7 @@ next.link=connecting.html <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li> <li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li> <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li> + <li><a href="{@docRoot}training/volley/index.html">Transmitting Network Data Using Volley</a></li> </ul> @@ -51,6 +52,14 @@ as a source of reusable code for your own application.</p> fundamental building blocks for creating Android applications that download content and parse data efficiently, while minimizing network traffic.</p> +<p class="note"><strong>Note:</strong> See the class <a href="{@docRoot} +training/volley/index.html">Transmitting Network Data Using Volley</a> +for information on Volley, an HTTP library that makes networking for Android apps +easier and faster. Volley is available through the open +<a href="https://android.googlesource.com/platform/frameworks/volley">AOSP</a> +repository. Volley may be able to help you streamline and improve the performance +of your app's network operations.</p> + <h2>Lessons</h2> diff --git a/docs/html/training/contacts-provider/retrieve-names.jd b/docs/html/training/contacts-provider/retrieve-names.jd index b034a6a..7106889 100644 --- a/docs/html/training/contacts-provider/retrieve-names.jd +++ b/docs/html/training/contacts-provider/retrieve-names.jd @@ -102,9 +102,9 @@ trainingnavtop=true <p> To display the search results in a {@link android.widget.ListView}, you need a main layout file that defines the entire UI including the {@link android.widget.ListView}, and an item layout - file that defines one line of the {@link android.widget.ListView}. For example, you can define - the main layout file <code>res/layout/contacts_list_view.xml</code> that contains the - following XML: + file that defines one line of the {@link android.widget.ListView}. For example, you could create + the main layout file <code>res/layout/contacts_list_view.xml</code> with + the following XML: </p> <pre> <?xml version="1.0" encoding="utf-8"?> @@ -250,7 +250,8 @@ public class ContactsFragment extends Fragment implements public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the fragment layout - return inflater.inflate(R.layout.contacts_list_layout, container, false); + return inflater.inflate(R.layout.contact_list_fragment, + container, false); } </pre> <h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3> @@ -268,7 +269,8 @@ public class ContactsFragment extends Fragment implements super.onActivityCreated(savedInstanceState); ... // Gets the ListView from the View list of the parent activity - mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view); + mContactsList = + (ListView) getActivity().findViewById(R.layout.contact_list_view); // Gets a CursorAdapter mCursorAdapter = new SimpleCursorAdapter( getActivity(), diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 0616b62..c5dc3c5 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -609,6 +609,35 @@ include the action bar on devices running Android 2.1 or higher." </li> </ul> </li> + <li class="nav-section"> + <div class="nav-section-header"> + <a href="<?cs var:toroot ?>training/volley/index.html" + description="How to perform fast, scalable UI operations over the network using Volley" + >Transmitting Network Data Using Volley</a> + </div> + <ul> + <li> + <a href="<?cs var:toroot ?>training/volley/simple.html"> + Sending a Simple Request + </a> + </li> + <li> + <a href="<?cs var:toroot ?>training/volley/requestqueue.html"> + Setting Up a RequestQueue + </a> + </li> + <li> + <a href="<?cs var:toroot ?>training/volley/request.html"> + Making a Standard Request + </a> + </li> + <li> + <a href="<?cs var:toroot ?>training/volley/request-custom.html"> + Implementing a Custom Request + </a> + </li> + </ul> + </li> </ul> </li> diff --git a/docs/html/training/volley/index.jd b/docs/html/training/volley/index.jd new file mode 100644 index 0000000..ba5b09f --- /dev/null +++ b/docs/html/training/volley/index.jd @@ -0,0 +1,133 @@ +page.title=Transmitting Network Data Using Volley +page.tags="" + +trainingnavtop=true +startpage=true + + +@jd:body + + + +<div id="tb-wrapper"> +<div id="tb"> + + +<!-- Required platform, tools, add-ons, devices, knowledge, etc. --> +<h2>Dependencies and prerequisites</h2> + +<ul> + <li>Android 1.6 (API Level 4) or higher</li> +</ul> + +<h2>You should also see</h2> +<ul> + <li>For a production quality app that uses Volley, see the 2013 Google I/O + <a href="https://github.com/google/iosched">schedule app</a>. In particular, see: + <ul> + <li><a + href="https://github.com/google/iosched/blob/master/android/src/main/java/com/google/android/apps/iosched/util/ImageLoader.java"> + ImageLoader</a></li> + <li><a + href="https://github.com/google/iosched/blob/master/android/src/main/java/com/google/android/apps/iosched/util/BitmapCache.java"> + BitmapCache</a></li> + </ul> + </li> +</ul> + +</div> +</div> + +<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728"> +<div> + <h3>Video</h3> + <p>Volley: Easy, Fast Networking for Android</p> +</div> +</a> + + +<p>Volley is an HTTP library that makes networking for Android apps easier and most importantly, +faster. Volley is available through the open +<a href="https://android.googlesource.com/platform/frameworks/volley">AOSP</a> repository.</p> + +<p>Volley offers the following benefits:</p> + +<ul> + +<li>Automatic scheduling of network requests.</li> +<li>Multiple concurrent network connections.</li> +<li>Transparent disk and memory response caching with standard HTTP +<a href=http://en.wikipedia.org/wiki/Cache_coherence">cache coherence</a>.</li> +<li>Support for request prioritization.</li> +<li>Cancellation request API. You can cancel a single request, or you can set blocks or +scopes of requests to cancel.</li> +<li>Ease of customization, for example, for retry and backoff.</li> +<li>Strong ordering that makes it easy to correctly populate your UI with data fetched +asynchronously from the network.</li> +<li>Debugging and tracing tools.</li> + +</ul> + +<p>Volley excels at RPC-type operations used to populate a UI, such as fetching a page of +search results as structured data. It integrates easily with any protocol and comes out of +the box with support for raw strings, images, and JSON. By providing built-in support for +the features you need, Volley frees you from writing boilerplate code and allows you to +concentrate on the logic that is specific to your app.</p> +<p>Volley is not suitable for large download or streaming operations, since Volley holds +all responses in memory during parsing. For large download operations, consider using an +alternative like {@link android.app.DownloadManager}.</p> + +<p>The core Volley library is developed in the open +<a href="https://android.googlesource.com/platform/frameworks/volley">AOSP</a> +repository at {@code frameworks/volley} and contains the main request dispatch pipeline +as well as a set of commonly applicable utilities, available in the Volley "toolbox." The +easiest way to add Volley to your project is to clone the Volley repository and set it as +a library project:</p> + +<ol> +<li>Git clone the repository by typing the following at the command line: + +<pre> +git clone https://android.googlesource.com/platform/frameworks/volley +</pre> +</li> + +<li>Import the downloaded source into your app project as an Android library project +(as described in <a href="{@docRoot}tools/projects/projects-eclipse.html"> +Managing Projects from Eclipse with ADT</a>, if you're using Eclipse) or make a +<a href="{@docRoot}guide/faq/commontasks.html#addexternallibrary"><code>.jar</code> file</a>.</li> +</ol> + +<h2>Lessons</h2> + +<dl> + <dt> + <strong><a href="simple.html">Sending a Simple Request</a></strong> + </dt> + <dd> + Learn how to send a simple request using the default behaviors of Volley, and how + to cancel a request. + + </dd> + <dt> + <strong><a href="requestqueue.html">Setting Up a RequestQueue</a></strong> + </dt> + <dd> + Learn how to set up a {@code RequestQueue}, and how to implement a singleton + pattern to create a {@code RequestQueue} that lasts the lifetime of your app. + </dd> + <dt> + <strong><a href="request.html">Making a Standard Request</a></strong> + </dt> + <dd> + Learn how to send a request using one of Volley's out-of-the-box request types + (raw strings, images, and JSON). + </dd> + <dt> + <strong><a href="request-custom.html">Implementing a Custom Request</a></strong> + </dt> + <dd> + Learn how to implement a custom request. + </dd> + +</dl> diff --git a/docs/html/training/volley/request-custom.jd b/docs/html/training/volley/request-custom.jd new file mode 100644 index 0000000..7b669b9 --- /dev/null +++ b/docs/html/training/volley/request-custom.jd @@ -0,0 +1,163 @@ +page.title=Implementing a Custom Request + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<!-- table of contents --> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#custom-request">Write a Custom Request</a></li> +</ol> + +</div> +</div> + +<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728"> +<div> + <h3>Video</h3> + <p>Volley: Easy, Fast Networking for Android</p> +</div> +</a> + +<p>This lesson describes how to implement your own custom request types, for types that +don't have out-of-the-box Volley support.</p> + +<h2 id="custom-request">Write a Custom Request</h2> + +Most requests have ready-to-use implementations in the toolbox; if your response is a string, +image, or JSON, you probably won't need to implement a custom {@code Request}.</p> + +<p>For cases where you do need to implement a custom request, this is all you need +to do:</p> + +<ul> + +<li>Extend the {@code Request<T>} class, where +{@code <T>} represents the type of parsed response +the request expects. So if your parsed response is a string, for example, +create your custom request by extending {@code Request<String>}. See the Volley +toolbox classes {@code StringRequest} and {@code ImageRequest} for examples of +extending {@code Request<T>}.</li> + +<li>Implement the abstract methods {@code parseNetworkResponse()} +and {@code deliverResponse()}, described in more detail below.</li> + +</ul> + +<h3>parseNetworkResponse</h3> + +<p>A {@code Response} encapsulates a parsed response for delivery, for a given type +(such as string, image, or JSON). Here is a sample implementation of +{@code parseNetworkResponse()}:</p> + +<pre> +@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 +... +} +</pre> + +<p>Note the following:</p> + +<ul> +<li>{@code parseNetworkResponse()} takes as its parameter a {@code NetworkResponse}, which +contains the response payload as a byte[], HTTP status code, and response headers.</li> +<li>Your implementation must return a {@code Response<T>}, which contains your typed +response object and cache metadata or an error, such as in the case of a parse failure.</li> +</ul> + +<p>If your protocol has non-standard cache semantics, you can build a {@code Cache.Entry} +yourself, but most requests are fine with something like this: +</p> +<pre>return Response.success(myDecodedObject, + HttpHeaderParser.parseCacheHeaders(response));</pre> +<p> +Volley calls {@code parseNetworkResponse()} from a worker thread. This ensures that +expensive parsing operations, such as decoding a JPEG into a Bitmap, don't block the UI +thread.</p> + +<h3>deliverResponse</h3> + +<p>Volley calls you back on the main thread with the object you returned in +{@code parseNetworkResponse()}. Most requests invoke a callback interface here, +for example: +</p> + +<pre> +protected void deliverResponse(T response) { + listener.onResponse(response); +</pre> + +<h3>Example: GsonRequest</h3> + +<p><a href="http://code.google.com/p/google-gson/">Gson</a> is a library for converting +Java objects to and from JSON using reflection. You can define Java objects that have the +same names as their corresponding JSON keys, pass Gson the class object, and Gson will fill +in the fields for you. Here's a complete implementation of a Volley request that uses +Gson for parsing:</p> + +<pre> +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)); + } + } +} +</pre> + +<p>Volley provides ready-to-use {@code JsonArrayRequest} and {@code JsonArrayObject} classes +if you prefer to take that approach. See <a href="request.html"> +Using Standard Request Types</a> for more information.</p> diff --git a/docs/html/training/volley/request.jd b/docs/html/training/volley/request.jd new file mode 100644 index 0000000..d8ccab2 --- /dev/null +++ b/docs/html/training/volley/request.jd @@ -0,0 +1,281 @@ +page.title=Making a Standard Request + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<!-- table of contents --> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#request-image">Request an Image</a></li> + <li><a href="#request-json">Request JSON</a></li> +</ol> + +</div> +</div> + +<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728"> +<div> + <h3>Video</h3> + <p>Volley: Easy, Fast Networking for Android</p> +</div> +</a> + +<p> +This lesson describes how to use the common request types that Volley supports:</p> + +<ul> + <li>{@code StringRequest}. Specify a URL and receive a raw string in response. See + <a href="requestqueue.html">Setting Up a Request Queue</a> for an example.</li> + <li>{@code ImageRequest}. Specify a URL and receive an image in response.</li> + <li>{@code JsonObjectRequest} and {@code JsonArrayRequest} (both subclasses of + {@code JsonRequest}). Specify a URL and get a JSON object or array (respectively) in + response.</li> +</ul> + +<p>If your expected response is one of these types, you probably won't have to implement a +custom request. This lesson describes how to use these standard request types. For +information on how to implement your own custom request, see <a href="requests-custom.html"> +Implementing a Custom Request</a>.</p> + + +<h2 id="request-image">Request an Image</h2> + +<p>Volley offers the following classes for requesting images. These classes layer on top +of each other to offer different levels of support for processing images:</p> + +<ul> + <li>{@code ImageRequest}—a canned request for getting an image at a given URL and + calling back with a decoded bitmap. It also provides convenience features like specifying + a size to resize to. Its main benefit is that Volley's thread scheduling ensures that + expensive image operations (decoding, resizing) automatically happen on a worker thread.</li> + + <li>{@code ImageLoader}—a helper class that handles loading and caching images from + remote URLs. {@code ImageLoader} is a an orchestrator for large numbers of {@code ImageRequest}s, + for example when putting multiple thumbnails in a {@link android.widget.ListView}. + {@code ImageLoader} provides an in-memory cache to sit in front of the normal Volley + cache, which is important to prevent flickering. This makes it possible to achieve a + cache hit without blocking or deferring off the main thread, which is impossible when + using disk I/O. {@code ImageLoader} also does response coalescing, without which almost + every response handler would set a bitmap on a view and cause a layout pass per image. + Coalescing makes it possible to deliver multiple responses simultaneously, which improves + performance.</li> + <li>{@code NetworkImageView}—builds on {@code ImageLoader} and effectively replaces + {@link android.widget.ImageView} for situations where your image is being fetched over + the network via URL. {@code NetworkImageView} also manages canceling pending requests if + the view is detached from the hierarchy.</li> +</ul> + +<h3>Use ImageRequest</h3> + +<p>Here is an example of using {@code ImageRequest}. It retrieves the image specified by +the URL and displays it in the app. Note that this snippet interacts with the +{@code RequestQueue} through a singleton class (see <a href="{@docRoot} +training/volley/requestqueue.html#singleton">Setting Up a RequestQueue</a> for more discussion of +this topic):</p> + +<pre> +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<Bitmap>() { + @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);</pre> + + +<h3>Use ImageLoader and NetworkImageView</h3> + +<p>You can use {@code ImageLoader} and {@code NetworkImageView} in concert to efficiently +manage the display of multiple images, such as in a {@link android.widget.ListView}. In your +layout XML file, you use {@code NetworkImageView} in much the same way you would use +{@link android.widget.ImageView}, for example:</p> + +<pre><com.android.volley.toolbox.NetworkImageView + android:id="@+id/networkImageView" + android:layout_width="150dp" + android:layout_height="170dp" + android:layout_centerHorizontal="true" /></pre> + +<p>You can use {@code ImageLoader} by itself to display an image, for example:</p> + +<pre> +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)); +</pre> + +<p>However, {@code NetworkImageView} can do this for you if all you're doing is populating +an {@link android.widget.ImageView}. For example:</p> + +<pre> +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); +</pre> + +<p>The above snippets access the {@code RequestQueue} and the {@code ImageLoader} +through a singleton class, as described in <a href="{@docRoot}training/volley/requestqueue.html#singleton"> +Setting Up a RequestQueue</a>. This approach ensures that your app creates single instances of +these classes that last the lifetime of your app. The reason that this is important for +{@code ImageLoader} (the helper class that handles loading and caching images) is that +the main function of the in-memory cache is to allow for flickerless rotation. Using a +singleton pattern allows the bitmap cache to outlive the activity. If instead you create the +{@code ImageLoader} in an activity, the {@code ImageLoader} would be recreated along with +the activity every time the user rotates the device. This would cause flickering.</p> + +<h4 id="lru-cache">Example LRU cache</h4> + +<p>The Volley toolbox provides a standard cache implementation via the +{@code DiskBasedCache} class. This class caches files directly onto the hard disk in the +specified directory. But to use {@code ImageLoader}, you should provide a custom +in-memory LRU bitmap cache that implements the {@code ImageLoader.ImageCache} interface. +You may want to set up your cache as a singleton; for more discussion of this topic, see +<a href="{@docRoot}training/volley/requestqueue.html#singleton"> +Setting Up a RequestQueue</a>.</p> + +<p>Here is a sample implementation for an in-memory {@code LruBitmapCache} class. +It extends the {@link android.support.v4.util.LruCache} class and implements the +{@code ImageLoader.ImageCache} interface:</p> + +<pre> +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; + } +} +</pre> + +<p>Here is an example of how to instantiate an {@code ImageLoader} to use this +cache:</p> + +<pre> +RequestQueue mRequestQueue; // assume this exists. +ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache( + LruBitmapCache.getCacheSize())); +</pre> + + +<h2 id="request-json">Request JSON</h2> + +<p>Volley provides the following classes for JSON requests:</p> + +<ul> + <li>{@code JsonArrayRequest}—A request for retrieving a + {@link org.json.JSONArray} + response body at a given URL.</li> + <li>{@code JsonObjectRequest}—A request for retrieving a + {@link org.json.JSONObject} + response body at a given URL, allowing for an optional + {@link org.json.JSONObject} + to be passed in as part of the request body.</li> +</ul> + +<p>Both classes are based on the common base class {@code 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:</p> + +<pre> +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<JSONObject>() { + + @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); +</pre> + +For an example of implementing a custom JSON request based on +<a href="http://code.google.com/p/google-gson/">Gson</a>, see the next lesson, +<a href="request-custom.html">Implementing a Custom Request</a>. diff --git a/docs/html/training/volley/requestqueue.jd b/docs/html/training/volley/requestqueue.jd new file mode 100644 index 0000000..6858d91 --- /dev/null +++ b/docs/html/training/volley/requestqueue.jd @@ -0,0 +1,204 @@ +page.title=Setting Up a RequestQueue + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<!-- table of contents --> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#network">Set Up a Network and Cache</a></li> + <li><a href="#singleton">Use a Singleton Pattern</a></li> +</ol> + +</div> +</div> + +<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728"> +<div> + <h3>Video</h3> + <p>Volley: Easy, Fast Networking for Android</p> +</div> +</a> + + +<p>The previous lesson showed you how to use the convenience method +<code>Volley.newRequestQueue</code> to set up a {@code RequestQueue}, taking advantage of +Volley's default behaviors. This lesson walks you through the explicit steps of creating a +{@code RequestQueue}, to allow you to supply your own custom behavior.</p> + +<p>This lesson also describes the recommended practice of creating a {@code RequestQueue} +as a singleton, which makes the {@code RequestQueue} last the lifetime of your app.</p> + +<h2 id="network">Set Up a Network and Cache</h2> + +<p>A {@code RequestQueue} needs two things to do its job: a network to perform transport +of the requests, and a cache to handle caching. There are standard implementations of these +available in the Volley toolbox: {@code DiskBasedCache} provides a one-file-per-response +cache with an in-memory index, and {@code BasicNetwork} provides a network transport based +on your choice of {@link android.net.http.AndroidHttpClient} or {@link java.net.HttpURLConnection}.</p> + +<p>{@code BasicNetwork} is Volley's default network implementation. A {@code BasicNetwork} +must be initialized with the HTTP client your app is using to connect to the network. +Typically this is {@link android.net.http.AndroidHttpClient} or +{@link java.net.HttpURLConnection}:</p> +<ul> +<li>Use {@link android.net.http.AndroidHttpClient} for apps targeting Android API levels +lower than API Level 9 (Gingerbread). Prior to Gingerbread, {@link java.net.HttpURLConnection} +was unreliable. For more discussion of this topic, see +<a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html"> +Android's HTTP Clients</a>. </li> + +<li>Use {@link java.net.HttpURLConnection} for apps targeting Android API Level 9 +(Gingerbread) and higher.</li> +</ul> +<p>To create an app that runs on all versions of Android, you can check the version of +Android the device is running and choose the appropriate HTTP client, for example:</p> + +<pre> +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); +</pre> + +<p>This snippet shows you the steps involved in setting up a +{@code RequestQueue}:</p> + +<pre> +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); +... +</pre> + +<p>If you just need to make a one-time request and don't want to leave the thread pool +around, you can create the {@code RequestQueue} wherever you need it and call {@code stop()} on the +{@code RequestQueue} once your response or error has come back, using the +{@code Volley.newRequestQueue()} method described in <a href="simple.html">Sending a Simple +Request</a>. But the more common use case is to create the {@code RequestQueue} as a +singleton to keep it running for the lifetime of your app, as described in the next section.</p> + + +<h2 id="singleton">Use a Singleton Pattern</h2> + +<p>If your application makes constant use of the network, it's probably most efficient to +set up a single instance of {@code RequestQueue} that will last the lifetime of your app. +You can achieve this in various ways. The recommended approach is to implement a singleton +class that encapsulates {@code RequestQueue} and other Volley +functionality. Another approach is to subclass {@link android.app.Application} and set up the +{@code RequestQueue} in {@link android.app.Application#onCreate Application.onCreate()}. +But this approach is <a href="{@docRoot}reference/android/app/Application.html"> +discouraged</a>; a static singleton can provide the same functionality in a more modular +way. </p> + +<p>A key concept is that the {@code RequestQueue} must be instantiated with the +{@link android.app.Application} context, not an {@link android.app.Activity} context. This +ensures that the {@code RequestQueue} will last for the lifetime of your app, instead of +being recreated every time the activity is recreated (for example, when the user +rotates the device). + +<p>Here is an example of a singleton class that provides {@code RequestQueue} and +{@code ImageLoader} functionality:</p> + +<pre>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; + } +}</pre> + +<p>Here are some examples of performing {@code RequestQueue} operations using the singleton +class:</p> + +<pre> +// 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); +</pre> diff --git a/docs/html/training/volley/simple.jd b/docs/html/training/volley/simple.jd new file mode 100644 index 0000000..942c57f --- /dev/null +++ b/docs/html/training/volley/simple.jd @@ -0,0 +1,169 @@ +page.title=Sending a Simple Request + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<!-- table of contents --> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#manifest">Add the INTERNET Permission</a></li> + <li><a href="#simple">Use newRequestQueue</a></li> + <li><a href="#send">Send a Request</a></li> + <li><a href="#cancel">Cancel a Request</a></li> +</ol> + +</div> +</div> + +<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728"> +<div> + <h3>Video</h3> + <p>Volley: Easy, Fast Networking for Android</p> +</div> +</a> + +<p>At a high level, you use Volley by creating a {@code RequestQueue} and passing it +{@code Request} objects. The {@code RequestQueue} manages worker threads for running the +network operations, reading from and writing to the cache, and parsing responses. Requests +do the parsing of raw responses and Volley takes care of dispatching the parsed response +back to the main thread for delivery.</p> + +<p> This lesson describes how to send a request using the <code>Volley.newRequestQueue</code> +convenience method, which sets up a {@code RequestQueue} for you. +See the next lesson, +<a href="requestqueue.html">Setting Up a RequestQueue</a>, for information on how to set +up a {@code RequestQueue} yourself.</p> + +<p>This lesson also describes how to add a request to a {@code RequestQueue} and cancel a +request.</p> + +<h2 id="manifest">Add the INTERNET Permission</h2> + +<p>To use Volley, you must add the +{@link android.Manifest.permission#INTERNET android.permission.INTERNET} permission +to your app's manifest. Without this, your app won't be able to connect to the network.</p> + + +<h2 id="simple">Use newRequestQueue</h2> + +<p>Volley provides a convenience method <code>Volley.newRequestQueue</code> that sets up a +{@code RequestQueue} for you, using default values, and starts the queue. For example:</p> + +<pre> +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<String>() { + @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); +</pre> + +<p>Volley always delivers parsed responses on the main thread. Running on the main thread +is convenient for populating UI controls with received data, as you can freely modify UI +controls directly from your response handler, but it's especially critical to many of the +important semantics provided by the library, particularly related to canceling requests. +</p> + +<p>See <a href="requestqueue.html">Setting Up a RequestQueue</a> for a +description of how to set up a {@code RequestQueue} yourself, instead of using the +<code>Volley.newRequestQueue</code> convenience method.</p> + +<h2 id="send">Send a Request</h2> + +<p>To send a request, you simply construct one and add it to the {@code RequestQueue} with +{@code add()}, as shown above. Once you add the request it moves through the pipeline, +gets serviced, and has its raw response parsed and delivered.</p> + +<p>When you call {@code add()}, Volley runs one cache processing thread and a pool of +network dispatch threads. When you add a request to the queue, it is picked up by the cache +thread and triaged: if the request can be serviced from cache, the cached response is +parsed on the cache thread and the parsed response is delivered on the main thread. If the +request cannot be serviced from cache, it is placed on the network queue. The first +available network thread takes the request from the queue, performs the HTTP transaction, +parsse the response on the worker thread, writes the response to cache, and posts the parsed +response back to the main thread for delivery.</p> + +<p>Note that expensive operations like blocking I/O and parsing/decoding are done on worker +threads. You can add a request from any thread, but responses are always delivered on the +main thread.</p> + +<p>Figure 1 illustrates the life of a request:</p> + + <img src="{@docRoot}images/training/volley-request.png" + alt="system bars"> +<p class="img-caption"><strong>Figure 1.</strong> Life of a request.</p> + + +<h2 id="cancel">Cancel a Request</h2> + +<p>To cancel a request, call {@code cancel()} on your {@code Request} object. Once cancelled, +Volley guarantees that your response handler will never be called. What this means in +practice is that you can cancel all of your pending requests in your activity's +{@link android.app.Activity#onStop onStop()} method and you don't have to litter your +response handlers with checks for {@code getActivity() == null}, +whether {@code onSaveInstanceState()} has been called already, or other defensive +boilerplate.</p> + +<p>To take advantage of this behavior, you would typically have to +track all in-flight requests in order to be able to cancel them at the +appropriate time. There is an easier way: you can associate a tag object with each +request. You can then use this tag to provide a scope of requests to cancel. For +example, you can tag all of your requests with the {@link android.app.Activity} they +are being made on behalf of, and call {@code requestQueue.cancelAll(this)} from +{@link android.app.Activity#onStop onStop()}. +Similarly, you could tag all thumbnail image requests in a +{@link android.support.v4.view.ViewPager} tab with their respective tabs and cancel on swipe +to make sure that the new tab isn't being held up by requests from another one.</p> + +<p>Here is an example that uses a string value for the tag:</p> + +<ol> +<li>Define your tag and add it to your requests. +<pre> +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);</pre> +</li> + +<li>In your activity's {@link android.app.Activity#onStop onStop()} method, cancel all requests that have this tag. +<pre> +@Override +protected void onStop () { + super.onStop(); + if (mRequestQueue != null) { + mRequestQueue.cancelAll(TAG); + } +} +</pre></li></ol> + +<p>Take care when canceling requests. If you are depending on your response handler to +advance a state or kick off another process, you need to account for this. Again, the +response handler will not be called. +</p> |