발견록_07

김재현·2023년 7월 3일
0

안드로이드

목록 보기
10/12

1. socket.io

ws 대신 socket.io 라이브러리의 장점

  • 연결 끊기면 자동재접속 기능
  • 웹소켓 접속자마다 자동 id 부여
  • 모든 웹소켓유저에게 전체 메세지 전송가능
  • 웹소켓방을 생성가능

2. android studio flamingo

플라밍고로 안드로이드 스튜디오를 업데이트 했더니 빈액티비티를 만들었을때 이전과 다르게 샘플 코드와 더불어서 ui.theme폴더에 무언가 코드가 적혀서 나온다.

그리고 values>theme에는 아무 내용이 없어진다.

기본 메인 액티비이는이렇게 생겼으며 화면 실행결과

다음과 같이 나오게 된다.

이 테마가 자동으로 지정되는 이유는 Flamingo버전에서는 Material Design 구성 요소와 스타일을 사용할 수 있도록 설정되어 있기 때문이다. Material Design 구성 요소를 사용하여 일관된 디자인과 사용자 경험을 제공하기 위한... 이렇게 설명되어있다.

기존적으로 Theme에서 noActionBar로 설정되어있을뿐만아니라 글씨 폰트등도 설정되어있는것 같다. 적응하고 수정하려면 시간이 조금 걸릴 것 같다...


3. Android 실행 시 Run으로 넘어가지 말고 Logcat화면을 보여주기

  • 안드로이드 스튜디오에서 메뉴 바에서 "Run"탭 선택
  • "Edit configurations"를 클릭하여 실행 구성 열기

    Show logcat보여달라고 하면 끝!

4. EditText의 소프트키보드 내리기

보통 EditText를 클릭했을때 생긴 소프트키보드를 내리기 위해서

fun Context.hideKeyboard(view: View){
    val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(view.windowToken,0)
}

이런 함수를 사용해서 키보드를 내릴 수 있는 것 같다.

그런데 키보드를 내리기 위해서는 다른 화면이 클릭이 되어야 하는데 이렇게 root의 화면이 비어있거나 클릭을 가로채는 다른 뷰가 없을때는 클릭을 정상적으로 인식을 한다.

하지만
이렇게 상단은 toolbar, 중간은 recycerview등으로 구성되어있을경우, 지금 루트뷰가 클릭인식하는 부분은 빨간색으로 쳐진 저 작은 틈 사이의 부분밖에 없다. 이럴경우 toolbar나 recyclerview, edittext, button은 루트뷰의 클릭이벤트를 가로채가는지 정상적으로 루트뷰의 클릭을 인식하지 못한다.

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <View
        android:id="@+id/backgroundView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" />

</RelativeLayout>

그럴 경우 위와같이 리사이클러뷰를 덮는 또다른 뷰를 만들어서 그 뷰를 클릭하게 만들었는데... 성능면에서든.. 리사이클러뷰의 터치 이벤트를 막는게 되버리니까.. 제한적인 상황에서 사용될 수 있을것같다. 또한 계속 터치가 일어나니까 불필요한 터치 이벤트를 막게. 키보드가 나타났을때만 터치가 된다던가? 하는 방법이 필요해보인다.


5. Intent에 객체 넘겨주기 - 1 (Parcelable)

화면 전환을 하면서 전환된 화면에 데이터를 넘겨주어야 할경우 Intent의 put~~를 사용한다. 그런데 우리가 직접 만든 data class같은 경우에는 지정할 수가 없다. 이걸 해결하기 위해서 data class를 Parcelable 형식으로 만들어주고 Bundle()에 싸서 intent의 값으로 넘겼다.

data class같은 객체는 직렬화등 일련의 과정을 거쳐서 데이터를 가공시켜 주어야 intent로 다음 화면에 넘길 수가 있다. 이때 Serializable로도 가공시킬수 있지만 Parcelable에 비해 성능이 떨어진다는 점이 있다. 대신 Parcelable은 구현해야할게 좀 많다.

첫번째 방법으로는, data 클래스를 만들고 : Parcelable로 인터페이스를 상속받으면 자동으로 구현할 것들이 자동완성으로 만들어진다. 데이터 타입이 String인 경우에는 constructor부분에서 null체크를 해줘야 하는것 같았다.이렇게 말이다.

두번째 방법으로는 Parcelize를 활용한 애노테이션 방법이다.
build.gradle(Modlue:app)에서 kotlin-parcelize플러그인을 추가해준다.

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
    id 'kotlin-parcelize'
}

그후 애노테이션으로만 쓰고 이외의 constructor나 그런것들의 구현을 안해도 됀다.
Parcelize에는 두가지 라이브러리가 있는데 kotlinx.parcelizekotlinx.android.parcel이다. 이중 kotlinx.parcelize는 Kotlin 공식 Parcelable 지원 라이브러리로, Android 프로젝트에서 Kotlin으로 작성된 Parcelable 객체를 사용하는 경우에 많이 사용된다.
kotlinx.android.parcel은 @Parcelize이외에 @RawValue, @Parcel등을 지원하는 라이브러리이다.
특별한 요구사항이 없으면 그냥 kotlinx.parcelize를 사용하는 듯하다.


6. Intent에 객체 넘겨주기 - 2 (Bundle, getParcelable)

5에서 객체를 Parcelable로 만들었다면 이제 필요한 데이터들을 Bundle()에 모조리 담아서 보낼 수 있다.

보내는 시점이 다양하게 보낼수 있는데

액티비티에서 보낼때
리사이클러뷰의 어뎁터에서보낼때

등등 보낼 수있다.

이렇게 보낸 Parcelable데이터는 버전에 맞게 가져오면 되는데 TIRAMISU버전 이상부터는 getParcelableExtra의 형식 변경이 있기 때문에 다음과 같이 사용할 수 있다.


7. 뷰모델 공유하기

액티비티인 MainLobby.kt 안에 뷰페이저2가 있고 이 뷰페이저2에 들어가는 프래그먼트인 GroupChatFragment.ktPersonalChatFragment.kt 2개가 있다.

이 세뷰들이 한개의 뷰모델을 가지게 하려면 어떻게해야할까?

만약 뷰모델에 대한 종속성을 추가했다면 이후

android {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {
    implementation 'androidx.activity:activity-ktx:1.1.0'
    implementation 'androidx.fragment:fragment-ktx:1.2.5'
}

을 더 추가해준다.

뷰모델 정의

class MainLobbyViewModel : ViewModel() {...}

메인이 되는 액티비티

class MainLobby : AppCompatActivity() {

    private val mainLobbyViewModel: MainLobbyViewModel by viewModels()
}

이렇게 by viewModels()만 넣어주면 된다. 그럼 프래그먼트에서는?

class GroupChatFragment : Fragment() , View.OnClickListener{

    private val viewModel: MainLobbyViewModel by activityViewModels()
}

class PersonalChatFragment : Fragment() {

    private val viewModel: MainLobbyViewModel by activityViewModels()

처럼 activityViewModels()로 넣어주면 된다.

처음에 말한 종속성을 추가해주면
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)이렇게 onCreate()안에서 안쓰고 그냥 위와 같이 쉽게 작성할 수 있다. ktx의 힘..


8. 액티비티와 프래그먼트의 생명주기(with viewPager2, viewModel)

채팅앱을 만들면서 7번과 같은 구조의 화면을 만들었고 viewModel에서 API와 통신을 해서 가져온 데이터를 통해서 fragment1과 fragment2의 어뎁터의 아이템으로 넣어주고 싶었다.
그런데 문제가 여러개 있었다.

  1. 처음화면에 같이 보이는 fragment1에는 제대로 아이템이 들어가는데 두번째로 스와이프 해서 보이는 fragment2에는 아이템이 제대로 안들어가는 현상
  2. fragment2에서 onViewCreated()안에 만들어놓은 목록불러오는 함수가 한번만 실행되는 현상
  3. fragment2에서 리사이클러뷰의 목록을 클릭해 다른 액티비티로 넘어간 후, 그 액티비티에서 돌아왔을때 fragment2 목록이 갱신이 안되는 현상.

등등...

찾은 해결법

  1. 액티비티가 만들어지고 viewpager에 fragment를 연결할때 프래그먼트1 만 로딩되게 하는게 아니라 프래그먼트2도 같이 로딩되게 만들기. offScreenLimit을 사용하는 방법.

  2. 프래그먼트2에서 필요한 정보는 stateFlow에 담아줘서 HotStream방식으로 가져올수 있도록 만들기. 즉, 정보가 주어진 당시에 바로 받지 않아도, 프래그먼트2가 만들어지면 collect{}로 받아오는 방식.

    2-1. 이를 위해서는 액티비티에서 onResume단계에서 정보를 불러오면 편하다. 왜냐하면 프래그먼트2에서 클릭을 통해 채팅 액티비티로 넘어갈수있게 되는데, 뒤로가기 버튼을 눌러서 다시 화면으로 나올때, 프래그먼트2가 갱신이 될 수 있기 때문이다.


거의 3,4주 동안 소켓통신과 API를 통해서 채팅앱을 만들었었다. 이 발견록은 인턴기간의 마지막 평가 프로젝트로, 막상 앱을 다 만들고 정리하려니 적을 양이 너무 많고 서버도 닫혀서 이제 실행을 해볼수가 없어서 이만 줄이려고 한다. 버그를 수정할 수가 없다. ㅠㅠㅠ

자세한 코드는
깃허브 주소 - ChattingApp에서 확인해 볼 수 있다.

profile
배운거 정리하기

1개의 댓글

comment-user-thumbnail
2023년 7월 5일

ㅊㅋㅊㅋ 고생하셨어용~~

답글 달기