当前位置:   article > 正文

【Android 性能优化:内存篇】——ExoPlayer 释放后内存没有恢复问题探索_exoplayer 播放时出现卡顿解决

exoplayer 播放时出现卡顿解决

背景

最近笔者承接项目的内存优化指标,在内存调研的过程中发现项目中视频播放结束后,内存没有恢复到播放前到水平。项目中用的 EXO 版本为2.19.1,并且笔者自己也写了个简单的 Demo,发现也是如此。虽然有一些偏门方法可以优化,但是暂时还是未能正面突破,各位看官,如果有什么idea,欢迎留言多多指教~

分析

笔者的 Demo 如下

 api 'com.google.android.exoplayer:exoplayer:2.19.1'

VideoTestFragment.java 

  1. package com.mikel.projectdemo.uiframework;
  2. import android.content.Context;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.ViewGroup;
  7. import androidx.annotation.NonNull;
  8. import androidx.annotation.Nullable;
  9. import androidx.fragment.app.Fragment;
  10. import com.google.android.exoplayer2.MediaItem;
  11. import com.google.android.exoplayer2.SimpleExoPlayer;
  12. import com.google.android.exoplayer2.ui.PlayerView;
  13. import com.mikel.projectdemo.R;
  14. import org.jetbrains.annotations.NotNull;
  15. public class VideoTestFragment extends Fragment {
  16. public static VideoTestFragment build() {
  17. return new VideoTestFragment();
  18. }
  19. private Context mContext;
  20. private SimpleExoPlayer mSimpleExoPlayer;
  21. private PlayerView playerView;
  22. @Override
  23. public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable @org.jetbrains.annotations.Nullable ViewGroup container, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
  24. mContext = getActivity();
  25. View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_video_item, null, true);
  26. initUI(rootView);
  27. return rootView;
  28. }
  29. private void initUI(View rootView) {
  30. mSimpleExoPlayer = new SimpleExoPlayer.Builder(getActivity()).build();
  31. // 准备要播放的媒体资源
  32. MediaItem mediaItem = MediaItem.fromUri("https://vfx.mtime.cn/Video/2019/01/15/mp4/190115161611510728_480.mp4");
  33. mSimpleExoPlayer.setMediaItem(mediaItem);
  34. // 将ExoPlayer关联到要显示视频的View
  35. playerView = rootView.findViewById(R.id.player_view);
  36. playerView.setPlayer(mSimpleExoPlayer);
  37. }
  38. public void startPlay() {
  39. // 准备播放器
  40. mSimpleExoPlayer.prepare();
  41. mSimpleExoPlayer.play();
  42. }
  43. /**
  44. * 停止播放
  45. */
  46. public void stopPlay() {
  47. pausePlay();
  48. if(mSimpleExoPlayer != null) {
  49. mSimpleExoPlayer.release();
  50. mSimpleExoPlayer = null;
  51. }
  52. }
  53. public void resumePlay() {
  54. if(mSimpleExoPlayer != null) {
  55. mSimpleExoPlayer.setPlayWhenReady(true);
  56. } else {
  57. startPlay();
  58. }
  59. }
  60. public void pausePlay() {
  61. if(mSimpleExoPlayer != null) {
  62. mSimpleExoPlayer.setPlayWhenReady(false);
  63. }
  64. }
  65. @Override
  66. public void onDestroyView() {
  67. super.onDestroyView();
  68. stopPlay();
  69. }
  70. @Override
  71. public void onResume() {
  72. super.onResume();
  73. resumePlay();
  74. }
  75. @Override
  76. public void onStop() {
  77. super.onStop();
  78. pausePlay();
  79. }
  80. }

 fragment_video_item.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <com.google.android.exoplayer2.ui.PlayerView
  6. android:id="@+id/player_view"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent">
  9. </com.google.android.exoplayer2.ui.PlayerView>
  10. </FrameLayout>

VideoTestActivity.java

  1. public class VideoTestActivity extends AppCompatActivity {
  2. public static void startActivity(Context context) {
  3. Intent intent = new Intent(context, VideoTestActivity.class);
  4. context.startActivity(intent);
  5. }
  6. @Override
  7. protected void onCreate(@Nullable Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_video_test);
  10. FragmentManager fragmentManager = getSupportFragmentManager();
  11. VideoTestFragment videoTestFragment = VideoTestFragment.build();
  12. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
  13. fragmentTransaction.add(R.id.fragment_container, videoTestFragment);
  14. fragmentTransaction.commit();
  15. }
  16. }

activity_video_test.xml 

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <FrameLayout
  6. android:id="@+id/fragment_container"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent">
  9. </FrameLayout>
  10. </FrameLayout>

 打开播放页面前和播放后关闭页面,内存水位如下:

内存水位简直毫无波澜,笔者也在 ExoPlayer 上发现不少相关 Issue:

https://github.com/google/ExoPlayer/issues/9755

 Memory leak · Issue #1855 · google/ExoPlayer · GitHub

android - ExoPlayer occupying memory even after releasing - Stack Overflow 

issue 里有一个方法是说在页面onDestroy的时候不仅释放Exoplayer, 还需要加上 simpleExoPlayerView.setPlayer(null),并且把 simpleExoPlayerView也设置为空,笔者尝试了下,内存水位依旧没有太大变化

解决方案探索

方案1 独立进程

业务允许的情况下,把播放页面设置成独立进程,

  1. <activity android:name=".video.VideoTestActivity"
  2. android:process=":video">

退出页面后调用

android.os.Process.killProcess(android.os.Process.myPid());

 该方案适合播放场景单一,使用Activity 来承接视频播放,播放结束后少频繁进入播放页面

方案 2 主动触发 gc

如果业务限制,无法把播放页面放到独立进程,尝试下 VideoFragment onDestroy 的时候主动 Runtime.getRuntime().gc()

该方案剑走偏峰,也是适合播放场景单一,不是频繁打开播放页面的场景,否则频繁手动 gc 可能带来卡顿的性能问题。

各位看官,如果对 ExoPlayer 研究深入或者有什么idea,欢迎留言多多指教~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/852489
推荐阅读
相关标签
  

闽ICP备14008679号