RecyclerView中MP4视频预览实现方案咨询(兼容SDK19)
Hey there! Let's break down your problem step by step—you want to show MP4 previews in a RecyclerView, can't use Glide for that, and need to work around the SDK 21+ requirement for the recommended thumbnail method. Plus you're worried about resource bloat with MediaPlayer or ExoPlayer. Let's cover all bases.
First: Handle SDK Compatibility for Static Thumbnails (Fallback Option)
While ContentResolver.loadThumbnail() is the modern way, it's locked to API 21+. For API 19-20, you can safely fall back to the deprecated ThumbnailUtils.createVideoThumbnail()—it's marked deprecated but still works reliably on those older versions. Here's a quick implementation:
fun getVideoThumbnail(context: Context, videoUri: Uri, targetSize: Size): Bitmap? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Use the recommended method for API 21+ context.contentResolver.loadThumbnail(videoUri, targetSize, null) } else { // Fallback for API 19-20 ThumbnailUtils.createVideoThumbnail( getLocalFilePathFromUri(context, videoUri), MediaStore.Video.Thumbnails.MINI_KIND ) } } // Helper to get local file path from Uri (for API 19) private fun getLocalFilePathFromUri(context: Context, uri: Uri): String? { val projection = arrayOf(MediaStore.Video.Media.DATA) return context.contentResolver.query(uri, projection, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA) cursor.getString(columnIndex) } else null } }
This gives you a static thumbnail fallback for older devices, which is great if you want a low-resource option or need to support devices that can't handle dynamic previews.
Dynamic Video Previews: MediaPlayer vs. ExoPlayer
Now, for the dynamic MP4 preview you want. Let's compare the two options and why one is better for RecyclerView:
MediaPlayer: The Lightweight but Risky Choice
- Pros: No extra dependencies, uses system APIs, quick to set up.
- Cons:
- Resource management is a nightmare in RecyclerView. You have to manually stop, release, and reinitialize players as views are recycled—miss one step and you'll get memory leaks or crashed players.
- Compatibility issues: Older devices might struggle with certain MP4 codecs, leading to failed playback.
- No built-in loop support; you have to listen for playback completion events to restart the video manually.
ExoPlayer: The Robust, Recommended Choice
- Pros:
- Google's official media library, built to handle edge cases like RecyclerView view reuse. It has better codec support across devices, so fewer playback failures.
- Built-in features like mute (perfect for previews!), loop playback, and easy resource management.
- You can use
PlayerViewwhich integrates smoothly with RecyclerView—pause playback when a view is detached, resume when it's reattached, and release players properly when views are recycled.
- Cons: Adds a small dependency to your APK, but you can trim it down by only including the core and UI modules.
My recommendation? Go with ExoPlayer. The extra dependency is worth it to avoid the headache of managing MediaPlayer in a RecyclerView, and it's more stable for production apps.
ExoPlayer Best Practices for RecyclerView
To keep resource usage in check, follow these tips:
- Mute by default: Previews don't need sound—set
player.volume = 0fto skip audio processing. - Reuse player instances (optional): Instead of creating a new ExoPlayer for every view, use a singleton or pool to reuse players. This cuts down on initialization overhead.
- Manage lifecycle tightly:
- In
onViewAttachedToWindow: Initialize the player, set the media source, and start playback. - In
onViewDetachedFromWindow: Pause playback and release the player (or return it to the pool). - In
onViewRecycled: Ensure the player is fully released to free up resources.
- In
- Cache local previews: If your
thumbPreviewfiles are local, you don't need extra caching, but if they're remote, use ExoPlayer'sCacheDataSourceto avoid re-downloading.
Here's a simplified adapter example to get you started:
class VideoPreviewAdapter(private val videos: List<VideosDetails>) : RecyclerView.Adapter<VideoPreviewAdapter.VideoViewHolder>() { inner class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val playerView: PlayerView = itemView.findViewById(R.id.player_view) private var player: SimpleExoPlayer? = null fun bind(video: VideosDetails) { val context = itemView.context player = SimpleExoPlayer.Builder(context).build().apply { volume = 0f // Mute for preview repeatMode = Player.REPEAT_MODE_ONE // Loop the preview setMediaItem(MediaItem.fromUri(video.thumbPreview)) prepare() play() } playerView.player = player } fun cleanup() { player?.stop() player?.release() player = null playerView.player = null } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_video_preview, parent, false) return VideoViewHolder(view) } override fun onBindViewHolder(holder: VideoViewHolder, position: Int) { holder.bind(videos[position]) } override fun onViewDetachedFromWindow(holder: VideoViewHolder) { super.onViewDetachedFromWindow(holder) holder.cleanup() } }
Alternative: Static Thumbnail + Play Button
If you're still worried about resource usage, a simpler approach is to show a static thumbnail (using the compatible method above) with a play icon overlay. When the user taps it, open a full-screen player for the video. This uses almost no extra resources and works great on low-end devices.
内容的提问来源于stack exchange,提问作者Biscuit




