Browse Source

使用hook解决toast在7.0.0上出现的崩溃bug

杨充 5 years ago
parent
commit
a8a9de57c4

+ 6 - 6
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/config/PlayerConfig.java

@@ -6,8 +6,8 @@ import androidx.annotation.Nullable;
 import com.yc.kernel.factory.PlayerFactory;
 import com.yc.kernel.impl.media.MediaPlayerFactory;
 import org.yczbj.ycvideoplayerlib.player.manager.ProgressManager;
-import org.yczbj.ycvideoplayerlib.surface.RenderViewFactory;
-import org.yczbj.ycvideoplayerlib.surface.TextureRenderViewFactory;
+import org.yczbj.ycvideoplayerlib.surface.SurfaceViewFactory;
+import org.yczbj.ycvideoplayerlib.surface.TextureViewFactory;
 
 
 
@@ -40,7 +40,7 @@ public class PlayerConfig {
 
     public final int mScreenScaleType;
 
-    public final RenderViewFactory mRenderViewFactory;
+    public final SurfaceViewFactory mRenderViewFactory;
 
     public final boolean mAdaptCutout;
 
@@ -53,7 +53,7 @@ public class PlayerConfig {
         private ProgressManager mProgressManager;
         private PlayerFactory mPlayerFactory;
         private int mScreenScaleType;
-        private RenderViewFactory mRenderViewFactory;
+        private SurfaceViewFactory mRenderViewFactory;
         private boolean mAdaptCutout = true;
 
         /**
@@ -115,7 +115,7 @@ public class PlayerConfig {
         /**
          * 自定义RenderView
          */
-        public Builder setRenderViewFactory(RenderViewFactory renderViewFactory) {
+        public Builder setRenderViewFactory(SurfaceViewFactory renderViewFactory) {
             mRenderViewFactory = renderViewFactory;
             return this;
         }
@@ -151,7 +151,7 @@ public class PlayerConfig {
         }
         if (builder.mRenderViewFactory == null) {
             //默认使用TextureView渲染视频
-            mRenderViewFactory = TextureRenderViewFactory.create();
+            mRenderViewFactory = TextureViewFactory.create();
         } else {
             mRenderViewFactory = builder.mRenderViewFactory;
         }

+ 27 - 13
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/player/video/VideoPlayer.java

@@ -30,7 +30,8 @@ import org.yczbj.ycvideoplayerlib.player.manager.ProgressManager;
 import org.yczbj.ycvideoplayerlib.config.PlayerConfig;
 import org.yczbj.ycvideoplayerlib.player.manager.VideoViewManager;
 import org.yczbj.ycvideoplayerlib.surface.ISurfaceView;
-import org.yczbj.ycvideoplayerlib.surface.RenderViewFactory;
+import org.yczbj.ycvideoplayerlib.surface.SurfaceViewFactory;
+import org.yczbj.ycvideoplayerlib.tool.toast.BaseToast;
 import org.yczbj.ycvideoplayerlib.tool.utils.PlayerUtils;
 import com.yc.kernel.utils.VideoLogUtils;
 
@@ -52,6 +53,7 @@ import java.util.Map;
 public class VideoPlayer<P extends AbstractPlayer> extends FrameLayout
         implements MediaPlayerControl, AbstractPlayer.PlayerEventListener {
 
+    private Context mContext;
     /**
      * 播放器
      */
@@ -65,14 +67,13 @@ public class VideoPlayer<P extends AbstractPlayer> extends FrameLayout
      */
     @Nullable
     protected BaseVideoController mVideoController;
-
     /**
      * 真正承载播放器视图的容器
      */
     protected FrameLayout mPlayerContainer;
 
     protected ISurfaceView mRenderView;
-    protected RenderViewFactory mRenderViewFactory;
+    protected SurfaceViewFactory mRenderViewFactory;
     protected int mCurrentScreenScaleType;
 
     protected int[] mVideoSize = {0, 0};
@@ -87,19 +88,25 @@ public class VideoPlayer<P extends AbstractPlayer> extends FrameLayout
      * 当前正在播放视频的位置
      */
     protected long mCurrentPosition;
-
     /**
      * 当前播放器的状态
+     * 比如:错误,开始播放,暂停播放,缓存中等等状态
      */
     protected int mCurrentPlayState = ConstantKeys.CurrentState.STATE_IDLE;
-
+    /**
+     * 播放模式,普通模式,小窗口模式,正常模式等等
+     * 存在局限性:比如小窗口下的正在播放模式,那么mCurrentMode就是STATE_PLAYING,而不是MODE_TINY_WINDOW并存
+     **/
     protected int mCurrentPlayerState = ConstantKeys.PlayMode.MODE_NORMAL;
-
-    protected boolean mIsFullScreen;//是否处于全屏状态
-
-    protected boolean mIsTinyScreen;//是否处于小屏状态
+    /**
+     * 是否处于全屏状态
+     */
+    protected boolean mIsFullScreen;
+    /**
+     * 是否处于小屏状态
+     */
+    protected boolean mIsTinyScreen;
     protected int[] mTinyScreenSize = {0, 0};
-
     /**
      * 监听系统中音频焦点改变,见{@link #setEnableAudioFocus(boolean)}
      */
@@ -138,6 +145,12 @@ public class VideoPlayer<P extends AbstractPlayer> extends FrameLayout
 
     public VideoPlayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mContext = context;
+        init(attrs);
+    }
+
+    private void init(AttributeSet attrs) {
+        BaseToast.init(mContext.getApplicationContext());
 
         //读取全局配置
         PlayerConfig config = VideoViewManager.getConfig();
@@ -148,12 +161,13 @@ public class VideoPlayer<P extends AbstractPlayer> extends FrameLayout
         mRenderViewFactory = config.mRenderViewFactory;
 
         //读取xml中的配置,并综合全局配置
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VideoPlayer);
+        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.VideoPlayer);
         mEnableAudioFocus = a.getBoolean(R.styleable.VideoPlayer_enableAudioFocus, mEnableAudioFocus);
         mIsLooping = a.getBoolean(R.styleable.VideoPlayer_looping, false);
         mCurrentScreenScaleType = a.getInt(R.styleable.VideoPlayer_screenScaleType, mCurrentScreenScaleType);
         mPlayerBackgroundColor = a.getColor(R.styleable.VideoPlayer_playerBackgroundColor, Color.BLACK);
         a.recycle();
+
         initView();
     }
 
@@ -692,9 +706,9 @@ public class VideoPlayer<P extends AbstractPlayer> extends FrameLayout
     }
 
     /**
-     * 自定义RenderView,继承{@link RenderViewFactory}实现自己的RenderView
+     * 自定义RenderView,继承{@link SurfaceViewFactory}实现自己的RenderView
      */
-    public void setRenderViewFactory(RenderViewFactory renderViewFactory) {
+    public void setRenderViewFactory(SurfaceViewFactory renderViewFactory) {
         if (renderViewFactory == null) {
             throw new IllegalArgumentException("RenderViewFactory can not be null!");
         }

+ 0 - 15
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/surface/RenderViewFactory.java

@@ -1,15 +0,0 @@
-package org.yczbj.ycvideoplayerlib.surface;
-
-import android.content.Context;
-
-/**
- * 此接口用于扩展自己的渲染View。使用方法如下:
- * 1.继承IRenderView实现自己的渲染View。
- * 2.重写createRenderView返回步骤1的渲染View。
- * 可参考{@link RenderTextureView}和{@link TextureRenderViewFactory}的实现。
- */
-public abstract class RenderViewFactory {
-
-    public abstract ISurfaceView createRenderView(Context context);
-
-}

+ 18 - 0
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/surface/SurfaceViewFactory.java

@@ -0,0 +1,18 @@
+package org.yczbj.ycvideoplayerlib.surface;
+
+import android.content.Context;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://github.com/yangchong211
+ *     time  : 2018/11/9
+ *     desc  : 扩展自己的渲染View
+ *     revise: 可以使用TextureView,可参考{@link RenderTextureView}和{@link TextureViewFactory}的实现。
+ * </pre>
+ */
+public abstract class SurfaceViewFactory {
+
+    public abstract ISurfaceView createRenderView(Context context);
+
+}

+ 0 - 15
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/surface/TextureRenderViewFactory.java

@@ -1,15 +0,0 @@
-package org.yczbj.ycvideoplayerlib.surface;
-
-import android.content.Context;
-
-public class TextureRenderViewFactory extends RenderViewFactory {
-
-    public static TextureRenderViewFactory create() {
-        return new TextureRenderViewFactory();
-    }
-
-    @Override
-    public ISurfaceView createRenderView(Context context) {
-        return new RenderTextureView(context);
-    }
-}

+ 24 - 0
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/surface/TextureViewFactory.java

@@ -0,0 +1,24 @@
+package org.yczbj.ycvideoplayerlib.surface;
+
+import android.content.Context;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://github.com/yangchong211
+ *     time  : 2018/11/9
+ *     desc  : 实现类
+ *     revise:
+ * </pre>
+ */
+public class TextureViewFactory extends SurfaceViewFactory {
+
+    public static TextureViewFactory create() {
+        return new TextureViewFactory();
+    }
+
+    @Override
+    public ISurfaceView createRenderView(Context context) {
+        return new RenderTextureView(context);
+    }
+}

+ 34 - 19
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/tool/toast/BaseToast.java

@@ -38,6 +38,8 @@ import android.widget.Toast;
 
 import org.yczbj.ycvideoplayerlib.R;
 
+import java.lang.ref.SoftReference;
+
 
 /**
  * <pre>
@@ -54,14 +56,17 @@ public final class BaseToast {
     @SuppressLint("StaticFieldLeak")
     private static Context mApp;
     private static int toastBackColor;
+    private static SoftReference<Toast> mToast;
 
     /**
      * 初始化吐司工具类
      * @param app 应用
      */
     public static void init(@NonNull final Context app) {
-        mApp = app;
-        toastBackColor = Color.BLACK;
+        if (mApp==null){
+            mApp = app;
+            toastBackColor = Color.BLACK;
+        }
     }
 
     public static void setToastBackColor(@ColorInt int color){
@@ -81,7 +86,7 @@ public final class BaseToast {
      */
     private static void checkContext(){
         if(mApp==null){
-            throw new NullPointerException("BaseToast context is not null,please first init");
+            throw new NullPointerException("ToastUtils context is not null,please first init");
         }
     }
 
@@ -96,21 +101,20 @@ public final class BaseToast {
     public static void showToast(String content) {
         checkMainThread();
         checkContext();
-        if (toast == null) {
-            //toast = Toast.makeText(mApp, content, Toast.LENGTH_SHORT);
-            toast = Toast.makeText(mApp, "", Toast.LENGTH_SHORT);
-            toast.setText(content);
-        } else {
-            toast.setText(content);
+        if (!checkNull(mToast)) {
+            mToast.get().cancel();
         }
+        Toast toast = Toast.makeText(mApp, "", Toast.LENGTH_SHORT);
+        toast.setText(content);
         toast.show();
+        mToast = new SoftReference<>(toast);
     }
 
 
     /**
      * 某些系统可能屏蔽通知
      * 1:检查 SystemUtils.isEnableNotification(BaseApplication.getApplication());
-     * 2:替代方案 BaseSnackBar.showSnack(topActivity, noticeStr);
+     * 2:替代方案 SnackBarUtils.showSnack(topActivity, noticeStr);
      * 圆角
      * 屏幕中间
      * @param notice                        内容
@@ -252,9 +256,11 @@ public final class BaseToast {
         }
 
         public Toast build() {
-            if(toast==null){
-                toast = new Toast(context);
+            if (!checkNull(mToast)) {
+                mToast.get().cancel();
             }
+            Toast toast = new Toast(context);
+            HookToast.hook(toast);
             if (isFill) {
                 toast.setGravity(gravity | Gravity.FILL_HORIZONTAL, 0, yOffset);
             } else {
@@ -263,8 +269,7 @@ public final class BaseToast {
             toast.setDuration(duration);
             toast.setMargin(0, 0);
             if(layout==0){
-                CardView rootView = (CardView) LayoutInflater.from(context)
-                        .inflate(R.layout.view_toast_custom, null);
+                CardView rootView = (CardView) LayoutInflater.from(context).inflate(R.layout.view_toast_custom, null);
                 TextView textView = rootView.findViewById(R.id.toastTextView);
                 TextView descTv = rootView.findViewById(R.id.desc);
                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -287,25 +292,35 @@ public final class BaseToast {
                 View view = LayoutInflater.from(context).inflate(layout, null);
                 toast.setView(view);
             }
+            mToast = new SoftReference<>(toast);
             return toast;
         }
     }
 
-    private static int dip2px(Context context, float dpValue) {
-        final float scale = context.getResources().getDisplayMetrics().density;
-        return (int) (dpValue * scale + 0.5f);
+    public static boolean checkNull(SoftReference softReference) {
+        if (softReference == null || softReference.get() == null) {
+            return true;
+        }
+        return false;
     }
 
 
-    private static void checkMainThread(){
+    public static void checkMainThread(){
         if (!isMainThread()){
             throw new IllegalStateException("请不要在子线程中做弹窗操作");
         }
     }
 
     private static boolean isMainThread(){
-        //是否是主线程
+        //判断是否是主线程
         return Looper.getMainLooper() == Looper.myLooper();
     }
 
+
+    public static int dip2px(Context context, float dpValue) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+
+
 }

+ 73 - 0
VideoPlayer/src/main/java/org/yczbj/ycvideoplayerlib/tool/toast/HookToast.java

@@ -0,0 +1,73 @@
+package org.yczbj.ycvideoplayerlib.tool.toast;
+
+import android.os.Handler;
+import android.os.Message;
+import android.widget.Toast;
+
+import java.lang.reflect.Field;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     email  : yangchong211@163.com
+ *     time  : 20120/5/6
+ *     desc  : 利用hook解决toast崩溃问题
+ *     revise:
+ * </pre>
+ */
+public class HookToast {
+
+    private static Field sField_TN;
+    private static Field sField_TN_Handler;
+
+    static {
+        try {
+            Class<?> clazz =  Toast.class;
+            sField_TN = clazz.getDeclaredField("mTN");
+            sField_TN.setAccessible(true);
+            sField_TN_Handler = sField_TN.getType().getDeclaredField("mHandler");
+            sField_TN_Handler.setAccessible(true);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void hook(Toast toast) {
+        try {
+            Object tn = sField_TN.get(toast);
+            Handler preHandler = (Handler) sField_TN_Handler.get(tn);
+            sField_TN_Handler.set(tn, new SafelyHandler(preHandler));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /*public void showToast(Context context, CharSequence cs, int length) {
+        Toast mToast = Toast.makeText(context, cs, length);
+        hook(mToast);
+        mToast.show();
+    }*/
+
+    public static class SafelyHandler extends Handler {
+
+        private Handler impl;
+
+        public SafelyHandler(Handler impl) {
+            this.impl = impl;
+        }
+
+        public void dispatchMessage(Message msg) {
+            try {
+                super.dispatchMessage(msg);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void handleMessage(Message msg) {
+            //需要委托给原Handler执行
+            impl.handleMessage(msg);
+        }
+    }
+
+}

+ 0 - 2
app/src/main/java/org/yczbj/ycvideoplayer/BaseApplication.java

@@ -60,8 +60,6 @@ public class BaseApplication extends Application {
         }else {
             VideoLogUtils.setIsLog(false);
         }
-
-        BaseToast.init(this);
         //播放器配置,注意:此为全局配置,按需开启
         VideoViewManager.setConfig(PlayerConfig.newBuilder()
                 .setLogEnabled(BuildConfig.DEBUG)//调试的时候请打开日志,方便排错

+ 2 - 2
app/src/main/java/org/yczbj/ycvideoplayer/tiktok/TikTokRenderViewFactory.java

@@ -3,11 +3,11 @@ package org.yczbj.ycvideoplayer.tiktok;
 import android.content.Context;
 
 import org.yczbj.ycvideoplayerlib.surface.ISurfaceView;
-import org.yczbj.ycvideoplayerlib.surface.RenderViewFactory;
+import org.yczbj.ycvideoplayerlib.surface.SurfaceViewFactory;
 import org.yczbj.ycvideoplayerlib.surface.RenderTextureView;
 
 
-public class TikTokRenderViewFactory extends RenderViewFactory {
+public class TikTokRenderViewFactory extends SurfaceViewFactory {
 
     public static TikTokRenderViewFactory create() {
         return new TikTokRenderViewFactory();