12月 21, 2025
36 分で読める

ジュニアAndroid開発者の面接質問と回答

interview
career-advice
job-search
entry-level
ジュニアAndroid開発者の面接質問と回答
Milad Bonakdar

Milad Bonakdar

著者

Kotlin、ライフサイクル、Jetpack Compose、Room、ネットワーク、コルーチンを中心にジュニアAndroid面接を準備しましょう。


はじめに

ジュニアAndroid開発者の面接では、Kotlinの基礎、Androidのライフサイクル、Composeの状態管理、ローカル保存、ネットワーク通信、コルーチンを実務目線で説明できるかが見られます。よい回答は、UI状態を適切な場所に置く、メインスレッドを止めない、データを安全に保存する、エラーを明確に扱う、といった小さく信頼できるアプリ作りに結びついています。

このガイドで短い回答を練習し、履歴書やポートフォリオの小さなプロジェクトに結びつけてください。ToDoアプリ、天気アプリ、API連携画面などを例にすると、暗記ではなく具体的な経験として話しやすくなります。


Kotlinの基礎 (6つの質問)

1. Kotlinにおける valvar の違いは何ですか?

回答:

  • val: 不変(読み取り専用)変数を宣言します。一度割り当てられると、その値を変更することはできません。
  • var: 可変変数を宣言します。初期化後に値を変更できます。
  • ベストプラクティス: 安全のため、デフォルトで val を使用してください。値を再割り当てする必要がある場合にのみ var を使用してください。
val name = "John"  // 変更不可
var age = 25       // 変更可能
age = 26           // 有効
// name = "Jane"   // エラー: Val は再割り当てできません

希少度: 非常に一般的 難易度: 簡単


2. KotlinにおけるNullable型とSafe Call演算子について説明してください。

回答: Kotlinの型システムは、nullポインタ例外を防ぐために、Nullable型とNon-Nullable型を区別します。

  • Nullable型: 型の後に ? を追加します: String?
  • Safe Call (?.): オブジェクトがnullでない場合にのみメソッドを呼び出します
  • Elvis演算子 (?:): nullの場合にデフォルト値を提供します
  • Not-null Assertion (!!): nullの場合に例外をスローします(控えめに使用してください)
var email: String? = "[email protected]"

// Safe call
val length = email?.length  // emailがnullの場合はnullを返します

// Elvis演算子
val displayEmail = email ?: "No email"

// Let function
email?.let {
    println("Email: $it")
}

// Not-null assertion (危険)
val len = email!!.length  // emailがnullの場合は例外をスローします

希少度: 非常に一般的 難易度: 簡単


3. Kotlinにおける classdata class の違いは何ですか?

回答:

  • 通常のクラス: 標準的なクラス定義
  • データクラス: データを保持するための便利なメソッドを自動的に生成します
    • equals()hashCode()
    • toString()
    • copy() 関数
    • 分解のための componentN() 関数
// 通常のクラス
class Person(val name: String, val age: Int)

// データクラス
data class User(val name: String, val age: Int)

val user1 = User("John", 25)
val user2 = User("John", 25)

println(user1 == user2)  // true (equals() が生成される)
println(user1)           // User(name=John, age=25) (toString() が生成される)

// コピーと変更
val user3 = user1.copy(age = 26)

// 分解
val (name, age) = user1

希少度: 非常に一般的 難易度: 簡単


4. Kotlinにおけるラムダ式と高階関数とは何ですか?

回答:

  • ラムダ: 値として渡すことができる匿名関数
  • 高階関数: 関数をパラメータとして受け取るか、関数を返す関数
// ラムダ式
val sum = { a: Int, b: Int -> a + b }
println(sum(5, 3))  // 8

// 高階関数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

val result = calculate(10, 5) { x, y -> x * y }
println(result)  // 50

// コレクション操作
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
val evens = numbers.filter { it % 2 == 0 }

println(doubled)  // [2, 4, 6, 8, 10]
println(evens)    // [2, 4]

希少度: 非常に一般的 難易度: 普通


5. Kotlinにおける拡張関数について説明してください。

回答: 拡張関数を使用すると、ソースコードを変更せずに既存のクラスに新しい関数を追加できます。

// Stringの拡張関数
fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

println("radar".isPalindrome())  // true
println("hello".isPalindrome())  // false

// パラメータ付きの拡張関数
fun Int.times(action: () -> Unit) {
    repeat(this) {
        action()
    }
}

3.times {
    println("Hello")
}
// "Hello" が3回出力されます

希少度: 一般的 難易度: 簡単


6. Kotlinにおける ===== の違いは何ですか?

回答:

  • ==: 構造的等価性(equals() を使用して値を比較します)
  • ===: 参照的等価性(メモリ参照を比較します)
val str1 = "Hello"
val str2 = "Hello"
val str3 = str1

println(str1 == str2)   // true (同じ値)
println(str1 === str2)  // true (文字列プール最適化)
println(str1 === str3)  // true (同じ参照)

val list1 = listOf(1, 2, 3)
val list2 = listOf(1, 2, 3)

println(list1 == list2)   // true (同じ内容)
println(list1 === list2)  // false (異なるオブジェクト)

希少度: 一般的 難易度: 簡単


Androidコンポーネント (5つの質問)

7. Activityとは何ですか?そのライフサイクルについて説明してください。

回答: Activityは、ユーザーインターフェースを持つ単一の画面を表します。明確に定義されたライフサイクルがあります。

Loading diagram...
  • onCreate(): Activityが作成されます。UIを初期化し、コンテンツビューを設定します。
  • onStart(): Activityが表示されます。
  • onResume(): Activityがフォアグラウンドにあり、インタラクティブです。
  • onPause(): Activityがフォーカスを失います(別のアクティビティがフォアグラウンドに来ます)。
  • onStop(): Activityが表示されなくなります。
  • onDestroy(): Activityが破棄されます。
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // ビューを初期化し、データを設定します
    }
    
    override fun onStart() {
        super.onStart()
        // Activityが表示されます
    }
    
    override fun onResume() {
        super.onResume()
        // Activityはインタラクティブです。アニメーションを開始します
    }
    
    override fun onPause() {
        super.onPause()
        // Activityがフォーカスを失います。進行中のアクションを一時停止します
    }
}

希少度: 非常に一般的 難易度: 簡単


8. ActivityとFragmentの違いは何ですか?

回答:

  • Activity: フルスクリーンを表します。ユーザーインタラクションのエントリポイントです。独自のライフサイクルがあります。
  • Fragment: Activity内のUIの再利用可能な部分です。1つのActivityに複数のFragmentが存在できます。ホストActivityに関連付けられた独自のライフサイクルがあります。
  • Fragmentの利点:
    • Activity間での再利用性
    • モジュール化されたUIコンポーネント
    • タブレットのサポート(マルチペインレイアウト)
    • Navigationコンポーネントの統合
class MyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_my, container, false)
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // ビューを初期化します
    }
}

// Activity内
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container, MyFragment())
            .commit()
    }
}

希少度: 非常に一般的 難易度: 簡単


9. Intentとは何ですか?その種類は何ですか?

回答: Intentは、別のアプリアクセスコンポーネントからのアクションを要求するために使用されるメッセージングオブジェクトです。

  • 明示的Intent: 開始する正確なコンポーネントを指定します(クラス名で指定)
  • 暗黙的Intent: 一般的なアクションを宣言し、システムが適切なコンポーネントを見つけます
// 明示的Intent - 特定のアクティビティを開始
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("USER_ID", 123)
startActivity(intent)

// 暗黙的Intent - URLを開く
val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
startActivity(webIntent)

// 暗黙的Intent - テキストを共有
val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "Check this out!")
}
startActivity(Intent.createChooser(shareIntent, "Share via"))

// 暗黙的Intent - 電話をかける
val callIntent = Intent(Intent.ACTION_DIAL).apply {
    data = Uri.parse("tel:1234567890")
}
startActivity(callIntent)

希少度: 非常に一般的 難易度: 簡単


10. AndroidにおけるServiceとは何ですか?

回答: Serviceは、ユーザーインターフェースなしでバックグラウンドで長時間実行される操作を実行するコンポーネントです。

  • 種類:
    • フォアグラウンドService: 目に見える操作を実行します(音楽プレーヤー)。通知を表示します。
    • バックグラウンドService: ユーザーが直接気づかない操作を実行します。
    • バインドService: コンポーネントがバインドして対話できるようにします。
class MyService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // バックグラウンド作業を実行します
        Thread {
            // 長時間実行される操作
            Thread.sleep(5000)
            stopSelf()
        }.start()
        
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

// Serviceを開始
val serviceIntent = Intent(this, MyService::class.java)
startService(serviceIntent)

// Serviceを停止
stopService(serviceIntent)

希少度: 一般的 難易度: 普通


11. BroadcastReceiverとは何ですか?

回答: BroadcastReceiverは、システム全体のブロードキャストアナウンスに応答するコンポーネントです。

  • ユースケース: バッテリー残量が少ない、ネットワーク接続の変更、SMS受信、起動完了
  • 登録:
    • 静的: AndroidManifest.xml内(新しいAndroidバージョンでは制限されています)
    • 動的: コード内(推奨)
class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent?.action) {
            Intent.ACTION_BATTERY_LOW -> {
                // バッテリー残量が少ない場合の処理
            }
            ConnectivityManager.CONNECTIVITY_ACTION -> {
                // ネットワーク変更の処理
            }
        }
    }
}

// 動的に登録
class MainActivity : AppCompatActivity() {
    private val receiver = MyBroadcastReceiver()
    
    override fun onStart() {
        super.onStart()
        val filter = IntentFilter(Intent.ACTION_BATTERY_LOW)
        registerReceiver(receiver, filter)
    }
    
    override fun onStop() {
        super.onStop()
        unregisterReceiver(receiver)
    }
}

希少度: 一般的 難易度: 簡単


UI開発 (4つの質問)

12. LinearLayout、RelativeLayout、ConstraintLayoutの違いは何ですか?

回答:

  • LinearLayout: 子を単一の行または列に配置します。シンプルですが、ネストされたレイアウトになる可能性があります。
  • RelativeLayout: 子を互いまたは親に対して相対的に配置します。より柔軟ですが、複雑です。
  • ConstraintLayout: 最新の柔軟なレイアウト。フラットなビュー階層。複雑なUIに推奨されます。
<!-- ConstraintLayoutの例 -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

希少度: 非常に一般的 難易度: 簡単


13. RecyclerViewとは何ですか?どのように機能しますか?

回答: RecyclerViewは、ビューをリサイクルして大きなリストを効率的に表示するためのウィジェットです。

  • コンポーネント:
    • Adapter: データをビューにバインドします
    • ViewHolder: ビューへの参照を保持します(findViewById呼び出しを回避します)
    • LayoutManager: アイテムを配置します(Linear、Grid、Staggered)
// データクラス
data class User(val name: String, val email: String)

// ViewHolder
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val nameText: TextView = itemView.findViewById(R.id.nameText)
    private val emailText: TextView = itemView.findViewById(R.id.emailText)
    
    fun bind(user: User) {
        nameText.text = user.name
        emailText.text = user.email
    }
}

// Adapter
class UserAdapter(private val users: List<User>) : 
    RecyclerView.Adapter<UserViewHolder>() {
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        holder.bind(users[position])
    }
    
    override fun getItemCount() = users.size
}

// Activity内
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = UserAdapter(userList)

希少度: 非常に一般的 難易度: 普通


14. Jetpack Composeとは何ですか?

回答: Jetpack Composeは、Androidの最新の宣言型UIツールキットです。

  • 宣言型: UIをどのように構築するかではなく、どのように見えるかを記述します
  • 利点: コードが少なく、直感的で、強力で、開発が加速されます
  • Composable関数: Compose UIの構成要素
@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    
    Column(
        modifier = Modifier.padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Count: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

@Preview
@Composable
fun PreviewCounter() {
    Counter()
}

希少度: 非常に一般的 難易度: 簡単


15. match_parentwrap_content の違いは何ですか?

回答: これらは、ビューの寸法を定義するレイアウトパラメータです。

  • match_parent: ビューは親のサイズに合わせて展開します
  • wrap_content: ビューはコンテンツに合わせてサイズを調整します
  • 固定サイズ: 特定のdp値(例:100dp
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <!-- 親の幅に合わせて、コンテンツの高さに合わせて調整 -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Title" />
    
    <!-- 両方の寸法を調整 -->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />
    
    <!-- 固定サイズ -->
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/icon" />
</LinearLayout>

希少度: 非常に一般的 難易度: 簡単


データとネットワーク (5つの質問)

16. Androidでネットワークリクエストを行うにはどうすればよいですか?

回答: ネットワークにはRetrofitやOkHttpなどのライブラリを使用します。HttpURLConnectionを直接使用することは避けてください。

// Retrofitの設定
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: Int): User
    
    @POST("users")
    suspend fun createUser(@Body user: User): User
}

object RetrofitClient {
    private const val BASE_URL = "https://api.example.com/"
    
    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// ViewModelでの使用
class UserViewModel : ViewModel() {
    fun fetchUser(userId: Int) {
        viewModelScope.launch {
            try {
                val user = RetrofitClient.apiService.getUser(userId)
                // UIを更新します
            } catch (e: Exception) {
                // エラーを処理します
            }
        }
    }
}

希少度: 非常に一般的 難易度: 普通


17. Roomとは何ですか?どのように使用しますか?

回答: Roomは、SQLiteの抽象化レイヤーであり、データベースへのアクセスを容易にします。

  • コンポーネント:
    • Entity: テーブルを表します
    • DAO (Data Access Object): データベース操作を定義します
    • Database: データベースホルダー
// Entity
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "email") val email: String
)

// DAO
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAllUsers(): List<User>
    
    @Query("SELECT * FROM users WHERE id = :userId")
    fun getUserById(userId: Int): User?
    
    @Insert
    suspend fun insertUser(user: User)
    
    @Delete
    suspend fun deleteUser(user: User)
    
    @Update
    suspend fun updateUser(user: User)
}

// Database
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        
        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

希少度: 非常に一般的 難易度: 普通


18. SharedPreferencesとは何ですか?

回答: SharedPreferencesは、少量のプリミティブデータをキーと値のペアとして保存します。

  • ユースケース: ユーザー設定、プリファレンス、単純なフラグ
  • 用途: 大量のデータ、複雑なオブジェクト(代わりにRoomを使用してください)
// データを保存
val sharedPref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
    putString("username", "John")
    putInt("age", 25)
    putBoolean("isLoggedIn", true)
    apply()  // または commit()
}

// データを読み取る
val username = sharedPref.getString("username", "Guest")
val age = sharedPref.getInt("age", 0)
val isLoggedIn = sharedPref.getBoolean("isLoggedIn", false)

// データを削除
with(sharedPref.edit()) {
    remove("username")
    apply()
}

// すべてクリア
with(sharedPref.edit()) {
    clear()
    apply()
}

希少度: 非常に一般的 難易度: 簡単


19. SharedPreferencesの apply()commit() の違いは何ですか?

回答: どちらもSharedPreferencesへの変更を保存しますが、動作が異なります。

  • apply(): 非同期。すぐに戻ります。変更はバックグラウンドでディスクに書き込まれます。戻り値はありません。
  • commit(): 同期。変更が書き込まれるまでブロックします。boolean(成功/失敗)を返します。
  • ベストプラクティス: 戻り値が必要な場合を除き、apply()を使用してください。
val editor = sharedPref.edit()

// apply() - 非同期、推奨
editor.putString("key", "value")
editor.apply()  // すぐに戻ります

// commit() - 同期
editor.putString("key", "value")
val success = editor.commit()  // 書き込みを待機し、booleanを返します
if (success) {
    // 保存成功
}

希少度: 一般的 難易度: 簡単


20. Kotlinにおけるコルーチンとは何ですか?

回答: コルーチンは、非同期コードを順番に記述する方法を提供し、読みやすく保守しやすくします。

  • 利点: 軽量、構造化された並行性、例外処理
  • キーコンセプト:
    • suspend: 一時停止および再開できる関数
    • launch: コルーチンを開始します(fire and forget)
    • async: コルーチンを開始し、結果を返します
    • Dispatchers: コルーチンが実行されるスレッドを制御します
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            try {
                // メインスレッド
                showLoading()
                
                // ネットワーク呼び出しのためにIOスレッドに切り替え
                val data = withContext(Dispatchers.IO) {
                    fetchDataFromNetwork()
                }
                
                // メインスレッドに戻る
                updateUI(data)
            } catch (e: Exception) {
                handleError(e)
            }
        }
    }
    
    suspend fun fetchDataFromNetwork(): String {
        delay(2000)  // ネットワーク遅延をシミュレートします
        return "Data from server"
    }
}

// 並列実行
viewModelScope.launch {
    val deferred1 = async { fetchUser(1) }
    val deferred2 = async { fetchUser(2) }
    
    val user1 = deferred1.await()
    val user2 = deferred2.await()
}

希少度: 非常に一般的 難易度: 普通

Newsletter subscription

実際に機能する週次のキャリアのヒント

最新の洞察をメールボックスに直接お届けします

採用担当者に目立ち、夢の仕事を手に入れよう

ATSを通過し、採用担当者を感動させるAI搭載の履歴書でキャリアを変えた数千人の仲間に加わりましょう。

今すぐ作成を開始

この投稿を共有

50%速く採用される

プロフェッショナルなAI強化履歴書を使用する求職者は、標準的な10週間に比べて平均5週間で職を得ています。待つのをやめて、面接を始めましょう。