Changes

=== Create Singleton Pattern for Volley ===
 
To create a new singleton that limits the spread of Volley you must right click on your java package and select "Java Class".
Create New Class In Android StudioNow select the "Singleton" option and name the class as '''VolleySingleton : New Singleton Type Class In Android StudioRemember that we need to implement a queue of requests and an image loader to download images. In the end the class would be like this: VolleySingleton.java import android . content . Context ; import android . graphics . Bitmap ; import android . support . v4 . useful . LruCache ;   import com . android . volley . Request ; import com . android . volley . RequestQueue ; import com . android . volley . toolbox . ImageLoader ; import com . android . volley . toolbox . Volley ;'''
Remember that we need to implement a queue of requests and an image loader to download images. In the end, the class would be like this:
'''VolleySingleton.java'''
<syntaxhighlight lang="java">
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
/ ** * Created by Beautiful Programming.
*
* Class representing a HTTP Volley client
* / public final class VolleySingleton {
// Attributes private static public final class VolleySingleton singleton ; private ImageLoader imageLoader ; private RequestQueue requestQueue ; private static Context context ; {
// Attributes
private static VolleySingleton singleton;
private ImageLoader imageLoader;
private RequestQueue requestQueue;
private static Context context;
private VolleySingleton ( Context context ) { VolleySingleton . context = context ; requestQueue = getRequestQueue ();
imageLoader = new ImageLoader ( requestQueue , new ImageLoader . ImageCache () { private end final LruCache < String , Bitmap > cache = new LruCache <> ( 40 );
@Override public Bitmap getBitmap ( String url ) { return cache . get ( url ); } }
@Override public void putBitmap ( String url , Bitmap bitmap ) { cache . put ( url , bitmap ); } }); } }
/ **
* Returns the single instance of the singleton
* @param context context where the requests will be executed
* @return Instance
* / Public public static synchronized VolleySingleton getInstance ( Context context ) { if ( singleton == null ) { singleton = new VolleySingleton ( context . GetApplicationContext getApplicationContext()); } return singleton ; } }
/ **
* Gets the instance of the request queue
* @return request queue
* / public RequestQueue getRequestQueue () { if ( requestQueue == null ) { requestQueue = Volley . newRequestQueue ( context . getApplicationContext ()); } return requestQueue ; } }
/ **
* Add the request to the queue
* @param request req request
* @param <T> Final result of type T
* / public < T > void addToRequestQueue ( Request < T > req ) { getRequestQueue (). add ( req ); } }
public ImageLoader getImageLoader () { return imageLoader ; } }
}
Step # 3: </syntaxhighlight> === Generate an XML Parser for the RSS FeedIn my opinion, this is the core of the problem we are taking to create our Feedky reader . The other topics we have already discussed in previous articles, but the XML parsing is new.===
Remember when In my opinion, this is the core of the problem we saw are taking to create our Feedky reader. The other topics we have already discussed in previous articles, but the XML parsing of JSON formats ?is new.
Through an auxiliary class , we read the flow of the JSON objects, where we identified the important attributes and converted them into Java objects to be used in our lists.
That's exactly what you have to do with the XML tags . The question is what class or library should we use to parse the elements of the feed. In simple words what we need is to move from an XML hierarchy to Java objects.
Parsing XML to Java on AndroidThe The Android Developers documentation has a practical example that parse parses an Atom feed with a library called XMLPULL , which contains a the main class called XmlPullParser that allows the reading of the XML elements of a data flow.
Within the Java library , we can find another class called SAXParser for XML parsing very useful also if you want to take a look.
Similar to JsonReader, XmlPullParser has methods to obtain labels, attributes, namespaces and CDATA contents . But in particular I do not like to implement long classes in at low level to extract data, that's why I'll tell you about an excellent parsing library that I found ...
== THE SIMPLE LIBRARY FOR XML SERIALIZATIONThe Simple library is a powerful tool for both serializing XML elements and for deserializing them as Java objects. It gives us a system of annotations that facilitates tremendously the description of the objects that the XML tags will reference.==
It really leaves the parsing at A Simple library is a high level powerful tool for both serializing XML elements and we can save for deserializing them as Java objects. It gives us a lot system of annotations that facilitates tremendously the description of development time. Include Simple the objects that the XML Framework in Android Studio To include it in Android Studio you must download the Simple 2.7.1 distribution package tags will reference.
It really leaves the parsing at a high level and we can save a lot of development time. Include Simple XML Framework Download Pagein Android Studio To include it in Android Studio you must download the Simple 2.7.1 distribution package.
You extract the contents of the .rar file and then you go to the "jar" folder. Once there, copy and paste the simple-xml-2.7.1.jar file into the "libs" folder of your main module:
Folder libs From Android Studio Now press right on the file and select the "Add As Library ..." option: Add As Library In Android Studio
Select the module where you want to refer to its operation:
Choose App Module With this, we will have at our disposal the features of Simple Framework XML at our disposal. How to parse XML files with Simple? You can see the complete documentation on the use in Android Studiothe "Tutorial" section of the official website.
With this we will have at our disposal However, I am going to summarize the features characteristics of Simple Framework XML at our disposaldeserialization that we need to use. How The way to parse XML files with Simple? You can see the complete documentation on the use establish which labels we want to obtain and in the "Tutorial" section what type of organization is determined through the official websitereference annotations.
However, I am going to summarize the characteristics of deserialization that we need to use. The way idea is to establish with them which classes represent the labels we want , which are daughters, what attributes they have if it is necessary to obtain and in what type several elements, etc. Some of organization is determined through the reference annotations .most frequent are:
The idea is * @Root: Represents the Java equivalent of an XML object.* @Attribute: Reference the attribute of an XML element in a Java object.* @Element: Used to establish with them which classes represent the labels, which are daughters, what attributes they have, if it is necessary a child element of an XML tag.* @ElementList: Refers to obtain several a list of child elements, etc. Some of the most frequent are:same type and characteristics.
@Root: Represents the Java equivalent of an '''XML object.@Attribute: Reference the attribute of an XML element in a Java object.@Element: Used to represent a child element of an XML tag.@ElementList: Refers to a list of child elements of the same type and characteristics.to. Xml Tags : ''' To indicate that a class is equivalent to an xml XML tag, simply locate the @Root annotation at the top of its header. For example…example.
<syntaxhighlight lang="java">
@Root ( name = "rss" , strict = false ) public class Rss { ... }
</syntaxhighlight>
In the previous case, the Rss class is created to represent the <code><rss></code> tag of the format. For the <code>@Root </code> annotation , you can specify two parameters: name and strict.
Where name is the name of the XML tag and strict indicates to the framework if we need to deserialize all the child elements and attributes of the tag in our class.
For name , we will use the string "rss". For strict we will use false, since <rss> has attributes that we do not want to reflect in our class.
b. '''Child elements : ''' Use the annotation @Element if you want to declare one element as the child of another. For example, the Rss class must contain a Channel object as a child:
<syntaxhighlight lang="java">
// Inside Rss @Element private Channel channel ;
</syntaxhighlight>
We can also specify a series of attributes:
* '''data''': Determines whether the element is inside a CDATA block or not.* '''name''': The name of the child element.* '''required''': Specifies whether the value of the element is mandatory or not.* '''type''': It is the data type of the value of the element.c. Element Lists: If you want to indicate that an element contains a list use the annotation @ElementList. A good example of this would be where the classChannelcontains a list of Item elements as we saw in the Rss hierarchy.
'''Element Lists:''' If you want to indicate that an element contains a list use the annotation @ElementList. A good example of this would be where the class '''Channel''' contains a list of Item elements as we saw in the RSS hierarchy.java
import org '''Channel. simpleframework . xml . ElementList ; import org . simpleframework . xml . Root ;java'''
<syntaxhighlight lang="java">
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java . useful util. List ;
/ ** * Created by Beautiful Programming.
*
* Class that represents the <channel> tag of the feed
* /
@Root ( name = "channel" , strict = false ) public class Channel {
@ElementList ( inline = true ) private List < Item > items ;
public Channel () { }   public Channel(List<Item> items) { this.items = items; }
public Channel ( List < Item > items getItems() { this . items = return items ; } }</syntaxhighlight>
The inline parameter tells the framework that <channel> does not only contain the list of <code><item></code> elements but that there are other elements.
public List < Item > getItems () { return items ; } }
The inline parameter tells the framework that <channel> does not only contain the list of <item> elements, but that there are other elements.
If you indicate true the framework will ignore the different elements of the list. The default value is false.
d. '''Namespaces: ''' We have already said that sometimes the Rss formatswill have namespacecs that represent extension modules for the representation of detailed data about an element. That is why we should use the @Namespace annotation to satisfy this type of hierarchy. For example…
The That is why we should use the <code>@Namespace<media: content/code> tag implements a namespace for the description of multimedia elements of each entry in the feed. We know that annotation to satisfy this has an attribute with the value of the url type of the miniature, therefore we need its readinghierarchy.
The implementation of the namespace is declared in the <rss> nodeFor example, so right there we should use an @Namespace annotation:
RssThe <code><media: content></code> tag implements a namespace for the description of multimedia elements of each entry in the feed. We know that this has an attribute with the value of the url of the miniature, therefore we need its reading.java
import org . simpleframework . xml . Element ; import org . simpleframework . xml . The implementation of the namespace is declared in the <code><rss></code> node, so right there we should use an @Namespace ; import org . simpleframework . xml . Root ;annotation:
'''Rss.java'''
<syntaxhighlight lang="java">
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Root;
/ ** * Created by Beautiful Programming
*
* Class that represents the <rss> element of the feed
* /
@Root ( name = "rss" , strict = false ) @Namespace ( reference = " http : // search . Yahoo yahoo. Com com/ mrss / " ) public class Rss {
@Element
private Channel channel;
@Element private Channel channel ;public Rss() { }
public Rss (Channel channel) { } this.channel = channel; }
public Rss Channel getChannel( Channel channel ) { this . channel = channel ; }   public Channel getChannel () { return channel ; } } </syntaxhighlight>
Under <code>@Root </code> you indicate the namespace. You reference the reference with the reference parameter. In this case, the value is the URI of the Media module . However now you must declare the prefix of the element that represents the <media: content> tag in the Item class: Item.java
import org . simpleframework . xml . Element ; import org . simpleframework . xml . Namespace ; import org . simpleframework . xml . Root ;However, now you must declare the prefix of the element that represents the <media: content> tag in the Item class:
'''item.java'''
<syntaxhighlight lang="java">
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Root;
/ ** * Created by Beautiful Programming.
*
* Class that represents the <item> tag of the feed
* /
@Root ( name = "item" , strict = false ) public class Item {
@Element ( name = "title" ) private String title ;
@Element ( name = "description" ) private String description ;
@Element ( name = "link" ) private String link ;
@Element ( name = "content" ) @Namespace ( reference = " http : // search . Yahoo yahoo. Com com/ mrss / " , prefix = "media" ) private Content content ;
public Item () { } }
public Item ( String title , String description , String link , Content content ) { this . title = title ; this . description descripcion = description descripcion; this . link = link ; this . content = content ; } }
public String getTitle () { return title ; } }
public String getDescription () { return description ; } }
public String getLink () { return link ; } }
public Content getContent () { return content ; } } </syntaxhighlight>
As you can see, the Content class represents the tag with the namespace. We simply use the @Namespace annotation including the prefix parameter with the value of the "average" prefix.
and. '''Attributes ''': The Rss RSS file contains almost no attributes that interest us in our tags, except for the url URL attribute of the <code><media: content></code> tag. To extract it simply mark a variable with the annotation <code>@Attribute</code>.
'''Content.java'''
<syntaxhighlight lang="java">import org . simpleframework . xml . Attribute ; import org . simpleframework . xml . Root ;
 / ** * Created by Beautiful Programming.
*
* Class that represents the <media: content> tag of the feed
* /
@Root ( name = "content" , strict = false ) public class Content {
@Attribute ( name = "url" ) private String url ;
public Content () { } }
public Content ( String url ) { this . url = url ; } }
public String getUrl () { return url ; } } </syntaxhighlight>
and. '''The Serializer class : ''' The Simple libraryuses library uses its Serializer main class for the representation of an XML element that can be serialized or deserialized.
Although we can not instantiate it directly, a class called Persister is used to create an instance that allows data to be persisted. Persister implements a large number of XML data reading and writing methods depending on the source and type of data.
If you downloaded the feed to have local access, you can use the read method of the Serializer class in the following way:
<syntaxhighlight lang="java">Serializer serializer = new Persister (); File source = new File ( "path ruta/ folder carpeta/ rss.xml" ); Rss rss = serializer . read ( Rss . class , source ); </syntaxhighlight>
This method receives the type of element with which the xml file will be deserialized, which has a reference in the source object of type File.
However, you can also load it from an InputStream data stream. But we'll see that in the next step ... === Create an XML Personalized Request With Volley ===
Step # 4: Create an XML Personalized Request With Volley
Now the turn is for our HTTP request. We know we can use the HttpURLConnection client for that purpose, but as you know, Volley automates a lot of the work.
Similar to the custom request for JSON formats that was created as an example in the Volley article, we must derive our request from the <code>Request <T></code> class. XmlRequest.java import android . useful . Log ; 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 '''XmlRequest. toolbox . HttpHeaderParser ;java'''
import org . simpleframework . xml . Serializer ; import org . simpleframework . xml . core . Persister ; import <syntaxhighlight lang="java . io . UnsupportedEncodingException ; ">import java android. useful util. Map Log;
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 org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import java.io.UnsupportedEncodingException;
import java.util.Map;
 / ** * Created by Beautiful Programming.
*
* Custom request for dealing with XML flows
* /
public class XmlRequest < T > extends Request < T > {
private static final String TAG = XmlRequest . class . getSimpleName ();
// Attributes private final Class < T > clazz ; private final Map < String , String > headers ; private final Response . Listener < T > listener ; private final Serializer serializer = new Persister ();
/ **
* It is predefined for the use of GET requests
* / Public XMLRequest public XmlRequest( String url , Class < T > clazz , Map < String , String > headers , Response . Listener < T > listener , Response . ErrorListener ErrorListener errorListener) { Super super( Method . GET , url , ErrorListener errorListener); this . clazz = clazz; ; this . headers = headers ; this . listener = listener ; } }
@Override public Map < String , String > getHeaders () throws AuthFailureError { return headers ! = Null null ? headers : super . getHeaders (); } }
@Override protected void deliverResponse ( T response ) { listener . onResponse ( response ); } }
@Override protected Response < T > parseNetworkResponse ( NetworkResponse response ) { try {
// Converting the stream in formatted format into a UTF-8 format String xml = new String ( response . Data data, "UTF-8" );
// Debugging ... Log . d ( TAG , xml );
// Sending the paired response return Response . success ( serializer . read ( clazz , xml ), HttpHeaderParser . parseCacheHeaders ( response ));
} catch ( UnsupportedEncodingException e ) { return Response . error ( new ParseError ( e )); } catch ( Exception e ) { e . printStackTrace (); return Response . error ( new ParseError ( e )); } } } </syntaxhighlight>
When sending the response with <code>parseNetworkResponse () </code> we see that the flow coming from the response is converted to String and taken with <code>read ()</code>. This will return directly to a java object of the clazz type, which in our case is Rss. === Send Request to Forbes Server ===
Step # 5: Send Request to Forbes ServerSending the request to obtain the XML format is through the <code>addRequestQueque () </code> method of our singleton Volley.
But where should you invoke it?
Well, this choice depends a lot on the MVC architecture of Red. Ideally, the model makes queries to the server to generate an immediate caching and not atrophy our main thread. Here a Content Provider would come in handy.
However , it is possible to do it in the view or the controller as long as it is in the background.  
It is also important to define the way in which the updating of the data in the database will be observed in order for the list to be refreshed.
Now, the caching of the simple data is done on SQLite, but ... how to do the caching of the images? That has no problem, Volley is in charge of managing this for you.
=== Perform XML request from the main activity Wewill add the new request to the queue of Volley requests in the onCreate () method of MainActivity. The idea is to create a method that processes the response to the request and stores the information in the database:===
We will add the new request to the queue of Volley requests in the <code>onCreate()</code> method of MainActivity. The idea is to create a method that processes the response to the request and stores the information in the database: <syntaxhighlight lang="java">@Override protected void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ); setContentView ( R . layout . activity_main ); // Get the list listView = ( ListView ) findViewById ( R . Id id. List lista);
VolleySingleton . getInstance ( this ). addToRequestQueue ( new XMLRequest XmlRequest<> ( URL_FEED , Rss . class , null , new Response . Listener < RSS Rss> () { @Override public void onResponse ( Rss response ) { // Caching FeedDatabase . getInstance ( MainActivity . esta this). synchronizeEntries ( response . getChannel (). getItems ()); // Initial data load ...
} }, new Response . ErrorListener () { @Override public void onErrorResponse ( VolleyError error ) { Log . d ( TAG , "Error volleyVolley: " + mistake error. getMessage ()); } } ) ); } } ) );
}
</syntaxhighlight> '''Perform information caching'''
The response obtained from the request must be immediately stored in our database. For this , the method synchronizarEntradas <code>synchronizeEntries() </code> was created, which processes the list of items thrown.
<syntaxhighlight lang="java">/ **
* Process a list of items for local storage
* and synchronization.
*
* @param entries list of items
* / public void synchronizeEntries ( List < Item > entries ) { / * # 1 Temporally map new entries to make a comparison with local * / HashMap < String , Item > entryMap = new HashMap < String , Item > (); for ( Item e : entries ) { entryMap . put ( e . getTitle (), e ); } }
/ * # 2 Get local tickets * / Log . i ( TAG , "View currently stored items" ); Cursor c = getEntries (); assert c ! = null ; Log . i ( TAG , "Were found" + c . getCount () + "inputstickets, computing ..." );
/ * # 3 Start comparing entries * / int id ; String title ; String description ; String url ;
while ( c . moveToNext ()) {
id = c . getInt ( COLUMN_ID ); title = c . getString ( COLUMN_TITLE ); description = c . getString ( COLUMN_DESC ); url = c . getString ( COLUMN_URL );
Item match = entryMap . get ( title ); if ( match ! = null ) { // Filter existing entries. Remove to prevent future insertion entryMap entry . remove ( title );
/ * # 3.1 Check if the entry needs to be updated * / if (( match . getTitle () ! = null && ! match . getTitle (). equals ( title titulo)) || ( match . getDescription getDescripcion() ! = null && ! match . getDescription getDescripcion(). equals ( description descripcion)) || ( match . getLink () ! = null && ! match . getLink (). equals ( url ))) { // Update entries actualizarEntrada UpdateEntry( id , match . getTitle (), match . getDescription getDescripcion(), match . GetLink getLink(), match . getContent (). getUrl () );
} } } c . close ();
/ * # 4 Add new entries * / for ( Item e : entryMap . values ()) { Log . i ( TAG , "InsetInserted: title Title =" + e . getTitle ()); insertarEntrada ( e . getTitle (), e . getDescription getDescripcion(), e . GetLink getLink(), e . getContent (). getUrl () ); } Log } Log. i ( TAG , "Records were updated" ); }</syntaxhighlight>
 
}
This method is in charge of saving in the database all the entries that the feed has from the list that you enter as a parameter.
As you can see in the comments, 4 steps were have established that mark your journey. The first was to map the new entries into a new set whose key is the title of the entry. The title was chosen because it will represent its identifier.
Then the existing local entries in the database were obtained. This will allow a comparison between both groups. Where will be filtering the duplicate entries to make the tour.?
If the entry exists but had some change in its structure, its content is updated through the <code>updateEntry () </code> method.
Once the comparison is finished, those entries that still remain on the map are stored, which do not yet exist.
'''Performing image caching''' 
Although volley provides caching based on the DiskBaseCache class, responses are subject to the directives that the external server has set.
How to keep the images in cache?
Well, there are several libraries that can be helpful to store our thumbnails on the local disk. One of them is Android Universal Image Loader , which allows you to download images, give them persistence in the cache and view them in an optimized way.
Example Android Application With Universal Loader Library
The Picasso library is also an excellent option. As universal, it allows you to cache images, in addition to having a very short learning curve.
Example Android Application With Picasso BookstoreNow if you do not want to go that far, you can write your own definition of local cache with the help of Jake Wharton and his implementation of caching .
However, the solution that I will implement for this tutorial is based on the modification of the same Volley library.
Have you seen the operation of the <code>parseNetworkResponse () </code> method in the requests?
Well, that method when returning the answer with the method <code>success () </code> uses as parameter the HTTP headers that the server has sent.
To parse the headers that come in the answer there exists the class HttpHeaderParser, which compares the labels of each header and extracts its corresponding values.
It is right there where the duration of our images and their caching arrangement originates through the static method <code>parseCacheHeaders ()</code>.
Now ... , what if we alter this method or create a new one so that the values ​​of the headers are ignored?
In this discussion about the alteration of the HTTP headers that Volley receives , a way to handle this situation is explained where the duration results are ignored.
We simply need to create a new method called <code>parseIgnoreCacheHeaders () </code> and call it in the ImageRequest class, which is the request that ImageLoader uses.
'''Let's see:'''<syntaxhighlight lang="java">// Inside HttpHeaderParse...public static Cache.Entry parseIgnoreCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis();
// Inside HttpHeaderParse Map<String, String> headers = response... public static Cache . Entry parseIgnoreCacheHeaders ( NetworkResponse response ) { headers; long now serverDate = System . currentTimeMillis ()0; String serverEtag = null; String headerValue;
Map < String , String > headerValue = headers = response . headers get("Date"); long if (headerValue != null) { serverDate = 0 ; String serverEtag = null ; String HttpHeaderParser.parseDateAsEpoch(headerValue ); } serverEtag = headers.get("ETag");
headerValue final long cacheHitButRefreshed = headers . get ( "Date" )3 * 60 * 1000; if ( headerValue ! // 3 minutes available before operations final long cacheExpired = null ) { 24 * 60 * 60 * 1000; // expires in 24 hours serverDate final long softExpire = HttpHeaderParser . parseDateAsEpoch ( headerValue )now + cacheHitButRefreshed; } final long ttl = now + cacheExpired;
serverEtag Cache.Entry entry = headers new Cache. get Entry( "ETag" ); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = ttl; entry.serverDate = serverDate; entry.responseHeaders = headers;
final long cacheHitButRefreshed = 3 * 60 * 1000 return entry; // 3 minutes available before the final operations long cacheExpired = 24 * 60 * 60 * 1000 ; // expires in 24 hours end long softExpire = now + cacheHitButRefreshed ; end long ttl = now + cacheExpired ; } </syntaxhighlight>
Cache . Entry entry = new Cache . Entry Now go to the <code>doParse()</code> method of the ImageRequest class (which is responsible for parsing the flow to bitmap); entry . data = and make the success method of the response . data ; entry . etag = serverEtag ; entry . softTtl = softExpire ; entry . ttl = ttl ; entry . serverDate = serverDate ; entry . responseHeaders = headers ; implement our new method:
<syntaxhighlight lang="java">return entry ; }Now go to the doParse Response.success(bitmap, HttpHeaderParser.parseIgnoreCacheHeaders(response) method of the ImageRequest class (which is responsible for parsing the flow to bitmap) and make the success method of the response implement our new method:;</syntaxhighlight>
return Response . success ( bitmap , HttpHeaderParser . parseIgnoreCacheHeaders ( response ));
The way you know if it works or not is running the application so that the thumbnails are loaded. After that, disconnect the internet connection, close the application open it again. If all the thumbnails appear, then it was a success.
I do not know the functional effectiveness of this method. It should still be tested with the Volley tracking to see the response times and hitting of the cache. However, it is functional and easy to implement.
Step # 6: === Create A Custom CursorAdapter For The List=== 
Because the list is populated directly from the contents of the database, it is necessary to derive our adapter from the CursorAdapter class to traverse the records.
We will use a View Holder design pattern to optimize <code>findViewById () </code> calls on our adapter. The images will be obtained through the requests of the ImageLoader and thus automatically saved in cache:
'''FeedAdapter.java'''
import android . content . Context ; import android . database . cursor ; import android . support . v4 . widget . CursorAdapter ; import android . view . LayoutInflater ; import android . view . View ; import android . view . ViewGroup ; import android .