一、测试

实现:

android开发 预览下载视频 android下载网页视频_android开发 预览下载视频


android开发 预览下载视频 android下载网页视频_android studio_02


二、项目:

android开发 预览下载视频 android下载网页视频_保存视频到手机_03


三、准备工作

1.添加权限,网络和读写

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2.添加依赖包:

implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.50'
    implementation 'com.github.dueeeke.dkplayer:dkplayer-java:2.5.5'
    implementation 'com.github.dueeeke.dkplayer:dkplayer-armv7a:2.5.5'
    implementation 'com.github.dueeeke.dkplayer:dkplayer-ui:2.5.5'
    implementation 'me.jessyan:autosize:1.1.2'

3.在app的build.gradle的defaultConfig里面添加,和视频播放器有关

ndk {
            // 设置支持的SO库架构
            abiFilters "armeabi-v7a"
        }

4.在AndroidManifest.xml中的application里面添加:

<!-- autoSize配置-->
        <meta-data
            android:name="design_width_in_dp"
            android:value="360"/>
        <meta-data
            android:name="design_height_in_dp"
            android:value="640"/>

5.保存视频到手机的路经封装:
   (1).ExternalStorage.java

public class ExternalStorage {
    /**
     * 外部存储根目录
     */
    private String sdkStorageRoot = null;

    private static ExternalStorage instance;

    private static final String TAG = "ExternalStorage";

    private boolean hasPermission = true; // 是否拥有存储卡权限

    private Context context;

    private ExternalStorage() {

    }

    synchronized public static ExternalStorage getInstance() {
        if (instance == null) {
            instance = new ExternalStorage();
        }
        return instance;
    }

    public void init(Context context, String sdkStorageRoot) {
        this.context = context;
        // 判断权限
        hasPermission = checkPermission(context);

        if (!TextUtils.isEmpty(sdkStorageRoot)) {
            File dir = new File(sdkStorageRoot);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            if (dir.exists() && !dir.isFile()) {
                this.sdkStorageRoot = sdkStorageRoot;
                if (!sdkStorageRoot.endsWith("/")) {
                    this.sdkStorageRoot = sdkStorageRoot + "/";
                }
            }
        }

        if (TextUtils.isEmpty(this.sdkStorageRoot)) {
            loadStorageState(context);
        }

        createSubFolders();
    }

    private void loadStorageState(Context context) {
        String externalPath = Environment.getExternalStorageDirectory().getPath();
        this.sdkStorageRoot = externalPath + "/" + context.getPackageName() + "/";
    }

    private void createSubFolders() {
        boolean result = true;
        File root = new File(sdkStorageRoot);
        if (root.exists() && !root.isDirectory()) {
            root.delete();
        }
        for (StorageType storageType : StorageType.values()) {
            result &= makeDirectory(sdkStorageRoot + storageType.getStoragePath());
        }
        if (result) {
            createNoMediaFile(sdkStorageRoot);
        }
    }

    /**
     * 创建目录
     *
     * @param path
     * @return
     */
    private boolean makeDirectory(String path) {
        File file = new File(path);
        boolean exist = file.exists();
        if (!exist) {
            exist = file.mkdirs();
        }
        return exist;
    }

    protected static String NO_MEDIA_FILE_NAME = ".nomedia";

    private void createNoMediaFile(String path) {
        File noMediaFile = new File(path + "/" + NO_MEDIA_FILE_NAME);
        try {
            if (!noMediaFile.exists()) {
                noMediaFile.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件全名转绝对路径(写)
     *
     * @param fileName 文件全名(文件名.扩展名)
     * @return 返回绝对路径信息
     */
    public String getWritePath(String fileName, StorageType fileType) {
        return pathForName(fileName, fileType, false, false);
    }

    private String pathForName(String fileName, StorageType type, boolean dir,
                               boolean check) {
        String directory = getDirectoryByDirType(type);
        StringBuilder path = new StringBuilder(directory);

        if (!dir) {
            path.append(fileName);
        }

        String pathString = path.toString();
        File file = new File(pathString);

        if (check) {
            if (file.exists()) {
                if ((dir && file.isDirectory())
                        || (!dir && !file.isDirectory())) {
                    return pathString;
                }
            }

            return "";
        } else {
            return pathString;
        }
    }

    /**
     * 返回指定类型的文件夹路径
     *
     * @param fileType
     * @return
     */
    public String getDirectoryByDirType(StorageType fileType) {
        return sdkStorageRoot + fileType.getStoragePath();
    }

    /**
     * 根据输入的文件名和类型,找到该文件的全路径。
     *
     * @param fileName
     * @param fileType
     * @return 如果存在该文件,返回路径,否则返回空
     */
    public String getReadPath(String fileName, StorageType fileType) {
        if (TextUtils.isEmpty(fileName)) {
            return "";
        }

        return pathForName(fileName, fileType, false, true);
    }

    public boolean isSdkStorageReady() {
        String externalRoot = Environment.getExternalStorageDirectory().getAbsolutePath();
        if (this.sdkStorageRoot.startsWith(externalRoot)) {
            return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        } else {
            return true;
        }
    }

    /**
     * 获取外置存储卡剩余空间
     *
     * @return
     */
    public long getAvailableExternalSize() {
        return getResidualSpace(sdkStorageRoot);
    }

    /**
     * 获取目录剩余空间
     *
     * @param directoryPath
     * @return
     */
    private long getResidualSpace(String directoryPath) {
        try {
            StatFs sf = new StatFs(directoryPath);
            long blockSize = sf.getBlockSize();
            long availCount = sf.getAvailableBlocks();
            long availCountByte = availCount * blockSize;
            return availCountByte;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * SD卡存储权限检查
     */
    private boolean checkPermission(Context context) {
        if (context == null) {
            Log.e(TAG, "checkMPermission context null");
            return false;
        }

        // 写权限有了默认就赋予了读权限
        PackageManager pm = context.getPackageManager();
        if (PackageManager.PERMISSION_GRANTED !=
                pm.checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context.getApplicationInfo().packageName)) {
            Log.e(TAG, "without permission to access storage");
            return false;
        }

        return true;
    }

    /**
     * 有效性检查
     */
    public boolean checkStorageValid() {
        if (hasPermission) {
            return true; // M以下版本&授权过的M版本不需要检查
        }

        hasPermission = checkPermission(context); // 检查是否已经获取权限了
        if (hasPermission) {
            Log.i(TAG, "get permission to access storage");

            // 已经重新获得权限,那么重新检查一遍初始化过程
            createSubFolders();
        }
        return hasPermission;
    }
}

   (2).StorageType.java

public enum StorageType {
    TYPE_LOG(DirectoryName.LOG_DIRECTORY_NAME),
    TYPE_TEMP(DirectoryName.TEMP_DIRECTORY_NAME),
    TYPE_FILE(DirectoryName.FILE_DIRECTORY_NAME),
    TYPE_AUDIO(DirectoryName.AUDIO_DIRECTORY_NAME),
    TYPE_IMAGE(DirectoryName.IMAGE_DIRECTORY_NAME),
    TYPE_VIDEO(DirectoryName.VIDEO_DIRECTORY_NAME),
    TYPE_THUMB_IMAGE(DirectoryName.THUMB_DIRECTORY_NAME),
    TYPE_THUMB_VIDEO(DirectoryName.THUMB_DIRECTORY_NAME),
    ;
    private DirectoryName storageDirectoryName;
    private long storageMinSize;

    public String getStoragePath() {
        return storageDirectoryName.getPath();
    }

    public long getStorageMinSize() {
        return storageMinSize;
    }

    StorageType(DirectoryName dirName) {
        this(dirName, StorageUtil.THRESHOLD_MIN_SPCAE);
    }

    StorageType(DirectoryName dirName, long storageMinSize) {
        this.storageDirectoryName = dirName;
        this.storageMinSize = storageMinSize;
    }

    enum DirectoryName {
        AUDIO_DIRECTORY_NAME("audio/"),
        DATA_DIRECTORY_NAME("data/"),
        FILE_DIRECTORY_NAME("file/"),
        LOG_DIRECTORY_NAME("log/"),
        TEMP_DIRECTORY_NAME("temp/"),
        IMAGE_DIRECTORY_NAME("image/"),
        THUMB_DIRECTORY_NAME("thumb/"),
        VIDEO_DIRECTORY_NAME("video/"),
        ;

        private String path;

        public String getPath() {
            return path;
        }

        DirectoryName(String path) {
            this.path = path;
        }
    }
}

   (3).StorageUtil.java

public class StorageUtil {
    public final static long K = 1024;
    public final static long M = 1024 * 1024;
    // 外置存储卡默认预警临界值
    private static final long THRESHOLD_WARNING_SPACE = 100 * M;
    // 保存文件时所需的最小空间的默认值
    static final long THRESHOLD_MIN_SPCAE = 20 * M;

    public static void init(Context context, String rootPath) {
        ExternalStorage.getInstance().init(context, rootPath);
    }

    /**
     * 获取文件保存路径,没有toast提示
     *
     * @param fileName
     * @param fileType
     * @return 可用的保存路径或者null
     */
    public static String getWritePath(String fileName, StorageType fileType) {
        return getWritePath(null, fileName, fileType, false);
    }

    /**
     * 获取文件保存路径
     *
     * @param fileName 文件全名
     * @param tip      空间不足时是否给出默认的toast提示
     * @return 可用的保存路径或者null
     */
    private static String getWritePath(Context context, String fileName, StorageType fileType, boolean tip) {
        String path = ExternalStorage.getInstance().getWritePath(fileName, fileType);
        if (TextUtils.isEmpty(path)) {
            return null;
        }
        File dir = new File(path).getParentFile();
        if (dir != null && !dir.exists()) {
            dir.mkdirs();
        }
        return path;
    }

    /**
     * 判断能否使用外置存储
     */
    public static boolean isExternalStorageExist() {
        return ExternalStorage.getInstance().isSdkStorageReady();
    }


    /**
     * 判断外部存储是否存在,以及是否有足够空间保存指定类型的文件
     *
     * @param context
     * @param fileType
     * @param tip      是否需要toast提示
     * @return false: 无存储卡或无空间可写, true: 表示ok
     */
    public static boolean hasEnoughSpaceForWrite(Context context, StorageType fileType, boolean tip) {
        if (!ExternalStorage.getInstance().isSdkStorageReady()) {
            return false;
        }

        long residual = ExternalStorage.getInstance().getAvailableExternalSize();
        if (residual < fileType.getStorageMinSize()) {
            return false;
        } else if (residual < THRESHOLD_WARNING_SPACE) {
        }

        return true;
    }

    /**
     * 根据输入的文件名和类型,找到该文件的全路径。
     *
     * @param fileName
     * @param fileType
     * @return 如果存在该文件,返回路径,否则返回空
     */
    public static String getReadPath(String fileName, StorageType fileType) {
        return ExternalStorage.getInstance().getReadPath(fileName, fileType);
    }

    /**
     * 获取文件保存路径,空间不足时有toast提示
     *
     * @param context
     * @param fileName
     * @param fileType
     * @return 可用的保存路径或者null
     */
    public static String getWritePath(Context context, String fileName, StorageType fileType) {
        return getWritePath(context, fileName, fileType, true);
    }

    public static String getDirectoryByDirType(StorageType fileType) {
        return ExternalStorage.getInstance().getDirectoryByDirType(fileType);
    }

    public static String getSystemImagePath() {
//        String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "eWorld";
//        return filePath + "/image/";
        if (Build.VERSION.SDK_INT > 7) {
            String picturePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath();
            return picturePath + "/eWorld/";
        } else {
            String picturePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath();
            return picturePath + "/eWorld/";
        }
    }

    public static String getSaveVideoPath() {
//        String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "eWorld";
        String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + File.separator + "eWorld";
        return filePath + "/video/";
    }

    public static boolean isInvalidVideoFile(String filePath) {
        return filePath.toLowerCase().endsWith(".3gp")
                || filePath.toLowerCase().endsWith(".mp4");
    }

    public static void checkValid() {
        ExternalStorage.getInstance().checkStorageValid();
    }
}

6.视频播放器PlayVideoActivity.java

public class PlayVideoActivity extends AppCompatActivity {
    private String url;
    private IjkVideoView videoView;
    private Button button;
    private static String dstPath;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_play_video);
        initData();
        initViews();
        initPlayer();
        startPlay();
    }


    private void initViews() {
        //播放view
        videoView = findViewById(R.id.video_view);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final AlertDialog.Builder alert = new AlertDialog.Builder(PlayVideoActivity.this);
                alert.setTitle("保存视频");
                alert.setMessage("你确定要保存视频吗?");
                alert.setNegativeButton("取消", null);
                alert.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        saveVideo();
                        dialog.dismiss();
                    }
                });
                alert.show();
            }
        });
    }

    private void initData() {
        if (getIntent() != null && getIntent().getExtras() != null) {
            //视频url
            url = getIntent().getExtras().getString("url");
        }
    }

    /**
     * 初始化Player
     */
    private void initPlayer() {
        com.dueeeke.videocontroller.StandardVideoController controller = new StandardVideoController(this);
        IjkPlayer ijkPlayer = new IjkPlayer(this) {
            @Override
            public void setOptions() {
                super.setOptions();
                mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);
            }
        };
        videoView.setPlayerConfig(new PlayerConfig.Builder().setCustomMediaPlayer(ijkPlayer).build());
        videoView.setVideoController(controller);
    }

    /**
     * 开始播放
     */
    private void startPlay() {
        if (url != null) {
            videoView.setUrl(url);
            videoView.start();
            videoView.isFullScreen();
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        //横竖屏切换
        refresh();
    }

    /**
     * 横竖屏切换刷新
     */
    private void refresh() {
        final boolean isBaseOnWidth = (getResources().getDisplayMetrics().widthPixels <= getResources().getDisplayMetrics().heightPixels);
        final Window window = getWindow();
        if (window != null) {
            window.getDecorView().post(new Runnable() {
                @Override
                public void run() {
                    window.getDecorView().setVisibility(View.GONE);
                    AutoSizeCompat.autoConvertDensity(PlayVideoActivity.this.getResources(), 420, isBaseOnWidth);
                    window.getDecorView().setVisibility(View.VISIBLE);
                }
            });
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (videoView != null) {
            videoView.resume();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (videoView != null) {
            videoView.pause();
        }
    }

    @Override
    public void onBackPressed() {
        if (!videoView.onBackPressed()) {
            super.onBackPressed();
        }
        super.onBackPressed();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //释放播放器
        if (videoView != null) {
            videoView.release();
            videoView = null;
        }
    }

    private void saveVideo() {
        String picPath = StorageUtil.getSaveVideoPath();
        StorageUtil.init(PlayVideoActivity.this, picPath);
        dstPath = picPath + (System.currentTimeMillis() / 1000) + ".mp4";
        loadNewVideoProgress();
    }

    /**
     * 保存视频,需要子线程
     */
    private void loadNewVideoProgress() {
        final ProgressDialog pd;    //进度条对话框
        pd = new ProgressDialog(this);
        pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        pd.setMessage("正在下载更新");
        pd.show();
        //启动子线程下载任务
        new Thread() {
            @Override
            public void run() {
                try {
                    getFileFromServer(url, pd);
                    sleep(3000);
                    pd.dismiss(); //结束掉进度条对话框
                } catch (Exception e) {
                    //保存视频失败
                    Toast.makeText(getApplicationContext(), "保存视频失败", Toast.LENGTH_LONG).show();
                    e.printStackTrace();
                }
            }
        }.start();
    }

    /**
     * 从服务器获取apk文件的代码
     * 传入网址uri,进度条对象即可获得一个File文件
     * (要在子线程中执行哦)
     */
    public static File getFileFromServer(String uri, ProgressDialog pd) throws Exception {
        //如果相等的话表示当前的sdcard挂载在手机上并且是可用的
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            URL url = new URL(uri);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            //获取到文件的大小
            pd.setMax(conn.getContentLength());
            InputStream is = conn.getInputStream();
            long time = System.currentTimeMillis();//当前时间的毫秒数
//            File file = new File(Environment.getExternalStorageDirectory(), time+".mp4");
            File file = new File(dstPath);
            FileOutputStream fos = new FileOutputStream(file);
            BufferedInputStream bis = new BufferedInputStream(is);
            byte[] buffer = new byte[1024];
            int len;
            int total = 0;
            while ((len = bis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
                total += len;
                //获取当前下载量
                pd.setProgress(total);
            }
            fos.close();
            bis.close();
            is.close();
            return file;
        } else {
            return null;
        }
    }
}

三、使用工作
主活动MainActivity.java

public class MainActivity extends AppCompatActivity {
    private String url = "https://vd4.bdstatic.com/mda-jbppbefbbztvws50/sc/mda-jbppbefbbztvws50.mp4";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public void button(View view) {
        Intent intent = new Intent(this, PlayVideoActivity.class);
        intent.putExtra("url", Objects.requireNonNull(url));
        startActivity(intent);
    }
}