Android MediaLoader

# MediaLoader

媒体查询 android 读取本地相册 视频照片 之前读取的方式 是视频照片 分开读取 然后还要 按照时间倒序排序 如果照片视频很多的话就会加载缓慢 2800{视频+照片(照片多数)} 加载大概需要7-8秒的样子 不能忍受后来查资料的时候我发现一个不错的开源库

github.com/sucese/phoe… 知道了LoaderManger 之前加载 都是AsyncTask -> ContentResolver ...

# LoaderManger

  • package androidx.loader.app
  • getInstance()
  • initLoader()
  • interface LoaderCallbacks {}
  • destroyLoader()

# getInstance()

  @NonNull
    public static  LoaderManager getInstance(
            @NonNull T owner) {
        return new LoaderManagerImpl(owner, owner.getViewModelStore());
    }
复制代码

# LifecycleOwner

# ViewModelStoreOwner

# initLoader()

    /**
     * @param id {ps:不能重复哦用完需要释放}此加载程序的唯一标识符。可以是任何你想要的。标识符的
     * 作用域是特定的LoaderManager实例
     * @param callback Interface the LoaderManager will call to report about changes in the state of the loader.    * Required.
     */
    @MainThread
    @NonNull
    public abstract  Loader initLoader(int id, @Nullable Bundle args,
            @NonNull LoaderManager.LoaderCallbacks callback);
复制代码

# LoaderCallbacks {}

 /**
     * Callback interface for a client to interact with the manager.
     */
    public interface LoaderCallbacks<D> {
        /**
         * Instantiate and return a new Loader for the given ID.
         *
         * 

This will always be called from the process's main thread. * * @param id The ID whose loader is to be created. * @param args Any arguments supplied by the caller. * @return Return a new Loader instance that is ready to start loading. * 创建 loader */ @MainThread @NonNull Loader onCreateLoader(int id, @Nullable Bundle args); /** * Called when a previously created loader has finished its load. Note * that normally an application is not allowed to commit fragment * transactions while in this call, since it can happen after an * activity's state is saved. See {@link androidx.fragment.app.FragmentManager#beginTransaction() * FragmentManager.openTransaction()} for further discussion on this. * *

This function is guaranteed to be called prior to the release of * the last data that was supplied for this Loader. At this point * you should remove all use of the old data (since it will be released * soon), but should not do your own release of the data since its Loader * owns it and will take care of that. The Loader will take care of * management of its data so you don't have to. In particular: * *

    *
  • The Loader will monitor for changes to the data, and report * them to you through new calls here. You should not monitor the * data yourself. For example, if the data is a {@link android.database.Cursor} * and you place it in a {@link android.widget.CursorAdapter}, use * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context, * android.database.Cursor, int)} constructor without passing * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY} * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER} * (that is, use 0 for the flags argument). This prevents the CursorAdapter * from doing its own observing of the Cursor, which is not needed since * when a change happens you will get a new Cursor throw another call * here. *

  • The Loader will release the data once it knows the application * is no longer using it. For example, if the data is * a {@link android.database.Cursor} from a {@link android.content.CursorLoader}, * you should not call close() on it yourself. If the Cursor is being placed in a * {@link android.widget.CursorAdapter}, you should use the * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)} * method so that the old Cursor is not closed. *
* *

This will always be called from the process's main thread. * * @param loader The Loader that has finished. * @param data The data generated by the Loader. * 返回初始化的loader data: Cursor? */ @MainThread void onLoadFinished(@NonNull Loader loader, D data); /** * Called when a previously created loader is being reset, and thus * making its data unavailable. The application should at this point * remove any references it has to the Loader's data. * *

This will always be called from the process's main thread. * * @param loader The Loader that is being reset. */ @MainThread void onLoaderReset(@NonNull Loader loader); } 复制代码

# MediLoader.kt

companion object {
        private const val TYPE_ALL = 0 //全部
        private const val TYPE_IMAGE = 1 //照片
        private const val TYPE_VIDEO = 2 //视频

        private val ALL_QUERY_URI = MediaStore.Files.getContentUri("external") //external-db/files 表
        private const val DURATION = "duration"
        private const val SIZE = "_size"
        private const val LATITUDE = "latitude"
        private const val LONGITUDE = "longitude"

        /**
         * 全部媒体数据 - SELECTION_ARGS
         */
        private val ALL_SELECTION_ARGS = arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
                MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString())

        /**
         * 全部媒体数据 - PROJECTION
         */
        private val ALL_PROJECTION = arrayOf(MediaStore.Images.Media._ID,
                MediaStore.MediaColumns.DATA,
                MediaStore.MediaColumns.DISPLAY_NAME,
                MediaStore.MediaColumns.DATE_ADDED,
                MediaStore.Files.FileColumns.MEDIA_TYPE,
                MediaStore.MediaColumns.SIZE,
                MediaStore.Video.VideoColumns.DURATION)

        /**
         * 全部媒体数据 - SELECTION
         */
        private const val ALL_SELECTION = (
                MediaStore.Files.FileColumns.MEDIA_TYPE + "="
                        + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
                        + " OR "
                        + MediaStore.Files.FileColumns.MEDIA_TYPE + "="
                        + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
                        + " OR "
                        + MediaStore.Files.FileColumns.SIZE + ">0"
                        + " AND "
                        + DURATION + ">0")


        /**
         * 图片 - PROJECTION
         */
        private val IMAGE_PROJECTION = arrayOf(MediaStore.Images.Media._ID,
                MediaStore.MediaColumns.DATA,
                MediaStore.MediaColumns.DISPLAY_NAME,
                MediaStore.MediaColumns.DATE_ADDED,
                MediaStore.MediaColumns.MIME_TYPE,
                MediaStore.MediaColumns.SIZE,
                MediaStore.MediaColumns.WIDTH,
                MediaStore.MediaColumns.HEIGHT,
                LATITUDE,
                LONGITUDE)

        /**
         * 图片 - SELECTION
         */
        private const val IMAGE_SELECTION = (
                MediaStore.Images.Media.MIME_TYPE + "=? or " +
                        MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " or "
                        + MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " AND "
                        + MediaStore.MediaColumns.WIDTH +
                        ">0"
                )

        /**
         * 图片 - SELECTION_ARGS
         */
        private val IMAGE_SELECTION_ARGS = arrayOf("image/jpeg", "image/png", "image/webp")

        /**
         * 视频 - PROJECTION
         */
        private val VIDEO_PROJECTION = arrayOf(MediaStore.Images.Media._ID,
                MediaStore.MediaColumns.DATA,
                MediaStore.MediaColumns.DISPLAY_NAME,
                MediaStore.MediaColumns.DATE_ADDED,
                MediaStore.MediaColumns.MIME_TYPE,
                MediaStore.MediaColumns.SIZE,
                MediaStore.MediaColumns.WIDTH,
                MediaStore.MediaColumns.HEIGHT,
                LATITUDE,
                LONGITUDE,
                DURATION)

        /**
         * 视频 - SELECTION
         */
        private const val VIDEO_SELECTION = (
                MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " AND "
                        + MediaStore.MediaColumns.WIDTH + ">0"
                        + " AND "
                        + DURATION + ">0"
                )

        /**
         * 视频 - SELECTION_ARGS
         */
        private val VIDEO_SELECTION_ARGS = arrayOf("video/mp4")

        /**
         * 音频 - PROJECTION
         */
        private val AUDIO_PROJECTION = arrayOf(MediaStore.Images.Media._ID,
                MediaStore.MediaColumns.DATA,
                MediaStore.MediaColumns.DISPLAY_NAME,
                MediaStore.MediaColumns.DATE_ADDED,
                MediaStore.MediaColumns.MIME_TYPE,
                MediaStore.MediaColumns.SIZE,
                DURATION)

        /**
         * 音频 - SELECTION
         */
        private val AUDIO_SELECTION = (
                MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " AND "
                        + DURATION + ">0"
                )

        /**
         * 音频 - SELECTION_ARGS
         */
        private val AUDIO_SELECTION_ARGS = arrayOf("audio/wav")

        /**
         * 获取全部图片和视频,但过滤掉gif图片
         */
        private val SELECTION_NOT_GIF = (
                MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " OR "
                        + MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " OR "
                        + MediaStore.Images.Media.MIME_TYPE + "=?"
                        + " OR "
                        + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
                        + " AND "
                        + MediaStore.MediaColumns.SIZE + ">0"
                        + " AND "
                        + MediaStore.MediaColumns.WIDTH + ">0"
                )

        private val SELECTION_NOT_GIF_ARGS = arrayOf("image/jpeg", "image/png", "image/webp", MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString())

        private val ORDER_BY = MediaStore.Files.FileColumns._ID + " DESC"

        //相册列表
        private val ALL_ALBUM_PROJECTION = arrayOf(MediaStore.Images.ImageColumns.BUCKET_ID,
                MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, MediaStore.MediaColumns.DATA)
    }
    
 /**
     * 读取本地 MediaFile
     * @param activity
     * @param videoFilterTime
     * @param mediaFilterSize
     * @param mediaLoaderListener
     */
    fun loadMedia(activity: AppCompatActivity, @IntRange(from = 0, to = 2) type: Int, videoFilterTime: Int = 0, mediaFilterSize: Int = 0,
                  mediaLoaderListener: LocalMediaLoadListener) {
        val instance = getInstance(activity)
        instance.initLoader(type, null, object : LoaderManager.LoaderCallbacks {
            override fun onCreateLoader(id: Int, args: Bundle?): Loader {

                //视频长度
                val durationCondition = if (videoFilterTime > 0) " AND $DURATION<$videoFilterTime" else ""
                //文件大小
                val sizeCondition = if (mediaFilterSize > 0) " AND $SIZE<$mediaFilterSize" else ""

                return when (id) {
                    TYPE_ALL -> CursorLoader(activity, ALL_QUERY_URI, ALL_PROJECTION, ALL_SELECTION
                            + durationCondition
                            + sizeCondition, null,
                            MediaStore.Files.FileColumns.DATE_ADDED + " DESC")
                    TYPE_IMAGE -> CursorLoader(activity, ALL_QUERY_URI, ALL_PROJECTION, IMAGE_SELECTION + sizeCondition, IMAGE_SELECTION_ARGS,
                            MediaStore.Files.FileColumns.DATE_ADDED + " DESC")
                    TYPE_VIDEO -> CursorLoader(activity, ALL_QUERY_URI, ALL_PROJECTION, VIDEO_SELECTION, VIDEO_SELECTION_ARGS,
                            MediaStore.Files.FileColumns.DATE_ADDED + " DESC")
                    else -> CursorLoader(
                            activity,
                            ALL_QUERY_URI,
                            ALL_PROJECTION,
                            ALL_SELECTION + durationCondition + sizeCondition,
                            null,
                            MediaStore.Files.FileColumns.DATE_ADDED + " DESC")
                }
            }

            override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
                if (data != null) {
                    if (data.count > 0) {
                        val fileModels = ArrayList()
                        while (data.moveToNext()) {
                            val m = FileModel()
                            val filePath = data.getString(1)
                            if (!isNotImageFile(filePath)) {
                                m.fileId = data.getString(0)
                                m.filePath = "file://" + data.getString(1)
                                when (type) {
                                    TYPE_ALL -> {
                                        if (data.getInt(4) == 1) {
                                            m.thumbnail = "file://" + data.getString(1)
                                            m.fileType = FileModel.FileType.FILE_TYPE_IMG
                                        } else if (data.getInt(4) == 3) {
                                            initVideoThumbnail(m, activity, data)
                                            m.videoTime = data.getLong(6)
                                        }
                                    }
                                    TYPE_VIDEO -> {
                                        initVideoThumbnail(m, activity, data)
                                        m.videoTime = data.getLong(6)
                                    }
                                    TYPE_IMAGE -> {
                                        m.thumbnail = "file://" + data.getString(1)
                                        m.fileType = FileModel.FileType.FILE_TYPE_IMG
                                    }
                                }
                                m.fileName = data.getString(2)
                                m.shootDate = getDate(data.getLong(3))
                                m.isLocal = true
                                m.fileLength = data.getLong(5)
                                fileModels.add(m)
                            }
                        }
                        data.close()
                        instance.destroyLoader(type)
                        if (fileModels.size > 0) {
                            mediaLoaderListener.loadComplete(fileModels)
                        }
                    }
                }
            }

            override fun onLoaderReset(loader: Loader<Cursor>) {}
        })
    }
复制代码

# CursorLoader

注:

用loaderManger 加 cursorLoader 加载 2800 张 大概 冷家在 3s 热加载 1s 导致加载缓慢的原因 1:Corsor 循环处理判断 复杂 处理太多问题耗时 2.查询太多无用的列 3.分别读取

转载于:https://juejin.im/post/5d09861d6fb9a07eb55f5f94

你可能感兴趣的:(Android MediaLoader)