yangchong 5 лет назад
Родитель
Сommit
1cd6cec140
50 измененных файлов с 2234 добавлено и 143 удалено
  1. 6 17
      README.md
  2. 1 12
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/bean/VideoClarity.java
  3. 117 0
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/bean/VideoInfo.java
  4. 5 1
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/controller/AbsVideoPlayerController.java
  5. 12 8
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/controller/VideoPlayerController.java
  6. 2 3
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/inter/listener/OnTextureListener.java
  7. 15 0
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/inter/player/InterVideoController.java
  8. 15 0
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/inter/player/VideoControllerView.java
  9. 4 4
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/player/VideoMediaPlayer.java
  10. 5 13
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/player/VideoPlayer.java
  11. 38 2
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/utils/VideoPlayerUtils.java
  12. 15 6
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/view/VideoSurfaceView.java
  13. 20 11
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/view/VideoTextureView.java
  14. 1 1
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/FloatPhone.java
  15. 11 1
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/FloatPlayerView.java
  16. 10 6
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/FloatWindow.java
  17. 2 2
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/IFloatWindowImpl.java
  18. 10 1
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/PermissionActivity.java
  19. 0 9
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/PermissionListener.java
  20. 3 1
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/SmallWindowTouch.java
  21. 3 3
      YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/WindowScreen.java
  22. 2 2
      YCVideoPlayerLib/src/main/res/layout/view_window_dialog.xml
  23. 2 2
      app/build.gradle
  24. 37 0
      app/src/main/AndroidManifest.xml
  25. 196 0
      app/src/main/java/org/yczbj/ycvideoplayer/SchemeActivity.java
  26. 4 6
      app/src/main/java/org/yczbj/ycvideoplayer/TestFullActivity.java
  27. 2 3
      app/src/main/java/org/yczbj/ycvideoplayer/TestTinyActivity.java
  28. 2 2
      app/src/main/java/org/yczbj/ycvideoplayer/TestWindowActivity.java
  29. 0 0
      read/api.md
  30. 23 0
      read/audio_study.md
  31. 0 0
      read/cache_play.md
  32. 269 0
      read/design.md
  33. 26 0
      read/float_play.md
  34. 0 0
      read/framework.md
  35. 34 2
      read/optimize.md
  36. 3 2
      read/question.md
  37. 621 0
      read/test.md
  38. 56 0
      read/todo.md
  39. 122 0
      read/video_study0.md
  40. 24 0
      read/video_study1.md
  41. 24 0
      read/video_study10.md
  42. 33 0
      read/video_study2.md
  43. 258 0
      read/video_study3.md
  44. 79 0
      read/video_study4.md
  45. 24 0
      read/video_study5.md
  46. 24 0
      read/video_study6.md
  47. 26 0
      read/video_study7.md
  48. 24 0
      read/video_study8.md
  49. 24 0
      read/video_study9.md
  50. 0 23
      read/wiki3.md

+ 6 - 17
README.md

@@ -25,21 +25,9 @@
     - 4.9 其他零碎知识
 - 5.运行的效果展示
 - 6.版本更新说明
-    - 6.0.0 v0.0.0 写于2017年7月1日
-    - 6.0.1 V1.0.0 更新于2017年9月4日
-    - 6.0.2 V1.0.1 更新于2017年11月18日
-    - 6.0.3 v1.1.0 更新于2018年1月15日
-    - 6.0.4 v2.0.0 更新于2018年1月18日
-    - 6.0.5 v2.4.5 更新于2018年4月21日
-    - 6.0.6 v2.4.6 更新于2018年8月2日
-    - 6.0.7 v2.4.8 更新于2018年8月12日
-    - 6.0.8 v2.4.9 更新于2018年8月16日
-    - 6.0.9 v2.5.0 更新与2018年8月20日
-    - 6.1.0 v2.6.0 更新于2018年9月25日
-    - 6.1.1 v2.7.0 更新于2019年2月14日
-    - 6.1.2 v2.8.0 更新于2019年4月13日
 - 7.性能优化和库大小
-- 8.其他说明
+- 8.主流视频框架选择
+- 9.其他说明
 
 
 
@@ -325,12 +313,13 @@
 ### 7.性能优化和库大小
 
 
+### 8.主流视频框架选择
 
-### 8.其他说明
+### 9.其他说明
 - ![image](https://upload-images.jianshu.io/upload_images/4432347-7100c8e5a455c3ee.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 
 
-#### 8.1 其他推荐说明
+#### 9.1 其他推荐说明
 - 1.[技术博客汇总](https://www.jianshu.com/p/614cb839182c)
 - 2.[开源项目汇总](https://blog.csdn.net/m0_37700275/article/details/80863574)
 - 3.[生活博客汇总](https://blog.csdn.net/m0_37700275/article/details/79832978)
@@ -340,7 +329,7 @@
 
 
 
-#### 8.2 关于LICENSE
+#### 9.2 关于LICENSE
 ```
 Copyright 2017 yangchong211(github.com/yangchong211)
 

+ 1 - 12
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/dialog/VideoClarity.java → YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/bean/VideoClarity.java

@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
-package org.yczbj.ycvideoplayerlib.dialog;
+package org.yczbj.ycvideoplayerlib.bean;
 
 
 /**
@@ -50,23 +50,12 @@ public class VideoClarity {
         return grade;
     }
 
-    public void setGrade(String grade) {
-        this.grade = grade;
-    }
-
     public String getP() {
         return p;
     }
 
-    public void setP(String p) {
-        this.p = p;
-    }
-
     public String getVideoUrl() {
         return videoUrl;
     }
 
-    public void setVideoUrl(String videoUrl) {
-        this.videoUrl = videoUrl;
-    }
 }

+ 117 - 0
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/bean/VideoInfo.java

@@ -0,0 +1,117 @@
+/*
+Copyright 2017 yangchong211(github.com/yangchong211)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package org.yczbj.ycvideoplayerlib.bean;
+
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://github.com/yangchong211
+ *     time  : 2018/1/29
+ *     desc  : 视频信息实体类
+ *     revise:
+ * </pre>
+ */
+public class VideoInfo implements Serializable {
+
+    /**
+     * 视频的标题
+     */
+    private String title;
+    /**
+     * 播放的视频地址
+     */
+    private String videoUrl;
+    /**
+     * 请求header
+     */
+    private Map<String, String> headers;
+    /**
+     * 视频封面
+     */
+    private String cover;
+    /**
+     * 视频时长
+     */
+    private long length;
+    /**
+     * 异常状态下的文案
+     */
+    private String errorMsg;
+    /**
+     * 播放完成的文案
+     */
+    private String completeMsg;
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getVideoUrl() {
+        return videoUrl;
+    }
+
+    public void setVideoUrl(String videoUrl) {
+        this.videoUrl = videoUrl;
+    }
+
+    public Map<String, String> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, String> headers) {
+        this.headers = headers;
+    }
+
+    public String getCover() {
+        return cover;
+    }
+
+    public void setCover(String cover) {
+        this.cover = cover;
+    }
+
+    public long getLength() {
+        return length;
+    }
+
+    public void setLength(long length) {
+        this.length = length;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    public String getCompleteMsg() {
+        return completeMsg;
+    }
+
+    public void setCompleteMsg(String completeMsg) {
+        this.completeMsg = completeMsg;
+    }
+}

+ 5 - 1
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/controller/AbsVideoPlayerController.java

@@ -240,6 +240,7 @@ public abstract class AbsVideoPlayerController extends VideoControllerView imple
                         }
                     }
                 }
+                //是否需要改变播放的进度
                 if (mNeedChangePosition) {
                     long duration = mVideoPlayer.getDuration();
                     long toPosition = (long) (mGestureDownPosition + duration * deltaX / getWidth());
@@ -247,18 +248,21 @@ public abstract class AbsVideoPlayerController extends VideoControllerView imple
                     int newPositionProgress = (int) (100f * mNewPosition / duration);
                     showChangePosition(duration, newPositionProgress);
                 }
+                //是否改变亮度
                 if (mNeedChangeBrightness) {
                     deltaY = -deltaY;
                     float deltaBrightness = deltaY * 3 / getHeight();
                     float newBrightness = mGestureDownBrightness + deltaBrightness;
                     newBrightness = Math.max(0, Math.min(newBrightness, 1));
                     float newBrightnessPercentage = newBrightness;
-                    WindowManager.LayoutParams params = VideoPlayerUtils.scanForActivity(mContext).getWindow().getAttributes();
+                    WindowManager.LayoutParams params = VideoPlayerUtils.scanForActivity(mContext)
+                            .getWindow().getAttributes();
                     params.screenBrightness = newBrightnessPercentage;
                     VideoPlayerUtils.scanForActivity(mContext).getWindow().setAttributes(params);
                     int newBrightnessProgress = (int) (100f * newBrightnessPercentage);
                     showChangeBrightness(newBrightnessProgress);
                 }
+                //是否改变音量
                 if (mNeedChangeVolume) {
                     deltaY = -deltaY;
                     int maxVolume = mVideoPlayer.getMaxVolume();

+ 12 - 8
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/controller/VideoPlayerController.java

@@ -36,7 +36,7 @@ import android.widget.TextView;
 
 import org.yczbj.ycvideoplayerlib.dialog.ChangeClarityDialog;
 import org.yczbj.ycvideoplayerlib.R;
-import org.yczbj.ycvideoplayerlib.dialog.VideoClarity;
+import org.yczbj.ycvideoplayerlib.bean.VideoClarity;
 import org.yczbj.ycvideoplayerlib.inter.listener.OnPlayerTypeListener;
 import org.yczbj.ycvideoplayerlib.player.VideoPlayer;
 import org.yczbj.ycvideoplayerlib.receiver.BatterReceiver;
@@ -797,7 +797,6 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
             case ConstantKeys.PlayMode.MODE_NORMAL:
                 //隐藏锁屏控件
                 mFlLock.setVisibility(View.GONE);
-                mBack.setVisibility(View.VISIBLE);
                 mFullScreen.setImageResource(R.drawable.ic_player_open);
                 mFullScreen.setVisibility(View.VISIBLE);
                 //隐藏清晰度
@@ -817,7 +816,6 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
             //全屏模式
             case ConstantKeys.PlayMode.MODE_FULL_SCREEN:
                 mFlLock.setVisibility(View.VISIBLE);
-                mBack.setVisibility(View.VISIBLE);
                 mFullScreen.setVisibility(View.VISIBLE);
                 mFullScreen.setImageResource(R.drawable.ic_player_close);
                 if (clarities != null && clarities.size() > 1) {
@@ -836,13 +834,12 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
             //小窗口模式
             case ConstantKeys.PlayMode.MODE_TINY_WINDOW:
                 mFlLock.setVisibility(View.GONE);
-                mBack.setVisibility(View.GONE);
-                mClarity.setVisibility(View.GONE);
                 setTopBottomVisible(false);
                 mIsLock = false;
                 if (mOnPlayerTypeListener!=null){
                     mOnPlayerTypeListener.onTinyWindow();
                 }
+                setCenterVisible(true);
                 VideoLogUtil.d("播放模式--------小窗口模式");
                 break;
             default:
@@ -902,7 +899,7 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
             } else if (mVideoPlayer.isTinyWindow()) {
                 //如果是小窗口,则退出小窗口
                 mVideoPlayer.exitTinyWindow();
-            }else {
+            } else {
                 //如果两种情况都不是,执行逻辑交给使用者自己实现
                 if(mBackListener!=null){
                     mBackListener.onBackClick();
@@ -1077,10 +1074,10 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
      * @param visible                   true显示,false隐藏.
      */
     private void setTopBottomVisible(boolean visible) {
+        setCenterVisible(visible);
         mTop.setVisibility(visible ? View.VISIBLE : View.GONE);
         mBottom.setVisibility(visible ? View.VISIBLE : View.GONE);
         mLine.setVisibility(visible ? View.GONE : View.VISIBLE);
-        mCenterStart.setVisibility(visible ? View.VISIBLE : View.GONE);
         topBottomVisible = visible;
         if (visible) {
             if (!mVideoPlayer.isPaused() && !mVideoPlayer.isBufferingPaused()) {
@@ -1091,6 +1088,14 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
         }
     }
 
+    /**
+     * 设置center的显示和隐藏
+     * @param visible                   true显示,false隐藏.
+     */
+    private void setCenterVisible(boolean visible){
+        mCenterStart.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
 
     /**
      * 开启top、bottom自动消失的timer
@@ -1294,7 +1299,6 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
         this.mOnCompletedListener = listener;
     }
 
-
     /**
      * 视频播放模式监听
      */

+ 2 - 3
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/inter/listener/OnTextureListener.java

@@ -38,8 +38,8 @@ public interface OnTextureListener {
     /**
      * SurfaceTexture缓冲大小变化
      * @param surface                   surface
-     * @param width                     width
-     * @param height                    height
+     * @param width                     WIDTH
+     * @param height                    HEIGHT
      */
     void onSurfaceSizeChanged(SurfaceTexture surface, int width, int height);
 
@@ -50,7 +50,6 @@ public interface OnTextureListener {
      */
     boolean onSurfaceDestroyed(SurfaceTexture surface);
 
-
     /**
      * SurfaceTexture通过updateImage更新
      * @param surface                   surface

+ 15 - 0
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/inter/player/InterVideoController.java

@@ -1,3 +1,18 @@
+/*
+Copyright 2017 yangchong211(github.com/yangchong211)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 package org.yczbj.ycvideoplayerlib.inter.player;
 
 import android.support.annotation.DrawableRes;

+ 15 - 0
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/inter/player/VideoControllerView.java

@@ -1,3 +1,18 @@
+/*
+Copyright 2017 yangchong211(github.com/yangchong211)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 package org.yczbj.ycvideoplayerlib.inter.player;
 
 import android.content.Context;

+ 4 - 4
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/player/VideoMediaPlayer.java

@@ -29,7 +29,7 @@ import tv.danmaku.ijk.media.player.IjkTimedText;
  *     blog  : https://github.com/yangchong211
  *     time  : 2017/11/21
  *     desc  : MediaPlayer帮助累
- *     revise: 主要处理音视频player初始化操作
+ *     revise: 主要处理音视频player初始化操作和各种监听
  * </pre>
  */
 public class VideoMediaPlayer {
@@ -56,7 +56,8 @@ public class VideoMediaPlayer {
     @RequiresApi(api = Build.VERSION_CODES.FROYO)
     public AudioManager initAudioManager() {
         if (mAudioManager == null) {
-            mAudioManager  = (AudioManager) videoPlayer.getContext().getSystemService(Context.AUDIO_SERVICE);
+            mAudioManager  = (AudioManager) videoPlayer.getContext()
+                    .getSystemService(Context.AUDIO_SERVICE);
             mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC,
                     AudioManager.AUDIOFOCUS_GAIN);
         }
@@ -370,7 +371,7 @@ public class VideoMediaPlayer {
                 public void onVideoSizeChanged(IMediaPlayer mp, int width, int height,
                                                int sar_num, int sar_den) {
                     mTextureView.adaptVideoSize(width, height);
-                    VideoLogUtil.d("listener---------onVideoSizeChanged ——> width:" + width + ", height:" + height);
+                    VideoLogUtil.d("listener---------onVideoSizeChanged ——> WIDTH:" + width + ", HEIGHT:" + height);
                 }
             };
 
@@ -398,7 +399,6 @@ public class VideoMediaPlayer {
     };
 
 
-
     /**
      * 设置视频信息监听器
      */

+ 5 - 13
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/player/VideoPlayer.java

@@ -18,38 +18,31 @@ package org.yczbj.ycvideoplayerlib.player;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.Color;
-import android.graphics.SurfaceTexture;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
-import android.view.Surface;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.Toast;
+
 import org.yczbj.ycvideoplayerlib.constant.ConstantKeys;
 import org.yczbj.ycvideoplayerlib.controller.AbsVideoPlayerController;
-import org.yczbj.ycvideoplayerlib.inter.listener.OnTextureListener;
+import org.yczbj.ycvideoplayerlib.inter.player.InterPropertyVideoPlayer;
 import org.yczbj.ycvideoplayerlib.inter.player.InterScreenVideoPlayer;
 import org.yczbj.ycvideoplayerlib.inter.player.InterStateVideoPlayer;
-import org.yczbj.ycvideoplayerlib.inter.player.InterPropertyVideoPlayer;
 import org.yczbj.ycvideoplayerlib.manager.VideoPlayerManager;
 import org.yczbj.ycvideoplayerlib.utils.VideoLogUtil;
 import org.yczbj.ycvideoplayerlib.utils.VideoPlayerUtils;
 import org.yczbj.ycvideoplayerlib.view.BaseToast;
-import org.yczbj.ycvideoplayerlib.view.VideoTextureView;
-import java.io.IOException;
+
 import java.util.Map;
+
 import tv.danmaku.ijk.media.player.AndroidMediaPlayer;
-import tv.danmaku.ijk.media.player.IMediaPlayer;
 import tv.danmaku.ijk.media.player.IjkMediaPlayer;
-import tv.danmaku.ijk.media.player.IjkTimedText;
 
 
 /**
@@ -731,7 +724,6 @@ public class VideoPlayer extends FrameLayout implements InterPropertyVideoPlayer
         VideoLogUtil.d("播放模式-------MODE_TINY_WINDOW");
     }
 
-
     /**
      * 退出小窗口播放
      */

+ 38 - 2
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/utils/VideoPlayerUtils.java

@@ -21,9 +21,12 @@ import android.content.Context;
 import android.content.ContextWrapper;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.os.Build;
+import android.support.annotation.Nullable;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ContextThemeWrapper;
+import android.text.TextUtils;
 import android.util.TypedValue;
 import android.view.WindowManager;
 import java.util.Formatter;
@@ -63,11 +66,17 @@ public final class VideoPlayerUtils {
      * @param activity      activity
      * @return
      */
-    public static boolean isActivityLiving(Activity activity) {
+    public static boolean isActivityLiving(@Nullable Activity activity) {
         if (activity == null) {
             return false;
         }
-        return !activity.isFinishing();
+        if (activity.isFinishing()) {
+            return false;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
+            return false;
+        }
+        return true;
     }
 
 
@@ -223,5 +232,32 @@ public final class VideoPlayerUtils {
         return manager.getActiveNetworkInfo();
     }
 
+    /**
+     * 空指针检测,不能为null,可以为""
+     * @param obj           obj对象
+     * @param message       消息
+     * @param <T>           泛型
+     * @return              obj
+     */
+    public static <T> T requireNonNull(T obj, String message) {
+        if (obj == null) {
+            throw new NullPointerException(message);
+        }
+        return obj;
+    }
+
+    /**
+     * 空指针检测,不能为null,也不能为""
+     * @param obj           obj对象
+     * @param message       消息
+     * @return              字符串
+     */
+    public static String requireNonEmpty(String obj, String message) {
+        if (TextUtils.isEmpty(obj)) {
+            throw new IllegalArgumentException(message);
+        }
+        return obj;
+    }
+
 
 }

+ 15 - 6
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/view/VideoSurfaceView.java

@@ -46,6 +46,7 @@ public class VideoSurfaceView extends SurfaceView{
     private int videoHeight;
     private int videoWidth;
     private OnSurfaceListener onSurfaceListener;
+    private static final float EQUAL_FLOAT = 0.0000001f;
 
     public VideoSurfaceView(Context context) {
         super(context);
@@ -166,12 +167,20 @@ public class VideoSurfaceView extends SurfaceView{
         // 如果判断成立,则说明显示的TextureView和本身的位置是有90度的旋转的,所以需要交换宽高参数。
         float viewRotation1 = 90f;
         float viewRotation2 = 270f;
-        if (viewRotation == viewRotation1 || viewRotation == viewRotation2) {
+        //如果是横竖屏旋转切换视图,则宽高属性互换
+        if (Math.abs(viewRotation-viewRotation1)> EQUAL_FLOAT && Math.abs(viewRotation1-viewRotation)> EQUAL_FLOAT ||
+                (Math.abs(viewRotation-viewRotation2)> EQUAL_FLOAT && Math.abs(viewRotation2-viewRotation)> EQUAL_FLOAT)){
             int tempMeasureSpec = widthMeasureSpec;
             //noinspection SuspiciousNameCombination
             widthMeasureSpec = heightMeasureSpec;
             heightMeasureSpec = tempMeasureSpec;
         }
+        /*if (viewRotation == viewRotation1 || viewRotation == viewRotation2) {
+            int tempMeasureSpec = widthMeasureSpec;
+            //noinspection SuspiciousNameCombination
+            widthMeasureSpec = heightMeasureSpec;
+            heightMeasureSpec = tempMeasureSpec;
+        }*/
 
         int width = getDefaultSize(videoWidth, widthMeasureSpec);
         int height = getDefaultSize(videoHeight, heightMeasureSpec);
@@ -193,7 +202,7 @@ public class VideoSurfaceView extends SurfaceView{
                     height = width * videoHeight / videoWidth;
                 }
             } else if (widthSpecMode == MeasureSpec.EXACTLY) {
-                // only the width is fixed, adjust the height to match aspect ratio if possible
+                // only the WIDTH is fixed, adjust the HEIGHT to match aspect ratio if possible
                 width = widthSpecSize;
                 height = width * videoHeight / videoWidth;
                 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
@@ -202,7 +211,7 @@ public class VideoSurfaceView extends SurfaceView{
                     width = height * videoWidth / videoHeight;
                 }
             } else if (heightSpecMode == MeasureSpec.EXACTLY) {
-                // only the height is fixed, adjust the width to match aspect ratio if possible
+                // only the HEIGHT is fixed, adjust the WIDTH to match aspect ratio if possible
                 height = heightSpecSize;
                 width = height * videoWidth / videoHeight;
                 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
@@ -211,16 +220,16 @@ public class VideoSurfaceView extends SurfaceView{
                     height = width * videoHeight / videoWidth;
                 }
             } else {
-                // neither the width nor the height are fixed, try to use actual video size
+                // neither the WIDTH nor the HEIGHT are fixed, try to use actual video size
                 width = videoWidth;
                 height = videoHeight;
                 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
-                    // too tall, decrease both width and height
+                    // too tall, decrease both WIDTH and HEIGHT
                     height = heightSpecSize;
                     width = height * videoWidth / videoHeight;
                 }
                 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
-                    // too wide, decrease both width and height
+                    // too wide, decrease both WIDTH and HEIGHT
                     width = widthSpecSize;
                     height = width * videoHeight / videoWidth;
                 }

+ 20 - 11
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/view/VideoTextureView.java

@@ -25,6 +25,7 @@ import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import org.yczbj.ycvideoplayerlib.inter.listener.OnTextureListener;
+import org.yczbj.ycvideoplayerlib.utils.VideoLogUtil;
 
 
 /**
@@ -47,6 +48,7 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
     private int videoHeight;
     private int videoWidth;
     private OnTextureListener onTextureListener;
+    private static final float EQUAL_FLOAT = 0.0000001f;
 
 
     public VideoTextureView(Context context) {
@@ -57,8 +59,8 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
     /**
      * SurfaceTexture准备就绪
      * @param surface                   surface
-     * @param width                     width
-     * @param height                    height
+     * @param width                     WIDTH
+     * @param height                    HEIGHT
      */
     @Override
     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
@@ -71,8 +73,8 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
     /**
      * SurfaceTexture缓冲大小变化
      * @param surface                   surface
-     * @param width                     width
-     * @param height                    height
+     * @param width                     WIDTH
+     * @param height                    HEIGHT
      */
     @Override
     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
@@ -180,13 +182,20 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
         float viewRotation1 = 90f;
         float viewRotation2 = 270f;
         //如果是横竖屏旋转切换视图,则宽高属性互换
-        if (viewRotation == viewRotation1 || viewRotation == viewRotation2) {
+        if (Math.abs(viewRotation-viewRotation1)> EQUAL_FLOAT && Math.abs(viewRotation1-viewRotation)> EQUAL_FLOAT ||
+                (Math.abs(viewRotation-viewRotation2)> EQUAL_FLOAT && Math.abs(viewRotation2-viewRotation)> EQUAL_FLOAT)){
             int tempMeasureSpec = widthMeasureSpec;
             //noinspection SuspiciousNameCombination
             widthMeasureSpec = heightMeasureSpec;
             heightMeasureSpec = tempMeasureSpec;
+            VideoLogUtil.d("TextureView---------"+"如果是横竖屏旋转切换视图,则宽高属性互换");
         }
-
+        /*if (viewRotation == viewRotation1 || viewRotation == viewRotation2) {
+            int tempMeasureSpec = widthMeasureSpec;
+            //noinspection SuspiciousNameCombination
+            widthMeasureSpec = heightMeasureSpec;
+            heightMeasureSpec = tempMeasureSpec;
+        }*/
         int width = getDefaultSize(videoWidth, widthMeasureSpec);
         int height = getDefaultSize(videoHeight, heightMeasureSpec);
         if (videoWidth > 0 && videoHeight > 0) {
@@ -207,7 +216,7 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
                     height = width * videoHeight / videoWidth;
                 }
             } else if (widthSpecMode == MeasureSpec.EXACTLY) {
-                // only the width is fixed, adjust the height to match aspect ratio if possible
+                // only the WIDTH is fixed, adjust the HEIGHT to match aspect ratio if possible
                 width = widthSpecSize;
                 height = width * videoHeight / videoWidth;
                 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
@@ -216,7 +225,7 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
                     width = height * videoWidth / videoHeight;
                 }
             } else if (heightSpecMode == MeasureSpec.EXACTLY) {
-                // only the height is fixed, adjust the width to match aspect ratio if possible
+                // only the HEIGHT is fixed, adjust the WIDTH to match aspect ratio if possible
                 height = heightSpecSize;
                 width = height * videoWidth / videoHeight;
                 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
@@ -225,16 +234,16 @@ public class VideoTextureView extends TextureView implements TextureView.Surface
                     height = width * videoHeight / videoWidth;
                 }
             } else {
-                // neither the width nor the height are fixed, try to use actual video size
+                // neither the WIDTH nor the HEIGHT are fixed, try to use actual video size
                 width = videoWidth;
                 height = videoHeight;
                 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
-                    // too tall, decrease both width and height
+                    // too tall, decrease both WIDTH and HEIGHT
                     height = heightSpecSize;
                     width = height * videoWidth / videoHeight;
                 }
                 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
-                    // too wide, decrease both width and height
+                    // too wide, decrease both WIDTH and HEIGHT
                     width = widthSpecSize;
                     height = width * videoHeight / videoWidth;
                 }

+ 1 - 1
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/FloatPhone.java

@@ -61,7 +61,7 @@ public class FloatPhone extends FloatView {
                 mLayoutParams.format = PixelFormat.RGBA_8888;
                 mWindowManager.addView(mView, mLayoutParams);
             } else {
-                PermissionActivity.request(mContext, new PermissionListener() {
+                PermissionActivity.request(mContext, new PermissionActivity.PermissionListener() {
                     @Override
                     public void onSuccess() {
                         mLayoutParams.format = PixelFormat.RGBA_8888;

+ 11 - 1
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/FloatPlayerView.java

@@ -67,8 +67,15 @@ public class FloatPlayerView extends FrameLayout {
                     }
                 }
             });
+            //controller.onPlayModeChanged(ConstantKeys.PlayMode.MODE_TINY_WINDOW);
             mVideoPlayer.setController(controller);
-            mVideoPlayer.start();
+            //mVideoPlayer.enterTinyWindow();
+            mVideoPlayer.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mVideoPlayer.start();
+                }
+            },300);
             view.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -91,6 +98,9 @@ public class FloatPlayerView extends FrameLayout {
     }
 
     public interface CompletedListener{
+        /**
+         * 播放完成
+         */
         void Completed();
     }
 

+ 10 - 6
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/FloatWindow.java

@@ -49,7 +49,10 @@ public class FloatWindow {
         if (mFloatWindowMap == null || !mFloatWindowMap.containsKey(tag)) {
             return;
         }
-        mFloatWindowMap.get(tag).dismiss();
+        IFloatWindow iFloatWindow = mFloatWindowMap.get(tag);
+        if (iFloatWindow != null) {
+            iFloatWindow.dismiss();
+        }
         mFloatWindowMap.remove(tag);
     }
 
@@ -98,7 +101,7 @@ public class FloatWindow {
         }
 
         public B setWidth(@WindowScreen.screenType int screenType, float ratio) {
-            mWidth = (int) ((screenType == WindowScreen.width ?
+            mWidth = (int) ((screenType == WindowScreen.WIDTH ?
                     WindowUtil.getScreenWidth(mApplicationContext) :
                     WindowUtil.getScreenHeight(mApplicationContext)) * ratio);
             return this;
@@ -106,7 +109,7 @@ public class FloatWindow {
 
 
         public B setHeight(@WindowScreen.screenType int screenType, float ratio) {
-            mHeight = (int) ((screenType == WindowScreen.width ?
+            mHeight = (int) ((screenType == WindowScreen.WIDTH ?
                     WindowUtil.getScreenWidth(mApplicationContext) :
                     WindowUtil.getScreenHeight(mApplicationContext)) * ratio);
             return this;
@@ -124,14 +127,14 @@ public class FloatWindow {
         }
 
         public B setX(@WindowScreen.screenType int screenType, float ratio) {
-            xOffset = (int) ((screenType == WindowScreen.width ?
+            xOffset = (int) ((screenType == WindowScreen.WIDTH ?
                     WindowUtil.getScreenWidth(mApplicationContext) :
                     WindowUtil.getScreenHeight(mApplicationContext)) * ratio);
             return this;
         }
 
         public B setY(@WindowScreen.screenType int screenType, float ratio) {
-            yOffset = (int) ((screenType == WindowScreen.width ?
+            yOffset = (int) ((screenType == WindowScreen.WIDTH ?
                     WindowUtil.getScreenWidth(mApplicationContext) :
                     WindowUtil.getScreenHeight(mApplicationContext)) * ratio);
             return this;
@@ -172,7 +175,8 @@ public class FloatWindow {
                 mFloatWindowMap = new HashMap<>();
             }
             if (mFloatWindowMap.containsKey(mTag)) {
-                throw new IllegalArgumentException("FloatWindow of this tag has been added, Please set a new tag for the new FloatWindow");
+                throw new IllegalArgumentException("FloatWindow of this tag has been added," +
+                        " Please set a new tag for the new FloatWindow");
             }
             if (mView == null && mLayoutId == 0) {
                 throw new IllegalArgumentException("View has not been set!");

+ 2 - 2
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/IFloatWindowImpl.java

@@ -103,7 +103,7 @@ public class IFloatWindowImpl extends IFloatWindow {
     @Override
     public void updateX(int screenType, float ratio) {
         checkMoveType();
-        mB.xOffset = (int) ((screenType == WindowScreen.width ?
+        mB.xOffset = (int) ((screenType == WindowScreen.WIDTH ?
                 WindowUtil.getScreenWidth(mB.mApplicationContext) :
                 WindowUtil.getScreenHeight(mB.mApplicationContext)) * ratio);
         mFloatView.updateX(mB.xOffset);
@@ -113,7 +113,7 @@ public class IFloatWindowImpl extends IFloatWindow {
     @Override
     public void updateY(int screenType, float ratio) {
         checkMoveType();
-        mB.yOffset = (int) ((screenType == WindowScreen.width ?
+        mB.yOffset = (int) ((screenType == WindowScreen.WIDTH ?
                 WindowUtil.getScreenWidth(mB.mApplicationContext) :
                 WindowUtil.getScreenHeight(mB.mApplicationContext)) * ratio);
         mFloatView.updateY(mB.yOffset);

+ 10 - 1
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/PermissionActivity.java

@@ -84,5 +84,14 @@ public class PermissionActivity extends AppCompatActivity {
 
     private static PermissionListener mPermissionListener;
 
-
+    public interface PermissionListener {
+        /**
+         * 成功
+         */
+        void onSuccess();
+        /**
+         * 失败
+         */
+        void onFail();
+    }
 }

+ 0 - 9
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/PermissionListener.java

@@ -1,9 +0,0 @@
-package org.yczbj.ycvideoplayerlib.window;
-
-
-interface PermissionListener {
-
-    void onSuccess();
-
-    void onFail();
-}

+ 3 - 1
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/SmallWindowTouch.java

@@ -48,7 +48,6 @@ public class SmallWindowTouch implements View.OnTouchListener {
                 FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) mView.getLayoutParams();
                 _xDelta = X - lParams.leftMargin;
                 _yDelta = Y - lParams.topMargin;
-
                 break;
             case MotionEvent.ACTION_UP:
                 if (Math.abs(mDownY - Y) < 5 && Math.abs(mDownX - X) < 5) {
@@ -76,6 +75,9 @@ public class SmallWindowTouch implements View.OnTouchListener {
                     layoutParams.topMargin = 0;
                 }
                 mView.setLayoutParams(layoutParams);
+                break;
+            default:
+                break;
         }
         return false;
     }

+ 3 - 3
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/window/WindowScreen.java

@@ -7,10 +7,10 @@ import java.lang.annotation.RetentionPolicy;
 
 
 public class WindowScreen {
-    public static final int width = 0;
-    public static final int height = 1;
+    public static final int WIDTH = 0;
+    public static final int HEIGHT = 1;
 
-    @IntDef({width, height})
+    @IntDef({WIDTH, HEIGHT})
     @Retention(RetentionPolicy.SOURCE)
     @interface screenType {}
 }

+ 2 - 2
YCVideoPlayerLib/src/main/res/layout/view_window_dialog.xml

@@ -7,7 +7,7 @@
 
     <org.yczbj.ycvideoplayerlib.player.VideoPlayer
         android:id="@+id/video_player"
-        android:layout_width="160dp"
-        android:layout_height="120dp"/>
+        android:layout_width="240dp"
+        android:layout_height="150dp"/>
 
 </org.yczbj.ycvideoplayerlib.window.MyLinearLayout>

+ 2 - 2
app/build.gradle

@@ -60,7 +60,7 @@ dependencies {
 
     //自己封装的库,都有对应的案例项目【欢迎star】:https://github.com/yangchong211
     implementation 'cn.yc:YCStateLib:1.1'                                  //状态管理
-//    implementation 'cn.yc:YCVideoPlayerLib:2.6.6'                            //播放器
-    implementation project(':YCVideoPlayerLib')
+    implementation 'cn.yc:YCVideoPlayerLib:2.6.6'                            //播放器
+//    implementation project(':YCVideoPlayerLib')
     implementation 'cn.yc:YCStatusBarLib:1.4.0'                              //状态栏
 }

+ 37 - 0
app/src/main/AndroidManifest.xml

@@ -26,6 +26,43 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+
+        <!--用于AppLink,html跳到此页面  scheme_Adr: 'yilu://link/?page=main',-->
+        <activity android:name=".SchemeActivity"
+            android:screenOrientation="portrait">
+            <!--Android 接收外部跳转过滤器-->
+            <intent-filter android:autoVerify="true">
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="http"/>
+                <data android:scheme="https"/>
+                <data android:host="yc.com"/>
+            </intent-filter>
+            <intent-filter >
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <!-- 协议部分配置 ,要在web配置相同的-->
+                <!--yilu://link/?page=main-->
+                <data
+                    android:host="link"
+                    android:scheme="yilu" />
+            </intent-filter>
+
+            <intent-filter >
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <!-- 协议部分配置 ,要在web配置相同的-->
+                <!--yilu://www.yc.com/?page=main-->
+                <data
+                    android:host="www.yc.com"
+                    android:scheme="yilu" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".TestTinyActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
             android:screenOrientation="portrait"/>

+ 196 - 0
app/src/main/java/org/yczbj/ycvideoplayer/SchemeActivity.java

@@ -0,0 +1,196 @@
+package org.yczbj.ycvideoplayer;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://github.com/yangchong211
+ *     time  : 2019/11/29
+ *     desc  : scheme协议页面
+ *     revise: AppLink
+ * </pre>
+ */
+public class SchemeActivity extends AppCompatActivity {
+
+    /**
+     * URL Scheme使用场景,目前1,2,5使用场景很广
+     * 1.通过小程序,利用Scheme协议打开原生app
+     * 2.H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面
+     * 3.APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
+     * 4.APP根据URL跳转到另外一个APP指定页面
+     * 5.通过短信息中的url打开原生app
+     */
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        try {
+            getDataFromBrower();
+        } catch (Exception e){
+            e.printStackTrace();
+            Intent intent = new Intent(this, MainActivity.class);
+            readGoActivity(intent, this);
+        }
+        finish();
+    }
+
+    private void getDataFromBrower() {
+        Uri uri = getIntent().getData();
+        if (uri != null) {
+            // 完整的url信息
+            String url = uri.toString();
+            LogUtils.i("SchemeActivity---" + "url: " + uri);
+            // scheme部分
+            String scheme = uri.getScheme();
+            LogUtils.i("SchemeActivity---" + "scheme: " + scheme);
+            // host部分
+            String host = uri.getHost();
+            LogUtils.i("SchemeActivity---" + "host: " + host);
+            //port部分
+            int port = uri.getPort();
+            LogUtils.i("SchemeActivity---" + "host: " + port);
+            // 访问路劲
+            String path = uri.getPath();
+            LogUtils.i("SchemeActivity---" + "path: " + path);
+            // 获取参数
+            List<String> pathSegments = uri.getPathSegments();
+            LogUtils.i("SchemeActivity---" + "pathSegments: " + pathSegments.size());
+            // Query部分
+            String query = uri.getQuery();
+            LogUtils.i("SchemeActivity---" + "query: " + query);
+            //获取指定参数值
+            String page = uri.getQueryParameter("page");
+            LogUtils.i("SchemeActivity---" + "page: " + page);
+
+            //获取指定参数值,该方法获取值一直是空
+            //String level = uri.getQueryParameter("level");
+            ///Log.e( "UrlUtils","level: " + level);
+
+            //String level = getValueByName(url, "level");
+            //LoggerUtils.i( "SchemeActivity---","level: " + level);
+            if (page==null || page.length()==0) {
+                finish();
+                return;
+            }
+            switch (page) {
+                case "main":
+                    //唤起客户端,进入首页
+                    //https://yc.com?page=main
+                    Intent intent1 = new Intent(this, MainActivity.class);
+                    readGoActivity(intent1, this);
+                    break;
+                case "full":
+                    //唤起客户端,进入A页面
+                    //https://yc.com?page=full
+                    Intent intent2 = new Intent(this, TestFullActivity.class);
+                    readGoActivity(intent2, this);
+                    break;
+                case "list":
+                    //唤起客户端,进入B页面,携带参数
+                    //https://yc.com?page=list&id=520
+                    Intent intent3 = new Intent(this, TestListActivity.class);
+                    String id = getValueByName(url, "id");
+                    intent3.putExtra("id",id);
+                    readGoActivity(intent3, this);
+                    break;
+                case "small":
+                    //唤起客户端,进入C页面
+                    Intent intent4 = new Intent(this, TestRecyclerActivity.class);
+                    readGoActivity(intent4, this);
+                    break;
+                default:
+                    Intent intent = new Intent(this, MainActivity.class);
+                    readGoActivity(intent, this);
+                    break;
+            }
+        }
+    }
+
+
+    /***
+     * 获取url 指定name的value;
+     * @param url                       url
+     * @param name                      参数名
+     * @return 获取某个参数值
+     */
+    private String getValueByName(String url, String name) {
+        String result = "";
+        //如果不包含参数就直接返回,避免空指针异常
+        if (!url.contains("?")) {
+            return result;
+        }
+        int index = url.indexOf("?");
+        String temp = url.substring(index + 1);
+        if (temp.contains("&")) {
+            String[] keyValue = temp.split("&");
+            for (String str : keyValue) {
+                if (str.contains(name)) {
+                    result = str.replace(name + "=", "");
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    public void readGoActivity(Intent intent, Context context) {
+        // 如果app 运行中,直接打开页面,没有运行中就先打开主界面,在打开
+        if (isAppRunning(context, context.getPackageName())) {
+            openActivity(intent, context);
+        } else {
+            //先打开首页,然后跳转指定页面
+            reStartActivity(intent, context);
+        }
+    }
+
+    public void openActivity(Intent intent, Context context) {
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(intent);
+    }
+
+    /**
+     * 注意,为何要这样跳转,首先需要先跳转首页,然后在跳转到指定页面,那么回来的时候始终是首页Main页面
+     * @param intent                            intent
+     * @param context                           上下文
+     */
+    public void reStartActivity(Intent intent, Context context) {
+        Intent[] intents = new Intent[2];
+        Intent mainIntent = new Intent(context, MainActivity.class);
+        mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intents[0] = mainIntent;
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intents[1] = intent;
+        context.startActivities(intents);
+    }
+
+    /**
+     * 判断app是否正在运行
+     * @param context                           上下文
+     * @param packageName                       应用的包名
+     * @return true 表示正在运行,false 表示没有运行
+     */
+    public boolean isAppRunning(Context context, String packageName) {
+        ActivityManager am = (ActivityManager) context.getApplicationContext()
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningTaskInfo> list = am.getRunningTasks(100);
+        if (list.size() <= 0) {
+            return false;
+        }
+        for (ActivityManager.RunningTaskInfo info : list) {
+            if (info.baseActivity.getPackageName().equals(packageName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 4 - 6
app/src/main/java/org/yczbj/ycvideoplayer/TestFullActivity.java

@@ -10,8 +10,6 @@ import org.yczbj.ycvideoplayerlib.controller.VideoPlayerController;
 import org.yczbj.ycvideoplayerlib.inter.listener.OnVideoControlListener;
 import org.yczbj.ycvideoplayerlib.manager.VideoPlayerManager;
 import org.yczbj.ycvideoplayerlib.player.VideoPlayer;
-import org.yczbj.ycvideoplayerlib.view.BaseToast;
-
 import cn.ycbjie.ycstatusbarlib.bar.StateAppBar;
 
 
@@ -81,16 +79,16 @@ public class TestFullActivity extends BaseActivity implements View.OnClickListen
             public void onVideoControlClick(int type) {
                 switch (type){
                     case ConstantKeys.VideoControl.DOWNLOAD:
-                        BaseToast.showRoundRectToast("下载");
+                        //BaseToast.showRoundRectToast("下载");
                         break;
                     case ConstantKeys.VideoControl.SHARE:
-                        BaseToast.showRoundRectToast("分享");
+                        //BaseToast.showRoundRectToast("分享");
                         break;
                     case ConstantKeys.VideoControl.MENU:
-                        BaseToast.showRoundRectToast("更多");
+                        //BaseToast.showRoundRectToast("更多");
                         break;
                     case ConstantKeys.VideoControl.AUDIO:
-                        BaseToast.showRoundRectToast("下载");
+                        //BaseToast.showRoundRectToast("下载");
                         break;
                     default:
                         break;

+ 2 - 3
app/src/main/java/org/yczbj/ycvideoplayer/TestTinyActivity.java

@@ -12,7 +12,6 @@ import org.yczbj.ycvideoplayerlib.controller.VideoPlayerController;
 import org.yczbj.ycvideoplayerlib.inter.listener.OnVideoControlListener;
 import org.yczbj.ycvideoplayerlib.manager.VideoPlayerManager;
 import org.yczbj.ycvideoplayerlib.player.VideoPlayer;
-import org.yczbj.ycvideoplayerlib.view.BaseToast;
 
 import cn.ycbjie.ycstatusbarlib.bar.StateAppBar;
 
@@ -72,10 +71,10 @@ public class TestTinyActivity extends BaseActivity implements View.OnClickListen
             public void onVideoControlClick(int type) {
                 switch (type){
                     case ConstantKeys.VideoControl.TV:
-                        BaseToast.showRoundRectToast("投影tv电视");
+                        //BaseToast.showRoundRectToast("投影tv电视");
                         break;
                     case ConstantKeys.VideoControl.HOR_AUDIO:
-                        BaseToast.showRoundRectToast("切换音频");
+                        //BaseToast.showRoundRectToast("切换音频");
                         break;
                     default:
                         break;

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

@@ -105,8 +105,8 @@ public class TestWindowActivity extends BaseActivity implements View.OnClickList
         FloatWindow
                 .with(getApplicationContext())
                 .setView(floatPlayerView)
-                //.setWidth(WindowScreen.width, 0.4f)
-                //.setHeight(WindowScreen.width, 0.3f)
+                //.setWidth(WindowScreen.WIDTH, 0.4f)
+                //.setHeight(WindowScreen.WIDTH, 0.3f)
                 //这个是设置位置
                 .setX(WindowScreen.width, 0.8f)
                 .setY(WindowScreen.height, 0.3f)

+ 0 - 0
read/wiki1.md → read/api.md


+ 23 - 0
read/audio_study.md

@@ -0,0 +1,23 @@
+# 音频相关知识点学习
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 0 - 0
read/wiki6.md → read/cache_play.md


+ 269 - 0
read/design.md

@@ -0,0 +1,269 @@
+#### 面向对象优化介绍
+- 01.面向对象6大原则介绍
+- 02.单一指责的演变和迭代
+- 07.视频状态View的演变
+
+
+
+
+
+
+
+### 07.视频状态View的演变
+- 关于视频状态view都有那些呢?先简单列举一下常见的视图……
+    - 视频加载视图,视频播放错误视图,视频重试视图,视频播放完成视图,视频网络变化视图,视频投屏视图,视频倍速播放视图,视频滑动缩略图,视频弹幕视图,视频清晰度视图,视频手势指导视图,视频手势滑动改变音量和亮度视图,视频广告视图,视频下载视图……这个仅仅是一部分,有没有发现真的特别多,而且这么多视频的显示和隐藏状态,随着后期的迭代,如何降低维护的成本也是值得思考的问题。
+- 最刚开始的做法是,将视频加载loading,视频播放错误,视频重试,网络变化等布局写到了一起,代码如下所示。
+    - 这个是最原始的做法,让每种状态的布局都写出来,然后放到一起。根据播放的逻辑进行动态VISIBLE和GONE处理。
+    ```
+    //省略部分代码
+    <?xml version="1.0" encoding="utf-8"?>
+    <RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        
+        <!--加载动画view-->
+        <include layout="@layout/custom_video_player_loading"/>
+        <!--改变播放位置-->
+        <include layout="@layout/custom_video_player_change_position"/>
+        <!--改变亮度-->
+        <include layout="@layout/custom_video_player_change_brightness"/>
+        <!--改变声音-->
+        <include layout="@layout/custom_video_player_change_volume"/>
+        <!--播放完成,你也可以自定义-->
+        <include layout="@layout/custom_video_player_completed"/>
+        <!--播放错误-->
+        <include layout="@layout/custom_video_player_error"/>
+        <!--播放重试-->
+        <include layout="@layout/custom_video_player_reset"/>
+    
+    </RelativeLayout>
+    ```
+    - 然后是如何控制播放状态的呢?代码如下所示,后来发现如果状态页面过多的话,关于状态视图的展示和隐藏有点麻烦。当然可以实现,但是容易出错……
+    ```
+    /**
+     * 当播放状态发生改变时
+     * @param playState 播放状态:
+     */
+    @SuppressLint("SetTextI18n")
+    @Override
+    public void onPlayStateChanged(@ConstantKeys.CurrentState int playState) {
+        switch (playState) {
+            case ConstantKeys.CurrentState.STATE_IDLE:
+                break;
+            //播放准备中
+            case ConstantKeys.CurrentState.STATE_PREPARING:
+                startPreparing();
+                break;
+            //播放准备就绪
+            case ConstantKeys.CurrentState.STATE_PREPARED:
+                startUpdateProgressTimer();
+                //取消缓冲时更新网络加载速度
+                cancelUpdateNetSpeedTimer();
+                break;
+            //正在播放
+            case ConstantKeys.CurrentState.STATE_PLAYING:
+                statePlaying();
+                break;
+            //暂停播放
+            case ConstantKeys.CurrentState.STATE_PAUSED:
+                statePaused();
+                break;
+            //正在缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,缓冲区数据足够后恢复播放)
+            case ConstantKeys.CurrentState.STATE_BUFFERING_PLAYING:
+                stateBufferingPlaying();
+                break;
+            //暂停缓冲
+            case ConstantKeys.CurrentState.STATE_BUFFERING_PAUSED:
+                stateBufferingPaused();
+                break;
+            //播放错误
+            case ConstantKeys.CurrentState.STATE_ERROR:
+                stateError();
+                break;
+            //播放完成
+            case ConstantKeys.CurrentState.STATE_COMPLETED:
+                stateCompleted();
+                break;
+            default:
+                break;
+        }
+    }
+    ```
+- 迭代留下的遗留问题
+    - 如果是添加新的状态页面,则需要在布局中又要添加一个类型,后期的布局变得不好维护。
+    - 关于新的状态页面的展示和隐藏,又需要看视频的播放逻辑,然后动态显示和隐藏。如果状态页面很多,则变得不好维护。
+    - 比如针对视频播放异常,视频播放重试,需要定制不同的UI。那么又该如何操作,是不是缺乏一定的灵活性。
+- 根据面向对象思想的优化
+    - 针对视频加载loading,视频播放错误,视频重试,网络变化,视频试看等n中状态页面是否能够用状态管理器管理。这个管理器功能单一,主要是处理切换视频状态view的显示和隐藏逻辑。
+    - n中状态页面必须要单独抽出来,不要统一写在布局中,避免后期代码维护臃肿。比如播放错误view,能否把它封装成一个ErrorView对象,然后只需要加载自己布局即可,至于设置错误的文案,可以用set方法暴露出来。
+    - 针对n个状态页面,可能会有触发事件。比如视频重试,点击会重新加载视频;比如网络从wifi切换4g,点击会进行4g播放视频。点击事件需要暴露给开发者处理。
+- 然后看看优化后的伪代码思路
+    - 下面仅仅是针对加载错误的视图View,可以看出该类功能单一,也很灵活,针对文案相关内容可以直接设置。点击事件也暴露给外部开发者。然后以此类推,n个状态视图都可以定义为这样的类。
+    ```
+    /**
+     * 错误提示对话框。出错的时候会显示。
+     */
+    public class ErrorView extends RelativeLayout {
+    
+        private static final String TAG = ErrorView.class.getSimpleName();
+        //错误信息
+        private TextView mMsgView;
+        //错误码
+        private TextView mCodeView;
+        //重试的图片
+        private View mRetryView;
+        //重试的按钮
+        private TextView mRetryBtn;
+    
+        private OnRetryClickListener mOnRetryClickListener = null;//重试点击事件
+    
+        public ErrorView(Context context) {
+            super(context);
+            init();
+        }
+    
+    
+        public ErrorView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            init();
+        }
+    
+    
+        public ErrorView(Context context, AttributeSet attrs, int defStyleAttr) {
+            super(context, attrs, defStyleAttr);
+            init();
+        }
+    
+        private void init() {
+            LayoutInflater inflater = (LayoutInflater) getContext()
+                                      .getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            Resources resources = getContext().getResources();
+    
+            View view = inflater.inflate(R.layout.alivc_dialog_error, null);
+            addView(view);
+    
+            mRetryBtn = (TextView) view.findViewById(R.id.retry_btn);
+            mMsgView = (TextView) view.findViewById(R.id.msg);
+            mCodeView = (TextView) view.findViewById(R.id.code);
+            mRetryView = view.findViewById(R.id.retry);
+            //重试的点击监听
+            mRetryView.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mOnRetryClickListener != null) {
+                        mOnRetryClickListener.onRetryClick();
+                    }
+                }
+            });
+        }
+    
+        /**
+         * 更新提示文字
+         * @param errorCode 错误码
+         * @param errorEvent 错误事件
+         * @param errMsg 错误码
+         */
+        public void updateTips(int errorCode, String errorEvent, String errMsg) {
+            mMsgView.setText(errMsg);
+            mCodeView.setText(getContext().getString(R.string.alivc_error_code) + errorCode + " - " + errorEvent);
+        }
+    
+        /**
+         * 更新提示文字,不包含错误码
+         */
+        public void updateTipsWithoutCode(String errMsg) {
+            mMsgView.setText(errMsg);
+            mCodeView.setVisibility(View.GONE);
+        }
+    
+        /**
+         * 重试的点击事件
+         */
+        public interface OnRetryClickListener {
+            /**
+             * 重试按钮点击
+             */
+            void onRetryClick();
+        }
+    
+        /**
+         * 设置重试点击事件
+         * @param l 重试的点击事件
+         */
+        public void setOnRetryClickListener(OnRetryClickListener l) {
+            mOnRetryClickListener = l;
+        }
+    }
+    ```
+    - 写到这里,n个不同视图状态的类定义好了,那么它们是怎么进行显示和隐藏呢?是如何添加到视频播放器上的呢。这个时候就需要用到一个状态管理的类,这里只是写了为代码。有没有发现这样操作的话,就特别灵活,而且在加载布局这块,主要当需要用到的时候,才会把视图addView到主视图中
+    ```
+    public class TipsView extends RelativeLayout{
+    
+        /**
+         * 显示重播view
+         */
+        public void showReplayTipView() {
+            if (mReplayView == null) {
+                mReplayView = new ReplayView(getContext());
+                mReplayView.setOnReplayClickListener(onReplayClickListener);
+                addSubView(mReplayView);
+            }
+    
+            if (mReplayView.getVisibility() != VISIBLE) {
+                mReplayView.setVisibility(VISIBLE);
+            }
+        }
+    
+        /**
+         * 隐藏重播的tip
+         */
+        public void hideReplayTipView() {
+            if (mReplayView != null && mReplayView.getVisibility() == VISIBLE) {
+                mReplayView.setVisibility(INVISIBLE);
+            }
+        }
+    
+        /**
+         * 提示view中的点击操作
+         */
+        public interface OnTipClickListener {
+            /**
+             * 继续播放
+             */
+            void onContinuePlay();
+    
+            /**
+             * 停止播放
+             */
+            void onStopPlay();
+    
+            /**
+             * 重试播放
+             */
+            void onRetryPlay();
+    
+            /**
+             * 重播
+             */
+            void onReplay();
+    
+            /**
+             * 刷新sts
+             */
+            void onRefreshSts();
+        }
+    
+        /**
+         * 设置提示view中的点击操作 监听
+         *
+         * @param l 监听事件
+         */
+        public void setOnTipClickListener(OnTipClickListener l) {
+            mOnTipClickListener = l;
+        }
+    }
+    ```
+
+
+

+ 26 - 0
read/float_play.md

@@ -0,0 +1,26 @@
+# 视频全局悬浮窗播放
+
+
+
+
+
+
+
+
+
+#### 参考博客
+- Android仿YouTube拖拽视频效果的实现:https://www.jianshu.com/p/62bfa86110ac?from=jiantop.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 0 - 0
read/wiki4.md → read/framework.md


+ 34 - 2
read/wiki2.md → read/optimize.md

@@ -10,7 +10,9 @@
 - 08.关于网络状态监听优化
 - 09.关于代码规范优化
 - 10.关于布局优化
-- 11.选择SurfaceView还是TextureView
+- 11.SurfaceView和TextureView
+- 12.视频旋转角度float数值对比优化
+- 13.如何做到一次播放一个视频
 
 
 
@@ -221,7 +223,7 @@
 
 
 
-### 11.选择SurfaceView还是TextureView
+### 11.SurfaceView和TextureView
 #### 11.1 SurfaceView优缺点
 - 优点:
     - 可以在一个独立的线程中进行绘制,不会影响主线程;使用双缓冲机制,播放视频时画面更流畅
@@ -237,6 +239,36 @@
 
 
 
+### 12.视频旋转角度float数值对比优化
+- 如何获取视频旋转的角度,代码如下所示
+    ```
+    // 获取视图旋转的角度
+    float viewRotation = getRotation();
+    ```
+- 先看一个案例,思考一下是否妥当
+    - 下面这个案例。用==从语法上说没错,但是本来应该相等的两个浮点数由于计算机内部表示的原因可能略有微小的误差,这时用==就会认为它们不等。
+    - float运算后会有误差。因为浮点数在内存中存放,可能无法精确的储存,所以同一个值,可能有不同的内存数据
+    ```
+    float a = 2.0000001f
+    float b = 2.0000001f
+    if(a==b){
+    }
+    ```
+- 浮点数判断需要注意,float和double的精度范围,超过范围的数字会被忽略
+    - (1) 浮点数大小判断:如果没有等号关系在里面,也就必然一大一小,那么直接用  > 或者 <
+    - (2) 浮点数相等判断:因为浮点数在内存中存放,可能无法精确的储存,所以同一个值,可能有不同的内存数据
+- 正确做法
+    - 应该使用两个浮点数之间的差异的绝对值小于某个可以接受的值来判断判断它们是否相等
+    ```
+    if (Math.abs(viewRotation-viewRotation1)> EQUAL_FLOAT && Math.abs(viewRotation1-viewRotation)> EQUAL_FLOAT ||
+            (Math.abs(viewRotation-viewRotation2)> EQUAL_FLOAT && Math.abs(viewRotation2-viewRotation)> EQUAL_FLOAT)){
+        int tempMeasureSpec = widthMeasureSpec;
+        //noinspection SuspiciousNameCombination
+        widthMeasureSpec = heightMeasureSpec;
+        heightMeasureSpec = tempMeasureSpec;
+    }
+    ```
+
 
 
 

+ 3 - 2
read/wiki5.md → read/question.md

@@ -3,6 +3,9 @@
 - 01.视频全屏播放崩溃
 - 02.视频播放解析异常
 - 03.视频
+- 07.如何查看m3u8时长以及播放m3u8视频有何特点
+- 08.为什么播放视频有的设备声画不同步
+- 09.视频初始化时为什么会出现黑屏
 - 10.如何实现视频的自动播放
 - 11.怎么获取网络视频的第一帧的截图
 - 14.拖动进度条后,加载中的转圈动画怎么才能显示出来
@@ -158,5 +161,3 @@ header.put("Cache-control","no-cache");
 
 
 
-
-

+ 621 - 0
read/test.md

@@ -0,0 +1,621 @@
+### 目录介绍
+- 1.0.0.1 请手写equal方法【String类】,讲讲具体的原理?Object类的equla方法是怎样的?
+- 1.0.0.2 请说下String与StringBuffer区别,StringBuffer底部如何实现?String类可以被继承吗,为什么?
+- 1.0.0.3 String a=""和String a=new String("")的的关系和异同?String的创建机制如何理解?
+- 1.0.0.4 为什么 Java 中的 String 是不可变的(Immutable)?字符串设计和实现考量?String不可变的好处?
+- 1.0.0.5 static关键字可以修饰什么?static使用的注意事项有哪些?static关键字的特点?使用static存在什么问题?
+- 1.0.0.6 static变量存储位置是哪里?静态变量的生命周期?静态变量何时销毁?静态引用的对象回收如何理解?
+- 1.0.0.7 访问修饰符public,private,protected,以及不写(默认)时的区别?访问修饰符底层怎么实现访问权限管理?
+- 1.0.0.8 静态变量和实例变量的区别?成员变量与局部变量的区别有那些?外部类和内部类有何区别,生命周期是怎样的?
+- 1.0.1.0 int和Integer的区别?装箱、拆箱什么含义?什么时候装箱/拆箱?装箱和拆箱是如何实现的?
+- 1.0.1.1 Object有哪些公有方法?Object类toString()返回的是什么?为什么说类一定要实现Cloneable接口才可以克隆?
+- 1.0.1.2 final,finally,finalize有什么不同?finally什么情况下不会被执行?java.lang 包下为什么要设置final?
+- 1.0.1.3 为什么要用通配符?上界通配符和下界通配符注意要点?什么是无界通配符?如何理解泛型编译器类型检查?
+- 1.0.1.4 什么是泛型擦除,能否通过开发中实际案例说下?如何获取泛型的具体的类型【反射】?
+- 1.0.1.5 如何验证int类型是否线程安全?那些类型是线程安全的?举一个线程安全的例子【AtomicInteger】?
+- 1.0.1.6 Java序列话中如果有些字段不想进行序列化怎么办?Java序列化机制底层实现原理是怎样的?
+- 1.0.1.8 原始数据类型和引用类型局限性?为何要引用基本数据包装类?基本数据类型一定存储在栈中吗?
+- 1.0.1.9 new Integer(123) 与 Integer.valueOf(123)有何区别,请从底层实现分析两者区别?
+- 1.0.2.0 instanceof它的作用是什么?在使用过程中注意事项有哪些?它底层原理是如何实现的,说说你的理解?
+- 1.0.2.1 float存储为何会精度丢失?计算机如何存储浮点型?float和double输出有何区别?
+
+
+
+### 好消息
+- 博客笔记大汇总【15年10月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
+- **链接地址:https://github.com/yangchong211/YCBlogs**
+- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!**所有博客将陆续开源到GitHub!**
+
+
+#### 1.0.0.1 请手写equal方法【String类】,讲讲具体的原理?Object类的equla方法是怎样的?
+- 代码如下所示,如果是手写代码,一定要弄清楚逻辑思路!
+    ```
+    public boolean equals(Object anObject) {
+        if (this == anObject) {
+            return true;
+        }
+        if (anObject instanceof String) {
+            String anotherString = (String) anObject;
+            int n = count;
+            if (n == anotherString.count) {
+                int i = 0;
+                while (n-- != 0) {
+                    if (charAt(i) != anotherString.charAt(i))
+                            return false;
+                    i++;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+    ```
+- Object类的equla方法是怎样的?
+    ```
+    public boolean equals(Object obj) {
+        return (this == obj);
+    }
+    ```
+
+
+#### 1.0.0.2 请说下String与StringBuffer区别,StringBuffer底部如何实现?String类可以被继承吗,为什么?
+- String类的特点
+    - String的特点是一旦被创建,就不能被改变。注意是地址不能改变。StringBuffer底层是可变的字节序列……
+- String类可以被继承吗
+    - 看String源码可知,String类被final关键字修饰了,所以不能被继承。这个地方可以说下final关键字作用。
+- String、StringBuffer和StringBuilder的区别?
+    - String是字符串常量,而StringBuffer、StringBuilder都是字符串变量,即String对象一创建后不可更改,而后两者的对象是可更改的:
+    - StringBuffer是线程安全的,而StringBuilder是非线程安全的,这是由于StringBuffer对方法加了同步锁或者对调用的方法加了同步锁
+    - String更适用于少量的字符串操作的情况,StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况,StringBuffer适用于多线程下在字符缓冲区进行大量操作的情况
+    - [技术博客大总结](https://github.com/yangchong211/YCBlogs)
+
+
+
+#### 1.0.0.3 String a=""和String a=new String("")的的关系和异同?String的创建机制如何理解?
+- 区别
+    - 通过String a=""直接赋值的方式得到的是一个字符串常量,存在于常量池;注意,相同内容的字符串在常量池中只有一个,即如果池已包含内容相等的字符串会返回池中的字符串,反之会将该字符串放入池中
+    - 通过new String("")创建的字符串不是常量是实例对象,会在堆内存开辟空间并存放数据,且每个实例对象都有自己的地址空间
+- String的创建机制
+    - 由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。
+
+
+
+
+#### 1.0.0.4 为什么Java中的 String 是不可变的(Immutable)?字符串设计和实现考量?String不可变的好处?
+- 不可变类String的原因
+    - String主要的三个成员变量 char value[], int offset, int count均是private,final的,并且没有对应的 getter/setter;
+    - String 对象一旦初始化完成,上述三个成员变量就不可修改;并且其所提供的接口任何对这些域的修改都将返回一个新对象;
+    - [技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 是典型的 Immutable 类,被声明成为 final class,所有属性也都是final的。也由于它的不可变,类似拼接、裁剪字符串等动作,都会产生新的 String 对象。
+- 字符串设计和实现考量?
+    - String 是 Immutable 类的典型实现,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,这种便利甚至体现在拷贝构造函数中,由于不可变,Immutable 对象在拷贝时不需要额外复制数据。
+    - 为了实现修改字符序列的目的,StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized。
+    - 这个内部数组应该创建成多大的呢?如果太小,拼接的时候可能要重新创建足够大的数组;如果太大,又会浪费空间。目前的实现是,构建时初始字符串长度加 16(这意味着,如果没有构建对象时输入最初的字符串,那么初始值就是 16)。我们如果确定拼接会发生非常多次,而且大概是可预计的,那么就可以指定合适的大小,避免很多次扩容的开销。扩容会产生多重开销,因为要抛弃原有数组,创建新的(可以简单认为是倍数)数组,还要进行arraycopy。
+- String不可变的好处?
+    - **可以缓存 hash 值** 
+        - 因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
+    - **String Pool 的需要** 
+        - 如果一个String对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
+    - **安全性** [技术博客大总结](https://github.com/yangchong211/YCBlogs)
+        - String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。
+    - **线程安全** 
+        - String 不可变性天生具备线程安全,可以在多个线程中安全地使用。
+
+
+
+#### 1.0.0.5 static关键字可以修饰什么?static使用的注意事项有哪些?static关键字的特点?使用static存在什么问题?
+- 可以用来修饰:成员变量,成员方法,代码块,内部类等。具体如下所示
+    - **修饰成员变量和成员方法** 
+        - 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。
+        - 被static 声明的成员变量属于静态成员变量,静态变量存放在Java内存区域的方法区。
+    - **静态代码块** 
+        - 静态代码块定义在类中方法外,静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)
+        - 该类不管创建多少对象,静态代码块只执行一次.
+    - **静态内部类(static修饰类的话只能修饰内部类)** 
+    - 静态内部类与非静态内部类之间存在一个最大的区别:
+        - 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:1.它的创建是不需要依赖外围类的创建。2.它不能使用任何外围类的非static成员变量和方法。
+    - **静态导包(用来导入类中的静态资源,1.5之后的新特性):**
+        - 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
+- static使用的注意事项有哪些?
+	* 在静态方法中是没有this关键字的
+		* 静态是随着类的加载而加载,this是随着对象的创建而存在。
+		* 静态比对象先存在。
+	* 静态方法只能访问静态的成员变量和静态的成员方法【静态只能访问静态,非静态可以访问静态的也可以访问非静态的】
+- static关键字的特点?
+	* 随着类的加载而加载
+	* 优先于对象存在
+	* 被类的所有对象共享
+	* 可以通过类名调用【静态修饰的内容一般我们称其为:与类相关的,类成员】
+- 使用static存在什么问题?
+    - 1.占用内存,并且内存一般不会释放;
+    - 2.在系统不够内存情况下会自动回收静态内存,这样就会引起访问全局静态错误。
+    - 3.在Android中不能将activity作为static静态对象,这样使activity的所有组件对象都存入全局内存中,并且不会被回收;
+
+
+
+#### 1.0.0.6 static变量存储位置是哪里?静态变量的生命周期?静态变量何时销毁?静态引用的对象回收如何理解?
+- static变量存储位置
+    - 注意是:存储在JVM的方法区中
+    - static变量在类加载时被初始化,存储在JVM的方法区中,整个内存中只有一个static变量的拷贝,可以使用类名直接访问,也可以通过类的实例化对象访问,一般不推荐通过实例化对象访问,通俗的讲static变量属于类,不属于对象,任何实例化的对象访问的都是同一个static变量,任何地放都可以通过类名来访问static变量。
+- 静态变量的生命周期
+    - 类在什么时候被加载?[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 当我们启动一个app的时候,系统会创建一个进程,此进程会加载一个Dalvik VM的实例,然后代码就运行在DVM之上,类的加载和卸载,垃圾回收等事情都由DVM负责。也就是说在进程启动的时候,类被加载,静态变量被分配内存。
+- 静态变量何时销毁
+    - 类在什么时候被卸载?在进程结束的时候。
+    - 说明:一般情况下,所有的类都是默认的ClassLoader加载的,只要ClassLoader存在,类就不会被卸载,而默认的ClassLoader生命周期是与进程一致的
+- 静态引用的对象回收
+    - 只要静态变量没有被销毁也没有置null,其对象一直被保持引用,也即引用计数不可能是0,因此不会被垃圾回收。因此,单例对象在运行时不会被回收
+
+
+
+
+#### 1.0.0.7 访问修饰符public,private,protected,以及不写(默认)时的区别?访问修饰符底层是怎么实现访问权限管理的?
+- 访问修饰符public,private,protected,以及不写(默认)时的区别?
+    - 公有,使用public修饰,声明为公有表示可供所有其他的任何类使用,例如main方法前面就有public修饰。使用了public修饰意味着访问权限是最大的。
+    - 私有,使用private修饰,声明为私有表示仅在本类中可见。私有的方法和属性不能被其他类使用,可以起到信息隐藏的作用,是封装的主要方式。同时使用private修饰会影响继承,private修饰的类不能被继承,private修饰的方法不能被重写。所有恰好与public相反,private修饰的访问权限最低,因此需谨慎使用。
+    - 默认,什么类,变量,方法时可以不使用任何访问修饰符,此时表示默认修饰。对同一个包中的类是可以进行访问和修改的,但是不能跨包使用,这种情况使用的相对较少。
+    - 受保护的,使用protected修饰,与默认的修饰不同,受保护的修饰访问权限要大于默认修饰的,因为protected修饰的除了可以在同一个包中使用外,在其他包中的子类也是可以使用的,因此他的访问权限是大于默认的而小于公有的。
+    ```
+    区别如下:
+    作用域          当前类      同包    子类     其他
+    public            √         √       √      √
+    protected         √         √       √      ×
+    default           √         √       ×      ×
+    private           √         ×       ×      ×
+    ```
+
+
+
+
+#### 1.0.0.8 静态变量和实例变量的区别?成员变量与局部变量的区别有那些?外部类和内部类有何区别,生命周期是怎样的?
+- 静态变量和实例变量的区别
+    - 静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝。静态变量可以实现让多个对象共享内存。在Java开发中,上下文类和工具类中通常会有大量的静态成员。
+    - 实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它
+- 成员变量与局部变量的区别
+    - 1.从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰;
+    - 2.从变量在内存中的存储方式来看,成员变量是对象的一部分,而对象存在于堆内存,局部变量存在于栈内存
+    - 3.从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
+    - 4.成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情况例外被 final 修饰但没有被 static 修饰的成员变量必须显示地赋值);而局部变量则不会自动赋值。
+- 外部类和内部类有何区别,生命周期是怎样的?
+    - Java中的内部类共分为四种:
+        - 静态内部类static inner class (also called nested class)
+        - 成员内部类member inner class
+        - 局部内部类local inner class
+        - 匿名内部类anonymous inner class
+    - 内部类就相当于一个外部类的成员变量,所以可以直接访问外部变量,外部类不能直接访问内部类变量,必须通过创建内部类实例的方法访问。
+        - new InnerClass(32).m就是创建内部类实例访问内部类成员变量。你想不通的肯定是指内部类的私有变量怎么可以被外部类访问吧,按常规,私有变量m只能在InnerClass里被访问,
+
+
+
+
+
+
+
+
+#### 1.0.1.0 int和Integer的区别?装箱、拆箱什么含义?什么时候装箱和拆箱?装箱和拆箱是如何实现的?
+- int和Integer的区别:基本数据类型、引用类型
+    - Integer是int的包装类,int则是java的一种基本数据类型
+    - Integer变量必须实例化后才能使用,而int变量不需要
+    - Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
+    - Integer的默认值是null,int的默认值是0
+- 装箱、拆箱[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 装箱就是自动将基本数据类型转换为包装器类型
+    - 拆箱就是自动将包装器类型转换为基本数据类型
+    ```
+    int a = 10;
+    //装箱操作
+    Integer integer1 = Integer.valueOf(a);
+
+    //拆箱操作
+    Integer integer2 = new Integer(5);
+    int i2 = integer2.intValue();
+    ```
+- jdk中如何操作装箱、拆箱
+    - 在JDK中,装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的xxxValue方法实现的(xxx代表对应的基本数据类型)。
+    - Integer、Short、Byte、Character、Long 这几个类的valueOf方法的实现是类似的,有限可列举,共享[-128,127];
+    - Double、Float的valueOf方法的实现是类似的,无限不可列举,不共享;
+    - Boolean的valueOf方法的实现不同于以上的整型和浮点型,只有两个值,有限可列举,共享;
+- 什么时候装箱/拆箱?
+    - 什么时候拆箱主要取决于:在当前场景下,你需要的是引用类型还是原生类型。若需要引用类型,但传进来的值是原生类型,则自动装箱(例如,使用equals方法时传进来原生类型的值);若需要的是原生类型,但传进来的值是引用类型,则自动拆箱(例如,使用运算符进行运算时,操作数是包装类型)。
+- 装箱和拆箱是如何实现的
+    - 以Interger类为例,下面看一段代码来了解装箱和拆箱的实现
+    ```
+    public class Main {
+        public static void main(String[] args) {
+            Integer y = 10;
+            int c = i;
+        }
+    }
+    ```
+    - 然后来编译一下:
+        - 从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。
+        - 因此可以用一句话总结装箱和拆箱的实现过程:装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。
+
+
+
+#### 1.0.1.1 Object有哪些公有方法?Object类toString()返回的是什么?为什么说类一定要实现Cloneable接口才可以克隆?
+- 常用方法
+    - equals(): 和==作用相似[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - hashCode():用于哈希查找,重写了equals()一般都要重写该方法
+    - getClass(): 获取Class对象
+    - wait():让当前线程进入等待状态,并释放它所持有的锁
+    - notify()&notifyAll(): 唤醒一个(所有)正处于等待状态的线程
+    - toString():转换成字符串
+- Android的Object类toString()返回的是什么?
+    - 返回的是类名和hashcode的组合字符串
+    ```
+    public String toString() {
+        return getClass().getName() + "@" + Integer.toHexString(hashCode());
+    }
+    ```
+- 为什么说类一定要实现Cloneable接口才可以克隆?具体看下Android中Object类代码
+    - 看一下代码即可知道,对象需要instanceof判断该对象是否是Cloneable的实例
+    ```
+    protected Object clone() throws CloneNotSupportedException {
+        if (!(this instanceof Cloneable)) {
+            throw new CloneNotSupportedException("Class " + getClass().getName() +
+                                                 " doesn't implement Cloneable");
+        }
+    
+        return internalClone();
+    }
+    ```
+
+
+
+
+#### 1.0.1.2 final,finally,finalize有什么不同?finally什么情况下不会被执行?java.lang 包下为什么要设置final?
+- **final可以修饰类,方法,变量**
+    - final修饰类代表类不可以继承拓展
+    - final修饰变量表示变量不可以修改
+    - final修饰方法表示方法不可以被重写
+- **finally则是Java保证重点代码一定要被执行的一种机制**
+    - 可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC连接、保证 unlock 锁等动作。
+- **finalize 是基础类 java.lang.Object的一个方法**
+    - 它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9开始被标记为 deprecated。
+- **final 关键字深入理解**[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 可以将方法或者类声明为 final,这样就可以明确告知别人,这些行为是不许修改的。
+    - 如果你关注过 Java 核心类库的定义或源码, 有没有发现java.lang 包下面的很多类,相当一部分都被声明成为final class?在第三方类库的一些基础类中同样如此,这可以有效避免 API 使用者更改基础功能,某种程度上,这是保证平台安全的必要手段。
+- **在以下4种特殊情况下,finally块不会被执行:**
+    - 1.在finally语句块中发生了异常。
+    - 2.在前面的代码中用了System.exit()退出程序。
+    - 3.程序所在的线程死亡。
+    - 4.关闭CPU。
+- java.lang 包下为什么要设置final?
+    - final 变量产生了某种程度的不可变(immutable)的效果,所以,可以用于保护只读数据,尤其是在并发编程中,因为明确地不能再赋值 final 变量,有利于减少额外的同步开销,也可以省去一些防御性拷贝的必要。
+    - 使用 final 修饰参数或者变量,也可以清楚地避免意外赋值导致的编程错误,甚至,有人明确推荐将所有方法参数、本地变量、成员变量声明成 final。
+
+
+
+
+
+
+
+#### 1.0.1.3 为什么要用通配符?上界通配符和下界通配符注意要点?什么是无界通配符?如何理解泛型编译器类型检查?
+- 为什么要使用通配符
+    - 通配符的设计存在一定的场景,例如在使用泛型后,首先声明了一个Animal的类,而后声明了一个继承Animal类的Cat类,显然Cat类是Animal类的子类,但是List<Cat>却不是List<Animal>的子类型,而在程序中往往需要表达这样的逻辑关系。为了解决这种类似的场景,在泛型的参数类型的基础上新增了通配符的用法。
+- <? extends T> 上界通配符
+    - 上界通配符顾名思义,<? extends T>表示的是类型的上界【 **包含自身**】,因此通配的参数化类型可能是T或T的子类。正因为无法确定具体的类型是什么,add方法受限(可以添加null,因为null表示任何类型),但可以从列表中获取元素后赋值给父类型。如上图中的第一个例子,第三个add()操作会受限,原因在于List<Animal>和List<Cat>是List<? extends Animal>的子类型。
+- <? super T> 下界通配符
+    - 下界通配符<? super T>表示的是参数化类型是T的超类型(**包含自身**),层层至上,直至Object,编译器无从判断get()返回的对象的类型是什么,因此get()方法受限。但是可以进行add()方法,add()方法可以添加T类型和T类型的子类型,如第二个例子中首先添加了一个Cat类型对象,然后添加了两个Cat子类类型的对象,这种方法是可行的,但是如果添加一个Animal类型的对象,显然将继承的关系弄反了,是不可行的。
+- <?> 无界通配符
+    - 任意类型,如果没有明确,那么就是Object以及任意的Java类了
+    - 无界通配符用<?>表示,?代表了任何的一种类型,能代表任何一种类型的只有null(Object本身也算是一种类型,但却不能代表任何一种类型,所以List<Object>和List<null>的含义是不同的,前者类型是Object,也就是继承树的最上层,而后者的类型完全是未知的)。
+    - [技术博客大总结](https://github.com/yangchong211/YCBlogs)
+- 如何理解编译器类型检查
+    * 看一个网上的案例
+        - ![image](https://upload-images.jianshu.io/upload_images/4432347-b5e6e5cfa996fc1f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
+    * 在引入泛型之后,通过将代码中的“public class Box”更改为“public class Box<T>”来创建泛型类型的声明,而这个声明的背后实质上是引入了可以在类中任何地方使用的类型变量T。如实例4中所示:可以看到,除了新增的泛型类型声明<T>外,所有在原来代码中出现的Object都被类型变量T所替换。
+    * 乍一看类型变量这个词,感觉有点晦涩难懂,但其实如果仔细思量一番会发现它其实并不难理解,上面的实例4可以理解为“在使用泛型时,可以将类型参数T传递给Box类型本身”,结合Oracle给出的官方定义“泛型的本质是类型参数化”会有更深的理解。
+    * 在实例5中,在对象声明和初始化的时候,都指定了类型参数T,在场景一种,T为String;在场景二中,T为Integer。这样,在场景二中向IntegerBox中传入String类型的数据“aaaaa”时,程序会报错。实例6中的泛型集合对象的操作也与之类似,在声明了一个List<String>的boxes对象之后,如果向boxes中传入Integer对象11111,程序会报错。
+    * 可以看到,通过对于泛型的使用,之前的多业务场景中的问题都得到了解决,因为现在在编译阶段就可以解决之前类型不匹配的问题,而不用等到运行时才暴露问题,只要合理使用泛型,就能在很大程度上规避此类风险。对于泛型的使用,这种参数化类型的作用表面上看是声明,背后其实是约定。
+
+
+
+#### 1.0.1.4 什么是泛型擦除,能否通过开发中实际案例说下?如何获取泛型的具体的类型【反射】?
+- 开发中的泛型擦除案例
+    - 泛型是提供给javac编译器使用的,限定集合的输入类型,编译器编译带类型说明的集合时会去掉“类型”信息。
+    ```
+    public class GenericTest {
+        public static void main(String[] args) {
+            new GenericTest().testType();
+        }
+        public void testType(){
+            ArrayList<Integer> collection1 = new ArrayList<Integer>();
+            ArrayList<String> collection2= new ArrayList<String>();
+            System.out.println(collection1.getClass()==collection2.getClass());
+            //两者class类型一样,即字节码一致
+            System.out.println(collection2.getClass().getName());
+            //class均为java.util.ArrayList,并无实际类型参数信息
+        }
+        
+        //输出结果
+        //true
+        //java.util.ArrayList
+    }
+    ```
+- 如何获取泛型的具体的类型?[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 使用反射可跳过编译器,往某个泛型集合加入其它类型数据。
+    - 只有引用类型才能作为泛型方法的实际参数,具体案例如下所示
+    ```
+    public class GenericTest {
+        public static void main(String[] args) {
+            swap(new String[]{"111","222"},0,1);//编译通过
+            
+            //swap(new int[]{1,2},0,1);
+            //编译不通过,因为int不是引用类型
+            
+            swap(new Integer[]{1,2},0,1);//编译通过
+        }
+        
+        /*交换数组a 的第i个和第j个元素*/
+        public static <T> void swap(T[]a,int i,int j){
+            T temp = a[i];
+            a[i] = a[j];
+            a[j] = temp;
+        }
+    }
+    ```
+    - 但注意基本类型**有时**可以作为实参,因为有**自动装箱**和**拆箱**。下面例子(编译通过了):
+    ```
+    public class GenericTest {
+        public static void main(String[] args) {
+            new GenericTest().testType();
+            int a = biggerOne(3,5);
+            //int 和 double,取交为Number
+            Number b = biggerOne(3,5.5);
+            //String和int 取交为Object
+            Object c = biggerOne("1",2);
+        }
+        //从x,y中返回y
+        public static <T> T biggerOne(T x,T y){
+            return y;
+        }
+    }
+    ```
+    - 同时,该例还表明,**当实参不一致时,T取交集,即第一个共同的父类。**
+    - 另外,如果用`Number b = biggerOne(3,5.5);`改为`String c = biggerOne(3,5.5);`则编译报错:
+
+    ```
+    Error:(17, 29) java: 不兼容的类型: 推断类型不符合上限
+        推断: java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>
+        上限: java.lang.String,java.lang.Object
+    ```
+
+
+#### 1.0.1.5 如何验证int类型是否线程安全?那些类型是线程安全的?举一个线程安全的例子【AtomicInteger】?
+- 如何验证int类型是否线程安全
+    - 200个线程,每个线程对共享变量 count 进行 50 次 ++ 操作 
+    - int 作为基本类型,直接存储在内存栈,且对其进行+,-操作以及++,–操作都不是原子操作,都有可能被其他线程抢断,所以不是线程安全。int 用于单线程变量存取,开销小,速度快
+    - [技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    ```
+    int count = 0;
+    private void startThread() {
+        for (int i = 0;i < 200; i++){
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    for (int k = 0; k < 50; k++){
+                        count++;
+                    }
+                }
+            }).start();
+        }
+        // 休眠10秒,以确保线程都已启动
+        try {
+            Thread.sleep(1000*10);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }finally {
+            Log.e("打印日志----",count+"");
+        }
+    }
+    
+    //期望输出10000,最后输出的是9818
+    //注意:打印日志----: 9818
+    ```
+- 那些类型是线程安全的
+    - Java自带的线程安全的基本类型包括: AtomicInteger, AtomicLong, AtomicBoolean, AtomicIntegerArray,AtomicLongArray等
+- AtomicInteger线程安全版
+    - AtomicInteger类中有有一个变量valueOffset,用来描述AtomicInteger类中value的内存位置 。
+    - 当需要变量的值改变的时候,先通过get()得到valueOffset位置的值,也即当前value的值.给该值进行增加,并赋给next
+    - compareAndSet()比较之前取到的value的值当前有没有改变,若没有改变的话,就将next的值赋给value,倘若和之前的值相比的话发生变化的话,则重新一次循环,直到存取成功,通过这样的方式能够保证该变量是线程安全的
+    - value使用了volatile关键字,使得多个线程可以共享变量,使用volatile将使得VM优化失去作用,在线程数特别大时,效率会较低。[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    ```
+    private static AtomicInteger atomicInteger = new AtomicInteger(1);
+    static Integer count1 = Integer.valueOf(0);
+    private void startThread1() {
+        for (int i = 0;i < 200; i++){
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    for (int k = 0; k < 50; k++){
+                        // getAndIncrement: 先获得值,再自增1,返回值为自增前的值
+                        count1 = atomicInteger.getAndIncrement();
+                    }
+                }
+            }).start();
+        }
+        // 休眠10秒,以确保线程都已启动
+        try {
+            Thread.sleep(1000*10);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }finally {
+            Log.e("打印日志----",count1+"");
+        }
+    }
+    
+    //期望输出10000,最后输出的是10000
+    //注意:打印日志----: 10000
+    
+    //AtomicInteger使用了volatile关键字进行修饰,使得该类可以满足线程安全。
+    private volatile int value;
+    public AtomicInteger(int initialValue) {
+        value = initialValue;
+    }
+    ```
+
+
+#### 1.0.1.6 Java序列话中如果有些字段不想进行序列化怎么办?Java序列化机制底层实现原理是怎样的?
+- 对于不想进行序列化的变量,使用transient关键字修饰。
+- transient关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。
+
+
+
+#### 1.0.1.8 原始数据类型和引用类型局限性?为何要引用基本数据包装类?基本数据类型一定存储在栈中吗?
+- 原始数据类型和引用类型局限性
+    - 原始数据类型和 Java 泛型并不能配合使用
+    - Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧,Java 编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为Object。
+- 为何要引用基本数据包装类[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 就比如,我们使用泛型,需要用到基本数据类型的包装类。
+    - Java 的对象都是引用类型,如果是一个原始数据类型数组,它在内存里是一段连续的内存,而对象数组则不然,数据存储的是引用,对象往往是分散地存储在堆的不同位置。这种设计虽然带来了极大灵活性,但是也导致了数据操作的低效,尤其是无法充分利用现代 CPU 缓存机制。
+    - Java 为对象内建了各种多态、线程安全等方面的支持,但这不是所有场合的需求,尤其是数据处理重要性日益提高,更加高密度的值类型是非常现实的需求。
+- 基本数据类型一定存储在栈中吗?
+    - 首先说明,"java中的基本数据类型一定存储在栈中的吗?”这句话肯定是错误的。
+    - 基本数据类型是放在栈中还是放在堆中,这取决于基本类型在何处声明,下面对数据类型在内存中的存储问题来解释一下:
+    - 一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因
+        - 在方法中声明的变量可以是基本类型的变量,也可以是引用类型的变量。当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在JAVA虚拟机栈中。当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在JAVA虚拟机的栈中,该变量所指向的对象是放在堆类存中的。
+    - 二:在类中声明的变量是成员变量,也叫全局变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)。同样在类中声明的变量即可是基本类型的变量,也可是引用类型的变量
+        - 当声明的是基本类型的变量其变量名及其值放在堆内存中的。引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中
+
+
+
+
+#### 1.0.1.9 new Integer(123) 与 Integer.valueOf(123)有何区别,请从底层实现分析两者区别?
+- new Integer(123) 与 Integer.valueOf(123) 的区别在于:
+    - new Integer(123) 每次都会新建一个对象;[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
+    ```
+    Integer x = new Integer(123);
+    Integer y = new Integer(123);
+    System.out.println(x == y);    // false
+    Integer z = Integer.valueOf(123);
+    Integer k = Integer.valueOf(123);
+    System.out.println(z == k);   // true
+    ```
+- valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。
+    ```
+    public static Integer valueOf(int i) {
+        if (i >= IntegerCache.low && i <= IntegerCache.high)
+            return IntegerCache.cache[i + (-IntegerCache.low)];
+        return new Integer(i);
+    }
+    ```
+- 在 Java 8 中,Integer 缓存池的大小默认为 -128\~127。
+    ```
+    static final int low = -128;
+    static final int high;
+    static final Integer cache[];
+    
+    static {
+        // high value may be configured by property
+        int h = 127;
+        String integerCacheHighPropValue =
+            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
+        if (integerCacheHighPropValue != null) {
+            try {
+                int i = parseInt(integerCacheHighPropValue);
+                i = Math.max(i, 127);
+                // Maximum array size is Integer.MAX_VALUE
+                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
+            } catch( NumberFormatException nfe) {
+                // If the property cannot be parsed into an int, ignore it.
+            }
+        }
+        high = h;
+    
+        cache = new Integer[(high - low) + 1];
+        int j = low;
+        for(int k = 0; k < cache.length; k++)
+            cache[k] = new Integer(j++);
+    
+        // range [-128, 127] must be interned (JLS7 5.1.7)
+        assert IntegerCache.high >= 127;
+    }
+    ```
+- 编译器会在自动装箱过程调用 valueOf() 方法,因此多个Integer实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。
+    ```
+    Integer m = 123;
+    Integer n = 123;
+    System.out.println(m == n); // true
+    ```
+- 基本类型对应的缓冲池如下:
+    - boolean values true and false
+    - all byte values
+    - short values between -128 and 127
+    - int values between -128 and 127
+    - char in the range \u0000 to \u007F
+- 在使用这些基本类型对应的包装类型时,就可以直接使用缓冲池中的对象。
+
+
+
+#### 1.0.2.0 instanceof它的作用是什么?在使用过程中注意事项有哪些?它底层原理是如何实现的,说说你的理解?
+- 它的作用是什么?
+    - instanceof是Java的一个二元操作符,和==,>,<是同一类东西。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。
+- 使用过程中注意事项有哪些?[技术博客大总结](https://github.com/yangchong211/YCBlogs)
+    - 类的实例包含本身的实例,以及所有直接或间接子类的实例
+    - instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误
+    ```
+    //比如下面就会编译错误
+    String s = null;
+    s instanceof null
+    s instanceof Integer
+    ```
+
+
+
+#### 1.0.2.1 float存储为何会精度丢失?计算机如何存储浮点型?float和double输出有何区别?
+- 精度为何丢失
+    - 先看一个例子。精度为什么会丢失,计算机能存那么多数字,一个0.3+0.6怎么就丢精度了呢?
+    ```java
+    public static void main(String[] args){
+        System.out.println(0.3f + 0.6f);
+        System.out.println(0.3 + 0.6);
+        System.out.println(0.9);
+    }
+    输出:
+    
+    0.90000004
+    0.8999999999999999
+    0.9
+    ```
+- 为何精度丢失
+    - 在计算机中,一切皆为整数在算术类型上,又分为整数和浮点数,浮点数是由 符号位、有效数字、指数位这些整数共同构成的。但是计算机又不是使用人类的10进制,而是使用的2进制进行(指数)存储,所以理所当然的会有精度丢失。
+    - 十进制。10进制中的1/3这种除法我们想表示的时候只能用0.33333....来表示,那么当我们显示的位数是固定的时候就存在精度问题,1/3!=0.33,也不等于0.333333333。假设小数点之后我们只能写8位或者16位那么久丢失了精度。
+    - 二进制。所以在二进制中精度丢失也是一个道理。毕竟这种无限循环的事情,计算器不可能开辟一个无限大的空间去给你存储吧!
+- 计算机如何存储浮点型
+    - 浮点数在计算机中的存储方式其实是以补码的形式。在计算机中,数字都是用补码来表示与存储的。
+    - 正数的补码是其二进制表示的,负数的补码是其正数的二进制取反再加一。然后就是为什么0.3+0.6会导致精度丢失了。(0.3转化为二进制为0.0100110011001100)结果是保留16位的,然后导致了精度的丢失;(0.6转化为0.1001100110011001)结果是保留16位的,也导致了精度的丢失;相加起来就是0.1110011001100101再转化为十进制为0.8999786376953125和上面结果不同,但差不多应该是计算精度的不同吧。
+- 为何 float 和 double 的计算输出结果差异还挺大的呢?
+    - 附加题的话就是float和double所占的字节不同,他们的计算精度也不同。最关键的是保留有效数字的规则不同,十进制中我们是“4舍5入”,而二进制中是“0舍1入”。
+    - 所以在上面的0.3f(转化为二进制保留8位小数是0.01001101),0.6f(转化为二进制保留8位小数是0.10011010),加起来是0.11100111,转化为十进制是0.90234375,所以 float 和 double 的计算输出结果差异还挺大的是因为保留有效数字时的“0舍1入”造成的。
+
+
+
+
+### 其他介绍
+#### 01.关于博客汇总链接
+- 1.[技术博客汇总](https://www.jianshu.com/p/614cb839182c)
+- 2.[开源项目汇总](https://blog.csdn.net/m0_37700275/article/details/80863574)
+- 3.[生活博客汇总](https://blog.csdn.net/m0_37700275/article/details/79832978)
+- 4.[喜马拉雅音频汇总](https://www.jianshu.com/p/f665de16d1eb)
+- 5.[其他汇总](https://www.jianshu.com/p/53017c3fc75d)
+
+
+
+#### 02.关于我的博客
+- 我的个人站点:www.yczbj.org,www.ycbjie.cn
+- github:https://github.com/yangchong211
+- 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
+- 简书:http://www.jianshu.com/u/b7b2c6ed9284
+- csdn:http://my.csdn.net/m0_37700275
+- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
+- 开源中国:https://my.oschina.net/zbj1618/blog
+- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
+- 邮箱:yangchong211@163.com
+- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
+- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
+- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
+
+

+ 56 - 0
read/todo.md

@@ -0,0 +1,56 @@
+
+
+
+
+
+
+### day26
+- 01.什么是面向对象编程和面向对象编程语言
+    - 什么面向对象编程
+        - 概念:将
+        - 4大特征:封装,继承,多态,抽象
+    - 什么是面向对象编程语言
+        - 支持类和对象的语法机制,能够比较方便实现4大特征中某些特性的语言,当然不一定非要同时具备4大属性。
+    - 编程语言如何划分
+        - 
+    - 面向对象分析和设计
+        - 对类的功能分析,然后再对类的拆分和交互设计
+        - 类中应该有那些属性,属性怎么划分;类和类之间怎么联系和调用
+    - 面向对象Vs面向过程
+        - 面向对象:
+        - 面向过程:
+    - UML
+        - 是指流程图,也叫建模图,表达面向对象设计思想
+- 02.封装、抽象、继承、多态分别可以解决哪些编程问题
+    - 封装
+        - 隐藏具体细节,暴露方法修改属性,提高代码维护性
+        - 暴露有限的对外方法,提高代码易用性
+    - 抽象
+        - 抽象就是讲如何隐藏方法的具体实现
+        - 抽象类抽象,接口抽象
+    - 继承
+        - 抽取共有属性,属于 is a 关系
+        - 单继承,多继承,注意这里是多层继承
+        - 提高代码复用性
+    - 多态
+        - 提高代码拓展性
+- 03.面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
+    - 面向对象和面向过程
+        - 
+    - 面向对象有哪些优势
+        - 
+    - 面向过程过时没
+        - 
+
+
+- day27
+- 什么是散列表
+    - hash+table 标示散列表
+    - 如何处理散列冲突
+        - 拉链法
+        - 碰撞
+- lru 淘汰算法,如何实现的
+
+
+
+

+ 122 - 0
read/video_study0.md

@@ -0,0 +1,122 @@
+# 视频基础概念术语
+#### 目录介绍
+
+
+
+#### **目录介绍**
+- **1.Codec基本含义**
+- 1.1 什么是Codec
+- 1.2 什么是编码和解码
+- 1.3 为什么视频要编解码
+- 1.4 音视频Codec的区别
+- 1.5 Codec编解码API封装
+- **2.视频容器文件**
+- 2.1 什么是视频容器文件
+- 2.2 什么是轨道
+- **3.自适应视频播放技术**
+- 3.1 什么叫自适应视频播放
+- 3.2 如何实现该技术
+- 3.3 自适应视频播放的规范
+- **4.视频文件基本概念**
+- 4.1 帧率
+- 4.2 分辨率
+- 4.3 码率
+- 4.4 这些概念联系
+
+
+### 1.Codec基本含义
+#### 1.1 什么是Codec
+- 一说到视频,音频,大家肯定都听说,至少有所耳闻这两个词编码(encode)和解码(decode)。
+- **这里提到的Codec就是一种程序,这种程序可以对视频文件进行编码和解码。**
+
+#### 1.2 什么是编码和解码
+- 编码就是压缩
+- 解码就是解压缩。
+
+#### 1.3 为什么视频要编解码
+- 视频文件的本质其实就是图片的集合而已,当一段连续的图片不断的出现在人眼前(一般一个连贯的电影或者动画至少要求一秒24帧,也就是一秒内连续出现24张图片),肉眼就会“欺骗性”的告诉大脑我们在看一个视频,而不是幻灯片。
+- 那假设一张像素为1280X720(清晰度,宽1280个像素点,高720个像素点)的图片,大小为约为1280X720X3 bytes,就是2.7MB。大家可以猜想一下为何我这里还需要乘以一个数字3.那么一段60秒钟的小电影,就需要60X24(24张图片)X2.7MB ,约为3.9GB了!
+- Codec这种程序就出现了,它会把这些连续的图片们通过一定的算法压缩成体积更小的文件格式,这就是我们所谓的编码,压缩。但是在播放器的客户端,不管是PC,手机也好,他们要显示在屏幕上的,必须是实实在在的图片啊,所以这些被压缩过的文件最终又必须被还原成图片格式,这就是解码,解压缩。
+
+#### 1.4 音视频Codec的区别
+- Codec的编码与解码包含对视频数据的编码解码和音频数据的编码解码,因为音频的本质是声波信息,视频是图片处理,他们本质上是不同的。
+
+#### 1.5 Codec编解码API封装
+- Codec是一套程序,它遵循不同的规范,根据规范的不同提供不同的压缩解压缩策略。既然是一套规范,那么就肯定需要实现啊!在安卓平台里面,谷歌提供了视频编码解码的API,对一些基础的编码解码规范做了API的封装。
+
+
+### 2.视频容器文件
+#### 2.1 什么是视频容器文件
+- 视频文件严格的来讲,他们应该被叫做容器文件。因为一个容器里,不仅仅包括了视频(video)数据,还包括了(audio)音频数据,有的容器还内嵌字幕,那么就还有文字(Text)数据。它说到底也就是一个结构化的文件而已。之所以说它结构化,就是它包含的视频,音频,文字数据都必须按照一定的规范,放在文件指定的位置(方便播放器解析)。
+
+#### 2.2 什么是轨道
+- 一个专业术语,用来区分不同的音视频/文字数据。但是MP4文件里面最重要的却是这个MetaData,它包含了很多关于视频的原始数据,比如视频的大小,视频的时长,还有一个索引表,这个索引表包含了不同轨道的起始位置(以字节为单位),又因为每个轨道会被分成若干块sample(采样,每一块采样都是可以单独被播放器播放的一段数据,以微妙为单位),metadata也会维护一个细粒度更小的索引表,记录了每一块sample的大小,起始位置,对应视频的时间是多少(以字节为单位)等等的信息。
+- 举个简单的例子,有些电影包含粤语,国语两个声道。我们想换声道的时候会告诉播放器,我想听粤语,那么播放器会去索引表查找粤语的轨道起始位置,并且源源不断的读取粤语音轨的数据并播放出来。
+
+
+### 3.自适应视频播放技术
+#### 3.1 什么叫自适应视频播放
+- 在在线视频播放中,最重要的一个要素应该就属于客户端的网络状况了,如果网络状况很差,俗称的网速很慢,那么大家一般的体验都是视频很卡,看一会就停一会。那么有没有可能让我们的播放器自动检测网络状况,在网络差的情况下播放清晰度较差,数据量较小的视频,当网络情况变得好的时候播放清晰度好,但是数据量大的视频呢?
+
+
+#### 3.2 如何实现该技术
+- 自适应播放技术一般包括前端的支持还有后台的支持,后台提供一个索引表(Manifest),上面记录了同一个视频不同清晰度的版本的Url(比如视频的240p,480p,720p不同的版本文件)。前端的播放器在拿到这个索引表之后,会根据自身的网络状态,在不同清晰度直接的视频文件转换。
+
+
+#### 3.3 自适应视频播放的规范
+- 一个叫DASH的规范最为流行,我们这次以DASH这个规范来深入了解一下自适应视频播放的一些细节。
+- **3.3.1 DASH里面的MPD文件**
+- MPD文件格式,就是我们在第一部分中说到的索引表Manifest了(HLS对应的索引表格式叫M3u8),它包含了所有DASH自适应视频的信息。
+- 每个mpd文件都会有至少一个adaptionset,用来记录音频/视频文件们的位置,如上图所示,该MPd有两组数据,一组是视频,一组是音频。如图所示:
+- ![image](https://raw.githubusercontent.com/richardissuperman/videoconcepts/master/image4/2.png)
+
+
+
+### 4.视频文件基本概念
+#### 4.1 帧率
+- **每秒显示的图片数**。影响画面流畅度,与画面流畅度成正比:帧率越大,画面越流畅;帧率越小,画面越有跳动感。由于人类眼睛的特殊生理结构,如果所看画面之帧率高于16的时候,就会认为是连贯的,此现象称之为视觉暂留。并且当帧速达到一定数值后,再增长的话,人眼也不容易察觉到有明显的流畅度提升了。
+
+
+#### 4.2 分辨率
+- (矩形)图片的长度和宽度,即图片的尺寸
+
+
+#### 4.3 码率
+- 把每秒显示的图片进行压缩后的数据量。影响体积,与体积成正比:码率越大,体积越大;码率越小,体积越小。 (体积=码率×时间)  
+- 帧率X分辨率=压缩前的每秒数据量(单位应该是若干个字节)   
+- 压缩比=压缩前的每秒数据量/码率 (对于同一个视频源并采用同一种视频编码算法,则:压缩比越高,画面质量越差。)
+
+
+#### 4.4 这些概念联系
+- 所谓“清晰”,是指画面十分细腻,没有马赛克。并不是分辨率越高图像就越清晰。  简单说:在码率一定的情况下,分辨率与清晰度成反比关系:分辨率越高,图像越不清晰,分辨率越低,图像越清晰。在分辨率一定的情况下,码率与清晰度成正比关系,码率越高,图像越清晰;码率越低,图像越不清晰。  
+- 但是,事实情况却不是这么简单。可以这么说:在码率一定的情况下,分辨率在一定范围内取值都将是清晰的;同样地,在分辨率一定的情况下,码率在一定范围内取值都将是清晰的。  在视频压缩的过程中, I帧是帧内图像数据压缩,是独立帧。而P帧则是参考I帧进行帧间图像数据压缩,不是独立帧。在压缩后的视频中绝大多数都是P帧,故视频质量主要由P帧表现出来。由于P帧不是独立帧,而只是保存了与邻近的I帧的差值,故实际上并不存在分辨率的概念,应该看成一个二进制差值序列。而该二进制序列在使用熵编码压缩技术时会使用量化参数进行有损压缩,视频的质量直接由量化参数决定,而量化参数会直接影响到压缩比和码率。  视频质量可以通过主观和客观方式来表现,主观方式就是通常人们提到的视频清晰度,而客观参数则是量化参数或者压缩比或者码率。在视频源一样,压缩算法也一样的前提下比较,量化参数,压缩比和码率之间是有直接的比例关系的。  分辨率的变化又称为重新采样。由高分辨率变成低分辨率称为下采样,由于采样前数据充足,只需要尽量保留更多的信息量,一般可以获得相对较好的结果。而由低分辨率变成高分辨率称为上采样,由于需要插值等方法来补充(猜测)缺少的像素点,故必然会带有失真,这就是一种视频质量(清晰度)的损失。
+
+
+
+
+
+
+#### 参考博客
+- Android视频开发进阶:https://www.jianshu.com/p/10e357946447
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 24 - 0
read/video_study1.md

@@ -0,0 +1,24 @@
+# VideoView学习
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 24 - 0
read/video_study10.md

@@ -0,0 +1,24 @@
+# 认识TextureView
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 33 - 0
read/video_study2.md

@@ -0,0 +1,33 @@
+# 认识SurfaceView
+#### 目录介绍
+- 01.SurfaceView有何特点
+
+
+
+### 01.SurfaceView有何特点
+- Android中 View是通过刷新来重绘视图,系统通过发出VSYNC信号来进行屏幕的重绘,刷新的时间间隔是16ms,如果我们可以在16ms以内将绘制工作完成,则没有任何问题,如果我们绘制过程逻辑很复杂,并且我们的界面更新还非常频繁,这时候就会造成界面的卡顿,影响用户体验,为此Android提供了SurfaceView来解决这一问题.
+- SurfaceView 继承自View,是 Android 中一种比较特殊的视图(View)
+    - 它跟普通View最大的区别是它有自己的Surface,在WMS中有对应的WindowState,在SurfaceFlinger中有Layer
+    - 一般的Activity包含的多个View会组成View hierachy的树形结构,只有最顶层的DecorView,也就是根结点视图,才是对WMS可见的。这个DecorView在WMS中有一个对应的WindowState。相应地,在SF中对应的Layer。
+    - SurfaceView自带一个Surface,这个Surface在WMS中有自己对应的WindowState,在SF中也会有自己的Layer。虽然在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染可以放到单独线程去做,渲染时可以有自己的GL context。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。
+- SurfaceView 应用场景
+    - 综合这些特点,SurfaceView 一般用在游戏、视频、摄影等一些复杂 UI 且高效的图像的显示,这类的图像处理都需要开单独的线程来处理。
+- SurfaceView 优点如下
+    - SurfaceView 通过子线程中进行画面更新,View 则在主线程中进行画面更新。
+    - SurfaceView 用于被动更新,如频繁画面更新,View 则用于主动更新,如触摸点击等事件响应等。
+    - SurfaceView 在底层实现了双缓冲机制,效率大大提升了,View 则没有。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 258 - 0
read/video_study3.md

@@ -0,0 +1,258 @@
+# 认识MediaPlayer
+#### 目录介绍
+
+#### **目录介绍**
+- **1.关于此视频封装库介绍**
+- 1.1 MediaPlayer简单介绍
+- **2.相关方法详解**
+- 2.1 获得MediaPlayer实例
+- 2.2 设置播放文件
+- 2.3 其他方法
+- **3.生命周期**
+- 3.1 生命周期图[摘自网络]
+- 3.2 周期状态说明
+- **4.播放视频**
+- 4.1 播放res/raw音频文件
+- 4.2 播放本地Uri
+- 4.3 播放网络文件
+- **5.MediaPlayer + SurfaceView播放视频**
+- 5.1 为什么要这样
+- 5.2 案例展示
+- 5.3 SurfaceView局限性
+- **6.VideoView播放视频**
+- 6.1 VideoView介绍
+- 6.2 使用方法代码
+- **7.MediaPlayer+TextureView**
+- 7.1 为什么使用TextureView
+- 7.2 如何实现视频播放功能
+
+
+### 1.关于此视频封装库介绍
+#### 1.1 MediaPlayer简单介绍
+- MediaPlayer类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频。它支持三种不同的媒体来源:
+- 本地资源
+- 内部URI,比如你可以通过ContentResolver来获取
+- 外部URL(流)
+- 对于Android支持的媒体格式列表,可见:Supported Media Formats文档在播放网络上的视频流时,Android原生的MediaPlayer支持两种协议,HTTP和RTSP,这两种协议最大的不同是,RTSP协议支持实时流媒体的播放,而HTTP协议不支持。因为VideoView的底层实现是MediaPlayer,因此VideoView也支持以上两种协议。但是Android原生MediaPalyer支持的协议(不支持RTMP、MMS等)和封装格式实在太有限了,如果我们想播放那些它不支持的视频,这时候就需要第三方播放器了,很多第三方播放器的底层实现都是基于FFmpeg
+
+
+### 2.相关方法详解
+#### 2.1 获得MediaPlayer实例
+- 可以直接new或者调用create方法创建:
+```
+MediaPlayer mp = new MediaPlayer();
+MediaPlayer mp = MediaPlayer.create(this, R.raw.test);
+//无需再调用setDataSource
+```
+- 另外create还有这样的形式:
+```
+create(Context context, Uri uri, SurfaceHolder holder) 
+```
+- 通过Uri和指定 SurfaceHolder 抽象类创建一个多媒体播放器。
+
+#### 2.2 设置播放文件
+- setDataSource()方法有多个,里面有这样一个类型的参数:FileDescriptor在使用这个API的时候,需要把文件放到res文件夹平级的assets文件夹里,然后使用下述代码设置DataSource:
+```
+MediaPlayer.create(this, R.raw.test); //①raw下的资源
+mp.setDataSource("/sdcard/test.mp3"); //②本地文件路径
+mp.setDataSource("http://www.xxx.com/music/test.mp3");//③网络URL文件
+```
+
+#### 2.3 其他方法
+```
+getCurrentPosition( ):得到当前的播放位置
+getDuration() :得到文件的时间
+getVideoHeight() :得到视频高度
+getVideoWidth() :得到视频宽度
+isLooping():是否循环播放
+isPlaying():是否正在播放
+pause():暂停
+prepare():准备(同步)
+prepareAsync():准备(异步)
+release():释放MediaPlayer对象
+reset():重置MediaPlayer对象
+seekTo(int msec):指定播放的位置(以毫秒为单位的时间)
+setAudioStreamType(int streamtype):指定流媒体的类型
+setDisplay(SurfaceHolder sh):设置用SurfaceHolder来显示多媒体
+setLooping(boolean looping):设置是否循环播放
+setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener):网络流媒体的缓冲监听
+setOnCompletionListener(MediaPlayer.OnCompletionListener listener):网络流媒体播放结束监听
+setOnErrorListener(MediaPlayer.OnErrorListener listener):设置错误信息监听
+setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener):视频尺寸监听
+setScreenOnWhilePlaying(boolean screenOn):设置是否使用SurfaceHolder显示
+setVolume(float leftVolume, float rightVolume):设置音量
+start():开始播放
+stop():停止播放
+```
+
+### 3.生命周期
+#### 3.1 生命周期图[摘自网络]
+![iamge](http://img.blog.csdn.net/20161128093058740)
+
+#### 3.2 周期状态说明
+- **状态1:Idel(空闲)状态**
+当 mediaplayer创建或者执行reset()方法后处于这个状态。
+- **状态2:Initialized(已初始化)状态**
+当调用mediaplayer的setDataResource()方法给mediaplayer设置播放的数据源后,mediaplayer会处于该状态。
+- **状态3:Prepared(准备就续)状态**
+设置完数据源后,调用mediaplayer的prepare()方法,让mediaplayer准备播放。值得一提的是,这里除了prepare()方法,还有prepareAsnyc()方法,此方法是异步方法,一般用于网络视频的缓冲。当缓冲完毕后,就会触发准备完毕的事件。我们要做的就是监听该事件(OnPreparedListener),当缓冲完成时,执行相应的操作。在此状态上,我们可以调用seekTo()方法定位视频,此方法不改变mediaplayer的状态;亦可调用stop()放弃视频播放,使mediaplayer处于Stopped状态。一般我们会在此状态上调用start()方法开始播放视频。
+- **状态4:Started(开始)状态**
+当处于Prepared状态、Paused状态和PlayebackCompeleted状态时,调用Started()方法即可进入该状态。在该状态中,mediaplayer开始播放视频,可以通过seekTo()方法和start()方法改变视频播放的进度,当Looping为真且播放完毕后,它会重新开始播放(即循环播放);否则播放完毕后,会触发事件并调用OnCompletionaListener.OnCompletion()方法,进行特定操作,并进入PlaybackCompleted状态。在此状态中,亦可调用pause()方法或者stop()方法让视频暂停或停止,此时mediaplayer分别处于Stopped和Paused状态。
+- **状态5:Stopped(停止)状态**
+当 mediaplayer处于Prepared、Started、Paused、PlaybackCompleted状态时,调用stop()方法即可进入本状态。应特别注意的是,在本状态中,若想重新开始播放,不能直接调用start()方法,必须调用prepare()方法或prepareAsync()方法重新让mediaplayer处于Prepared状态方可调用start()方法播放视频。
+- **状态6:Paused(暂停)状态**
+当mediaplayer处于Started状态是,调用pause()方法即可进入本状态。在本状态里,可直接调用start()方法使,mediaplayer回到Started状态,亦可调用stop()方法停止视频播放,让播放器处于停止态。
+- **状态7:PlaybackCompleted(播放完成)状态**
+当mediaplayer播放完成且Looping为假时即可进入本状态。在本状态可调用start()方法使mediaplayer回到Started状态(注意此时是从头开始播放);亦可调用stop()方法使mediaplayer处于停止态,结束播放。
+状态8:Error(错误)状态
+当mediaplayer出现错误时处于此状态。
+
+### 4.播放视频
+#### 4.1 播放res/raw音频文件
+- res/raw目录下的音频文件,创建MediaPlayer调用的是create方法,第一次启动播放前不需要再调用prepare(),如果是使用构造方法构造的话,则需要调用一次prepare()方法!
+```
+@Override 
+public void onClick(View v) { 
+  switch (v.getId()){ 
+    case R.id.btn_play: 
+       if(isRelease){
+             mPlayer = MediaPlayer.create(this,R.raw.fly);
+             isRelease = false;
+        }
+        mPlayer.start(); //开始播放 
+        break; 
+
+     case R.id.btn_pause:
+        mPlayer.pause(); //停止播放
+        break; 
+
+     case R.id.btn_stop:
+        mPlayer.reset(); //重置MediaPlayer 
+        mPlayer.release(); //释放MediaPlayer 
+        isRelease = true;
+        break;
+}
+```
+
+
+#### 4.2 播放本地Uri
+
+```
+Uri myUri = ....;   /**initialize Uri here*/
+MediaPlayer mediaPlayer = new MediaPlayer();
+mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+mediaPlayer.setDataSource(getApplicationContext(), myUri);
+mediaPlayer.prepare();
+mediaPlayer.start();
+```
+
+#### 4.3 播放网络文件
+
+```
+String url = "http://........"; // your URL here 
+MediaPlayer mediaPlayer = new MediaPlayer();
+mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+mediaPlayer.setDataSource(url);
+mediaPlayer.prepare(); // might take long! (for buffering, etc)
+mediaPlayer.start();
+```
+
+
+### 5.MediaPlayer + SurfaceView播放视频
+#### 5.1 为什么要这样
+- MediaPlayer主要用于播放音频,没有提供图像输出界面,所以我们需要借助其他的组件来显示MediaPlayer播放的图像输出,我们可以使用SurfaceView来显示
+
+#### 5.2 案例展示
+
+```
+
+```
+
+### 5.3 SurfaceView局限性
+- 在Android总播放视频可以直接使用VideoView,VideoView是通过继承自SurfaceView来实现的。**SurfaceView的大概原理就是在现有View的位置上创建一个新的Window,内容的显示和渲染都在新的Window中。这使得SurfaceView的绘制和刷新可以在单独的线程中进行,从而大大提高效率。但是呢,由于SurfaceView的内容没有显示在View中而是显示在新建的Window中, 使得SurfaceView的显示不受View的属性控制,不能进行平移,缩放等变换,也不能放在其它RecyclerView或ScrollView中,一些View中的特性也无法使用。**
+
+
+
+### 6.VideoView播放视频
+#### 6.1 VideoView介绍
+- 使用VideoView时,视频宽度等于VideoView布局宽,但是高是自适应的,自动调整宽高比到视频原始比例,所以不会有拉伸。
+
+#### 6.2 使用方法代码
+
+```
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_main);
+    button = (Button) findViewById(R.id.play);
+    videoview = (VideoView) findViewById(R.id.video);
+
+    mMediaController = new MediaController(this);
+    videoview.setMediaController(mMediaController);
+
+    button.setOnClickListener(new View.OnClickListener(){
+        @Override
+        public void onClick(View v) {
+            loadView(url.getText().toString());
+        }
+    });
+}
+
+
+public void loadView(String path) {
+    Uri uri = Uri.parse(path);
+    videoview.setVideoURI(uri);
+    videoview.start();
+    videoview.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+        @Override
+        public void onPrepared(MediaPlayer mp) {
+ //         mp.setLooping(true);
+            mp.start();// 播放
+            Toast.makeText(MainActivity.this, "开始播放!", Toast.LENGTH_LONG).show();
+        }
+    });
+    videoview.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+        @Override
+        public void onCompletion(MediaPlayer mp) {
+            Toast.makeText(MainActivity.this, "播放完毕", Toast.LENGTH_SHORT).show();
+        }
+    });
+}
+```
+
+
+### 7.MediaPlayer+TextureView
+#### 7.1 为什么使用TextureView
+- TextureView是在4.0(API level 14)引入的,与SurfaceView相比,它不会创建新的窗口来显示内容。它是将内容流直接投放到View中,并且可以和其它普通View一样进行移动,旋转,缩放,动画等变化。TextureView必须在硬件加速的窗口中使用。
+- TextureView被创建后不能直接使用,必须要在它被它添加到ViewGroup后,待SurfaceTexture准备就绪才能起作用(看TextureView的源码,TextureView是在绘制的时候创建的内部SurfaceTexture)。
+
+
+#### 7.2 如何实现视频播放功能
+- SurfaceTexture的准备就绪、大小变化、销毁、更新等状态变化时都会回调相对应的方法。当TextureView内部创建好SurfaceTexture后,在监听器的onSurfaceTextureAvailable方法中,用SurfaceTexture来关联MediaPlayer,作为播放视频的图像数据来源。
+- SurfaceTexture作为数据通道,把从数据源(MediaPlayer)中获取到的图像帧数据转为GL外部纹理,交给TextureVeiw作为View heirachy中的一个硬件加速层来显示,从而实现视频播放功能。
+
+
+#### 参考博客链接
+- Android TextureView简易教程:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1213/2153.html
+- 视频画面帧的展示控件SurfaceView及TextureView对比:https://www.tuicool.com/articles/AVnaeam
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 79 - 0
read/video_study4.md

@@ -0,0 +1,79 @@
+# 认识TextureView
+#### 目录介绍
+
+
+
+
+
+#### 目录介绍
+- 01.TextureView是什么
+- 02.TextureView优缺点
+- 03.TextureView源码分析
+- 04.与SurfaceView对比
+- 05.如何合理选择
+
+
+
+
+### 01.TextureView是什么
+#### 1.1 是继承view
+- 在4.0(API level 14)中引入,与SurfaceView一样继承View,它可以将内容流直接投影到View中,可以用于实现Live preview等功能。
+- 和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。
+- 值得注意的是TextureView必须在硬件加速的窗口中。它显示的内容流数据可以来自App进程或是远端进程。
+
+
+
+#### 1.2 如何实现视频播放功能
+- SurfaceTexture的准备就绪、大小变化、销毁、更新等状态变化时都会回调相对应的方法。当TextureView内部创建好SurfaceTexture后,在监听器的onSurfaceTextureAvailable方法中,用SurfaceTexture来关联MediaPlayer,作为播放视频的图像数据来源。
+- SurfaceTexture作为数据通道,把从数据源(MediaPlayer)中获取到的图像帧数据转为GL外部纹理,交给TextureVeiw作为View heirachy中的一个硬件加速层来显示,从而实现视频播放功能。
+
+
+
+### 02.TextureView优点及缺点
+- 优点:
+    - 支持移动、旋转、缩放等动画,支持截图
+- 缺点:
+    - 必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程。
+
+
+
+
+#### 04.与SurfaceView对比
+- SurfaceView的说明
+    - SurfaceView的工作方式是创建一个置于应用窗口之后的新窗口。这种方式的效率非常高,因为SurfaceView窗口刷新的时候不需要重绘应用程序的窗口(android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率非常低下,不过满足普通应用界面的需求还是绰绰有余),但是SurfaceView也有一些非常不便的限制。
+    - 因为SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()。
+
+
+
+
+### 05.如何合理选择
+- 从性能和安全性角度出发,使用播放器优先选SurfaceView。
+    - 1、在android 7.0上系统surfaceview的性能比TextureView更有优势,支持对象的内容位置和包含的应用内容同步更新,平移、缩放不会产生黑边。 在7.0以下系统如果使用场景有动画效果,可以选择性使用TextureView
+    - 2、由于失效(invalidation)和缓冲的特性,TextureView增加了额外1~3帧的延迟显示画面更新
+    - 3、TextureView总是使用GL合成,而SurfaceView可以使用硬件overlay后端,可以占用更少的内存带宽,消耗更少的能量
+    - 4、TextureView的内部缓冲队列导致比SurfaceView使用更多的内存
+    - 5、SurfaceView: 内部自己持有surface,surface 创建、销毁、大小改变时系统来处理的,通过surfaceHolder 的callback回调通知。当画布创建好时,可以将surface绑定到MediaPlayer中。SurfaceView如果为用户可见的时候,创建SurfaceView的SurfaceHolder用于显示视频流解析的帧图片,如果发现SurfaceView变为用户不可见的时候,则立即销毁SurfaceView的SurfaceHolder,以达到节约系统资源的目的
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 24 - 0
read/video_study5.md

@@ -0,0 +1,24 @@
+# 视频编码和解码
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 24 - 0
read/video_study6.md

@@ -0,0 +1,24 @@
+# 视频加密和解密
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 26 - 0
read/video_study7.md

@@ -0,0 +1,26 @@
+# 视频录制和编辑
+#### 目录介绍
+
+
+- https://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650822595&idx=1&sn=8f82333e9fd5ea798b78593f0df5ba1e&chksm=80b7835db7c00a4bdad824113adf22808d840a489f60e5775b578732b5724dcb6b0569608122&scene=38#wechat_redirect
+
+
+- https://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650822766&idx=1&sn=51073a8a6ac9c8930c103f54d4d84a84&chksm=80b78cf0b7c005e6491697a3eb5a4299e4aa01671bf104274603fd96ff37e302889b9677fb05&scene=38#wechat_redirect
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 24 - 0
read/video_study8.md

@@ -0,0 +1,24 @@
+# 认识TextureView
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 24 - 0
read/video_study9.md

@@ -0,0 +1,24 @@
+# 认识TextureView
+#### 目录介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 0 - 23
read/wiki3.md

@@ -1,23 +0,0 @@
-# 视频全局悬浮窗播放
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-