Android webView长按保存图片到本地相册(队列下载实现)

4,678 阅读3分钟

前言

自己写的app中 有大量的webView 在加载的过程中 新增了许多功能 比如

加载H5链接时 遇到有趣的图片 想长按保存下来是否可以呢

答案:“肯定是可以的”

网上有很多例子 但是很多有成功不了 不知道是我权限没给对 还是写法有问题 总之保存不了图片到本地

实现效果

效果图

思路

基本思路是 长按获取图片的url 然后将url 通过IO流的方式 下载到本地

代码实现

有了思路 就好解决问题了

首先监听webView的长按事件

       //WebView长按的监听
        mWebView.setOnLongClickListener(v -> {
            final WebView.HitTestResult hitTestResult = mWebView.getHitTestResult();
            // 如果是图片类型或者是带有图片链接的类型
            if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE ||
                    hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
                // 弹出保存图片的对话框
                new AlertDialog.Builder(getActivity())
                        .setItems(new String[]{"保存图片"}, (dialog, which) -> {
                            String pic = hitTestResult.getExtra();//获取图片
                            Log.e(TAG, " 获取到的图片地址为  :" + pic);
                            MyDownloadManager myDownloadManager = new MyDownloadManager();
                            switch (which) {
                                case 0:
                                    myDownloadManager.addDownloadTask(pic);
                                    ToastUtils.showShort(getActivity(), "保存成功");
                                    break;
                            }
                        })
                        .show();
                return true;
            }
            return false;//保持长按可以复制文字
        });

WebView长按时 ui的效果图展示 2

其中hitTestResult 是查看图片类型和图片链接

来看下获取到的图片 地址类型 1

这里是长按保存 触发的点击事件 下面紧接着提示一个保存的状态 实际已经开始下载 myDownloadManager.addDownloadTask(pic) 是封装好的下载类

				switch (which) {
                                case 0:
                                    myDownloadManager.addDownloadTask(pic);
                                    ToastUtils.showShort(getActivity(), "保存成功");
                                    break;
                            }

MyDownloadManager 封装类

MyDownloadManager 封装类 启用了线程池 队列形式下载 保存的图片

public class MyDownloadManager {
    public static final int EXTERNAL_STORAGE_REQ_CODE = 10;
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};

    /**
     * Checks if the app has permission to write to device storage
     * <p>
     * If the app does not has permission then the user will be prompted to
     * grant permissions
     *
     * @param activity
     */
    public static void verifyStoragePermissions(Activity activity) {
        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE);
        }

    }

    private static final String TAG = "MyDownloadManager";
    private File downloadDir; // 文件保存路径
    private static MyDownloadManager instance; // 单例

    // 单线程任务队列
    public static Executor executor;
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "MyDownloadManager #" + mCount.getAndIncrement());
        }
    };
    //线程池
    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(128);
    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1, 1,
            TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);


    public MyDownloadManager() {
        // 初始化下载路径
        downloadDir = new File("/sdcard/");
        if (!downloadDir.exists()) {
            downloadDir.mkdirs();
        }
        executor = new SerialExecutor();
    }

    /**
     * 顺序执行下载任务
     */
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(() -> {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

    /**
     * 获取单例对象
     *
     * @return
     */
    public static MyDownloadManager getInstance() {
        if (instance == null) {
            instance = new MyDownloadManager();
        }
        return instance;
    }

    /**
     * 添加下载任务
     *
     * @param path
     */
    public void addDownloadTask(final String path) {
        Log.e(TAG, "进入run : " + path);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "进入run");
                download(path);
            }
        });
    }

    /**
     * 下载文件
     *
     * @param path
     */
    private void download(String path) {
        String fileName = MD5(path);

        File savePath = new File(downloadDir, fileName); // 下载文件路径
        //File finallyPath = new File(downloadDir, fileName + ".png"); // 下载完成后加入
        if (savePath.exists()) {
            // 文件存在则已下载
            Log.e(TAG, "file is existed");
            return;
        }
        // 如果是Wifi则开始下载
        if (savePath.exists() && savePath.delete()) { // 如果之前存在文件,证明没有下载完成,删掉重新创建
            savePath = new File(downloadDir, fileName);
            Log.e(TAG, "download 111111");
        }
        Log.e(TAG, "download start");
        try {
            byte[] bs = new byte[1024];
            int len;
            URL url = new URL(path);
            InputStream is = url.openStream();
            OutputStream os = new FileOutputStream(savePath, true);
            while ((len = is.read(bs)) != -1) {
                os.write(bs, 0, len);
            }
            is.close();
            os.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 添加删除任务
     *
     * @param path
     */
    public void addDeleteTask(final String path) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                delete(path);
            }
        });
    }

    /**
     * 删除本地文件
     *
     * @param path
     */
    private void delete(String path) {
        String fileName = MD5(path);
        File savePath = new File(downloadDir, fileName + ".png");
        Log.i(TAG, savePath.getPath());
        if (savePath.exists()) {
            if (savePath.delete()) {
                Log.i(TAG, "file is deleted");
            }
        }
    }

    /**
     * 返回下载路径
     *
     * @return
     */
    public File getDownloadDir() {
        return downloadDir;
    }

    public String getfileName(String path) {
        String f = MD5(path);
        return f;
    }

    public static String MD5(String path) {
        // URL url = new URL(path);
        //String url = path;
        //String path = Environment.getExternalStorageDirectory().getAbsolutePath();
        //final long startTime = System.currentTimeMillis();
        //String filename=url.substring(url.lastIndexOf("/") + 1);

        //设置日期格式
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String time = df.format(new Date());
        //命名图片名字
        String filename = time + ".jpg";
        return filename;
    }

}

权限申请

  //动态申请 读写SD卡的权限
        int permission = ActivityCompat.checkSelfPermission(getActivity(),
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // 请求权限
            ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    EXTERNAL_STORAGE_REQ_CODE);
        }

如果是android10 androidQ版本 记得在manifest中加多一句 android:requestLegacyExternalStorage="true"

AndroidQ 参考另一篇博文

open failed: EACCES (Permission denied)解决方法 AndroidQ

共勉

2021 继续加油 ~