
The third edition will introduce you to the integrated environment of Android Studio, which greatly facilitates the development of applications. You will not only learn the basics of programming, but also learn about the capabilities of the most common versions of Android; new tools, such as layouts with limitations and data binding; unit testing; means of accessibility; MVVM architectural style; localization; new runtime permissions system. All educational applications have been designed to demonstrate important concepts and techniques of Android programming and give experience in their practical application.
Under the cut in more detail about the book and an excerpt from the book
"Multiple downloads"Book structure
In this book, we will write eight applications for Android. Two applications are very simple, and only one chapter is spent on their creation. Other applications are often more complex, and the longest application takes 13 chapters. All applications are designed to demonstrate important concepts and techniques and give experience of their practical application.
• GeoQuiz - in the first application, we explore the basic principles of creating Android projects, activity, layouts and explicit intents.
• CriminalIntent - the largest application in the book, designed to store information about the misdemeanors of your colleagues in the office. You will learn how to use fragments, interfaces “main-detailed view”, list interfaces, menus, camera, implicit intents and much more.
• BeatBox - horror your enemies and learn more about snippets, playback of multimedia content, MVVM architecture, data binding, testing, themes and graphic objects.
• NerdLauncher - a non-standard launcher, will reveal the subtleties of the system of intents and tasks.
• PhotoGallery - a Flickr client for downloading and displaying photos from a publicly available database.
Flickr The application demonstrates working with services, multi-threaded programming, accessing web services, etc.
• DragAndDraw — this simple graphical application deals with handling touch events and creating custom views.
• Sunset - in this "toy" application you will create a beautiful view of the sunset over the water, and at the same time learn the intricacies of the animation.
• Locatr - the application allows you to contact the Flickr service for images of the neighborhoods of your current location and display them on a map. You will learn to use the service and maps of the card.
Multiple downloads
Currently, the network part of PhotoGallery works as follows: PhotoGalleryFragment runs an instance of AsyncTask, which receives JSON from Flickr in the background thread, and parses the JSON into an array of GalleryItem objects. Each GalleryItem object now stores the URL that contains the miniature version of the photo.
')
The next step is to download these thumbnails. It would seem that additional network code can be simply added to the FetchItemsTask class's doInBackground () method. An array of GalleryItem objects contains 100 URLs to download. Images will be loaded one by one, until you have a hundred. When running onPostExecute (...), they will all appear in RecyclerView.
However, a one-time download of all the thumbnails creates two problems. First, it will take quite a long time, and the user interface will not be updated until it is completed. On a slow connection, users will have a long look at the wall from the bills.
Secondly, storing a full set of images requires resources. Hundreds of thumbnails easily fit in memory. But what if there are 1,000 of them? What if you want to implement endless scrolling? Over time, the free memory will be exhausted.
Given these problems, real applications often load images only when they are to be displayed on the screen. Loading as needed places additional requirements on the RecyclerView and its adapter. The adapter initiates image loading as part of the onBindViewHolder (...) implementation.
AsyncTask is the easiest way to get background threads, but for multiple executable and long-running operations, this mechanism is initially unsuitable. (About why this is the case, see the “For Inquisitive” section at the end of this chapter.)
Instead of using AsyncTask, we will create a specialized background thread. This is the most common way to implement a load as needed.
Interaction with the main thread
The specialized stream will load the photos, but how will it interact with the RecyclerView adapter to display them if it cannot directly access the main stream?
Remember the example of a shoe store and two Flash sellers. Background Flash has completed its telephone conversation with the supplier, and now he needs to inform Main Flash that the shoes have been ordered. If Flash Master is busy, Background Flash cannot do this immediately. He will have to wait at the rack and intercept the Main Flash in the free moment. This scheme works, but not too efficiently.
It is better to give each Flash in the mailbox. Background Flash writes a message stating that the shoes are ordered and puts it in the Main Flash box. Flash Master does the same when he wants to tell Background Flash that some product has ended.
The mailbox idea is extremely useful. Perhaps the seller has a task that needs to be completed soon, but not right now. In this case, he puts the message in his mailbox and processes it in his spare time.
In Android, such a “mailbox” used by threads is called a message queue. A thread using a message queue is called a message loop; he checks again and again new messages that might appear in the queue (Fig. 26.3).
The message loop consists of a stream and a Looper object that controls the message queue of the stream.
The main thread is a message loop, and it has a control object that extracts messages from the message queue and performs the task described in the message.
We will create a background thread that also uses a message loop. This will use the HandlerThread class, which provides a ready Looper object.
Create background thread
Create a new class named ThumbnailDownloader that extends HandlerThread. Define for it a constructor and a stub implementation of the method named queueThumbnail () (Listing 26.4).
Listing 26.4. Source version of the stream code (ThumbnailDownloader.java)
public class ThumbnailDownloader<T> extends HandlerThread { private static final String TAG = "ThumbnailDownloader"; private boolean mHasQuit = false; public ThumbnailDownloader() { super(TAG); } @Override public boolean quit() { mHasQuit = true; return super.quit(); } public void queueThumbnail(T target, String url) { Log.i(TAG, "Got a URL: " + url); } }
A single <T> argument is passed to the class. The user of ThumbnailDownloader will need an object to identify each download and define a user interface element that should be updated after the download is complete. Instead of restricting the user to one particular type of object, we use a generic parameter and make the implementation more flexible.
The queueThumbnail () method expects to receive an object of type T that acts as a download identifier and a String with the URL to download. This method will be called by the PhotoAdapter in its implementation of onBindViewHolder (...).
Open the PhotoGalleryFragment.java file. Define a ThumbnailDownloader field in PhotoGalleryFragment. In the onCreate (...) method, create a stream and start it. Override the onDestroy () method to terminate the stream.
Listing 26.5. Creating the ThumbnailDownloader class (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment { private static final String TAG = "PhotoGalleryFragment"; private RecyclerView mPhotoRecyclerView; private List<GalleryItem> mItems = new ArrayList<>(); private ThumbnailDownloader<PhotoHolder> mThumbnailDownloader; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); new FetchItemsTask().execute(); mThumbnailDownloader = new ThumbnailDownloader<>(); mThumbnailDownloader.start(); mThumbnailDownloader.getLooper(); Log.i(TAG, "Background thread started"); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ... } @Override public void onDestroy() { super.onDestroy(); mThumbnailDownloader.quit(); Log.i(TAG, "Background thread destroyed"); } ... }
You can specify any type in the generic argument ThumbnailDownloader. However, remember that this argument specifies the type of object that will be used as an identifier for the load. In this case, it is convenient to use the PhotoHolder object as an identifier, since it also determines the place where the downloaded images will eventually arrive.
A couple of notes: First, notice that the getLooper () call follows after the start () call for the ThumbnailDownloader (we will soon look at the Looper object in more detail). This ensures that the internal state of the stream is ready for continuation in order to exclude a theoretically possible (although rarely encountered) situation of a race (race condition). Before the getLooper () call, nothing guarantees that the onLooperPrepared () method was called, so there is a possibility that the call to queueThumbnail (...) will fail because the reference to Handler is null.
Secondly, the quit () call terminates the stream inside onDestroy (). This is a very important point. If you do not terminate the HandlerThread threads, they will never die like a zombie. Or rock and roll.
Finally, in the PhotoAdapter.onBindViewHolder (...) method, call the stream's queueThumbnail () method and pass it a PhotoHolder object, which will ultimately contain an image, and the URL of the GalleryItem object to download.
Listing 26.6. Connecting ThumbnailDownloader (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment { ... private class PhotoAdapter extends RecyclerView.Adapter<PhotoHolder> { ... @Override public void onBindViewHolder(PhotoHolder photoHolder, int position) { GalleryItem galleryItem = mGalleryItems.get(position); Drawable placeholder = getResources().getDrawable (R.drawable.bill_up_close); photoHolder.bindDrawable(placeholder); mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl()); } ... } ... }
Launch the PhotoGallery application and check the LogCat data. As you scroll through the RecyclerView in LogCat, lines appear that indicate that ThumbnailDownloader receives all download requests.
Now that our implementation of HandlerThread is working, the next step is to create a message with the information passed to queueThumbnail (), and place it in the message queue ThumbnailDownloader.
»More information about the book can be found on
the publisher's website.»
Table of Contents»
ExcerptFor Habrozhiteley a 20% discount on the coupon -
Android