AndroidManifest.xml 파일에 설정
INTERNET 권한 설정
http 인 경우 android:useClearTextTrafic 을 true 로 설정
이미지 다운로드
이미지를 ImageView 에 바로 출력하기
이미지를 매번 다시 출력
이미지를 다운로드 받아서 파일로 만들고 출력
처음 한 번 만 다운로드 받고 다음부터는 다운로드 받지않고 로컬에서 이미지를 가져와서 출력 가능
Data parsing
parsing : 데이터를 분석해서 자신에게 필요한 데이터를 원하는 포맷으로 만드는 것
서버에서 제공하는 데이터 포맷 :
csv - 변하지 않는 데이터 - 과거에 만들어져서 제공되는 데이터, 공백이나 쉼표로 구분된 데이터)
xml - 태그 형태로 표현하는 데이터 포맷 - 실시간으로 변경되는 데이터, 인간이 알아보기 쉽게 기계가 사용하기는 무거운 포맷, RSS 라고도 합니다.
json - 자바 스크립트 데이터 포맷으로 데이터 표현, XML 보다는 가벼워서 많이 사용 읽기가 어렵습니다.
html 은 데이터 포맷이 아니고 브라우저가 해석해서 랜더링하기 위한 포맷
open api 형태로 제공되지는 않고 웹화면으로만 제공되는 데이터의 경우는 html 파싱을 수행해서 필요한 데이터를 추출하는 경우가 있습니다.
=> html 파싱을 하고자 하는 경우에는 tag, class, id, xpath 의 개념을 알고 있어야 합니다.
=> java 에서는 jsoup 라는 라이브러리를 이용해서 html 파싱을 합니다.
=> android studio 는 gradle 방식의 빌드를 합니다.
build.gradle 파일에 의존성을 설정
=> java는 mvnrepository.com 에서 외부 라이브러리를 검색
html 크롤링이나 자동로그인 같은 작업을 만들고자 하시면 selenium 을 이용합니다.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="kr.co.tjoeun.webserveruse">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WebServerUse"
android:usesCleartextTraffic="true">
<activity android:name=".HanActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".HaniActivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
build.gradle(:app)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "kr.co.tjoeun.webserveruse"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// https://mvnrepository.com/artifact/org.jsoup/jsoup
implementation group: 'org.jsoup', name: 'jsoup', version: '1.13.1'
}
MainActivity.kt
package kr.co.tjoeun.webserveruse
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 java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
class MainActivity : AppCompatActivity() {
inner class TextDownloadThread : Thread(){
override fun run() {
// 다운로드 받을 URL 을 생성
val url = URL("https://www.google.com")
// Connection 생성
val con = url.openConnection() as HttpURLConnection
// 옵션 설정
con.setConnectTimeout(30000) // 최대 접속 시간
con.setUseCaches(false)
// post 방식이나 header 설정을 수행
// 문자열 다운로드 받는 스트림을 생성
val br = BufferedReader(InputStreamReader(con.inputStream))
// 다운로드 받은 문자열을 저장할 변수
var result = ""
// 줄 단위로 문자열 읽기
while (true){
val line = br.readLine() // 한 줄 읽기
// 읽은 데이터가 없으면 종료
if(line == null) {
break
}
// 읽은 데이터를 result 에 추가
result = result + line + "\n"
}
// 정리 작업
br.close()
con.disconnect()
// 메인 스레드가 아닌 곳에서는 UI 갱신 작업을 하면 안됩니다.
//resulttxt.text = result
// 필요한 데이터를 Message 에 저장하고 핸들러를 호출
val message = Message()
message.obj = result
handler.sendMessage(message)
}
}
val handler = object: Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message){
// 전송된 데이터를 읽습니다.
val str = msg.obj as String
resulttxt.text = str
}
}
inner class ImageThread : Thread(){
override fun run() {
val url = URL("http://cyberadam.cafe24.com/img/orange.jpg")
// 이미지를 바로 출력
/*
val fis = url.openStream()
val bit = BitmapFactory.decodeStream(fis)
fis.close()
val msg = Message()
msg.obj = bit
imageHandler.sendMessage(msg)
*/
// 이미지를 다운로드 받아서 앱에 저장
var con = url.openConnection() as HttpURLConnection
// 다운로드 받을 파일의 크기를 설정
val len = con.contentLength
// 다운로드 받은 내용을 저장할 배열 생성
val raster = ByteArray(len)
// 다운로드 받을 스트림 생성
val fis = con.inputStream
// 파일에 저장할 스트림 생성
var path = Environment.getDataDirectory().absolutePath +
"/data/kr.co.tjoeun.webserveruse/files/orange.jpg"
val fos = openFileOutput("orange.jpg", 0)
// 읽어서 파일에 쓰기
while (true) {
val read:Int = fis.read(raster)
if(read < 0) {
break
}
// 파일에 기록
fos.write(raster, 0, read)
}
// 작업 정리
fis.close()
fos.close()
con.disconnect()
val message : Message = Message()
// 이미지 파일의 전체 경로 넘겨주기
message.obj = path
imageHandler.sendMessage(message)
}
}
val imageHandler= object:Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
/*
val bitmap = msg.obj as Bitmap
resultimage.setImageBitmap(bitmap)
*/
val path = msg.obj as String
resultimage.setImageBitmap(BitmapFactory.decodeFile(path))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
htmldownload.setOnClickListener {
TextDownloadThread().start()
}
iamgedownload.setOnClickListener {
//ImageThread().start()
// 이미지 파일이 있는지 확인
// 이미지 파일 경로 생성
var path = Environment.getDataDirectory().absolutePath +
"/data/kr.co.tjoeun.webserveruse/files/orange.jpg"
// 파일의 존재 여부 확인
if(File(path).exists()){
Toast.makeText(this, "이미지가 존재합니다.",
Toast.LENGTH_LONG).show()
// 로컬에 있는 이미지 출력
resultimage.setImageBitmap(BitmapFactory.decodeFile(path))
} else{
Toast.makeText(this, "이미지가 존재하지 않아 다운로드 합니다.",
Toast.LENGTH_LONG).show()
// 로컬에 있는 이미지 출력
ImageThread().start()
}
}
}
}
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">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/htmldownload"
android:text="문자열 다운로드"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/iamgedownload"
android:text="이미지 다운로드"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/resultimage"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/resulttxt"/>
</ScrollView>
</LinearLayout>
RSS 에서 title 태그의 내용만 추출해서 출력
HanActivity.kt
package kr.co.tjoeun.webserveruse
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 kotlinx.android.synthetic.main.activity_han.*
import java.io.BufferedReader
import java.io.ByteArrayInputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
class HanActivity : AppCompatActivity() {
// 데이터를 다운로드 받을 스레드
inner class ThreadEx :Thread(){
override fun run(){
// 필요한 데이터 다운로드
val url = URL("http://www.hani.co.kr/rss/")
// 연결 객체 생성
val con = url.openConnection() as HttpURLConnection
// 연결 옵션 설정
con.connectTimeout = 30000
con.useCaches = false
// 문자열 가져올 스트림
val br = BufferedReader(InputStreamReader(con.inputStream))
// 가져온 문자열을 저장할 객체
var xml = ""
while (true) {
val line = br.readLine()
if(line == null) {
break
}
xml = xml + line + "\n"
}
br.close()
con.disconnect()
//Log.e("xml", xml)
// DOM Parsing : xml 문자열을 메모리에 펼침
val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()
val istream = ByteArrayInputStream(
xml.toByteArray(charset("utf-8")))
val doc = builder.parse(istream)
val root = doc.documentElement
// 원하는 태그를 추출
var items = root.getElementsByTagName("title")
var result = ""
// 태그 안의 내용만 가져오기
for (i in 0 until items.length){
val item = items.item(i)
val text = item.firstChild
val title = text.nodeValue
result = result + title + "\n"
}
// 핸들러에게 결과를 전송
val msg = Message()
msg.obj = result
handler.sendMessage(msg)
}
}
val handler = object: Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
val str = msg.obj as String
hanitext.text = str
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_han)
hanibtn.setOnClickListener {
ThreadEx().start()
}
}
}
activity_han.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=".HanActivity"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="한겨레 실시간 기사 가져오기"
android:id="@+id/hanibtn"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/hanitext"/>
</LinearLayout>
'안드로이드' 카테고리의 다른 글
안드로이드 : 웹 뷰 연동과 드로잉 (0) | 2020.11.09 |
---|---|
안드로이드 : 영화 소개 페이지 웹 크롤링 (0) | 2020.11.05 |
안드로이드 : Thread 와 Handler / 서버 통신 (0) | 2020.11.03 |
안드로이드 : 액티비티와 인텐트 (0) | 2020.11.02 |
안드로이드 (0) | 2020.10.29 |