안드로이드 앱 권장 아키텍처

build.gradle

plugins {
    id 'kotlin-kapt'
}




// ROOM
    def roomVersion = "2.4.0"

    implementation("androidx.room:room-runtime:$roomVersion")
    annotationProcessor("androidx.room:room-compiler:$roomVersion")

// To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:$roomVersion")

// Coroutine
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")


    // ViewModelScope
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")

}
 

 

Repository.kt

class Repository(context: Context) {


    val db = TextDataBase.getDatabase(context)

    fun getTextList() = db.textDao().getAllData()

    fun getWordList() = db.wordDao().getAllData()

    fun insertTextData(text: String) = db.textDao().insert(TextEntity(0, text))

    fun insertWordData(text: String) = db.wordDao().insert(WordEntity(0, text))

    fun deleteTextData() = db.textDao().deleteAllData()

    fun deleteWordData() = db.wordDao().deleteAllData()

}
 

 

 

MainViewModel.kt

class MainViewModel(application: Application) : AndroidViewModel(application) {


    val context = getApplication<Application>().applicationContext
    val db = TextDataBase.getDatabase(context)


    private var _textList = MutableLiveData<List<TextEntity>>()
    val textList : LiveData<List<TextEntity>>
        get() = _textList

    private var _wordList = MutableLiveData<List<WordEntity>>()
    val wordList : LiveData<List<WordEntity>>
        get() = _wordList

    val repository = Repository(context)


    fun getData() = viewModelScope.launch(Dispatchers.IO) {

        Log.d("MainViewModel",db.textDao().getAllData().toString())
        Log.d("MainViewModel",db.wordDao().getAllData().toString())

        _textList.postValue(repository.getTextList())

        _wordList.postValue(repository.getWordList())

    }

    fun insertData(text : String) = viewModelScope.launch(Dispatchers.IO) {

        repository.insertTextData(text)
        repository.insertWordData(text)

    }

    fun removeData() = viewModelScope.launch (Dispatchers.IO){

        repository.deleteTextData()
        repository.deleteWordData()

    }



}
 

Dispatcher.IO에서 사용하기위해 postValue를 사용해주어야함.

 

MainActivity.kt

// ROOM

// https://developer.android.com/training/data-storage/room?hl=ko
// https://developer.android.com/codelabs/android-room-with-a-view-kotlin?hl=ko#5
// https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ko

class MainActivity : AppCompatActivity() {

    lateinit var viewModel : MainViewModel

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


        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        viewModel.getData()


        val db = TextDataBase.getDatabase(this)

        val inputArea = findViewById<EditText>(R.id.textInputArea)
        val insertBtn = findViewById<Button>(R.id.insert)
        val getAllBtn = findViewById<Button>(R.id.getData)
        val deleteBtn = findViewById<Button>(R.id.delete)

        insertBtn.setOnClickListener {

            viewModel.insertData(inputArea.text.toString())
            inputArea.setText("")



        }


        val rv = findViewById<RecyclerView>(R.id.rv)

        viewModel.textList.observe(this, Observer {
            val customAdapter = CustomAdapter(it)
            rv.adapter = customAdapter
            rv.layoutManager = LinearLayoutManager(this)
        })

        getAllBtn.setOnClickListener {
           viewModel.getData()
        }

        deleteBtn.setOnClickListener {
           viewModel.removeData()
        }


    }
}
 

 

CustomAdapter.kt

class CustomAdapter(private val dataSet : List<TextEntity>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>(){

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view){

        val textView : TextView = view.findViewById(R.id.textView)

    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {

        val view = LayoutInflater.from(parent.context).inflate(R.layout.text_row_item,parent,false)

        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {
       holder.textView.text = dataSet[position].text
    }

    override fun getItemCount(): Int {
      return dataSet.size
    }


}
 

 

 

 

TextEntity.kt

@Entity(tableName = "text_table")
data class TextEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

WordEntity.kt

@Entity(tableName = "word_table")
data class WordEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

TextDataBase.kt

@Database(entities = [TextEntity::class, WordEntity::class], version = 2)
abstract class TextDataBase : RoomDatabase(){

    abstract fun textDao() : TextDao
    abstract fun wordDao() : WordDao

    companion object {
        @Volatile
        private var INSTANCE : TextDataBase? = null

        fun getDatabase(
            context : Context
        ) : TextDataBase {


            return INSTANCE ?: synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TextDataBase::class.java,
                    "text_database"
                )
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                instance


            }

        }


    }

}
 

WordDao.kt

@Dao
interface WordDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<WordEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : WordEntity)

    @Query("DELETE FROM word_table")
    fun deleteAllData()


}
 

TextDao.kt

@Dao
interface TextDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<TextEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : TextEntity)

    @Query("DELETE FROM text_table")
    fun deleteAllData()


}
 

실행결과 :

 

 

 

'Android > Room' 카테고리의 다른 글

Room + ViewModelScope  (0) 2022.11.22
ROOM( MultiTable )  (0) 2022.11.22
ROOM  (0) 2022.11.22

build.gradle

plugins {
    id 'kotlin-kapt'
}




// ROOM
    def roomVersion = "2.4.0"

    implementation("androidx.room:room-runtime:$roomVersion")
    annotationProcessor("androidx.room:room-compiler:$roomVersion")

// To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:$roomVersion")

// Coroutine
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")


    // ViewModelScope
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")

}

MainViewModel.kt

class MainViewModel(application: Application) : AndroidViewModel(application) {


    val context = getApplication<Application>().applicationContext
    val db = TextDataBase.getDatabase(context)

    fun getDate() = viewModelScope.launch(Dispatchers.IO) {

        Log.d("MainViewModel",db.textDao().getAllData().toString())
        Log.d("MainViewModel",db.wordDao().getAllData().toString())

    }

    fun insertData(text : String) = viewModelScope.launch(Dispatchers.IO) {
        db.textDao().insert(TextEntity(0,text))
        db.wordDao().insert(WordEntity(0, text))

    }

    fun removeData() = viewModelScope.launch (Dispatchers.IO){
        db.textDao().deleteAllData()
        db.wordDao().deleteAllData()
    }


}
 

MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var viewModel : MainViewModel

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


        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        viewModel.getDate()


        val db = TextDataBase.getDatabase(this)

        val inputArea = findViewById<EditText>(R.id.textInputArea)
        val insertBtn = findViewById<Button>(R.id.insert)
        val getAllBtn = findViewById<Button>(R.id.getData)
        val deleteBtn = findViewById<Button>(R.id.delete)

        insertBtn.setOnClickListener {

            viewModel.insertData(inputArea.text.toString())
            inputArea.setText("")



        }

        getAllBtn.setOnClickListener {
           viewModel.getDate()
        }

        deleteBtn.setOnClickListener {
           viewModel.removeData()
        }


    }
}
 

TextEntity.kt

@Entity(tableName = "text_table")
data class TextEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

WordEntity.kt

@Entity(tableName = "word_table")
data class WordEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

TextDataBase.kt

@Database(entities = [TextEntity::class, WordEntity::class], version = 2)
abstract class TextDataBase : RoomDatabase(){

    abstract fun textDao() : TextDao
    abstract fun wordDao() : WordDao

    companion object {
        @Volatile
        private var INSTANCE : TextDataBase? = null

        fun getDatabase(
            context : Context
        ) : TextDataBase {


            return INSTANCE ?: synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TextDataBase::class.java,
                    "text_database"
                )
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                instance


            }

        }


    }

}
 

WordDao.kt

@Dao
interface WordDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<WordEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : WordEntity)

    @Query("DELETE FROM word_table")
    fun deleteAllData()


}
 

TextDao.kt

@Dao
interface TextDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<TextEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : TextEntity)

    @Query("DELETE FROM text_table")
    fun deleteAllData()


}
 

실행결과 :

 

'Android > Room' 카테고리의 다른 글

Room + ViewModelScole + LiveData + RecyclerView(repository Architecture)  (0) 2022.11.28
ROOM( MultiTable )  (0) 2022.11.22
ROOM  (0) 2022.11.22

MainActivity.kt

class MainActivity : AppCompatActivity() {


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

        val db = TextDataBase.getDatabase(this)

        val inputArea = findViewById<EditText>(R.id.textInputArea)
        val insertBtn = findViewById<Button>(R.id.insert)
        val getAllBtn = findViewById<Button>(R.id.getData)
        val deleteBtn = findViewById<Button>(R.id.delete)

        insertBtn.setOnClickListener {

            CoroutineScope(Dispatchers.IO).launch {
                db.textDao().insert(TextEntity(0, inputArea.text.toString()))
                db.wordDao().insert(WordEntity(0, inputArea.text.toString()))
                inputArea.setText("")
            }

        }

        getAllBtn.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                Log.d("MAINACTIVITY", db.textDao().getAllData().toString())
                Log.d("MAINACTIVITY", db.wordDao().getAllData().toString())
            }
        }

        deleteBtn.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                db.textDao().deleteAllData()
                db.wordDao().deleteAllData()
            }
        }


    }
}
 
 

TextEntity.kt

@Entity(tableName = "text_table")
data class TextEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

WordEntity.kt

@Entity(tableName = "word_table")
data class WordEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

TextDataBase.kt

@Database(entities = [TextEntity::class, WordEntity::class], version = 2)
abstract class TextDataBase : RoomDatabase(){

    abstract fun textDao() : TextDao
    abstract fun wordDao() : WordDao

    companion object {
        @Volatile
        private var INSTANCE : TextDataBase? = null

        fun getDatabase(
            context : Context
        ) : TextDataBase {


            return INSTANCE ?: synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TextDataBase::class.java,
                    "text_database"
                )
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                instance


            }

        }


    }

}
 

WordDao.kt

@Dao
interface WordDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<WordEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : WordEntity)

    @Query("DELETE FROM word_table")
    fun deleteAllData()


}
 

TextDao.kt

@Dao
interface TextDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<TextEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : TextEntity)

    @Query("DELETE FROM text_table")
    fun deleteAllData()


}
 

실행결과 :

 

'Android > Room' 카테고리의 다른 글

Room + ViewModelScole + LiveData + RecyclerView(repository Architecture)  (0) 2022.11.28
Room + ViewModelScope  (0) 2022.11.22
ROOM  (0) 2022.11.22

 

Room은 AAC(Android Architecture Components), 스마트폰 내장 DB에 데이터를 저장하기 쉽도록 해주는 라이브러리이다.(DB데이터를 Java or Kotlin 객체로 매핑 해준다.)

다양한 Annotation을 통해서 컴파일시 코드들을 자동 생성 해주고 SQLite와 달리 LiveData, RxJava와 같은 Obervation 형태를 지원 하고 MVP,MVVM과 같은 아키텍쳐 패턴에 쉽게 활용할 수 있도록 되어있다.

Room 라이브러리의 엔티티(Entity), 데이터 접근 객체(DAO),데이터 베이스(DB)의 상세 설명과 사용법은 구글 공식 문서에도 나와있지만 아래 블로그에서 많이 참고하였다.

https://velog.io/@ryalya/Android-DB-Room이란

 

 

Gradle.

plugins {
    id 'kotlin-kapt'
}



 // ROOM
    def roomVersion = "2.4.0"

    implementation("androidx.room:room-runtime:$roomVersion")
    annotationProcessor("androidx.room:room-compiler:$roomVersion")

// To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:$roomVersion")

// Coroutine, Room을 사용 할때 Coroutine을 사용해야함.
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
 

MainActivty.kt

// https://developer.android.com/training/data-storage/room?hl=ko
// https://developer.android.com/codelabs/android-room-with-a-view-kotlin?hl=ko#5


class MainActivity : AppCompatActivity() {


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

        val db = TextDataBase.getDatabase(this)

        val inputArea = findViewById<EditText>(R.id.textInputArea)
        val insertBtn = findViewById<Button>(R.id.insert)
        val getAllBtn = findViewById<Button>(R.id.getData)
        val deleteBtn = findViewById<Button>(R.id.delete)

        insertBtn.setOnClickListener {

            CoroutineScope(Dispatchers.IO).launch {
                db.textDao().insert(TextEntity(0, inputArea.text.toString()))
                inputArea.setText("")
            }

        }

        getAllBtn.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                Log.d("MAINACTIVITY", db.textDao().getAllData().toString())
            }
        }

        deleteBtn.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                db.textDao().deleteAllData()
            }
        }


    }
}
 

Room과 Coroutine을 함께 사용하게 되면 DB 작업들은 UI 쓰레드가 아닌 디스패처에서 실행하게 된다. Room이어서가 아닌, Coroutine이어서 그렇다. suspend 함수는 다른 suspend 함수나 코루틴에서만 실행될 수 있다.

 

Dispatcher에 관한 문서: https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ko

 

TextDao.kt

@Dao
interface TextDao {

    @Query("SELECT * FROM text_table")
    fun getAllData() : List<TextEntity>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(text : TextEntity)

    @Query("DELETE FROM text_table")
    fun deleteAllData()


}
 

TextEntitiy

@Entity(tableName = "text_table")
data class TextEntity (

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id : Int,
    @ColumnInfo(name = "text")
    var text: String

)
 

TextDataBase.kt

@Database(entities = [TextEntity::class], version = 1)
abstract class TextDataBase : RoomDatabase(){

    abstract fun textDao() : TextDao


    companion object {
        @Volatile
        private var INSTANCE : TextDataBase? = null

        fun getDatabase(
            context : Context
        ) : TextDataBase {


            return INSTANCE ?: synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TextDataBase::class.java,
                    "text_database"
                )
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                instance


            }

        }


    }

}
 

'Android > Room' 카테고리의 다른 글

Room + ViewModelScole + LiveData + RecyclerView(repository Architecture)  (0) 2022.11.28
Room + ViewModelScope  (0) 2022.11.22
ROOM( MultiTable )  (0) 2022.11.22

 

SQLite 는 내장 DB이다. 지난 프로젝트에서 SQLite 를 사용하여 이미 익숙하지만 나중을 위해 간단하게 사용법을 남겨 놓을까 한다.

 

SQLiteHelper.kt

class SQLiteHelperSample(context: Context) : SQLiteOpenHelper(
    context, DATABASE_NAME, null,
    DATABASE_VERSION
) {

    companion object {
        private const val DATABASE_VERSION = 1
        private const val DATABASE_NAME = "myTestDB.db"
        private const val TBL_NAME = "my_table"

        private const val ID = "ID"
        private const val TITLE = "title"
    }

    override fun onCreate(db: SQLiteDatabase?) {

        val SQL_CREATE_ENTRIES =
            "CREATE TABLE $TBL_NAME (" +
                    "$ID INTEGER PRIMARY KEY," +
                    "$TITLE TEXT)"

        db?.execSQL(SQL_CREATE_ENTRIES)



    }

    fun insert(str: String) {

        val db = this.writableDatabase

        val values = ContentValues().apply{
            put(TITLE,str)
        }

        db.insert(TBL_NAME,null,values)

    }

    fun getAllData(): ArrayList<String> {

        val db = this.writableDatabase

        val query = "SELECT * FROM $TBL_NAME"

        val cursor = db.rawQuery(query,null)

        val arr = ArrayList<String>()

        with(cursor){
            while(moveToNext()){
                arr.add(getString(1))
            }
        }



        return arr
    }

    fun deleteAll(){

        val db = this.writableDatabase

        db?.execSQL("DROP TABLE IF EXISTS $TBL_NAME")
        onCreate(db)


    }



    //DATABASE VERSION
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {

        db?.execSQL("DROP TABLE IF EXISTS $TBL_NAME")
        onCreate(db)


    }


}
 

MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var db:SQLiteHelperSample

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

        db = SQLiteHelperSample(this)

        val inputArea = findViewById<EditText>(R.id.inputArea)
        val insertBtn = findViewById<Button>(R.id.insert)
        val getAllBtn = findViewById<Button>(R.id.getAll)
        val resultBtn = findViewById<Button>(R.id.resultArea)
        val deleteBtn = findViewById<Button>(R.id.deleteAll)

        insertBtn.setOnClickListener {
            val inputText = inputArea.text.toString()
            db.insert(inputText)
            inputArea.setText("")

        }

        getAllBtn.setOnClickListener {

            val arr = db.getAllData()
            resultBtn.text = arr.toString()
        }
        deleteBtn.setOnClickListener {
            db.deleteAll()
        }


    }

    override fun onDestroy() {
        super.onDestroy()
        db.close()
    }
}
 

실행결과:

 

 

 

안드로이드 앱 권장 아키텍처에 따라 지난 번에 만든 코드를 수정하였다.

 

build.gradle

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")

implementation 'com.github.bumptech.glide:glide:4.13.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.0'
 

 

AndroidManifest.xml

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

 

CustomAdapter.kt

class CustomAdapter(val context :Context, val dataSet : List<Plant>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    class ViewHolder(view : View) : RecyclerView.ViewHolder(view) {
        val textView : TextView = view.findViewById(R.id.textArea)
        val imageView : ImageView = view.findViewById(R.id.imageArea)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.text_row_item, parent, false)

        return ViewHolder(view)

    }

    override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {

        holder.textView.text = dataSet[position].name
        Glide.with(context)
            .load(dataSet[position].imageUrl)
            .into(holder.imageView)

    }

    override fun getItemCount(): Int {
        return dataSet.size
    }

}
 

 

RetrofitInstance.kt

object RetrofitInstance {

    val BASE_URL = "https://raw.githubusercontent.com/"

    val client = Retrofit
        .Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun getInstance() : Retrofit {
        return client
    }

}
 

 

Plant.kt

data class Plant (
    val plantId : String,
    val name : String,
    val description : String,
    val growZoneNumber : Int,
    val wateringInterval : Int,
    val imageUrl : String
)
 

 

 

MainViewModel.kt

class MainViewModel : ViewModel() {

    // val retrofitInstance : MyApi = RetrofitInstance.getInstance().create(MyApi::class.java)

    private val repository = Repository()

    private val _result = MutableLiveData<List<Plant>>()
    val result : LiveData<List<Plant>>
        get() = _result

    fun getAllData() = viewModelScope.launch {
        Log.d("MainViewModel", repository.getAllData().toString())
        _result.value = repository.getAllData()
    }

}
 

MainActivity.kt

// https://raw.githubusercontent.com/googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json

// https://developer.android.com/training/dependency-injection/manual?hl=ko

class MainActivity : AppCompatActivity() {

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

        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        viewModel.getAllData()

        val rv = findViewById<RecyclerView>(R.id.rv)

        viewModel.result.observe(this, Observer {

            val customAdapter = CustomAdapter(this, it)
            rv.adapter = customAdapter
            rv.layoutManager = LinearLayoutManager(this)


        })

    }
}
 

 

MyApi.kt

interface MyApi {

    @GET("googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json")
    suspend fun getAllPlants() : List<Plant>

}
 

RetrofitInstance.kt

object RetrofitInstance {

    val BASE_URL = "https://raw.githubusercontent.com/"

    val client = Retrofit
        .Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun getInstance() : Retrofit {
        return client
    }

}
 

 

Repository.kt

class Repository {

    private val client = RetrofitInstance.getInstance().create(MyApi::class.java)

    suspend fun getAllData() = client.getAllPlants()

}
 

 

 

'Android > Retrofit' 카테고리의 다른 글

Retrofit + ViewModelScope + RecyclerView + Glide / Retrofit 구조 변경  (0) 2022.11.22
Retrofit  (0) 2022.11.22

 

 

build.gradle

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")

implementation 'com.github.bumptech.glide:glide:4.13.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.0'
 

 

AndroidManifest.xml

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

 

CustomAdapter.kt

class CustomAdapter(val context :Context, val dataSet : List<Plant>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    class ViewHolder(view : View) : RecyclerView.ViewHolder(view) {
        val textView : TextView = view.findViewById(R.id.textArea)
        val imageView : ImageView = view.findViewById(R.id.imageArea)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.text_row_item, parent, false)

        return ViewHolder(view)

    }

    override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {

        holder.textView.text = dataSet[position].name
        Glide.with(context)
            .load(dataSet[position].imageUrl)
            .into(holder.imageView)

    }

    override fun getItemCount(): Int {
        return dataSet.size
    }

}
 

MyApi.kt

interface MyApi {

    @GET("googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json")
    suspend fun getAllPlants() : List<Plant>

}
 

RetrofitInstance.kt

object RetrofitInstance {

    val BASE_URL = "https://raw.githubusercontent.com/"

    val client = Retrofit
        .Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun getInstance() : Retrofit {
        return client
    }

}
 

 

Plant.kt

data class Plant (
    val plantId : String,
    val name : String,
    val description : String,
    val growZoneNumber : Int,
    val wateringInterval : Int,
    val imageUrl : String
)
 

 

 

MainViewModel.kt

// https://raw.githubusercontent.com/googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json

class MainActivity : AppCompatActivity() {

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

        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        viewModel.getAllData()

        val rv = findViewById<RecyclerView>(R.id.rv)

        viewModel.result.observe(this, Observer {

            val customAdapter = CustomAdapter(this, it)
            rv.adapter = customAdapter
            rv.layoutManager = LinearLayoutManager(this)


        })

    }
}
 

MainActivity.kt

// https://raw.githubusercontent.com/googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json

class MainActivity : AppCompatActivity() {

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

        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        viewModel.getAllData()

        val rv = findViewById<RecyclerView>(R.id.rv)

        viewModel.result.observe(this, Observer {

            val customAdapter = CustomAdapter(this, it)
            rv.adapter = customAdapter
            rv.layoutManager = LinearLayoutManager(this)


        })

    }
}
 

결과:

 

'Android > Retrofit' 카테고리의 다른 글

Retrofit구조 변경(Android 앱 권장 아키텍처)  (0) 2022.11.22
Retrofit  (0) 2022.11.22

 

Retrofit은 서버와 클라이언트 간 http 통신을 위한 라이브러리이다.

즉, Android에서 http 통신을 할 수 있도록 해주는 친구이다.

 

AndroidManifest.xml

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

build.gradle

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
 

 

 

 

MainActivity.kt (JSONPlaceholder사이트에서 API호출 및 테스팅 했음.)

https://jsonplaceholder.typicode.com/

 
// Simple Retrofit Ex
// https://jsonplaceholder.typicode.com/
// https://jsonplaceholder.typicode.com/posts
// https://jsonplaceholder.typicode.com/posts/2

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

        val api = RetrofitInstance.getInstance().create(MyApi::class.java)
        api.getPost1().enqueue(object : Callback<Post> {
            override fun onResponse(call: Call<Post>, response: Response<Post>) {
                Log.d("API1", response.body().toString())
            }

            override fun onFailure(call: Call<Post>, t: Throwable) {
                Log.d("API1", "fail")
            }

        })

        api.getPostNumber(2).enqueue(object : Callback<Post> {
            override fun onResponse(call: Call<Post>, response: Response<Post>) {
                Log.d("API2", response.body().toString())
            }

            override fun onFailure(call: Call<Post>, t: Throwable) {
                Log.d("API2", "fail")
            }

        })

    }
}
 

RetrofitInstance.kt

object RetrofitInstance {

    val BASE_URL = "https://jsonplaceholder.typicode.com/"

    val client = Retrofit
        .Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun getInstance() : Retrofit {
        return client
    }

}
 

 

Post.kt

data class Post (
    val userId : Int,
    val id : Int,
    val title : String,
    val body : String
)
 

 

MyApi.kt

interface MyApi {

    @GET("posts/1")
    fun getPost1() : Call<Post>

    @GET("posts/{number}")
    fun getPostNumber(
        @Path("number") number : Int
    ) : Call<Post>

}
 

결과:API로 받아온 정보 두개가 로그에 찍혔다.

 

 

+ Recent posts