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 <T extends LifecycleOwner & ViewModelStoreOwner> 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 <D> Loader<D> initLoader(int id, @Nullable Bundle args,
            @NonNull LoaderManager.LoaderCallbacks<D> 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.
         *
         * <p>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<D> onCreateLoader(int id, @Nullable Bundle args);

        /**
         * Called when a previously created loader has finished its load.  Note
         * that normally an application is <em>not</em> 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.
         *
         * <p>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:
         *
         * <ul>
         * <li> <p>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 <em>without</em> 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.
         * <li> 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.
         * </ul>
         *
         * <p>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<D> 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.
         *
         * <p>This will always be called from the process's main thread.
         *
         * @param loader The Loader that is being reset.
         */
        @MainThread
        void onLoaderReset(@NonNull Loader<D> 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<Cursor> {
            override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {

                //视频长度
                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<FileModel>()
                        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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值