안드로이드 앱을 사용하다보면 종종 첫화면 전에 빈 화면이 잠깐 나오는 것을 볼 수 있는데요, 이는 앱을 새로 실행하는 경우 첫 화면을 그리는데 시간이 필요하기 때문입니다.
안드로이드 시스템은 이 시간 동안 비어있는 Placeholder Screen을 앱의 windowBackground
color로 채우는데요, 액티비티 테마를 이용한다면 이 Placeholder Screen을 단순히 색깔만 가진 화면에서 Splash Screen
으로 확장시킬 수 있습니다.
<!-- splash_drawable.xml -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- background color -->
<item android:drawable="@android:color/white"/>
<!-- App logo -->
<item>
<bitmap
android:src="@drawable/app_logo"
android:gravity="center">
</bitmap>
</item>
</layer-list>
먼저 <layer-list>
를 통해 Splash Screen
으로 사용될 drawable
을 만듭니다.
<!-- styles.xml -->
<resources>
<style name="SplashTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowBackground">@drawable/splash_drawable</item>
</style>
</resources>
그런 다음 Spash Screen
을 위한 테마를 만들고 windowBackground
속성에 앞에서 만든 drawable
을 지정해줍니다.
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.~~~">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
만들어진 테마를 첫 화면이 될 액티비티에 적용하면 앱 실행 시 빈화면이 아닌 Splash Screen
이 보여지게 됩니다.
/* MainActivity.kt */
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
액티비티가 Splash Screen
과는 다른 테마를 가져야 한다면, 해당 액티비티 onCreate()
에서 setTheme()
메소드를 사용하여 Splash Screen
이후에는 기존 테마가 적용되록 만들어줘야 합니다.
이 방식을 이용한다면 첫 화면을 그리는 동안 빈 화면이 아닌 Splash Screen
을 보여줌으로써 사용자 경험을 개선할 수 있지만, Splash Screen
에 애니메이션이나 Progress Bar
를 포함할 수는 없다는 단점이 있습니다.
Handler
, Runnable
그리고 Timer
많은 앱들은 Splash Screen
을 위해 별도의 액티비티를 만들고 해당 액티비티에서 일정시간 대기한 후 로직에 따라 다른 액티비티로 이동하는 방식을 사용합니다.
이 방식은 주로 Handler
와 Runnable
클래스를 통해 구현됩니다.
/* MainActivity.kt */
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Handler().postDelayed(2000) {
var intent = Intent(this@MainActivity, NextActivity::class.java)
startActivity(intent)
finish()
}
}
override fun onDestroy() {
handler.removeCallbacksAndMessages(null)
super.onDestroy()
}
}
이 방식은 Splash Screen
에 애니메이션이나 Progress Bar
를 사용할 수 있고, 좀 더 자유로운 Splash Screen
화면을 구성할 수 있습니다. 또한 특정 액티비티로 리다이렉션 하는 로직도 추가할 수 있습니다.
그러나 하드웨어의 뒤로가기 버튼을 누르는 경우 handler
의 동작을 취소할 수 없고 예약된 동작으로 인해 화면이 랜덤하게 팝업된다는 단점이 있습니다. 이는 메모리 누수를 발생시키거나 데이터 손실을 유발할 수 있습니다.
이를 해결하기 위해서 Timer
클래스를 사용하기도 합니다.
/* MainActivity.kt */
class MainActivity : AppCompatActivity() {
var timer = Timer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_timer_splash)
timer.schedule(2000) {
var intent = Intent(this@MainActivity, NextActivity::class.java)
startActivity(intent)
finish()
}
}
override fun onPause() {
timer.cancel()
super.onPause()
}
}
Timer
는 Handler
와 달리 동작을 취소할 수 있습니다. 다만 Timer
는 완전히 새로운 쓰레드를 백그라운드에 생성하기 때문에 TimerTask
객체 안에서 UI와 상호작용할 수 없고, 메모리 측면에서 무거운 작업이며, 쓰레드 스위칭으로 인해 느리다는 단점이 있습니다.
Coroutine
사용Handler
와 Runnable
클래스 대신에 코틀린의 Coroutine
을 이용할 수도 있습니다.
/* MainActivity.kt */
class MainActivity : AppCompatActivity() {
val activityScope = CoroutineScope(Dispatchers.Main)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_coroutines_splash)
activityScope.launch {
delay(2000)
var intent = Intent(this@MainActivity, NextActivity::class.java)
startActivity(intent)
finish()
}
}
override fun onPause() {
activityScope.cancel()
super.onPause()
}
}
이 방식은 CoroutineScope
가 Dispatchers.Main
을 사용하기 때문에 작업을 Main UI Thread
에서 수행하며, Thread Switching이 없습니다. 또한 코루틴의 동작을 취소할 수도 있습니다.
즉, Coroutine
은 빠르고 안정적이며 메모리를 덜 쓰고, UI에 접근할 수 있다는 장점이 있습니다.
Reference