最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Kotlin實(shí)現(xiàn)網(wǎng)絡(luò)圖片下載和保存功能

 更新時(shí)間:2023年02月07日 09:51:55   作者:振華OPPO  
根據(jù)Android多線程和網(wǎng)絡(luò)編程的知識(shí)講解和案例使用,使用Handler消息機(jī)制實(shí)現(xiàn)網(wǎng)絡(luò)圖片下載,并且保存到模擬器中,強(qiáng)化對Android多線程編程、網(wǎng)絡(luò)編程和文件讀寫的理解,這篇文章主要介紹了Kotlin實(shí)現(xiàn)網(wǎng)絡(luò)圖片下載和保存功能,需要的朋友可以參考下

一、理論基礎(chǔ)

  • 掌握Kotlin面向?qū)ο蟮能浖_發(fā)方面的基礎(chǔ)知識(shí)。
  • 鞏固前期Activity、UI控件的使用。
  • 掌握Handler和Http請求的特點(diǎn)及用法。

二、實(shí)驗(yàn)?zāi)康?/h2>

根據(jù)Android多線程和網(wǎng)絡(luò)編程的知識(shí)講解和案例使用,使用Handler消息機(jī)制實(shí)現(xiàn)網(wǎng)絡(luò)圖片下載,并且保存到模擬器中,強(qiáng)化對Android多線程編程、網(wǎng)絡(luò)編程和文件讀寫的理解。要求:

  • 鞏固Android應(yīng)用開發(fā)工具(Android Studio)的常規(guī)用法;
  • 鞏固Activity、UI控件的常規(guī)用法;
  • 掌握Handler的編程要點(diǎn);
  • 掌握HTTP獲取網(wǎng)絡(luò)資源的方法。
  • 掌握文件輸入輸出流的寫法。

三、實(shí)驗(yàn)步驟

1、新建工程文件

首先打開Android Studio,新建Project,命名為WebDownload,Language為Kotlin,Minimum SDK選擇API 22,然后包名就是com.android.webdownload,回車創(chuàng)建成功,等待下載依賴進(jìn)行build。

2、引入布局管理

首先在模塊的build.gradle中加上下面的閉包,然后同步

 buildFeatures {
     viewBinding true
 }

在MainActivity里面先定義變量,

private lateinit var binding: ActivityMainBinding

在onCreate()方法中,添加如下代碼:

 binding = ActivityMainBinding.inflate(layoutInflater)
 setContentView(binding.root)

這下,我們想訪問某個(gè)控件,直接通過binding對象獲取即可,比如:

binding.tvShow.text = "下載成功!"

3、創(chuàng)建布局

外層父容器選擇LinearLayout,內(nèi)部元素對齊方式選擇vertical,從上至下放了一個(gè)ProgressBar,設(shè)置了max為100,進(jìn)度條顯示下載進(jìn)度。接著是一個(gè)TextView,顯示下載信息;ImageView是圖片框,顯示圖片。還有下載圖片和終止下載兩個(gè)按鈕在最底下。里面的textSize和textColor這些屬性就自定義設(shè)置,沒什么可講的。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">
    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:layout_marginBottom="20dp"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
    <TextView
        android:id="@+id/tv_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_margin="10dp"
        android:gravity="center" />
    <ImageView
        android:id="@+id/iv_show"
        android:layout_width="400dp"
        android:layout_height="400dp"
        android:layout_gravity="center"
        android:background="@mipmap/ic_launcher"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/btn_download"
            android:layout_width="0dp"
            android:text="下載圖片"
            android:layout_margin="10dp"
            android:textSize="20sp"
            android:padding="10dp"
            android:layout_height="match_parent"
            android:layout_weight="1"/>
        <Button
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:text="終止下載"
            android:layout_margin="10dp"
            android:textSize="20sp"
            android:padding="10dp"
            android:layout_height="match_parent"
            android:layout_weight="1"/>
    </LinearLayout>
</LinearLayout>

來看下簡單的布局,實(shí)現(xiàn)效果就是點(diǎn)擊下載,開始下載網(wǎng)絡(luò)圖片,下載好后顯示在圖片框上,而且保存到模擬器的存儲(chǔ)空間中,下載過程中可以停止下載(如果你的手速比網(wǎng)速快的前提):

4、訪問權(quán)限

因?yàn)橐l(fā)送網(wǎng)絡(luò)請求,所以需要訪問網(wǎng)絡(luò),因?yàn)橐3謭D片到模擬器,所以要文件讀取,還要有向SD卡中創(chuàng)建或者刪除的權(quán)限。網(wǎng)絡(luò)是不需要?jiǎng)討B(tài)申請的,但后面兩個(gè)權(quán)限需要。

    <!-- 訪問網(wǎng)絡(luò)的權(quán)限 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- 文件讀取的權(quán)限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 向SD卡中創(chuàng)建或者刪除的權(quán)限。 -->
    <uses-permission android:name="andorid.permission.MONUN_UNMOUNT_FILESYSTEMS"/>

動(dòng)態(tài)申請權(quán)限的代碼:

   if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
       ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
   }

在彈出對話框用戶操作之后,返回應(yīng)用權(quán)限請求的結(jié)果。requestCode是請求碼,permissions是權(quán)限列表,grantResults是允許的結(jié)果數(shù)組。運(yùn)用if-else分支完成同意權(quán)限和不同意的邏輯。

override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when(requestCode) {
            1-> {
                if (grantResults.isNotEmpty() &&
                        grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "你可以正常使用app", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "你拒絕了權(quán)限,無法正常保存圖片", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

5、實(shí)現(xiàn)邏輯

首先也是最核心的就是從網(wǎng)絡(luò)上下載圖片,顯然可以用HttpURLConnection或者OkHttp或者其他更好的網(wǎng)絡(luò)框架,總之就是和url地址創(chuàng)建輸入流,讀取文件,保存為bitmap格式返回,然后關(guān)閉輸入流。

    // 下載圖片,轉(zhuǎn)為位圖
    fun getImage(imageUrl: String): Bitmap? {
        var myBitmap:Bitmap? = null
        var connection:HttpURLConnection
        try {
            var url = URL(imageUrl)
            connection = url.openConnection() as HttpURLConnection
            connection.connectTimeout = 8000
            connection.doInput = true
            connection.useCaches = false
            val myInput = connection.inputStream
            myBitmap = BitmapFactory.decodeStream(myInput)
            myInput.close()
        } catch (e:Exception) {
            e.printStackTrace()
        }
        return myBitmap;
    }

接下來是保存圖片到模擬器外部存儲(chǔ),這一點(diǎn)就是文件的IO流,Java中流的知識(shí)太常用了。先定義目錄,如果不存在則創(chuàng)建目錄。try-catch包圍圈里面創(chuàng)建輸出流,保存的地址就是在之前的文件夾路徑基礎(chǔ)上加上了文件名和后綴,然后bitmap按照指定圖像格式進(jìn)行壓縮,最后關(guān)閉輸出流。

// 保存位圖到本地路徑
    private fun saveImage(bitmap:Bitmap?) {
        var file = File(saveDirs)
        // 如果文件不存在則創(chuàng)建目錄
        if (!file.exists()) {
            if (file.mkdir()) {
                Log.d("test", "mkdir")
            } else {
                Log.d("test", "failed")
            }
        }
        try {
            val fileOutputStream = FileOutputStream(savePath)
            bitmap?.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
            fileOutputStream.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

這里的saveImage和savePath要著重講下,不然很容易運(yùn)行后圖片無法保存到本地,原來都是路徑?jīng)]有搞清楚的問題。var file = File(saveDirs)是創(chuàng)建文件夾。savePath才是圖片的保存路徑,如果savePath寫的是"\android\scared\0\storage之類的"那是怎么也訪問不了存儲(chǔ)空間的。

private val saveDirs:String = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()
private val savePath:String = saveDirs + File.separator + System.currentTimeMillis() + ".png"

最后就是在子線程中下載網(wǎng)絡(luò)圖片并使用Handler發(fā)送message消息了,這段邏輯反而是最簡單的部分??梢钥吹綔?zhǔn)備下載前和下載完成后都發(fā)送message。what分別是0和1。

    // 創(chuàng)建下載圖片的子線程,準(zhǔn)備下載前和下載完成后都發(fā)送message
    val thread1 = Thread {
        val message = Message()
        message.what = 0
        handler.sendMessage(message)
        bitmap = getImage(url)
        val message2 = Message()
        message2.what = 1
        handler.sendMessage(message2)
    }

定義全局變量handler來接收自己發(fā)給自己的消息,0那就模擬進(jìn)度條下載進(jìn)度(因?yàn)榫W(wǎng)絡(luò)圖片的進(jìn)度無法通過子線程預(yù)先獲取到,這是后驗(yàn)的)。1就是binding.ivShow.setImageBitmap(bitmap)設(shè)置位圖,文本為"下載成功!",提示消息也為"下載已完成"。

    val handler:Handler = Handler {
        when(it.what) {
            0-> {
                for (i in 0..100) binding.progress.progress = i
            }
            1 -> {
                binding.ivShow.background = null
                binding.ivShow.setImageBitmap(bitmap)
                saveImage(bitmap)
                binding.tvShow.text = "下載成功!"
                Toast.makeText(this, "下載已完成", Toast.LENGTH_SHORT).show()
            }
            else -> Log.d("Test", "else")
        }
        false
    };

開始下載按鈕就是開啟線程,停止下載按鈕就是終止線程。

		binding.btnDownload.setOnClickListener({
            try {
                thread1.start()
            } catch (e:Exception) {
                e.printStackTrace()
            }
        })
        binding.btnStop.setOnClickListener({
            if (thread1.isAlive) {
                thread1.interrupt()
                Toast.makeText(this, "interrupt()", Toast.LENGTH_SHORT).show()
            }
            if (!thread1.isAlive) {
                binding.tvShow.text = "下載終止!"
                Toast.makeText(this, "下載已終止", Toast.LENGTH_SHORT).show()
            }
        })

完整的MainActivity代碼如下:

package com.android.webdownload

import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.*
import androidx.appcompat.app.AppCompatActivity
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.android.webdownload.databinding.ActivityMainBinding

import java.io.File
import java.io.FileOutputStream
import java.lang.Exception
import java.lang.ref.WeakReference
import java.net.HttpURLConnection
import java.net.URL
import java.util.jar.Manifest

class MainActivity : AppCompatActivity() {
    private var bitmap:Bitmap? = null
    private lateinit var binding: ActivityMainBinding
    private val url:String = "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg"
    private val saveDirs:String = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()
    private val savePath:String = saveDirs + File.separator + System.currentTimeMillis() + ".png"
    val handler:Handler = Handler {
        when(it.what) {
            0-> {
                for (i in 0..100) binding.progress.progress = i
            }
            1 -> {
                binding.ivShow.background = null
                binding.ivShow.setImageBitmap(bitmap)
                saveImage(bitmap)
                binding.tvShow.text = "下載成功!"
                Toast.makeText(this, "下載已完成", Toast.LENGTH_SHORT).show()
            }
            else -> Log.d("Test", "else")
        }
        false
    };
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
        }
        binding.btnDownload.setOnClickListener({
            try {
                thread1.start()
            } catch (e:Exception) {
                e.printStackTrace()
            }
        })
        binding.btnStop.setOnClickListener({
            if (thread1.isAlive) {
                thread1.interrupt()
                Toast.makeText(this, "interrupt()", Toast.LENGTH_SHORT).show()
            }
            if (!thread1.isAlive) {
                binding.tvShow.text = "下載終止!"
                Toast.makeText(this, "下載已終止", Toast.LENGTH_SHORT).show()
            }
        })
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when(requestCode) {
            1-> {
                if (grantResults.isNotEmpty() &&
                        grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "你可以正常使用app", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "你拒絕了權(quán)限,無法正常保存圖片", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    // 創(chuàng)建下載圖片的子線程,準(zhǔn)備下載前和下載完成后都發(fā)送message
    val thread1 = Thread {
        val message = Message()
        message.what = 0
        handler.sendMessage(message)
        bitmap = getImage(url)
        val message2 = Message()
        message2.what = 1
        handler.sendMessage(message2)
    }

    // 下載圖片,轉(zhuǎn)為位圖
    fun getImage(imageUrl: String): Bitmap? {
        var myBitmap:Bitmap? = null
        var connection:HttpURLConnection
        try {
            var url = URL(imageUrl)
            connection = url.openConnection() as HttpURLConnection
            connection.connectTimeout = 8000
            connection.doInput = true
            connection.useCaches = false
            val myInput = connection.inputStream
            myBitmap = BitmapFactory.decodeStream(myInput)
            myInput.close()
        } catch (e:Exception) {
            e.printStackTrace()
        }
        return myBitmap;
    }

    // 保存位圖到本地路徑
    private fun saveImage(bitmap:Bitmap?) {
        var file = File(saveDirs)
        // 如果文件不存在則創(chuàng)建目錄
        if (!file.exists()) {
            if (file.mkdir()) {
                Log.d("test", "mkdir")
            } else {
                Log.d("test", "failed")
            }
        }
        try {
            val fileOutputStream = FileOutputStream(savePath)
            bitmap?.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
            fileOutputStream.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

四、實(shí)驗(yàn)演示

1、首先看到啟動(dòng)頁面就是下載頁面,頂部的進(jìn)度條和TextView并沒有顯示出來,因?yàn)闆]有內(nèi)容。

2、點(diǎn)擊下載圖片按鈕,圖片很快下載完成,并且頂部進(jìn)度條也加載完畢了,圖片下載好后顯示在屏幕中央。在下載過程中可以停止下載,不過由于下載過快,根本來不及停止。

3、打開文件,選擇Android SDK built for x86

選擇Pictures

可以看到剛剛下載好的圖片

點(diǎn)擊全圖查看

五、實(shí)驗(yàn)總結(jié)

總體上來說還是非?;A(chǔ)的內(nèi)容,考察的點(diǎn)不多,可以作為學(xué)習(xí)Kotlin的點(diǎn)心食用,中間需要注意的小地方還是有的。越是簡單的東西遇到的問題越多,多練習(xí)才是王道。

腳本之家本地下載

源代碼已上傳GitHub,點(diǎn)擊下載源代碼

到此這篇關(guān)于Kotlin實(shí)現(xiàn)網(wǎng)絡(luò)圖片下載和保存功能的文章就介紹到這了,更多相關(guān)Kotlin網(wǎng)絡(luò)圖片下載和保存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android使用CardView實(shí)現(xiàn)圓角對話框

    Android使用CardView實(shí)現(xiàn)圓角對話框

    這篇文章主要為大家詳細(xì)介紹了Android使用CardView實(shí)現(xiàn)圓角對話框,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Kotlin協(xié)程的基礎(chǔ)與使用示例詳解

    Kotlin協(xié)程的基礎(chǔ)與使用示例詳解

    這篇文章主要為大家介紹了Kotlin協(xié)程的基礎(chǔ)與使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • android實(shí)現(xiàn)拖拽裁剪功能

    android實(shí)現(xiàn)拖拽裁剪功能

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)拖拽裁剪功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • android獲取監(jiān)聽SD Card狀態(tài)的方法

    android獲取監(jiān)聽SD Card狀態(tài)的方法

    這篇文章主要介紹了android獲取監(jiān)聽SD Card狀態(tài)的方法,涉及Android實(shí)現(xiàn)SD Card監(jiān)聽的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • 動(dòng)態(tài)添加LinearLayout的高度實(shí)例

    動(dòng)態(tài)添加LinearLayout的高度實(shí)例

    下面小編就為大家?guī)硪黄獎(jiǎng)討B(tài)添加LinearLayout的高度實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • Android實(shí)現(xiàn)加載對話框

    Android實(shí)現(xiàn)加載對話框

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)加載對話框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • 詳解Android MVP開發(fā)模式

    詳解Android MVP開發(fā)模式

    這篇文章主要為大家介紹了Android MVP開發(fā)模式的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • 安卓Android Context類實(shí)例詳解

    安卓Android Context類實(shí)例詳解

    在開發(fā)Android的過程中,總是能遇見Context類或者它的實(shí)例.Context類的實(shí)例經(jīng)常被用來提供“應(yīng)用程序”的引用,下面舉例說明Contex類實(shí)例詳解
    2016-07-07
  • 淺析SVN在Android Studio中的安裝和配置方法

    淺析SVN在Android Studio中的安裝和配置方法

    這篇文章主要介紹了SVN在Android Studio中的安裝和配置方法,本文通過圖文并茂給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Android自定義View實(shí)現(xiàn)鐘擺效果進(jìn)度條PendulumView

    Android自定義View實(shí)現(xiàn)鐘擺效果進(jìn)度條PendulumView

    這篇文章主要介紹了Android自定義View實(shí)現(xiàn)鐘擺效果進(jìn)度條PendulumView,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09

最新評論

宁陕县| 三台县| 中卫市| 高碑店市| 贵南县| 洞口县| 班玛县| 林西县| 巢湖市| 罗江县| 剑川县| 望奎县| 淮北市| 桐乡市| 长岛县| 五指山市| 菏泽市| 乐至县| 临夏市| 大丰市| 巨野县| 漯河市| 紫金县| 湘潭县| 广州市| 雷州市| 平远县| 英德市| 荔波县| 岑巩县| 工布江达县| 江华| 灵寿县| 同心县| 安福县| 固原市| 来凤县| 无为县| 家居| 渝北区| 上饶市|