前言
自己写的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的效果图展示
其中hitTestResult 是查看图片类型和图片链接
来看下获取到的图片 地址类型
这里是长按保存 触发的点击事件
下面紧接着提示一个保存的状态 实际已经开始下载
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 继续加油 ~