Search

'java'에 해당되는 글 3건

  1. 2021.06.19 Serializable 과 Parcelable
  2. 2021.04.23 Thread, Runnable, Callable, ThreadPool
  3. 2018.11.25 JAVA 파일 생성/읽기/쓰기

Serializable 과 Parcelable

개발/안드로이드 2021. 6. 19. 14:03 Posted by 아는 개발자

Serializable 

 

Serialization(직렬화)란 자바 시스템 내부에서 사용하는 객체를 외부의 자바 시스템에서도 사용할 수 있도록 byte형태로 데이터를 변환시키는 기술을 말하며 안드로이드 상에선 직렬화를 이용해 액티비티간 또는 서비스간 클래스 타입의 데이터를 주고 받는 용도로 주로 사용한다

 

아래 처럼 Student 클래스를 Serializable 선언 해준다면, 다른 액티비티에 클래스 형태 그대로 값을 전달 해줄 수 있다. Serializaable 선언 외에 추가하는 코드가 없어서 사용하기 정말 편리하다.

 

data class Student(val name: String, val age: Int) : Serializable

val intent = Intent().apply { this.putExtra("student", Student("kwony", 30)) }
startActivity(intent)

 

그러나 Serializable은 byte 형태로 변환된 데이터를 다시 객체의 형태로 변환시키는데 JVM 내부에서 임시 객체를 많이 만들게 되고 이 과정에서 garbage 가 생길 우려가 있다. 안드로이드의 경우에는 배터리 전력을 감소시키기도 한다는데 정확히 어느정도 영향이 있는지 수치가 나온것은 없다. 성능에 미치는 영향이 있다는 점은 기억해둘 필요가 있을 것 같다.

 

Parcelable 

 

자바 시스템 공용인 Serializable과 달리 Parcelable은 안드로이드 SDK에서 포함하는 인터페이스다. Serializable 이 갖고 있는 변환 과정에서의 성능 저하를 보완하기 위해 만들어졌는데 이 방법이 변환하는 부분을 개발자가 직접 하게끔(?) 하는 것이다. 그래서 Parcelable 인터페이스 상속 함수를 구현해야 하는데 아래 코드처럼 구현해야할 게 많아졌다.

 

data class Student(val name: String?, val age: Int) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readInt()
    )

    override fun describeContents(): Int {
        return 0
    }

    override fun writeToParcel(dest: Parcel?, flags: Int) {
        dest?.writeString(name)
        dest?.writeInt(age)
    }

    companion object CREATOR : Parcelable.Creator<Student> {
        override fun createFromParcel(parcel: Parcel): Student {
            return Student(parcel)
        }

        override fun newArray(size: Int): Array<Student?> {
            return arrayOfNulls(size)
        }
    }
}

 

코틀린을 사용한다면 @Parcelize 어노테이션을 사용하면 추가 함수 구현 없이 사용 가능하다. 몇몇 클래스에서는 어노테이션이 동작하지 않는 경우도 있는데 그럴때만 빼면 쓸만하다.

 

@Parcelize
data class Student(val name: String?, val age: Int) : Parcelable {}

 

Performance

디바이스별로 Serializable과 Parcelable을 사용한 경우를 각각 비교한 그래프다. 그래프만 봐선 Parcelable의 성능이 확실히 뛰어난 것 같다. Real World에선 어떤 차이가 있을지 나온 자료는 아니지만 기억해두면 좋을 그래프 일 것 같다.

728x90

'개발 > 안드로이드' 카테고리의 다른 글

suspend fun  (0) 2021.07.22
Single, Maybe, Completable  (0) 2021.07.04
Serializable 과 Parcelable  (0) 2021.06.19
kotlin lateinit, lazy by  (0) 2021.06.05
Kotlin - Coroutine  (0) 2021.05.21
Android 10 스토리지 정책 대처하기  (0) 2021.05.18

Thread, Runnable, Callable, ThreadPool

개발/안드로이드 2021. 4. 23. 17:29 Posted by 아는 개발자

1. Thread

 

Thread 클래스는 Java 언어에서 비동기 작업시 대표적으로 사용하는 클래스다. 코틀린과 람다를 이용하면 아래와 같이 간단하게 비동기 작업 코드를 짤 수 있어서 짧은 디코딩 작업이나, 연산처리를 할 때 주로 사용된다. 

 

fun testThread() {
    val thread1 = Thread {
        Thread.sleep(1000)
        Log.d(this.toString(), "this is test thread1")
    }

    val thread2 = Thread {
        Log.d(this.toString(), "this is test thread2")
    }

    thread1.start()
    thread2.start()
}

 

그런데 Thread를 생성하는 작업은 안드로이드 밑단 운영체제에서 pthread를 생성하게 되는 작업이므로 추가적인 메모리를 할당받게 된다. 그래서 Thread를 많이 생성할수록  더 많은 메모리 공간을 차지하게되기 때문에 무한정 만들 수 없다. 

 

2. Runnable 

 

앞서 언급한 Thread를 간소화한 형태다. Thread는 클래스인 반면 Runnable은 인터페이스라서 다른 클래스를 상속하고 있는 클래스에 Runnable을 추가할 수 있고 상속 형태 없이 익명 클래스 형태로 바로 써도 된다. 실행은 run() 클래스를 호출해서 가능하다. 

 

fun testRunnable() {
    val runnable = Runnable { Log.d(this.toString(), "test runnable") }

    runnable.run()
}

 

3. Callable 

 

Runnable과 비슷하지만 Callable은 비동기 작업에서 리턴값도 줄 수 있고 결과 값을 연산하지 못하면 Exception을 발생시키기도 한다. 리턴값을 제공해야하는 비동기작업이면 Runnable 말고 Callable을 쓰면 된다. 아래 코드는 Callable에서 정수 값들을 리턴하도록 하고 각각의 합을 로그로 찍은 것이다

 

fun testCallable() {
    val callableOne = Callable { 1 }
    val callableTwo = Callable { 2 }
    val callableLog = Callable { Log.d(this.toString(), "this is callable log") }

    try {
        val merged = callableOne.call() + callableTwo.call()
        callableLog.call()

        Log.d(this.toString(), "this is callable merged: $merged")
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

 

4. ThreadPool

 

앞서 설명한 Thread, Runnable, Callable 모두 쓰레드 계열이라서 개별로 메모리를 쓰게돼, 무한정 만들면 메모리 낭비가 발생한다. 그래서 자바에서는 ThreadPool이란 것으로 쓰레드를 관리할 수 있도록 했다.  ThreadPool 관리 객체는 Executors 클래스를 통해서 생성하는데 이 클래스 검색해보면 내부의 함수를 통해 다양한 방식으로 Thread를 관리할 수 있는 것을 확인 할 수 있다. 지면상 모두 설명할 수 없고, 아래 코드에서 쓰는 newFixedThreadPool 함수만 설명하면 쓰레드를 최대 4개만 만들 수 있게 하고 나머지 작업은 재사용하겠다는 듯이다. 새로운 작업을 추가해도 쓰레드는 4개 이상 생기지 않으며 나머지는 재사용되기 때문에 메모리 낭비에 대한 부담 없이 새로운 작업을 추가할 수 있다.

 

작업은 Callable, Runnable의 형태로 추가 가능하며 submit 함수로 추가한다. 이때 리턴 값은 Future 클래스로 받게되고, get() 함수를 통해 결과 값을 얻을 수 있다.

 

fun testExecutorService() {
    val cachedPool = Executors.newFixedThreadPool(4)
    val futureOne = cachedPool.submit(Callable { 1 })
    val futureTwo = cachedPool.submit(Callable { 2 })
    val printLog = cachedPool.submit(Runnable {
        Log.d(this.toString(), "something...") })

    try {
        val merged = futureOne.get() + futureTwo.get()
        printLog.get()
        Log.d(this.toString(), "merged: $merged")

    } catch (e: Exception) {
        e.printStackTrace()
    }
}

 

728x90

JAVA 파일 생성/읽기/쓰기

기술 2018. 11. 25. 13:11 Posted by 아는 개발자

JAVA는 객체지향관점에 따라 시스템에 존재하는 파일이나 폴더를 하나의 객체로 보고 관리한다. 시스템 내에서 특정 위치의 파일을 읽거나 삭제 또는 생성하고 싶다면 해당 파일에 대한 객체를 생성해주면 된다. 파일 객체는 아래의 예제 코드처럼 파일 경로와 파일 이름을 생성자의 인자로 넣어주면 간단히 생성할 수 있다.


File file = new File(filepath + File.separator + fileName);


1. 파일 유무 확인 및 생성


파일 객체 안에는 여러가지 함수가 있는데 가장 요긴하게 쓰이는 함수는 exists() 함수다. 주로 파일을 생성하거나 사용하기 전에 해당 위치에 파일 또는 폴더가 이미 존재하는지 아닌지를 확인할 때 사용한다. 아래의 코드는 파일의 유무를 확인하고 없는 경우 파일을 생성하는 예제다. 폴더를 생성 할 때는 createNewFile을 mkdir 함수로 변경하면 된다.


if (!file.exists()) {
    try {
        file.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
}


2. 파일 읽기


파일에 값을 읽을때는 FileInputStream, InputStreamReader, BufferedReader 객체를 사용한다. 예제코드를 따라가보면 세번의 객체 생성 작업이 있는 것을 볼 수 있는데 모두 방금 전에 생성한 객체를 인자로 사용한다. 이 방법은 객체가 가진 데이터 값을 변환하는과정이다.


FileInputStream은 시스템에 존재하는 파일의 byte를 획득하고 InputStreamReader는 InputStream 획득한 복수개의 byte 를 해석해 특정한 문자열 집합인 charset으로 만든다. 최종적으로 BufferedReader에서는 charset값을 읽어서 string으로 변환한다.


try {
    FileInputStream fIn = new FileInputStream(file);
    InputStreamReader isr = new InputStreamReader(fIn);
    BufferedReader inBuff = new BufferedReader(isr);

    while (true) {
        try {
            inputLine = inBuff.readLine();
        } catch(IOException e) {
            e.printStackTrace();
        }

        if (inputLine == null)
            break;

        outStringBuf.append(inputLine);
    }
} catch(FileNotFoundException e) {
    e.printStackTrace();
}


3. 파일 쓰기


파일에 값을 쓸 때는 FileOutputStream, OutputStreamWriter 객체를 사용한다. OutputStreamWriter는 Charater 형태의 stream을 byte 형태로 전환해 값을 입력 할 수 있는 객체다. OutputStreamWriter 생성시 변환한 값을 입력할 Stream 객체를 전달 받는데 이때 File 객체를 품은 FileOutputStream 객체를 사용하면 file에 값을 사용하도록 할 수 있다.


final String welcomeString = new String(initialValue);
FileOutputStream fOut = new FileOutputStream(file);
OutputStreamWriter osw = new OutputStreamWriter(fOut);

osw.write(welcomeString);
osw.flush();
osw.close();


728x90

'기술' 카테고리의 다른 글

JAVA 파일 생성/읽기/쓰기  (0) 2018.11.25
스택, 힙, 코드, 데이터영역  (6) 2018.11.10
VNC와 RDP  (2) 2018.09.12
jupyter notebook 소개  (0) 2018.08.04
URI (Uniform Resource Identifier)  (0) 2018.07.02
libgdx - Viewport  (0) 2018.06.22