Exoplayer in recyclerview with images and video like instagram but stop only last video other still playing after backpressed in android

Viewed 1830

I tried all StackOverflow answers and youtube solutions but cannot found anything suitable that's why I post my question in it.

I want to make an app like Instagram which I show images and video but the issue is

  1. when I swipe up in recycler view all video play automatically not only single shown screen.
  2. when I back pressed the button then only the last video stop with audio other videos(audio) still playing in the background until kill the app.

What I want to do is.

  1. single video play at a time which is shown on screen other video stores in cache like TikTok then after swipe I watch these videos.

  2. after back pressed all video with audio stopped

  3. onclicklistener with mute/unmute and double clicklistener.

here is my code...

Main Activity.class

public class MainActivity extends AppCompatActivity {
ActivityMainBinding mainBinding;
Toolbar toolbar;
String JSON_URl = "https://wasisoft.com/dev/index.php";
List<newsFeedModel> feedModels = new ArrayList<>();
newsFeedAdapter imageAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mainBinding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(mainBinding.getRoot());
    toolbar = findViewById(R.id.mews_feed_toolbar);
    toolbar.setTitle("NewsFeed");
    toolbar.setTitleTextColor(Color.WHITE);
    showList();

}

void showList() {
    AndroidNetworking.get(JSON_URl)
            .setPriority(Priority.LOW)
            .build()
            .getAsJSONArray(new JSONArrayRequestListener() {
                @Override
                public void onResponse(JSONArray response) {
                    // do anything with response
                    if (response != null) {
                        for (int i = 0; i < response.length(); i++) {
                            try {
                                JSONObject object = response.getJSONObject(i);
                                newsFeedModel FeedModel = new newsFeedModel();
                                FeedModel.setTitleName(object.getString("account_name"));
                                FeedModel.setLike(object.getString("likes"));
                                FeedModel.setDescrption(object.getString("media_type"));
                                FeedModel.setVideo(object.getString("media"));
                                FeedModel.setViewImg(object.getString("media"));
                                feedModels.add(FeedModel);
                                String res = object.getString("media");
                                Log.d("TAG", "onResponse: " + res);

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                        imageADD(feedModels);
                    } else {
                        Toast.makeText(MainActivity.this, "None ", Toast.LENGTH_SHORT).show();
                    }
                }

                @Override
                public void onError(ANError error) {
                    // handle error
                    Toast.makeText(MainActivity.this, "No Value " + error.getErrorBody(), Toast.LENGTH_SHORT).show();
                    Log.d("TAG", "onError: " + error);
                }
            });
}

void imageADD(List<newsFeedModel> feedModel) {
    imageAdapter = new newsFeedAdapter(this, feedModel, this::videoPlayBack);
    LinearLayoutManager manager = new LinearLayoutManager(this);
    manager.setSmoothScrollbarEnabled(true);
    imageAdapter.notifyDataSetChanged();
    mainBinding.newsFeedRecycler.setAdapter(imageAdapter);
    mainBinding.newsFeedRecycler.setHasFixedSize(true);
    mainBinding.newsFeedRecycler.setLayoutManager(manager);


}
void videoPlayBack(PlayerView player, Uri videoUrl,int position) {
    BandwidthMeter meter = new DefaultBandwidthMeter();
    TrackSelector track = new DefaultTrackSelector(
            new AdaptiveTrackSelection.Factory(meter)
    );
    imageAdapter.exoPlayer = ExoPlayerFactory.newSimpleInstance(this, track);
    DefaultHttpDataSourceFactory factory = new DefaultHttpDataSourceFactory("video");
    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
    MediaSource mediaSource = new ExtractorMediaSource(videoUrl, factory, extractorsFactory, null, null);
    player.setPlayer(imageAdapter.exoPlayer);
    player.setKeepScreenOn(true);
    imageAdapter.exoPlayer.prepare(mediaSource);
    imageAdapter.exoPlayer.setPlayWhenReady(true);
    player.setUseController(true);

}

@Override
public void onDestroy() {
    imageAdapter.releasePlayer();
    super.onDestroy();
}

@Override
protected void onPause() {
    super.onPause();
    imageAdapter.pausePlayer();
}
}

Adapter class

public class newsFeedAdapter extends RecyclerView.Adapter {
public static final int imageFeed = 0;
public static final int videoFeed = 1;
public SimpleExoPlayer exoPlayer;
Context context;
videoCallBack callBack;
List<newsFeedModel> feedModels;
videoNewsFeed videoNews;
newsFeedModel model;
String Json_Url = "https://wasisoft.com/dev/";

public newsFeedAdapter(Context context, List<newsFeedModel> feedModels, videoCallBack callBack) {
    this.context = context;
    this.feedModels = feedModels;
    this.callBack = callBack;
}

public newsFeedAdapter() {
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    if (viewType == 0) {
        View view = LayoutInflater.from(context).inflate(R.layout.images_news_feed, parent, false);
        return new imageNewsFeed(view);
    } else {

        View view = LayoutInflater.from(context).inflate(R.layout.video_news_feed, parent, false);
        return new videoNewsFeed(view);
    }
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (holder.getItemViewType() == 0) {
        newsFeedModel feed = feedModels.get(position);
        imageNewsFeed imageNews = (imageNewsFeed) holder;

        Glide
                .with(context)
                .load(Json_Url + feed.getViewImg())
                .centerCrop()
                .into(imageNews.viewPager);
        Log.d("TAG", "onBindViewHolder: " + feed.getViewImg());

        imageNews.title.setText(feed.getTitleName());
        imageNews.likes.setText(feed.getLike());
        imageNews.description.setText(feed.getDescription());

    } else {

        model = feedModels.get(position);
        videoNews = (videoNewsFeed) holder;
        videoNews.videoLikes.setText(model.getLike());
        videoNews.videoTitle.setText(model.getTitleName());
        videoNews.videoDescription.setText(model.getDescription());
        callBack.onSuccessPlay(videoNews.videoView, Uri.parse(Json_Url + model.getVideo()), position);
    }
}

@Override
public int getItemViewType(int position) {
    if (feedModels.get(position).getDescription().contains("picture")) {
        return imageFeed;
    } else
        return videoFeed;
}

@Override
public int getItemCount() {
    return feedModels.size();
}


public void releasePlayer() {
    if (exoPlayer != null) {
        videoNews.videoView.setPlayer(null);
        exoPlayer.setPlayWhenReady(true);
        exoPlayer.getPlaybackState();
        exoPlayer.release();
        exoPlayer = null;
    }
}

public void pausePlayer() {
    if (exoPlayer != null) {
        videoNews.videoView.setPlayer(null);
        exoPlayer.setPlayWhenReady(false);
        exoPlayer.getPlaybackState();
    }
}



public static class imageNewsFeed extends RecyclerView.ViewHolder {
    ImageView dpImage, viewPager;
    TextView title, likes, description;

    public imageNewsFeed(@NonNull View itemView) {
        super(itemView);
        dpImage = itemView.findViewById(R.id.profile_image_images_news_feed);
        viewPager = itemView.findViewById(R.id.profile_image_viewPage_news_feed);
        title = itemView.findViewById(R.id.profile_image_name_news_feed);
        likes = itemView.findViewById(R.id.profile_image_like_count_news_feed);
        description = itemView.findViewById(R.id.profile_image_comments_news_feed);
    }
}

public static class videoNewsFeed extends RecyclerView.ViewHolder {
    public PlayerView videoView;
    TextView videoTitle, videoLikes, videoDescription;

    public videoNewsFeed(@NonNull View itemView) {
        super(itemView);
        videoView = itemView.findViewById(R.id.item_video_exoplayer);
        videoTitle = itemView.findViewById(R.id.profile_video_name_news_feed);
        videoLikes = itemView.findViewById(R.id.profile_video_like_count_news_feed);
        videoDescription = itemView.findViewById(R.id.profile_video_comments_news_feed);
    }
    }
    }

videoFeed.xml

   <?xml version="1.0" encoding="utf-8"?>
   <androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <de.hdodenhof.circleimageview.CircleImageView
    android:id="@+id/profile_video_image_news_feed"
    android:layout_width="@dimen/_40sdp"
    android:layout_height="@dimen/_40sdp"
    android:layout_margin="@dimen/_8sdp"
    android:src="@drawable/ic_profile"
    app:civ_border_color="@color/black"
    app:civ_border_width="@dimen/_1sdp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<TextView
    android:id="@+id/profile_video_name_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/_8sdp"
    android:text="@string/app_name"
    android:textColor="@color/black"
    android:textSize="@dimen/_12ssp"
    app:layout_constraintBottom_toBottomOf="@id/profile_video_image_news_feed"
    app:layout_constraintStart_toEndOf="@id/profile_video_image_news_feed"
    app:layout_constraintTop_toTopOf="@id/profile_video_image_news_feed" />


<FrameLayout
    android:id="@+id/profile_video_video_news_feed"
    android:layout_width="match_parent"
    android:layout_height="@dimen/_300sdp"
    android:layout_marginTop="@dimen/_8sdp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/profile_video_image_news_feed">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/item_video_exoplayer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"

        app:layout_constraintTop_toTopOf="parent"
        app:resize_mode="fill"
        app:show_buffering="when_playing"
        app:surface_type="texture_view" />

    <!--  <ImageView
          android:id="@+id/img_vol"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_gravity="right|bottom"
          android:layout_margin="8dp"
          android:src="@drawable/ic_unmute" />-->
</FrameLayout>

<ImageView
    android:id="@+id/profile_video_like_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/_12sdp"
    android:layout_marginTop="@dimen/_12sdp"
    android:src="@drawable/ic_like"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/profile_video_video_news_feed"
    app:tint="@color/black" />

<ImageView
    android:id="@+id/profile_video_message_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/_12sdp"
    android:layout_marginTop="@dimen/_12sdp"
    android:rotation="290"
    android:src="@drawable/ic_message"
    app:layout_constraintStart_toEndOf="@id/profile_video_like_news_feed"
    app:layout_constraintTop_toBottomOf="@id/profile_video_video_news_feed" />

<ImageView
    android:id="@+id/profile_video_share_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/_12sdp"
    android:layout_marginTop="@dimen/_12sdp"
    android:src="@drawable/ic_send"
    app:layout_constraintStart_toEndOf="@id/profile_video_message_news_feed"
    app:layout_constraintTop_toBottomOf="@id/profile_video_video_news_feed" />

<TextView
    android:id="@+id/profile_video_like_count_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/_8sdp"
    android:text="@string/profileCount"
    android:textColor="@color/black"
    android:textSize="@dimen/_11ssp"
    app:layout_constraintStart_toStartOf="@id/profile_video_like_news_feed"
    app:layout_constraintTop_toBottomOf="@id/profile_video_like_news_feed" />

<TextView
    android:id="@+id/profile_video_like_text_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/_4sdp"
    android:text="@string/like"
    android:textColor="@color/black"
    android:textSize="@dimen/_11ssp"
    app:layout_constraintEnd_toEndOf="@id/profile_video_name_comment_news_feed"
    app:layout_constraintStart_toEndOf="@id/profile_video_like_count_news_feed"
    app:layout_constraintTop_toTopOf="@id/profile_video_like_count_news_feed" />

<TextView
    android:id="@+id/profile_video_name_comment_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/_4sdp"
    android:text="@string/app_name"
    android:textColor="@color/black"
    android:textSize="@dimen/_11ssp"
    android:textStyle="bold"
    app:layout_constraintStart_toStartOf="@id/profile_video_like_count_news_feed"
    app:layout_constraintTop_toBottomOf="@id/profile_video_like_count_news_feed" />

<TextView
    android:id="@+id/profile_video_comments_news_feed"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:ellipsize="end"
    android:ems="19"
    android:justificationMode="inter_word"
    android:maxLines="2"
    android:text="@string/dummy_text"
    android:textColor="@color/text_color"
    android:textSize="@dimen/_11ssp"
    android:textStyle="normal"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@id/profile_video_name_comment_news_feed"
    app:layout_constraintTop_toTopOf="@id/profile_video_name_comment_news_feed"
    tools:ignore="UnusedAttribute" />

   </androidx.constraintlayout.widget.ConstraintLayout>
2 Answers

From code, What I understand is that, when you click on the item, a callback comes in the video playback method. Here you are assigning

 imageAdapter.exoPlayer = ExoPlayerFactory.newSimpleInstance(this, track);

So every time, you click on the item, it will assign a new instance from the factory to exoplayer and when you back press, it will release only the last assigned exoplayer.

The previous instance are still active and playing.

So either releases the previous instance before assigning a new one or reuse the same instance.

void videoPlayBack(PlayerView player, Uri videoUrl,int position) {
    BandwidthMeter meter = new DefaultBandwidthMeter();
    TrackSelector track = new DefaultTrackSelector(
            new AdaptiveTrackSelection.Factory(meter)
    );
    imageAdapter.releasePlayer();
    imageAdapter.exoPlayer = ExoPlayerFactory.newSimpleInstance(this, track);

    DefaultHttpDataSourceFactory factory = new DefaultHttpDataSourceFactory("video");
    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
    MediaSource mediaSource = new ExtractorMediaSource(videoUrl, factory, extractorsFactory, null, null);
    player.setPlayer(imageAdapter.exoPlayer);
    player.setKeepScreenOn(true);
    imageAdapter.exoPlayer.prepare(mediaSource);
    imageAdapter.exoPlayer.setPlayWhenReady(true);
    player.setUseController(true);

}

I've made a github repo with different module and what you're looking for is in one of them called rvItemPreview the preview by a longClick on the item but you can look at the project I forked it from https://github.com/mitchtabian/Video-Player-RecyclerView

Just so you know the issue you're having now is because you're creating a new instance of Exoplayer for each item which isn't a good for CPU management and other thing. You need to create one instance of exoplayer and attach it to item that is on the screen.

You're creating a new instance for each item in onBindViewHolder with callBack.onSuccessPlay(videoNews.videoView, Uri.parse(Json_Url + model.getVideo()), position);

Related