2019-11-23

【Kotlin】Volley -- 輕量 httpclient

參考資料 ----
Volley overview  

github/google/volley


VolleyJetpack 的一員,能讓開發者以簡單的方式寫出與 httpclient 相同功能的程式,並且速度更快。不過 Volley 只適合輕量的網路傳輸,所以諸如檔案的下載或串流的操作,仍建議以原有的方式執行,如:DownloadManager


AndroidManifest.xml 加入網路權限

 
<uses-permission android:name="android.permission.INTERNET" />

<application
    ...
    ...
 



app 層級的 build.gradle 加入相關宣告
 
...
...

dependencies {
    ...
    implementation 'com.android.volley:volley:1.1.1'
}
 


activity_main.xml
 
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="96dp"
        android:text="Button"
        app:layout_constraintBottom_toTopOf="@+id/textView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.482"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.07"
        android:onClick="sendrequest"/>

</androidx.constraintlayout.widget.ConstraintLayout>
 


MainActivity.kt(最陽春的範例)
 

import android.app.Activity
import android.os.Bundle
import android.view.View
import android.widget.TextView
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley

class MainActivity : Activity()
{
    val TAG = "MyTag"


    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }


    fun sendrequest(v: View)
    {
        val queue = Volley.newRequestQueue(this)
        val url = "https://您的網址/php程式.php"
        val textView = findViewById(R.id.textView)
        val stringRequest = StringRequest(Request.Method.GET, 
                              url,
                              Response.Listener<String> { response -> textView.text = response.toString()},
                              Response.ErrorListener { error ->
                                  Log.d(TAG, "volley error: %s".format(error.toString()))
                                  textView.text = "volley error" 
                              }
                             )

        // Add the request to the RequestQueue.
        queue.add(stringRequest)
    }
}
 


app 執行後,一按 button,就會看到 TextView 顯示回傳的字串,超簡單的!





Google 仍強烈建議不要在主執行緒(UI thread) 執行。


方法二


新增一個 VolleyService.kt
 
package 完整packagename

import android.content.Context
import com.android.volley.RequestQueue
import com.android.volley.toolbox.Volley


object VolleyService
{
    private lateinit var context: Context
    val requestQueue: RequestQueue by lazy { Volley.newRequestQueue(context) }
    

    fun initialize(context: Context) 
    {
        this.context = context.applicationContext
    }
}
 


再新增一個 Application 級的 class:App.kt
 
package 完整packagename

import android.app.Application


class App : Application()
{
    override fun onCreate()
    {
        super.onCreate()
        VolleyService.initialize(this)
    }
}
 


微調一下 AndroidManifest.xml
 
...
...

<uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name=".App"
        ...
        ...
 


再微調 MainActivity.kt
 
...
...

fun sendrequest(v: View)
{
    val url = "https://網址/php程式"
    val textView = findViewById(R.id.textView)
    // Request a string response from the provided URL.
    val stringRequest = StringRequest(Request.Method.GET, url,
                                      Response.Listener<string> { response -> textView.text = response.toString() },
                                      Response.ErrorListener { textView.text = "That didn't work!" }
                                     )

    // 呼叫 VolleyService
    VolleyService.requestQueue.add(stringRequest)
    VolleyService.requestQueue.start()
}
 


Volley 也可以直接取得 json



在本例中,自網站取得的是一個 json 陣列,陣列元素組成為 exno, exname, exdate 三個字串,改用 ListView 顯示

activity_main.xml
 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginStart="32dp"
        android:layout_marginLeft="32dp"
        android:text="get json"
        android:onClick="getjson" />

    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/button"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp" />

</RelativeLayout>
 


新增 list_item.xml
 
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="6dp"
    android:paddingLeft="6dp"
    android:paddingRight="6dp"
    android:paddingTop="6dp" >

    <TextView
        android:id="@+id/lblExno"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="exno"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:visibility="gone"/>
    
    <TextView
        android:id="@+id/lblExname"
        android:layout_below="@+id/lblExno"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="exam_name"
        android:textColor="@android:color/holo_blue_dark"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/lblExdate"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/lblExname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="exam_date"
        android:textColor="@android:color/holo_blue_dark"
        android:textAppearance="?android:attr/textAppearanceSmall" />
    
</RelativeLayout>
 


新增 ListAdapter.kt
 
...
...

class ListAdapter(private val activity: Activity, private val mExno: List<*>, private val mExname: List<*>, private val mExdate: List<*>) : BaseAdapter()
{

    init
    {
        inflater = activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    }

    override fun getCount(): Int
    {
        return mExno.size
    }

    override fun getItem(position: Int): Any
    {
        return position
    }

    override fun getItemId(position: Int): Long
    {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View
    {
        var vi = convertView
        if (convertView == null)
        {
            vi = inflater!!.inflate(R.layout.list_item, null)
        }

        val lblExno = vi!!.findViewById<view>(R.id.lblExno) as TextView
        val lblExname = vi.findViewById<view>(R.id.lblExname) as TextView
        val lblExdate = vi.findViewById<view>(R.id.lblExdate) as TextView

        lblExno.text = mExno[position].toString()
        lblExname.text = mExname[position].toString()
        lblExdate.text = mExdate[position].toString()

        return vi
    }

    companion object
    {
        private var inflater: LayoutInflater? = null
    }
}
 


MainActivity.kt
 
...
...

class MainActivity : Activity()
{
    val TAG = "MyTag"

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStop()
    {
        super.onStop()
        VolleyService.requestQueue?.cancelAll(TAG)
    }

    fun getjson(v: View)
    {
        val lv = findViewById(R.id.lv) as ListView
        val url = "https://網址/php程式"
        val stringRequest = JsonArrayRequest(Request.Method.GET, url, null,
                                             Response.Listener<JSONArray>
                                             {
                                                 response -> var listExno: MutableList<String> = ArrayList()
                                                             var listExname: MutableList<String> = ArrayList()
                                                             var listExdate: MutableList<String> = ArrayList()
                                                             for(x in 0..(response.length()-1))
                                                             {
                                                                 var exam = response.getJSONObject(x)
                                                                 listExno.add(exam.getString("exno"))
                                                                 listExname.add(exam.getString("exname"))
                                                                 listExdate.add(exam.getString("exdate"))
                                                             }
                                                 val adapterExam = ListAdapter(this@MainActivity, listExno, listExname, listExdate)
                                                 lv.setAdapter(adapterExam)
                                             },
                                             Response.ErrorListener
                                             {
                                                 Toast.makeText(this, "That didn't work!", Toast.LENGTH_SHORT).show()
                                             }
                                            )

        // 呼叫 VolleyService
        VolleyService.requestQueue.add(stringRequest)
        VolleyService.requestQueue.start()

    }
}