Objective
Time flies, engaged in Android system development has been two years, always want to write something to comfort themselves. Thinking for a very long time always can not write, think there is no good writing. Now the final decision to write something that meets the needs of most people, people who presumably use Android phones must be very familiar with the application of the "Gallery" (hereinafter referred to as gallery). In the Android Market there are a variety of library applications, their original prototype is actually the Android system native "library", only to make a different difference (UI differentiation). Before studying the gallery source code, we need to have a certain understanding of the design patterns, according to their understanding of gallery, gallery design is like a well-designed and efficient operation of the machine (32 saved). It is no exaggeration to say that in the Android market, there is no "gallery" application design can be comparable with gallery. For the next period of time, let us together to uncover the magical veil of gallery.
Data loading
Before studying gallery, we would appreciate the overall effect of gallery, as seen in Figure 1-1:
Figure 1-1
First, let's take a look at the history of Gallery, before Android2.3 the Android system's "gallery" named Gallery3d, After Android2.3, the system changed the previous Gallery3d to Gallery2, followed by the latest version number (4.4), Gallery2 a qualitative leap in the UI and functionality, is now a very good Android source code module, for Android application developers are very Good open source projects, in which the design of new ideas and design patterns are worthy of our reference.
Now back to the subject of our research-data loading, let's take a look at Gallery2 's path in the source code (package/app/gallery2/), where the resources and source code used by the library are included in the path. When we design a software, we first consider the storage and access to data, so we also follow this design approach to explore the Gallery2 data onboarding process. To mention here a little bit about the way I analyze the source code, you may have a slight understanding of the Android source code of the students should know that the Android source code is very large, so the selection of the point of analysis program can be divided into two categories: the first is to follow the operating procedures to analyze the source code-- It is applicable to the program of the interface jumping clearly, and the other is the execution logic of the analysis program based on the printed log information--applicable to complex operation logic.
First, let's take a look at the Buckethelper.java class (/src/com/android/gallery3d/data/ Buckethelper.java), this class is primarily responsible for reading image and video data from the Mediaprovider database, with detailed code such as the following:
Package Com.android.gallery3d.data;import Android.annotation.targetapi;import Android.content.ContentResolver; Import Android.database.cursor;import Android.net.uri;import Android.provider.mediastore.files;import Android.provider.mediastore.files.filecolumns;import Android.provider.mediastore.images;import Android.provider.mediastore.images.imagecolumns;import Android.provider.mediastore.video;import Android.util.Log ; Import Com.android.gallery3d.common.apihelper;import Com.android.gallery3d.common.utils;import Com.android.gallery3d.util.threadpool.jobcontext;import Java.util.arraylist;import Java.util.Arrays;import Java.util.comparator;import java.util.hashmap;class buckethelper {private static final String TAG = "Buckethelper"; private static final String External_media = "EXTERNAL"; Bucket_display_name is a string like "Camera" which are the directory//NAME of where an image or video was in. BUCKET_ID is a hash of the path//name of this directory (see ComputebuckeTvalues () in Mediaprovider for//details). Media_type is video, image, audio, etc. The Bucket_display_name field is the file folder name bucket_id field is the hash value for the folder path (path)//The "albums" is not explicitly recorded in the Datab ASE, but per image//or video has the columns (bucket_id, media_type). We define AN//"album" To be the collection of the Images/videos which has the same value//for the and the columns. The "album" is divided into: When the file has the same folder (BUCKET_ID) and the multimedia type (media_type) that belongs to the same album//The goal of the query (used in Loadsubmediasetsfromfil Estable ()) is to//Find all albums, which is, all unique values for (bucket_id, Media_type). In the meantime sort them by the timestamp of the latest image/video in//each of the album. The order of columns below is important:it must match to the index in//Mediastore. private static final string[] Projection_bucket = {imagecolumns.bucket_id, filecolumns.media_type, Imagecolumns.bucket_display_nAME}; The indices should match the above projections. private static final int index_bucket_id = 0; private static final int index_media_type = 1; private static final int index_bucket_name = 2; We want to order the albums by reverse chronological order. We abuse The//' WHERE ' parameter to insert a ' GROUP by ' clause into the SQL statement. The template for "WHERE" parameter are like://SELECT ... From ... WHERE (%s)//And we make it look like://SELECT ... From ... WHERE (1) GROUP by 1, (2)//The "(1)" means true. The "1, (2)" means the first and columns specified//after SELECT. Note that because there are a ")" in the template, we use//"(2" to match it.) private static final String bucket_group_by = "1) GROUP by 1, (2"; private static final String bucket_order_by = "MAX (datetaken) DESC"; Before honeycomb there is no Files table. Thus, we need to query the//buckets info from the Images and Video tables and then meRge them//together. A bucket can exist in both tables. In this case, we need to find the//latest timestamp from the tables and sort ourselves. So we add the//MAX (Date_taken) to the projection and remove the media_type since we//already know the media type From the table, we query from. private static final string[] projection_bucket_in_one_table = {imagecolumns.bucket_id, MAX (Dateta Ken) ", Imagecolumns.bucket_display_name}; We keep the index_bucket_id and index_bucket_name the same as//projection_bucket so we can reuse the values define D before. private static final int index_date_taken = 1; When query is from the Images or Video tables, we are need to group by BUCKET_ID. private static final String bucket_group_by_in_one_table = "1) GROUP by (1"; public static bucketentry[] Loadbucketentries (jobcontext JC, contentresolver resolver, int type) {if ( Apihelper.has_media_provider_files_table) {//when api1>= 11 (i.e. after Android3.0 version number) return loadbucketentriesfromfilestable (JC, resolver, type) ;//Get the folder path and folder name of the multimedia files (Pictures and videos) in the Mediascanner database} else {//android3.0 before the version number return Loadbucketentriesfromimag Esandvideotable (JC, resolver, type); }} private static void Updatebucketentriesfromtable (Jobcontext JC, Contentresolver Resolver, Uri tableur I, Hashmap<integer, bucketentry> buckets) {cursor cursor = Resolver.query (Tableuri, Projection_bucket_in_one _table, bucket_group_by_in_one_table, NULL, NULL); if (cursor = = null) {LOG.W (TAG, "Cannot open Media database:" + Tableuri); Return } try {while (Cursor.movetonext ()) {int bucketid = Cursor.getint (index_bucket_id); int datetaken = Cursor.getint (Index_date_taken); Bucketentry entry = Buckets.get (bucketID); if (entry = = null) { Entry = new Bucketentry (bucketID, cursor.getstring (index_bucket_name)); Buckets.put (bucketID, entry); Entry.datetaken = Datetaken; } else {Entry.datetaken = Math.max (Entry.datetaken, Datetaken); }}} finally {utils.closesilently (cursor); }} private static bucketentry[] Loadbucketentriesfromimagesandvideotable (Jobcontext JC, Contentresolver resolver, int type) {Hashmap<integer, bucketentry> buckets = new Hashmap<integer, bucketentry> (64); if (type & mediaobject.media_type_image)! = 0) {updatebucketentriesfromtable (JC , resolver, Images.Media.EXTERNAL_CONTENT_URI, buckets); } if ((Type & mediaobject.media_type_video)! = 0) {updatebucketentriesfromtable ( JC, resolver, Video.Media.EXTERNAL_CONTENT_URI, buckets); } bucketentry[] entries = Buckets.values (). ToArray (New Bucketentry[buckets.size ()); Arrays.sort (entries, new comparator<bucketentry> () {@Override public int compare (bucketentry A, Bucketentry b) {//sorted by Datetaken in descending order return B.datetaken-a.dateta Ken } }); return entries; } private static bucketentry[] Loadbucketentriesfromfilestable (jobcontext JC, contentresolver Resolver, int Type) {URI uri = Getfilescontenturi (); cursor cursor = resolver.query (URI, projection_bucket, bucket_group_by, NULL, Bucket_order_b Y); if (cursor = = null) {LOG.W (TAG, "Cannot open Local database:" + URI); return new bucketentry[0]; } arraylist<bucketentry> buffer = new arraylist<bucketentry> (); int typebits = 0; if ((Type & mediaobject.media_type_image)! = 0) { Typebits |= (1 << filecolumns.media_type_image); } if ((Type & mediaobject.media_type_video)! = 0) {typebits |= (1 << filecolumns.media_type_v IDEO); } try {while (Cursor.movetonext ()) {if (Typebits & (1 << cursor.getint (INDEX _media_type))) = 0) {Bucketentry entry = new Bucketentry (Cursor.getint (in dex_bucket_id), cursor.getstring (Index_bucket_name));//Construction Metadata Bucketentry if ( !buffer.contains (Entry)) {Buffer.add (entry);//Add Data information}} if (jc.iscancelled ()) return null; }} finally {utils.closesilently (cursor); } return Buffer.toarray (New Bucketentry[buffer.size ())); } private static String getbucketnameintable (Contentresolver resolver, Uri tableuri, int bucketid) {String selectionargs[] = new string[] {string.valueof (bucketID)}; Uri uri = Tableuri.buildupon (). Appendqueryparameter ("Limit", "1"). Build (); cursor cursor = resolver.query (URI, projection_bucket_in_one_table, "bucket_id =?", Selectionargs, NULL); try {if (cursor! = NULL && cursor.movetonext ()) {return cursor.getstring (index_ Bucket_name); }} finally {utils.closesilently (cursor); } return null; } @TargetApi (Apihelper.version_codes. Honeycomb) private static Uri Getfilescontenturi () {return Files.getcontenturi (External_media); } public static String Getbucketname (contentresolver resolver, int bucketid) {if (apihelper.has_media_provider_ files_table) {String result = getbucketnameintable (resolver, Getfilescontenturi (), bucketID); return result = = null? "": result; } else { String result = getbucketnameintable (resolver, Images.Media.EXTERNAL_CONTENT_URI, bucketID ); if (result! = null) return result; result = getbucketnameintable (resolver, Video.Media.EXTERNAL_CONTENT_URI, bucketID); return result = = null? "": result; }} public static class Bucketentry {public String bucketname; public int bucketID; public int datetaken; public bucketentry (int ID, String name) {bucketID = ID; Bucketname = utils.ensurenotnull (name); } @Override public int hashcode () {return bucketid; } @Override public Boolean equals (Object object) {if (!) ( Object instanceof Bucketentry)) return false; Bucketentry entry = (Bucketentry) object; return bucketID = = Entry.bucketid; } }}
Let's take a look at the timing diagram of the invocation relationship of the Buckethelper class, as seen in detail in 1-2:
Figure 1-2
So far we've got a rough overview of the gallery data loading, and the next article will analyze the reading of album Data and the encapsulation of data.
Android Source Code Gallery Special Study (1)