欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

一、理論基礎(chǔ)

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

二、實驗?zāi)康?/h2>

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

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

三、實驗步驟

1、新建工程文件

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

2、引入布局管理

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

 buildFeatures {
     viewBinding true
 }

在MainActivity里面先定義變量,

private lateinit var binding: ActivityMainBinding

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

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

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

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

3、創(chuàng)建布局

外層父容器選擇LinearLayout,內(nèi)部元素對齊方式選擇vertical,從上至下放了一個ProgressBar,設(shè)置了max為100,進度條顯示下載進度。接著是一個TextView,顯示下載信息;ImageView是圖片框,顯示圖片。還有下載圖片和終止下載兩個按鈕在最底下。里面的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>

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

4、訪問權(quán)限

因為要發(fā)送網(wǎng)絡(luò)請求,所以需要訪問網(wǎng)絡(luò),因為要保持圖片到模擬器,所以要文件讀取,還要有向SD卡中創(chuàng)建或者刪除的權(quán)限。網(wǎng)絡(luò)是不需要動態(tài)申請的,但后面兩個權(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"/>

動態(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ù)組。運用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、實現(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;
    }

接下來是保存圖片到模擬器外部存儲,這一點就是文件的IO流,Java中流的知識太常用了。先定義目錄,如果不存在則創(chuàng)建目錄。try-catch包圍圈里面創(chuàng)建輸出流,保存的地址就是在之前的文件夾路徑基礎(chǔ)上加上了文件名和后綴,然后bitmap按照指定圖像格式進行壓縮,最后關(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要著重講下,不然很容易運行后圖片無法保存到本地,原來都是路徑?jīng)]有搞清楚的問題。var file = File(saveDirs)是創(chuàng)建文件夾。savePath才是圖片的保存路徑,如果savePath寫的是"\android\scared\0\storage之類的"那是怎么也訪問不了存儲空間的。

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消息了,這段邏輯反而是最簡單的部分??梢钥吹綔蕚湎螺d前和下載完成后都發(fā)送message。what分別是0和1。

    // 創(chuàng)建下載圖片的子線程,準備下載前和下載完成后都發(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那就模擬進度條下載進度(因為網(wǎng)絡(luò)圖片的進度無法通過子線程預(yù)先獲取到,這是后驗的)。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)建下載圖片的子線程,準備下載前和下載完成后都發(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()
        }
    }
}

四、實驗演示

1、首先看到啟動頁面就是下載頁面,頂部的進度條和TextView并沒有顯示出來,因為沒有內(nèi)容。

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

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

選擇Pictures

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

點擊全圖查看

五、實驗總結(jié)

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

腳本之家本地下載

源代碼已上傳GitHub,點擊下載源代碼

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

相關(guān)文章

最新評論