이전 블로그에서 가져온 글입니다. (2019.9.30)
이번에는 안드로이드를 통해 API를 불러오는 작업을 함.
이번에 한일은 json형태의 데이터를 불러와 안드로이드의 list형식으로 받아오는 작업이다.

필요한 정보 : 주소 (address), 번지 (bunji) , 도로명 (streetAddress)
그 전에 안드로이드의 비동기처리 (AsyncTask)에 대한 정리가 필요하다
AsyncTask란?
안드로이드에서는 기본적으로 ui작업을 처리하는데 있어 하나의 메인스레드(ui 스레드)만 사용한다. 즉 동시에 여러 ui이벤트를 담당하지 못한다. 이러한 스레드의 작업을 좀더 효율적으로 작업하기 위해 AsyncTask (비동기 처리)가 사용된다.
간단히 정리하자면 백그라운드에서 작업을 처리하고 메인에 올리는 방식이다.
AsyncTask작업 순서

AsyncTask는 메인 스레드에서 호출되며, 오로지 한개만 존재할 수 있으니 주의
- 메인스레드에서 execute()를 통해 AsyncTask 호출
- onPreExecute() : 백그라운드에서 작업이 진행되기 전에 필요한 ui 작업들을 세팅
- doInBackground() : 백그라운드에서 작업이 돌아간다. 그리고 중간중간 publishProgress()를 수행함으로서 현재 상태들을 업데이트 (onProgressUpdate()).
- onPostExecute() : 백그라운드에서 작업이 완료된 결과들을 return.
실습
API를 불러오기 위해 우선 manifest 파일에 아래 코드를 추가해준다
<uses-permission android:name="android.permission.INTERNET"/>
최상단의 오른쪽과 같은 형태로 데이터를 받아오기 위해 db_list.xml을 하나 생성해준다.
<!--db_list.xml-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="10"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/db_addr"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="5"
android:gravity="center_vertical"
android:textSize="15dp"
android:textColor="#3392ff"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/db_bunji"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="5"
android:gravity="center_vertical"
android:textSize="15dp"
android:textColor="#3392ff"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/db_street"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="5"
android:gravity="center_vertical"
android:textSize="10dp"
android:textColor="#3392ff"/>
</LinearLayout>
</LinearLayout>
※ 보통은 TextView를 쓰지만 AppCompatTextView를 사용한 이유는 몇몇 라이브러리가 textview에 지원되지 않아 경고를 없애고자 사용
추가로 list형태로 출력하기 위해 activity_main.xml에 listview하나만 추가해주자
<!--activity_main.xml-->
<?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"
android:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/data_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
xml은 여기서 끝
데이터를 받아올 예정이니 그 데이터들을 저장할 곳을 마련해주자. Address_data.kt는 말그대로 데이터만 저장할 장소
//ListAdapter.kt
package com.example.async_prc
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import androidx.appcompat.widget.AppCompatTextView
class ListAdapter(val context: Context, val list:ArrayList<Address_data>): BaseAdapter(){
override fun getView(position:Int, convertView: View?, parent:ViewGroup?):View{
val view:View= LayoutInflater.from(context).inflate(R.layout.db_list,parent,false)
val val_addr=view.findViewById(R.id.db_addr) as AppCompatTextView
val val_street=view.findViewById(R.id.db_street) as AppCompatTextView
val val_bunji=view.findViewById(R.id.db_bunji) as AppCompatTextView
val_addr.text=list[position].address
val_street.text=list[position].streetAddress
val_bunji.text=list[position].bunji
return view
}
override fun getItem(position: Int): Any {
return list[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return list.size
}
}
BaseAdapter()는 ListView를 사용하는데 있어 필요한 기본적인 기능을 제공해준다.
1. getView()
view를 나타낸다
- position : view의 위치
- contextView : 값이 null이면 view를 생성, 아니면 view를 return
- parent : return할 view의 부모 view
2. getItem()
연결된 arrayList의 특정 위치에 있는 아이템을 가져오는 역할
3. getItemId()
getItem()으로 가져온 item의 아이디 세팅
4. getCount()
화면에 표시해야하는 데이터의 개수 반환
마지막으로 MainActivity.kt코드. 여기서는 API를 불러와 json을 파싱하는 작업을 해준다.
//MainActivity.kt
package com.example.async_prc
import android.content.Context
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val url="https://manage-dev.rsquare.co.kr/api/sample/buildings"
//백그라운드 작업을 수행하면서 필요한 경우, 결과를 메인 스레드에서 실행하므로 ui 객체에 접근 가능
AsyncTaskHandleJson().execute(url)
}
inner class AsyncTaskHandleJson: AsyncTask<String, String, String>(){
override fun doInBackground(vararg url: String?): String {
var text:String
val connection= URL(url[0]).openConnection() as HttpURLConnection
try{
connection.connect()
text=connection.inputStream.use{
it.reader().use{
reader->reader.readText()
}
}
}
finally{
connection.disconnect()
}
return text
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
handleJson(result)
}
}
private fun handleJson(jsonString:String?){
val jsonArray=JSONArray(jsonString)
val list=ArrayList<Address_data>()
var x=0
while(x<jsonArray.length()){
val jsonObject=jsonArray.getJSONObject(x)
list.add(Address_data(
jsonObject.getString("address"),
jsonObject.getString("bunji"),
jsonObject.getString("streetAddress")
))
x++
}
val adapter=ListAdapter(this,list)
data_list.adapter=adapter
}
}
AsyncTask도 저어어어어어 위에서 설명했으니 이제 끝인가...
handleJson함수는 말그대로 json을 파싱해주는 녀석. 해당 태그 (address, bunji, streetAddress)에 속한 값들을 불러와 list에 추가해주고 그 값들을 이제 ListAdapter를 통해 얻고자 했던 형식으로 값들을 저장해주고 출력을 뙇!!
ㅇㅋ 길었다