안드로이드

안드로이드 : 영화 소개 페이지 웹 크롤링

ChloeLee 2020. 11. 5. 20:10

영화 소개 페이지 웹 크롤링

http://cyberadam.cafe24.com/movie/list?page=1

 

page 는 페이지 번호

데이터 구조: Json

객체 

    count 속성 : 전체 데이터 개수

    list 속성 : 데이터 배열

    객체 movieid, title, subtitle, director actor genre rating thumbnail link

    이미지 파일  http://cyberadam.cafe24.com/movieimage/썸네일 

{"count":4262,"list":[{"movieid":4264,"title":"바이러스 격리구역","subtitle":"Infected","pubdate":"2013","director":"필립 매시즈위츠|","actor":"딜라란 마틴|보 린튼|유지니아 쿠즈미나|아드리안 부|아디아 딘|니나 케이트|","genre":"공포","rating":10.0,"thumbnail":"REP_02310042540_1_1_0138.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=104605"},{"movieid":4263,"title":"쥬라기공원 : 다이노어택","subtitle":"Jurassic Attack","pubdate":"2013","director":"안소니 팽크하우저|","actor":"조던 로슨|코린 네멕|베론 웰스|게리 스트레치|","genre":"SF/판타지,공포,액션/모험","rating":10.0,"thumbnail":"REP_02310042015_1_1_0235.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=103735"},{"movieid":4262,"title":"영웅본색 2 HD","subtitle":null,"pubdate":null,"director":null,"actor":null,"genre":"드라마,액션/모험","rating":10.0,"thumbnail":"REP_02310042053_1_1_0219.jpg","link":"http://swiftapi.rubypaper.co.kr:2029/hoppin/detailView?movieId=P00000529234"},{"movieid":4261,"title":"페인킬러 엑스","subtitle":"Painkiller","pubdate":"2012","director":"베레니카 매시즈위츠|","actor":"마이크 파프|엘로디 하라|론 푸실로|딜라란 마틴|","genre":"액션/모험,스릴러","rating":0.0,"thumbnail":"REP_02310041823_1_1_0240.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=104607"},{"movieid":4260,"title":"스페셜 포스 : 특수부대 전랑","subtitle":"Wolf War","pubdate":"2015","director":"오경|","actor":"오경|위 난|조문탁|등자의|스콧 앳킨스|","genre":"액션/모험","rating":0.0,"thumbnail":"REP_02310039402_1_1_0839.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=122112"},{"movieid":4259,"title":"스파이 : 작전명 태풍","subtitle":"The Operation Typhoon","pubdate":"2013","director":"페트르 아멜린|","actor":"에두아르드 트루크메뇨프|마리아 쿨리코바|세르게이 바탈로프|미하일 예리세예브|","genre":"액션/모험","rating":10.0,"thumbnail":"REP_02310041598_1_1_0839.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=148679"},{"movieid":4258,"title":"데몬킹스 (자막)","subtitle":null,"pubdate":null,"director":null,"actor":null,"genre":"애니메이션,액션/모험","rating":10.0,"thumbnail":"REP_02310040479_1_1_0539.jpg","link":"http://swiftapi.rubypaper.co.kr:2029/hoppin/detailView?movieId=P00000529992"},{"movieid":4257,"title":"감독 미카엘 하네케","subtitle":"Michael H. Profession: Director","pubdate":"2013","director":"이브 몽마외르|","actor":"줄리엣 비노쉬|베아트리스 달|미카엘 하네케|이자벨 위페르|","genre":"다큐멘터리","rating":10.0,"thumbnail":"REP_02310036759_1_1_1057.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=106839"},{"movieid":4256,"title":"댐999 : 거대한 물폭탄","subtitle":"Dam999","pubdate":"2011","director":"소한 로이|","actor":"조슈아 프레드릭 스미스|비말라 라만|멕하 버만|","genre":"드라마,액션/모험","rating":0.0,"thumbnail":"REP_02310042164_1_1_0245.jpg","link":"https://movie.naver.com/movie/bi/mi/basic.nhn?code=89375"},{"movieid":4255,"title":"데몬킹스 (더빙)","subtitle":null,"pubdate":null,"director":null,"actor":null,"genre":"애니메이션,액션/모험","rating":5.5,"thumbnail":"REP_02310040479_1_1_0539.jpg","link":"http://swiftapi.rubypaper.co.kr:2029/hoppin/detailView?movieId=P00000529684"}]}

 

 

1. 프로젝트 기본 설정

1) build.gradle 파일을 열어서 플러그 인을 설치 >  id 'kotlin-android-extensions'

2) Web 에서 다운로드를 받아야 하므로 AndroidManifest.xml 에 INTERNET 권한을 설정

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

 

3) 웹 서버가 https 프로토콜을 사용하지 않는 경우에는 application 태그에 useCleartraffictext 속성을 true 로 설정해야 통신이 가능합니다.

android:usesCleartextTraffic="true"

 

4) activity_main.xml 파일에 기본 화면 구성을 설정 TextView 와 ListView 1 개를 배치

 

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"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="MOVIE"
        android:textSize="32sp"
        android:gravity="center"
        android:background="@color/teal_200"
        />

    <ProgressBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/downloadview"/>
    
    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/listview"/>
</LinearLayout>

 

2. 웹 주소에서 문자열 다운로드 받기

1) 가져온 데이터를 저장할 DTO 클래스 만들기 : Movie

 

Movie.kt

package kr.co.tjoeun.movie

class Movie {
    var movieid:Int? = null
    var title:String? = null
    var subtitle:String? = null
    var rating:String? = null
    var thumbnail:String? = null
    var link:String? = null

    // 디버깅을 편리하게 하기 위해서 toString 생성
    override fun toString(): String {
        return "Movie(movieid=$movieid, title=$title, subtitle=$subtitle, rating=$rating, thumbnail=$thumbnail, link=$link)"
    }
}

 

2) 다운로드 받은 문자열을 저장할 프로퍼티를 Activity 클래스에 선언

var json : String = ""

var json : String? =  null

    inner class MovieThread : Thread(){
        override fun run() {
            // URL 을 생성
            var url: URL = URL("http://cyberadam.cafe24.com/movie/list")
            // 연결 객체 생성
            var con:HttpURLConnection = url.openConnection() as HttpURLConnection
            // 옵션 설정
            con.requestMethod = "GET" // 전송 방식은 GET
            con.connectTimeout = "30000" // 최대 접속 시도 시간
            con.useCaches = false // 캐시 사용 여부 - 자주 변경되는 데이터는 false

            // 다운로드 받기 위한 스트림 생성
            val br:BufferedReader = BufferedReader(InputStreamReader(con.inputStream))
            json = ""

            while (true) {
                // 한 줄을 읽습니다.
                val line = br.readLine()
                // 읽은 데이터가 없으면 종료
                if(line == null)
                    break
                // 읽은게 있으면 json 추가
                json = json + line + "\n"
            }
            //연결 객체 정리
            br.close()
            con.disconnect()
            Log.e("json", json!!)
        }
    }

3) onCreate 에 실행할 스레드 시작

        MovieThread().start()

 

JSON Parsing

JSON : 자바스크립트 데이터 표현법으로 데이터를 표시하는 포맷

[] : 배열 - Array

{} : 객체 - Object

:: 속성과 값을 구분

, : 속성과 속성, 객체와 객체를 구분

 

1) JSON 문자열이 배열인지 객체인지 확인한 후

val 루트 = JSONArray(문자열) -> 객체라면 JSONObject 로 하면 됩니다.

 

2) 객체의 경우는 

get자료형("속성이름")

배열이 있으면 getJSONArray, 문자열이 있으면 getString, 정수는 getInt, 실수는 getDouble

 

3) 배열의 경우는 

var i = 0

while(i < 배열이름.length){

    val ? = 배열이름.get자료형(i)

    // 객체인지 아니면 일반 데이터인지 확인해서  get 작업을 수행 

    i= i+ 1

[1,2,3] => [{},{},{}]

 

 

3. JSON Parsing을 수행해서 데이터를 원하는 포맷으로 생성

 

1) 파싱한 결과를 저장할 프로퍼티를 Activity 클래스에 선언

    // 파싱한 결과를 저장할 프로퍼티
    var count : Int? = null // 데이터 개수 저장 
    var movieList : MutableList<Movie>? = null // 데이터 목록 저장

 

 

2) 다운로드 받은 스레드의 코드 하단에 파싱하는 코드를 추가

// 다운로드 받은 데이터가 있다면
            if(json!!.length > 0) {
                val root = JSONObject(json) // 객체로 변환
                // count 키의 값을 정수로 가져오기
                count = root.getInt("count")
                // list 키의 값 배열로 가져오기
                val ar = root.getJSONArray("list")
                // 배열 순회
                var i = 0
                while (i< ar.length()){
                    // 순서대로 객체 가져오기
                    val obj = ar.getJSONObject(i)

                    // 객체의 데이터를 읽어서 movieList 에 삽입
                    val movie = Movie()
                    movie.movieid = obj.getInt("movieid")
                    movie.title = obj.getString("title")
                    movie.subtitle = obj.getString("subtitle")
                    movie.rating = obj.getString("rating")
                    movie.thumbnail = obj.getString("link")

                    movieList!!.add(movie)

                    i = i + 1
                }
            }
            Log.e("movieList", movieList.toString())
            Log.e("count", count.toString())

 

3) onCreate 메소드에서 movieList 를 초기화를 해주어야 합니다.

        // 데이터 초기화
        movieList = mutableListOf<Movie>()


AdapterView

데이터를 공급 받아서 출력하는 뷰

ListView, RecyclerView, Spinner, Grid 등이 있습니다.

데이터 목록과 Adapter 그리고 View 가 있어야 합니다.

데이터를 Adapter 에 연결하고 출력 모양도 Adapter 가 결정하고 View 는 Adapter 로 부터 View 를 공급받아서 출력만 담당합니다.

데이터에 변경이 생기면 Adapter 가 View 에게 알려주기만 하면 자동으로 출력이 업데이트 됩니다.

 

4. ListView 에 movieList 출력하기 

1) Adapter 프로퍼티를 Activity 클래스에 추가 

    // Listview 에 데이터를 주입할 Adapter 프로퍼티 생성
    var movieAdapter : ArrayAdapter<Movie>? = null

2) onCreate 메소드에서 데이터와 adapter 그리고 listview 를 연결하는 코드 추가

        // 데이터 초기화
        movieList = mutableListOf<Movie>()
        // 어댑터 생성 - 기본 어댑터
        // 첫번째 매개 변수는 Context - Activity
        // 두번째 매개 변수는 셀의 모양 - android.R.layout 으로 시작하는 것은
        // 안드로이드가 제공해주는 모양
        // 세번째 매개 변수는 출력할 데이터 - 배열 아니면 List
        movieAdapter = ArrayAdapter(
            this, android.R.layout.simple_list_item_1, movieList!!)

        // listview 와 adapter 연결
        listview.adapter = movieAdapter

3) listview 의 데이터를 업데이트 할 수 있는 핸들러를 생성

    // Listview 를 업데이트 해주는 핸들러를 생성
    val handler = object:Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            // listview 업데이트
            movieAdapter!!.notifyDataSetChanged()
            // 프로그래스 중지 - 영역도 없앰앰
           downloadview.visibility = View.GONE
        

4) 핸들러에게 메시지를 전송해서 handleMessage를 호출

            // 핸들러에게 메시지를 전송해서 handleMessage를 호출
            handler.sendEmptyMessage(0)

 

ListView 에서 사용자 정의 셀을 사용하기

1) Layout 파일로 셀의 모양을 생성 

 

2) Adapter를 상속 받는 클래스를 만들어서 필요한 메소드를 오버라이딩 해서 셀의 모양과 출력할 데이터를 연결

 

3) AdapterView 에 새로 생성한 Adapter 의 하위 클래스의 인스턴스를 설정

 

5. ListView 에 사용자 정의 뷰를 설정

1) 사용자 정의 셀의 모양을 Layout 파일로 생성 - moviecell.xml

 

moviecell.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="match_parent"
    android:orientation="horizontal">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="8"
        android:orientation="vertical">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:textSize="20sp"
            android:id="@+id/movietitle"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:textSize="15sp"
            android:id="@+id/moviesubtitle"/>
        <RatingBar
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:numStars="5"
            android:id="@+id/movieration"
            android:isIndicator="true"/>
    </LinearLayout>

    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:id="@+id/movieiamge"/>
</LinearLayout>

 

2) Base Adapter로 부터 상속 받는 Adapter 클래스 생성 : Movie Adapter.kt

Movie Adapter.kt

package kr.co.tjoeun.movie

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.RatingBar
import android.widget.TextView
import kotlinx.android.synthetic.main.moviecell.view.*

class MovieAdapter<T> (var context:Context, var data:MutableList<Movie>, var layout:Int) :BaseAdapter(){


        // 남겨 받는 데이터 레이아웃 ID를 부러 만들어 줄 inflater
        var inflater: LayoutInflater
        // 초기 화 메소드
        init {
            //Layout 파일을 전개 할 수 있는 LayoutInflater 생성
            inflater = context.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE
            ) as LayoutInflater
        }




    // 실제로 출력될 뷰를 설정하는 메소드
    override fun getView(posision:Int, convertView: View?, parent: ViewGroup): View? {
        // 코틀린에서는 매개 변수가 val 이라서 수정을 못함
        // 다른 변수에 넣고 수정을 합니다.
        // convertView 는 재사용 가능한 뷰
        var returnView = convertView
        // 재사용 가능 뷰가 없으면 생성
        if(returnView == null)
            // layout 파일을 전개
            // 첫번째 매개 변수는 Layout 의 아이디
            // 두번째 매개 변수는 부모 뷰
            // 세번째 매개 변수는 루트 여부
            returnView = inflater.inflate(layout, parent, false)

        // 타이틀 출력
        var titleview = returnView?.findViewById<View>(R.id.movietitle) as TextView
        titleview.text = data[posision].title

        // 서브 타이틀 출력
        var subtitleview = returnView?.findViewById<View>(R.id.moviesubtitle) as TextView
        subtitleview.text = data[posision].subtitle

        // 별점 출력
        var ratingview = returnView?.findViewById<View>(
            R.id.movieration) as RatingBar
        ratingview.rating = (data[posision].rating!! / 5).toFloat()

        // 출력할 뷰 리턴
        return returnView
    }

    // 셀의 이름을 설정
    override fun getItem(posision:Int): String? {
        return data[posision].title
    }

    // 셀을 구분하기 위한 숫자를 설정
    override fun getItemId(posision: Int): Long {
        return posision.toLong()
    }

    override fun getCount(): Int {
        //여기서 리턶는 숫자만큼 셀이 생성
        return data.size
    }
}

3) Activity Adapter 클래스에서 프로퍼티 생성

// Listview 에 데이터를 주입할 Adapter 프로퍼티 생성
//var movieAdapter : ArrayAdapter<Movie>? = null
var movieAdapter : MovieAdapter<Movie>? = null

MainActivity.kt

package kr.co.tjoeun.movie

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import android.widget.ArrayAdapter
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject

class MainActivity : AppCompatActivity() {
    // 파싱한 결과를 저장할 프로퍼티
    var count : Int? = null // 데이터 개수 저장
    var movieList : MutableList<Movie>? = null // 데이터 목록 저장
    var msg : String = ""

    // Listview 에 데이터를 주입할 Adapter 프로퍼티 생성
    //var movieAdapter : ArrayAdapter<Movie>? = null
    var movieAdapter : MovieAdapter<Movie>? = null


    // 네트워크 기능은 스레드에서 수행해야 합니다.
    inner class MovieThread : Thread(){
        override fun run() {
            // URL 을 생성
            var url: URL = URL("http://cyberadam.cafe24.com/movie/list")
            // 연결 객체 생성
            var con:HttpURLConnection = url.openConnection() as HttpURLConnection
            // 옵션 설정
            con.requestMethod = "GET" // 전송 방식은 GET
            con.connectTimeout = 30000 // 최대 접속 시도 시간
            con.useCaches = false // 캐시 사용 여부 - 자주 변경되는 데이터는 false

            // 다운로드 받기 위한 스트림 생성
            val br:BufferedReader = BufferedReader(InputStreamReader(con.inputStream))
            msg = ""

            while (true) {
                // 한 줄을 읽습니다.
                val line = br.readLine()
                // 읽은 데이터가 없으면 종료
                if(line == null)
                    break
                // 읽은게 있으면 json 추가
                msg = msg + line + "\n"
            }
            //연결 객체 정리
            br.close()
            con.disconnect()
            Log.e("json", msg!!)

            // 다운로드 받은 데이터가 있다면
            if(msg!!.length > 0) {
                val root = JSONObject(msg) // 객체로 변환
                // count 키의 값을 정수로 가져오기
                count = root.getInt("count")
                // list 키의 값 배열로 가져오기
                val ar = root.getJSONArray("list")
                // 배열 순회
                var i = 0
                while (i< ar.length()){
                    // 순서대로 객체 가져오기
                    val obj = ar.getJSONObject(i)

                    // 객체의 데이터를 읽어서 movieList 에 삽입
                    val movie = Movie()
                    movie.movieid = obj.getInt("movieid")
                    movie.title = obj.getString("title")
                    movie.subtitle = obj.getString("subtitle")
                    movie.rating = obj.getDouble("rating")
                    movie.thumbnail = obj.getString("link")

                    movieList!!.add(movie)

                    i = i + 1
                }
            }
            Log.e("movieList", movieList.toString())
            Log.e("count", count.toString())
            // 핸들러에게 메시지를 전송해서 handleMessage를 호출
            handler.sendEmptyMessage(0)
        }
    }

    // Listview 를 업데이트 해주는 핸들러를 생성
    val handler = object:Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            // listview 업데이트
            movieAdapter!!.notifyDataSetChanged()
            // 프로그래스 중지 - 영역도 없앰앰
           downloadview.visibility = View.GONE
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 데이터 초기화
        movieList = mutableListOf<Movie>()
        // 어댑터 생성 - 기본 어댑터
        // 첫번째 매개 변수는 Context - Activity
        // 두번째 매개 변수는 셀의 모양 - android.R.layout 으로 시작하는 것은
        // 안드로이드가 제공해주는 모양
        // 세번째 매개 변수는 출력할 데이터 - 배열 아니면 List
        /*
        movieAdapter = ArrayAdapter(
            this, android.R.layout.simple_list_item_1, movieList!!)
        */
        movieAdapter = MovieAdapter(this, movieList!!, R.layout.moviecell)
        // listview 와 adapter 연결
        listview.adapter = movieAdapter

        // 스레드 시작
        MovieThread().start()
    }

}

 

MovieAdapter.kt

package kr.co.tjoeun.movie

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import android.widget.RatingBar
import android.widget.TextView
import kotlinx.android.synthetic.main.moviecell.view.*
import java.net.URL


class MovieAdapter<T> (var context:Context, var data:MutableList<Movie>, var layout:Int) :BaseAdapter(){

        // 남겨 받는 데이터 레이아웃 ID를 부러 만들어 줄 inflater
        var inflater: LayoutInflater
        // 초기 화 메소드
        init {
            //Layout 파일을 전개 할 수 있는 LayoutInflater 생성
            inflater = context.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE
            ) as LayoutInflater
        }

    // 실제로 출력될 뷰를 설정하는 메소드
    override fun getView(posision:Int, convertView: View?, parent: ViewGroup): View? {
        // 코틀린에서는 매개 변수가 val 이라서 수정을 못함
        // 다른 변수에 넣고 수정을 합니다.
        // convertView 는 재사용 가능한 뷰
        var returnView = convertView
        // 재사용 가능 뷰가 없으면 생성
        if(returnView == null)
            // layout 파일을 전개
            // 첫번째 매개 변수는 Layout 의 아이디
            // 두번째 매개 변수는 부모 뷰
            // 세번째 매개 변수는 루트 여부
            returnView = inflater.inflate(layout, parent, false)

        // 타이틀 출력
        var titleview = returnView?.findViewById<View>(R.id.movietitle) as TextView
        titleview.text = data[posision].title

        // 서브 타이틀 출력
        var subtitleview = returnView?.findViewById<View>(R.id.moviesubtitle) as TextView
        subtitleview.text = data[posision].subtitle

        // 별점 출력
        var ratingview = returnView?.findViewById<View>(
            R.id.movieration) as RatingBar
        ratingview.rating = (data[posision].rating!! / 5).toFloat()

        // 이미지 뷰 가져오기
        var imageView = returnView?.findViewById<View>(
            R.id.movieimage) as ImageView
        // 이미지를 가져올 스레드를 생성
        var imageThread = ImageThread()
        // 스레드에게 필요한 데이터 전달
        imageThread.imagename = data[posision].thumbnail
        imageThread.imageview = imageView

        // 출력할 뷰 리턴
        return returnView
    }

    // 셀의 이름을 설정
    override fun getItem(posision:Int): String? {
        return data[posision].title
    }

    // 셀을 구분하기 위한 숫자를 설정
    override fun getItemId(posision: Int): Long {
        return posision.toLong()
    }

    override fun getCount(): Int {
        //여기서 리턴하는 숫자만큼 셀이 생성
        return data.size
    }

    // 이미지를 출력하는 핸들러
    val imageHandler = object: Handler(Looper.getMainLooper()){
        override fun handleMessage(msg : Message){
            // 맵으로 데이터를 가져와서 출력
            val map = msg.obj as MutableMap<String, Any>
            val imageview = map.get("imageView") as ImageView
            val bit = map.get("bit") as Bitmap
            imageview.setImageBitmap(bit)
        }
    }

    // 이미지를 다운로드 받는 스레드
    inner class ImageThread : Thread() {
        // 이미지 파일 이름
        var imagename:String? = null
        // 출력할 이미지 뷰
        var imageview: ImageView? = null

        override fun run(){
            // 이미지 파일의 경로를 생성해서 스트림으로 변환
            var inputStream = URL("http://cyberadam.cafe24.com/movieimage/${imagename}")
                .openStream()
            // 이미지 데이터 가져오기
            var bit = BitmapFactory.decodeStream(inputStream)
            inputStream.close()
            // 이미지 뷰에 가져온 이미지를 연결
            //imageview!!.setImageBitmap(bit)
            val map = mutableMapOf<String, Any>()
            map.put("imageview", imageview!!)
            map.put("bit", bit)
            // 메시지로 생성
            var msg = Message()
            msg.obj = map
            // 핸들러에게 전송
            imageHandler.sendMessage(msg)
        }
    }
}