Android中Gif网络图片下载到本地并通知相册显示

把网络中的Gif和图片下载到本地,并通知系统相册显示,同时适配了Android Q版本,上代码(kotlin):

     thread {  //kotlin的异步代码块,并不是缩写,而是直接能运行
           var path = DownloadUtils.showLoadingImage(this, mUrl)  //必须异步调用处
           if (!path.isNullOrEmpty()) {
              runOnUiThread {
                      ToastUtil.showLong("保存成功")
               }
            }
     }
showLoadingImage() 方法
       /**
         * 下载图片保存至手机并通知系统相册显示
         */
        fun showLoadingImage(context: Context, urlPath: String): String? {
            if (urlPath.isNullOrEmpty()) return null
            var mMimeType = urlPath.substring(urlPath.lastIndexOf("."), urlPath.length) // .gif还是.jpg
            var outImageUri: Uri? = null
            var outputStream: OutputStream? = null
            var inputStream: InputStream? = null
            var inBuffer: BufferedSource? = null
            try {
                if (SdkVersionUtils.checkedAndroid_Q()) {   //针对Q版本创建uri
                    val contentValues = ContentValues()
                    contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "IMG_" + SimpleDateFormat("yyyyMMdd_HHmmssSS").format(System.currentTimeMillis()) + mMimeType)
                    contentValues.put(MediaStore.Images.Media.DATE_TAKEN, ValueOf.toString(System.currentTimeMillis()))
                    contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*")
                    contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, PictureMimeType.DCIM)
                    outImageUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
                } else {
                    val state = Environment.getExternalStorageState()
                    val rootDir = if (state == Environment.MEDIA_MOUNTED) Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) else context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
                    if (rootDir != null) {
                        if (!rootDir.exists()) {
                            rootDir.mkdirs()
                        }
                        val folderDir = File(if (state != Environment.MEDIA_MOUNTED) rootDir.absolutePath else rootDir.absolutePath + File.separator + PictureMimeType.CAMERA + File.separator)
                        if (!folderDir.exists() && folderDir.mkdirs()) {
                        }
                        val fileName ="IMG_" + SimpleDateFormat("yyyyMMdd_HHmmssSS").format(System.currentTimeMillis()) + mMimeType
                        val file = File(folderDir, fileName)
                        outImageUri = Uri.fromFile(file)
                    }
                }
                if (outImageUri != null) {
                    outputStream = Objects.requireNonNull(context.getContentResolver().openOutputStream(outImageUri))
                    val u = URL(urlPath)
                    inputStream = u.openStream()
                    inBuffer = inputStream.source().buffer()
                    val bufferCopy = MyFileUtils.bufferCopy(inBuffer, outputStream)
                    if (bufferCopy) {
                        var path = MyFileUtils.getPath(context, outImageUri)
                        /*--------------通知相册广播-----------------------------*/
                        // deprecated (4.4以后只有系统级应用才有使用广播通知系统扫描的权限)
                        val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
                        intent.data = outImageUri
                        context.sendBroadcast(intent)
                        MediaScannerConnection.scanFile(context, arrayOf(path), null) { path, uri -> Log.e("scan file ", "$path---$uri") }
                        /*--------------通知广播-----------------------------*/
                        return path
                    }
                }
            } catch (e: Exception) {
                if (outImageUri != null && SdkVersionUtils.checkedAndroid_Q()) {
                    context.getContentResolver().delete(outImageUri, null, null)
                }
            } finally {
                inputStream?.close()
                outputStream?.close()
                inBuffer?.close()
            }
            return null
        }
MyFileUtils类中的方法直接全copy,里面也包含bufferCopy()方法
import android.annotation.SuppressLint
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.util.Log
import okio.BufferedSink
import okio.BufferedSource
import okio.buffer
import okio.sink
import java.io.OutputStream
import java.util.*

class MyFileUtils{
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     * @author paulburke
     */
    fun isDownloadsDocument(uri: Uri): Boolean {
        return "com.android.providers.downloads.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     * @author paulburke
     */
    fun isExternalStorageDocument(uri: Uri): Boolean {
        return "com.android.externalstorage.documents" == uri.authority
    }

    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.<br></br>
     * <br></br>
     * Callers should check whether the path is local before assuming it
     * represents a local file.
     *
     * @param context The context.
     * @param uri     The Uri to query.
     * @author paulburke
     */
    @SuppressLint("NewApi")
    fun getPath(ctx: Context, uri: Uri): String? {
        val context = ctx.applicationContext
        val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            if (isExternalStorageDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":").toTypedArray()
                val type = split[0]
                if ("primary".equals(type, ignoreCase = true)) {
                    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                        context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).toString() + "/" + split[1]
                    } else {
                        Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                    }
                }

            } else if (isDownloadsDocument(uri)) {
                val id = DocumentsContract.getDocumentId(uri)
                val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
                return getDataColumn(context, contentUri, null, null)
            } else if (isMediaDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":").toTypedArray()
                val type = split[0]
                var contentUri: Uri? = null
                if ("image" == type) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                } else if ("video" == type) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                } else if ("audio" == type) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                }
                val selection = "_id=?"
                val selectionArgs = arrayOf(split[1])
                return getDataColumn(context, contentUri, selection, selectionArgs)
            }
        } else if ("content".equals(uri.scheme, ignoreCase = true)) {

            // Return the remote address
            return if (isGooglePhotosUri(uri)) {
                uri.lastPathSegment
            } else getDataColumn(context, uri, null, null)
        } else if ("file".equals(uri.scheme, ignoreCase = true)) {
            return uri.path
        }
        return null
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context       The context.
     * @param uri           The Uri to query.
     * @param selection     (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     * @author paulburke
     */
    fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String? {
        var cursor: Cursor? = null
        val column = "_data"
        val projection = arrayOf(column)
        try {
            cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
            if (cursor != null && cursor.moveToFirst()) {
                val column_index = cursor.getColumnIndexOrThrow(column)
                return cursor.getString(column_index)
            }
        } catch (ex: IllegalArgumentException) {
            Log.i("Query", String.format(Locale.getDefault(), "getDataColumn: _data - [%s]", ex.message))
        } finally {
            cursor?.close()
        }
        return null
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     * @author paulburke
     */
    fun isMediaDocument(uri: Uri): Boolean {
        return "com.android.providers.media.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    fun isGooglePhotosUri(uri: Uri): Boolean {
        return "com.google.android.apps.photos.content" == uri.authority
    }

    fun bufferCopy(inBuffer: BufferedSource?, outputStream: OutputStream?): Boolean {
        var outBuffer: BufferedSink? = null
        try {
            outBuffer = outputStream?.sink()?.buffer()
            inBuffer?.let {
                outBuffer?.writeAll(it)
            }
            outBuffer?.flush()
            return true
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            inBuffer?.close()
            outBuffer?.close()
        }
        return false
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、前言 图片加载的轮子有很多了,Universal-Image-Loader, Picasso, Glide, ...
    呼啸长风阅读 8,044评论 11 49
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,975评论 2 59
  • 在开发中有时候会用到H5调用本地图片或者相机,像第三方的实名认证,在线客服等等都需要上传图片。H5中只需要通过 调...
    慕涵盛华阅读 27,400评论 9 22
  • 前言:鉴于最近业内bug数量的急剧减少,遂决定写点什么。 概述:iphone相册不支持gif浏览,虽然相册中的gi...
    IThai阅读 1,641评论 0 0
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 126,127评论 2 7