I. Overview
Explain the optimization of the photo album before we look at the requirements of PM, PM requirements is very simple, is to do a similar local album Image query control, mainly consists of two two parts:
- Go to the Image selection page to display all the photos in the phone, including the system album Pictures and all the pictures in other directories, and in chronological order
- Switch album feature, switch album page List all the pictures in the phone directory, and show the number of pictures in each directory and the cover picture
These two requirements seem simple, but they hide a series of performance optimization problems. Before we did the optimization, we investigated some of the more famous apps that loaded the performance of a large number of images (the GIF recording was not clear enough, but it was enough to show the problem):
The picture query speed is very fast, basically into the picture Selection page, the album data has been identified, including the number of picture directory slices and the URL of the cover picture, this experience is better
Sina Weibo
Compared to the Sina Weibo experience is poor, into the image selection page, first black screen and then is white screen, even a progress bar is not, let the user think the app died, and so on for a period of time to show that the experience is poor
Qq
QQ comes up is the most recent 100 photos loaded, this speed is very fast, but into the camera album (there are more than 5,000), there is a progress bar waiting, I experienced the next, waiting time is still relatively long, this experience than Sina Weibo slightly better, worse than
Free Fish
Idle fish is to do the most rotten one, a card to die four or five seconds, then a black screen two or three seconds, and finally show up
Second, comprehensive comparison
After a comprehensive comparison, do better, basically into the album page can show all the photos, album Directory is also very quickly show out!!!
After our research, found that the use of cyclic paging load strategy, we optimize the idea is to adopt this strategy, first look at the optimized:
into the Picture Selection page, the picture can be displayed very quickly, into the replacement album page, the picture directory can also be very quickly displayed, here is not like the same as the image directory cache: One is because the query speed is very fast, basically less than 2 seconds to load out, two is able to refresh the album in real-time the latest data
Frequent switch each album directory, pictures can be very fast query out, experience is good!!!
Third, optimize the realization of the Search album catalogue
Because to enumerate all the album directory list, there is no other good way to directly request contentresolver Query method to inquire, here in order to speed up the query, the while loop to remove some time-consuming judgments, Some of the detection of the image of the logic of the decision to go outside, the specific use of the time to judge
Querying the URI of a picture
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
Because we only query the picture URL and the directory where the picture is located
String[] projection = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME};
PM requires albums to follow the picture of the time flashback arrangement, the creation of images, changes will affect the sorting of their directories, sort by time flashbacks
String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC ";
Based on these query conditions, after query to get a cursor, the cursor contains all of the images we need information, and then we loop through the cursor, in the while loop must not have time-consuming operation
An auxiliary collection that prevents the same directory from being scanned multiple times hashset<string> dirpaths = new hashset<string> (); while (Cursor.movetonext ()) {//Get Picture The path of String path = cursor.getstring (Cursor.getcolumnindex (MediaStore.Images.ImageColumns.DATA)); String bucketname = cursor.getstring (Cursor.getcolumnindex (MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME)); if (Textutils.isempty (Allfolderitem.coverimagepath)) {allfolderitem.coverimagepath = path; } File Parentfile = new file (path). Getparentfile (); if (Parentfile = = null) continue; String Dirpath = Parentfile.getabsolutepath (); Picfolderitem FolderItem = null; Use a hashset to prevent multiple scans of the same folder (without this judgment, the picture is still quite scary ~ ~) if (Dirpaths.contains (Dirpath)) {continue; } else {dirpaths.add (dirpath); Boolean isnew = true; Determine if the Dirpath is different, but bucketname the same for (Picfolderitem item:piclist) {if (Item.name.equals (bucketname)) {FolderItem = Item; Item.addparentpath (DIRPATH); IsNew = false; Break }} if (isnew) {FolderItem = new Picfolderitem (); Folderitem.coverimagepath = path; Folderitem.name = Bucketname; Folderitem.addparentpath (Dirpath); }} string[] array = parentfile.list (new FilenameFilter () {@Override public boolean accept (File dir, String filename {if (Filename.endswith (". jpg") | | filename.endswith (". png") || Filename.endswith (". jpeg")) return true; return false; } }); int arraycount = Array = = NULL? 0:array.length; Folderitem.count + = Arraycount; if (!piclist.contains (folderitem) && arraycount > 0) {piclist.add (FolderItem); }}
This makes it very quick to find out all the pictures in the phone, the number of pictures in the table of contents and the URL of the cover map. The main optimization here is three points:
Time-consuming judgment in while loop removal
There is a code in the previous code that determines whether a file picture exists:
public static boolean isFileExist(String path) { File file = new File(path); if (file == null || !file.exists()) { return false; } return true; }
This code in the while loop is very scary, I tested the next, more than 5,000 images to detect the total time will be increased by three or four seconds. This judgment can be placed outside, the specific operation of which picture when the specific business judgment!
Prevent a picture folder from being scanned multiple times
A variable is added here to store the scanned image directory, which has been scanned and is not processed:
//一个辅助集合,防止统一目录查询多次HashSet<String> dirPaths = new HashSet<String>();
This piece of optimization after the effect is still very obvious, the same directory will not be scanned multiple times!
Get the number of picture catalogs
String[] array = parentFile.list(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { if (filename.endsWith(".jpg") || filename.endsWith(".png") || filename.endsWith(".jpeg")) return true; return false; } });
This file.list () method inside is a native method, query efficiency very fast!!!
Of course, the number of images in a directory can also be retrieved through the cursor query way!!!
Query all photos in one album Directory
Before introducing the photos under the catalogue, let's introduce our two strategies of querying the pictures, one is more for the catalogue, the other is thousands of thousand sheets, and the other is that the catalogue is less than hundreds of pictures.
One-time load policy
When the number of tablets is less than 1000, use file.list This native method to load all the pictures at once, this native query efficiency is very fast, thousands of pictures are second-level query out
Cyclic paging page loading strategy
When the number of images is greater than or equal to 1000, the use of cyclic paging load strategy, this strategy specifically for the number of pictures of a particular situation, by paging the first page of the picture loaded out, so that users can first see the latest pictures, and then the background asynchronous Loop query Next page image, until all the images are query completed, This is also the query album policy.
One-time load policy implementation
Let's take a look at the next load of the policy implementation code, first through the file's list method to the suffix image format files filtered out, return a picture path array
File dirFile = new File(dir);String[] list = dirFile.list(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { if (filename.endsWith(".jpg") || filename.endsWith(".png") || filename.endsWith(".jpeg")) return true; return false; }});
Because we want to be in a chronological sequence of arrays, so the above query to sort out the array, the use of the file LastModified method
Collections.sort(strings, new Comparator<String>() { @Override public int compare(String lhs, String rhs) { Long time1 = new File(lhs).lastModified(); Long time2 = new File(rhs).lastModified(); return time2.compareTo(time1); } });
The implementation of cyclic paging load policy
This strategy draws on the page by page-by-page loading of the images until all the images are loaded.
The core here is the query criteria, add a directory you want to query into the query parameters
String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = ‘" + 目录名称 + "‘ ";
This selection must not write wrong, otherwise the query does not come out
Because to be paged, SortOrder is not a simple line of time flashbacks.
String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC limit " + PAGE_SIZE + " offset " + pageIndex * PAGE_SIZE;
And then loop through the cursor to get the picture path we want.
Page_size is a constant, indicating how many we want to query at a time, we set here is 200, one query 200 data, pageindex is the query page, starting from 0
At the beginning of the query on the first page of data, when the size of the query data list is greater than the size of the pagesize we want to query, we think there is the next page, pageindex plus 1 loop query the next page, until the list size of the query is less than pagesize.
With the above steps optimized, loading a local album image is basically no problem. We passed the real machine test, the picture 5549, can be very fast query out, comparable and gallery.
ANDROID_ optimized queries load large number of local photo albums