Skip to content

Commit

Permalink
提供图片预加载并缓存功能,手机图片搜索加载优化。
Browse files Browse the repository at this point in the history
  • Loading branch information
teach committed Nov 20, 2019
1 parent 161a3ce commit 61fefe6
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.donkingliang.imageselectdemo;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

import com.donkingliang.imageselectdemo.adapter.ImageAdapter;
Expand All @@ -16,6 +19,7 @@
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private static final int REQUEST_CODE = 0x00000011;
private static final int PERMISSION_WRITE_EXTERNAL_REQUEST_CODE = 0x00000011;

private RecyclerView rvImage;
private ImageAdapter mAdapter;
Expand All @@ -36,6 +40,17 @@ protected void onCreate(Bundle savedInstanceState) {
findViewById(R.id.btn_clip).setOnClickListener(this);
findViewById(R.id.btn_only_take).setOnClickListener(this);
findViewById(R.id.btn_take_and_clip).setOnClickListener(this);

int hasWriteExternalPermission = ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteExternalPermission == PackageManager.PERMISSION_GRANTED) {
//预加载手机图片。加载图片前,请确保app有读取储存卡权限
ImageSelector.preload(this);
} else {
//没有权限,申请权限。
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_WRITE_EXTERNAL_REQUEST_CODE);
}
}

@Override
Expand All @@ -49,6 +64,26 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}
}

/**
* 处理权限申请的回调。
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSION_WRITE_EXTERNAL_REQUEST_CODE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//预加载手机图片
ImageSelector.preload(this);
} else {
//拒绝权限。
}
}
}

@Override
public void onClick(View v) {
switch (v.getId()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ private void hideFolderList() {
public void run() {
rvFolder.setTranslationY(rvFolder.getHeight());
rvFolder.setVisibility(View.GONE);
rvFolder.setBackgroundColor(Color.WHITE);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.donkingliang.imageselector.model;

import android.Manifest;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.content.ContextCompat;

import com.donkingliang.imageselector.R;
import com.donkingliang.imageselector.entry.Folder;
Expand All @@ -21,74 +25,168 @@

public class ImageModel {

/**
* 缓存图片
*/
private static ArrayList<Folder> cacheImageList = null;
private static boolean isNeedCache = false;
private static PhotoContentObserver observer;

/**
* 预加载图片
*
* @param context
*/
public static void preloadAndRegisterContentObserver(final Context context) {
isNeedCache = true;
if (observer == null) {
observer = new PhotoContentObserver(context.getApplicationContext());
context.getApplicationContext().getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, observer);
}
preload(context);
}

private static void preload(final Context context) {
int hasWriteExternalPermission = ContextCompat.checkSelfPermission(context,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteExternalPermission == PackageManager.PERMISSION_GRANTED) {
//有权限,加载图片。
loadImageForSDCard(context, true, null);
}
}

/**
* 清空缓存
*/
public static void clearCache(Context context) {
isNeedCache = false;
if (observer != null) {
context.getApplicationContext().getContentResolver().unregisterContentObserver(observer);
observer = null;
}
new Thread(new Runnable() {
@Override
public void run() {
synchronized (ImageModel.class) {
if (cacheImageList != null) {
cacheImageList.clear();
cacheImageList = null;
}
}
}
}).start();
}

/**
* 从SDCard加载图片
*
* @param context
* @param callback
*/
public static void loadImageForSDCard(final Context context, final DataCallback callback) {
loadImageForSDCard(context, false, callback);
}

/**
* 从SDCard加载图片
*
* @param context
* @param isPreload 是否是预加载
* @param callback
*/
private static void loadImageForSDCard(final Context context, final boolean isPreload, final DataCallback callback) {
//由于扫描图片是耗时的操作,所以要在子线程处理。
new Thread(new Runnable() {
@Override
public void run() {
synchronized (ImageModel.class) {
boolean isAndroidQ = VersionUtils.isAndroidQ();
String imageCacheDir = ImageUtil.getImageCacheDir(context);
ArrayList<Folder> folders = null;
if (cacheImageList == null || isPreload) {
ArrayList<Image> imageList = loadImage(context);
ArrayList<Image> images = new ArrayList<>();

boolean isAndroidQ = VersionUtils.isAndroidQ();

//扫描图片
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = context.getContentResolver();

Cursor mCursor = mContentResolver.query(mImageUri, new String[]{
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.MIME_TYPE},
null,
null,
MediaStore.Images.Media.DATE_ADDED);

ArrayList<Image> images = new ArrayList<>();

//读取扫描到的图片
if (mCursor != null) {
while (mCursor.moveToNext()) {
// 获取图片的路径
long id = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Images.Media._ID));
String path = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
//获取图片名称
String name = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
//获取图片时间
long time = mCursor.getLong(
mCursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));

//获取图片类型
String mimeType = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));

//获取图片uri
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
.appendPath(String.valueOf(id)).build();

//过滤未下载完成或者不存在的文件
boolean isEffective = isAndroidQ ? ImageUtil.isEffective(context, uri) : ImageUtil.isEffective(path);
//过滤剪切保存的图片;
boolean isCutImage = ImageUtil.isCutImage(context, path);
if (isEffective && !isCutImage) {
images.add(new Image(path, time, name, mimeType, uri));
for (Image image : imageList) {
//过滤未下载完成或者不存在的文件
boolean isEffective = isAndroidQ ? ImageUtil.isEffective(context, image.getUri()) : ImageUtil.isEffective(image.getPath());
//过滤剪切保存的图片;
boolean isCutImage = ImageUtil.isCutImage(imageCacheDir, image.getPath());
if (isEffective && !isCutImage) {
images.add(image);
}
}
Collections.reverse(images);
folders = splitFolder(context, images);
if (isNeedCache) {
cacheImageList = folders;
}
} else {
folders = cacheImageList;
}

if (callback != null) {
callback.onSuccess(folders);
}
mCursor.close();
}
Collections.reverse(images);
callback.onSuccess(splitFolder(context,images));
}
}).start();
}

/**
* 从SDCard加载图片
*
* @param context
* @return
*/
private static synchronized ArrayList<Image> loadImage(Context context) {

//扫描图片
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = context.getContentResolver();

Cursor mCursor = mContentResolver.query(mImageUri, new String[]{
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.MIME_TYPE},
null,
null,
MediaStore.Images.Media.DATE_ADDED);

ArrayList<Image> images = new ArrayList<>();

//读取扫描到的图片
if (mCursor != null) {
while (mCursor.moveToNext()) {
// 获取图片的路径
long id = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Images.Media._ID));
String path = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
//获取图片名称
String name = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
//获取图片时间
long time = mCursor.getLong(
mCursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));

//获取图片类型
String mimeType = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));

//获取图片uri
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
.appendPath(String.valueOf(id)).build();

images.add(new Image(path, time, name, mimeType, uri));
}
mCursor.close();
}
return images;
}

/**
* 检查图片是否存在。ContentResolver查询处理的数据有可能文件路径并不存在。
*
Expand All @@ -110,7 +208,7 @@ private static String getPathForAndroidQ(Context context, long id) {
* @param images
* @return
*/
private static ArrayList<Folder> splitFolder(Context context,ArrayList<Image> images) {
private static ArrayList<Folder> splitFolder(Context context, ArrayList<Image> images) {
ArrayList<Folder> folders = new ArrayList<>();
folders.add(new Folder(context.getString(R.string.selector_all_image), images));

Expand Down Expand Up @@ -175,4 +273,20 @@ private static Folder getFolder(String name, List<Folder> folders) {
public interface DataCallback {
void onSuccess(ArrayList<Folder> folders);
}

private static class PhotoContentObserver extends ContentObserver {

private Context context;

public PhotoContentObserver(Context appContext) {
super(null);
context = appContext;
}

@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
preload(context);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.donkingliang.imageselector.utils;

import android.app.Activity;
import android.content.Context;
import android.support.v4.app.Fragment;

import com.donkingliang.imageselector.ClipImageActivity;
import com.donkingliang.imageselector.ImageSelectorActivity;
import com.donkingliang.imageselector.entry.RequestConfig;
import com.donkingliang.imageselector.model.ImageModel;

import java.util.ArrayList;

Expand Down Expand Up @@ -41,6 +43,22 @@ public class ImageSelector {

public static final int RESULT_CODE = 0x00000012;

/**
* 预加载图片
*
* @param context
*/
public static void preload(Context context) {
ImageModel.preloadAndRegisterContentObserver(context);
}

/**
* 清空缓存
*/
public static void clearCache(Context context) {
ImageModel.clearCache(context);
}

public static ImageSelectorBuilder builder() {
return new ImageSelectorBuilder();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,19 @@ public static boolean isEffective(Context context, Uri uri) {
* @return
*/
public static boolean isCutImage(Context context, String path) {
return isCutImage(getImageCacheDir(context),path);
}

/**
* 是否是剪切返回的图片
* @param dir
* @param path
* @return
*/
public static boolean isCutImage(String dir, String path) {
if (!StringUtils.isEmptyString(path)) {
String dir = getImageCacheDir(context);
return path.startsWith(dir);
}
return false;

}
}
Loading

0 comments on commit 61fefe6

Please sign in to comment.