본문 바로가기

안드로이드/안드로이드 디버깅

[안드로이드] 프래그먼트 생명 주기를 활용한 UX 개선

개요

아래와 같은 기존 코드의 경우 뷰가 완전히 생성된 이후 네트워킹 작업을 수행한 뒤 뷰를 갱신하여 UX가 좋지 않았다. 

내가 원하는 방향은 네트워킹을 통해 데이터를 전부 가져오고 이 작업이 마무리되고 뷰가 갱신되는 것을 원했다. 

class BoardFragment : Fragment() {
    lateinit var repository: Repository
    lateinit var adapter: PostPageAdapter
    private val mDisposable = CompositeDisposable()

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        adapter = PostPageAdapter(requireContext()) {
            val directions = BoardFragmentDirections.actionCommunityFragmentToPostFragment(it.id!!)
            findNavController().navigate(directions)
            (requireActivity() as MainActivity).main_bottom_navigation.visibility=View.GONE
        }
        repository = Repository(activity!!.application)
        return inflater.inflate(R.layout.fragment_board, container, false)
    }

    override fun onResume() {
        super.onResume()
        (requireActivity() as MainActivity).main_bottom_navigation.visibility=View.VISIBLE
        adapter.notifyDataSetChanged()
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        recycler_view.setItemViewCacheSize(20)
		...
        val pagedItems = builder.buildObservable()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe({
                    (recycler_view.adapter as PostPageAdapter).submitList(it)
                }, {
                    it.stackTrace
                })

        mDisposable.add(pagedItems)
        recycler_view.adapter = adapter
    }

  ...
}

 

 

액티비티와 프래그먼트의 생명주기

아래는 액티비티와 프래그먼트의 생명주기를 연관지어 보여준 흐름도이다. 현재 포스팅에서 주목할 부분은 프래그먼트 생명주기의 onCreate(), onCreateView()이다. 

 

onCreate()

해당 콜백은 프래그먼트가 생성될 때 단 한번만 호출된다. 따라서, 이 콜백에서는 데이터와 같은 리소스 초기화 작업을 수행하는게 적합하다.

 

onCreateView()

해당 콜백은 뷰가 생성되어 매개변수로 사용가능한 시점이다.  또한, 프래그먼트가 백스택에서 되돌아오는 지점이기도 하다.

 

onViewCreated()

해당 콜백은 onCreateView()가 호출된 직후 호출된다. 뷰가 완전히 생성되어 있는 지점이다.

 

 

리팩토링

아래는 위의 개념을 알고난 뒤 리팩토링한 코드이다.

 

onCreate에서는 adapter,repository를 초기화하고 네트워킹을 통해 데이터를 가져온다.

onCreateView에서는 view에 대한 초기화 작업을 수행한다.

onViewCreated에서는 리사이클러뷰에 실제 어답터를 붙여 데이터를 보여준다.

class BoardFragment : Fragment() {
    lateinit var repository: Repository
    lateinit var adapter: PostPageAdapter
    private val mDisposable = CompositeDisposable()
    private lateinit var pagedItems: Disposable


    override fun onAttach(context: Context) {
        super.onAttach(context)
        Util.progressOnInFragment(this)
    }

    //Callback when fragment is created
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        adapter = PostPageAdapter(requireContext()) {
            val directions = BoardFragmentDirections.actionCommunityFragmentToPostFragment(it.id!!)
            findNavController().navigate(directions)
            (requireActivity() as MainActivity).main_bottom_navigation.visibility=View.GONE
        }
        repository = Repository(activity!!.application)
        initData()
    }

    //Callback first init & return from back stack
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_board, container, false)
        (requireActivity() as MainActivity).main_bottom_navigation.visibility=View.VISIBLE
        view.write_post_btn.setOnClickListener { goToWritePostFragment() }
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        recycler_view.setItemViewCacheSize(20)
        recycler_view.adapter = adapter
    }

    private fun initData(){
        val config= PagedList.Config.Builder()
                .setPageSize(20)    //Defines the number of items loaded at once from the DataSource.
                .setInitialLoadSizeHint(20) //Defines how many items to load when first load occurs. Default = PAGE SIZE * 3
                .setPrefetchDistance(20)    //Defines how far from the edge of loaded content an access must be to trigger further loading.
                .setEnablePlaceholders(true)    //Pass false to disable null placeholders in PagedLists using this Config.
                .build()

        val builder = RxPagedListBuilder<Int, Post>(PostDataSourceFactory(repository,mDisposable), config)

        pagedItems = builder.buildObservable()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe({
                    adapter.submitList(it)
                    Util.progressOffInFragment()
                }, {
                    it.stackTrace
                })
        mDisposable.add(pagedItems)
    }

    ...
}

 

 

Note

안드로이드 개발을 하면 할수록 많은 문제는 생명주기와 연관이 있는 것 같다. 이에 대한 꾸준한 학습이 필요해 보인다.