Android: ジェネリクス学習用アプリ開発
作成したアプリ
- ジェネリクスを使った一つのメソッドを経由してString、Int、Boolean型の値をそれぞれ表示させるサンプルアプリ
ジェネリクスとは?
- <>を使って仮の型で定義できる仕組み。仮の型のパラメータにはV(value)やK(key)などの略語が使われることが多く、<V> や <K> などの形でコード内で使われる。
ジェネリクスのメリットは?
- 引数や戻り値の型が違うが同じ処理を行うメソッドがある場合、ジェネリクスを使って仮の型で定義すると、実際に使う時に色々な型に変更して一つのメソッドでいろいろな型の引数や戻り値を扱えるメリットがある
実装詳細
- ジェネリクスを使ったメソッド定義
今回はジェネリクスの型が適切に変更されているか確認したいだけの為、ただ値が通過するだけのメソッドを作成。
package com.example.samplegenericsapplication
class SampleGenerics {
// 値を受け取り何もせず値を返す
// 一つのVが変更されると連動して全てのVが選択した型に変更される
fun <V>viaValue(value: V): V {
return value
}
}
- 定義したジェネリクスメソッドを使用
今回色々な型にジェネリクスメソッドが変更されるのか確認したいため、String、Int、Booleanの値を使用して確認。
package com.example.samplegenericsapplication
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val sampleGenerics = SampleGenerics()
// Stringの場合
val stringValueText = view.findViewById<TextView>(R.id.stringValue)
// String型の値を代入
val stringValue = sampleGenerics.viaValue<String>("ゆやまる")
stringValueText.text = stringValue
// Intの場合
val intValueText = view.findViewById<TextView>(R.id.intValue)
// Int型の値を代入
val intValue = sampleGenerics.viaValue<Int>(3)
intValueText.text = intValue.toString()
// Booleanの場合
val booleanValueText = view.findViewById<TextView>(R.id.booleanValue)
// Boolean型の値を代入
val booleanValue = sampleGenerics.viaValue<Boolean>(true)
booleanValueText.text = booleanValue.toString()
}
}
上記コードではジェネリクスに対して直接型指定をしているが、型推論の機能があるため直接指定しなくても良い(例↓)
// String型の値を代入(型推論でString型と判断される)
val stringValue = sampleGenerics.viaValue("ゆやまる")
Android: 拡張関数とBundleを使った数字のみ入力制限ありの計算アプリ
作成したアプリ
- 入力できる文字に数字のみの制限をかけ、String型の拡張関数で計算メソッドを作成・利用し結果をBundleを経由して表示するアプリを作成
使用した機能
- EditText
-> 入力を行う値に今回は制限をかけた。今回数字のみの入力しか許さないように設定する為に inputType は "number" で設定。numberで数字のみの設定をしているがdigitsでも数字の入力制限ができるらしい(digits="123"の場合、入力できる値は1,2,3の3つのみ)
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLength="1"
android:digits="0123456789"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/executionButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/editText2"/>
- 拡張関数
-> Kotlin独自の機能。クラスに外部からメソッド追加をするには継承が必要だったが、メソッド作成の際にクラス名.メソッド名()の形で書く事で、メソッド名の前に指定したクラス型のインスタンス全てで作成したメソッドを利用できるようになる。
↓ 定義 + 利用方法
Utik.kt(拡張関数の定義)
package com.example.sampleextensionfunctionapplication
object Util {
fun String.executionCalculation(value: String): Int {
val result = this.toInt() + value.toInt()
return result
}
}
MainFragment(定義した拡張関数は外部クラスからでも内部クラスからでも利用が可能)
今回は外部クラスで利用のため、インポート
// 外部からインポート
import com.example.sampleextensionfunctionapplication.Util.executionCalculation
editText1はString型なので定義した拡張クラスを参照して利用できている。
// 今回は拡張関数を用いた計算を利用
val result = editText1.executionCalculation(editText2).toString()
- FragmetのBundle機能
-> 別Fragmentへデータを渡す仕組み。今回は計算の結果を別Fragmentで表示するためにBundleを使用。Activity破棄が起こってもBundle内のデータは破棄されない特徴がある。データの保持・取得はキーとバリューの関係。
↓保存手順
MainFragment(別画面インスタンス生成後、Bundleにデータ保持しインスタンスにそのBundleを保存)
// Fragment生成
val resultFragment = ResultFragment()
// データ保存の為のBundleインスタンス生成
val args = Bundle()
// データ保存
args.putString(ResultFragment.BUNDLE_RESULT, result)
// FragmentにsetArgumentsでBundleを保持
resultFragment.arguments = args
ResultFragment(Bundle(前画面)に保持したデータを取得)
// Bundleを首藤
val args = arguments
// BundleからMainFragmentで保存した値を取得。ない場合はnullになる
val result = args?.getString(BUNDLE_RESULT)
今回実装した主要クラスのコードと一部レイアウトのコードを展開しておく
fragment_main(入力制限を主に行なったレイアウト)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLength="1"
android:digits="0123456789"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/executionButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/editText2"/>
<EditText
android:id="@+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLength="1"
android:digits="0123456789"
app:layout_constraintTop_toTopOf="@id/editText1"
app:layout_constraintBottom_toBottomOf="@id/editText1"
app:layout_constraintStart_toEndOf="@id/editText1"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/executionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/execution_button_text"
app:layout_constraintTop_toBottomOf="@id/editText1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Util.kt(計算を行う拡張関数定義クラス)
package com.example.sampleextensionfunctionapplication
object Util {
fun String.executionCalculation(value: String): Int {
val result = this.toInt() + value.toInt()
return result
}
}
MainFragment(数字入力 + 計算実行クラス)
package com.example.sampleextensionfunctionapplication
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.fragment.app.Fragment
// 外部からインポート
import com.example.sampleextensionfunctionapplication.Util.executionCalculation
class MainFragment : Fragment() {
companion object {
private const val EDIT_TEXT1 = "editText1"
private const val EDIT_TEXT2 = "editText2"
}
private var inputEditText1: EditText? = null
private var inputEditText2: EditText? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
inputEditText1 = view.findViewById<EditText>(R.id.editText1)
inputEditText2 = view.findViewById<EditText>(R.id.editText2)
val executionButton = view.findViewById<Button>(R.id.executionButton)
val editText1 = savedInstanceState?.getString(EDIT_TEXT1)
val editText2 = savedInstanceState?.getString(EDIT_TEXT2)
// Activity破棄復帰時入力中のデータがあれば復元する
if (editText1 != null) inputEditText1?.setText(editText1)
if (editText2 != null) inputEditText2?.setText(editText2)
// ボタン押下処理
executionButton.setOnClickListener {
val editText1 = inputEditText1?.text.toString()
val editText2 = inputEditText2?.text.toString()
if (editText1 == "" || editText2 == "") {
Toast.makeText(view.context, getString(R.string.toast_message), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
// 今回は拡張関数を用いた計算を利用
val result = editText1.executionCalculation(editText2).toString()
// Fragment生成
val resultFragment = ResultFragment()
// データ保存の為のBundleインスタンス生成
val args = Bundle()
// データ保存
args.putString(ResultFragment.BUNDLE_RESULT, result)
// FragmentにsetArgumentsでBundleを保持
resultFragment.arguments = args
parentFragmentManager.beginTransaction().replace(R.id.mainActivity, resultFragment)
.addToBackStack(this::javaClass.name).commit()
// 結果表示後値を何も表示しないようにする
inputEditText1?.text = null
inputEditText2?.text = null
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val editText1 = inputEditText1?.text.toString()
val editText2 = inputEditText2?.text.toString()
/* outState.putString(EDIT_TEXT1, editText1)
outState.putString(EDIT_TEXT2, editText2)*/
}
}
ResultFragment(結果を表示するクラス)
package com.example.sampleextensionfunctionapplication
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
class ResultFragment : Fragment() {
companion object {
const val BUNDLE_RESULT = "bundle_result"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_result, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Bundleを首藤
val args = arguments
// BundleからMainFragmentで保存した値を取得。ない場合はnullになる
val result = args?.getString(BUNDLE_RESULT)
val resultText = view.findViewById<TextView>(R.id.resultText)
resultText.text = result
}
}
Android:ON・OFF設定のSwitchとSwichのデータ保持の為にsharedPreferencesを使用したサンプルアプリ実装
作成したアプリ
- Switchを使ったON・OFF切り替えアプリ。ON・OFF切り替え時SwitchとSwitchの文言が変更。アプリキル->再起動時ON・OFFの状態をsharedpreferencesで復元するアプリ
使用した機能
- SharedPreferences
-> アプリ内にデータを保持しておく仕組み。アプリキル後も.xmlにデータが保持される。
↓ sharedPreferencesの扱い方の例
// context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)で生成
// 第一引数に用いる名前でsharedPreferencesインスタンスを生成・取得する
sharedPreferences = view.context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
// sharedPreferencesの値をゲットする場合
// 保存した名前で値を取得。値が見つからない場合は第2引数のdefValueの値を取得
val isSwitchEnabled = sharedPreferences?.getBoolean(IS_ENABLED_SWITCH, true)
// sharedPreferencesに値をセットする場合
// edit()で編集開始 -> putメソッドでsharedPreferencesにデータ保存 -> applyで変更を保存
val editor = sharedPreferences?.edit()
if (isSwitchEnabled != null) {
editor?.putBoolean(IS_ENABLED_SWITCH, isSwitchEnabled)
}
editor?.apply()
- Switch
-> Buttonを継承しており、ON・OFFをタップとスライドで切り替えられるView。text()ではSwitchのボタンの横に自動で文言が追加でき、isCheckedにtrueかfalseをセットする事によりSwitchのON・OFFを設定できる。SwitchのON ・OFFの切り替え時に処理を切り替えたい場合は,CompoundButton.OnCheckedChangeListenerを実装しSwitchのsetOnCheckedChangeListenerにセットする。これによりonCheckedChange()はSwichが切り替わったタイミングで呼ばれるようになる。
今回実装した主要クラスのコードを展開しておく
package com.example.samplesharedpreferencesapplication
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.Switch
import androidx.fragment.app.Fragment
class MainFragment : Fragment(), CompoundButton.OnCheckedChangeListener {
companion object {
private const val FILE_NAME = "fileName"
private const val IS_ENABLED_SWITCH = "isEnabledSwitch"
}
private var sharedPreferences: SharedPreferences? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
}
@SuppressLint("UseSwitchCompatOrMaterialCode")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedPreferences = view.context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
val sampleSwitch = view.findViewById<Switch>(R.id.sampleSwitch)
val isSwitchEnabled = sharedPreferences?.getBoolean(IS_ENABLED_SWITCH, true)
Log.d("sharedPreferences", "$sharedPreferences")
isSwitchEnabled?.let { setSampleSwitch(it, sampleSwitch) }
}
private fun setSampleSwitch(isSwitchEnabled: Boolean, sampleSwitch: Switch) {
sampleSwitch.setOnCheckedChangeListener(null)
sampleSwitch.isChecked = isSwitchEnabled
sampleSwitch.setOnCheckedChangeListener(this)
sampleSwitch.text =
context?.getString(R.string.switch_text1) +
isSwitchEnabled.toString() +
context?.getString(R.string.switch_text2)
val editor = sharedPreferences?.edit()
editor?.putBoolean(IS_ENABLED_SWITCH, isSwitchEnabled)
editor?.apply()
}
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
setSampleSwitch(isChecked, buttonView as Switch)
}
}
Android: ボタン押下時ランダムかつ動的に背景色変更とStyleで文字サイズ変更を行うサンプルアプリ実装について
作成したアプリ
- ボタン押下で背景色と画面中央の文字列のサイズが変更するアプリ
実装した機能
- 文字サイズを変更する為のStyle.xmlの定義
- ボタン押下処理
- どの文字サイズ・背景色を扱うかのランダム処理
- 文字サイズ変更処理
- 背景色変更処理
実装手順と詳細
1.文字サイズを変更する為のStyle.xmlの定義
文字サイズを変更するのみなので、styleタグの中のitemタグの中に文字サイズを定義したStyleを三つ用意
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="text1">
<item name="android:textSize">14dp</item>
</style>
<style name="text2">
<item name="android:textSize">24dp</item>
</style>
<style name="text3">
<item name="android:textSize">34dp</item>
</style>
</resources>
styleで定義したものを今回はFragment内のリストで扱うように実装
val stylesList = listOf(R.style.text1, R.style.text2, R.style.text3)
今回背景色に使う色は既存で入っているものを使う為color.xmlは触らず、このタイミングでstyleに対する背景色リストを作成
val colorList = listOf(R.color.purple_500, R.color.teal_200, R.color.white)
2.ボタン押下処理実装
2-1.ボタンタップ時のリスナーを定義
button.setOnClickListener {}
2-2.リスナー内でランダムにstyleとstyleに対する背景色を取得する実装
Q.ランダムでリスト内から要素を取得するには?
A_1.Randomインスタンスを生成し、nextInt(listsize: Int)を使用してランダムな値を取得する。今回引数にはリストの要素数を利用(ランダムで取得できる値が0 ~ 引数で指定した値 -1なので常にリストの要素番号を指定出来るようにする為)
A_2.取得したランダムな値を使ってリストからstyleとそれに対する背景色を取得する
// ランダムインスタンス生成
val random = Random()
// nextInt()を使用して 0 ~ (スタイルの要素数 - 1) の間でランダムな数値を一つ取り出す
val randomNumber = random.nextInt(stylesList.size)
// 取得してきたランダムな値をリストの要素番号の指定に利用して、styleと背景色を決定する
val style = stylesList[randomNumber]
val colorResource = colorList[randomNumber]
2-3.背景色変更と文字サイズ変更を実装
- 文字サイズ変更については、textViewに対してstyleを動的に変更する為に、textViewCompatのsetTextAppearance(textView: TextView, styleResources: Int)を利用する。
(メモ:Compatクラスは~Compatという名前で複数あり、~に記載されているクラスのサポートを行う互換性がありのクラスなので~クラスでできない事は~Compatクラスを使うと解決できる可能性がある。)
- 背景色変更については、レイアウトのsetBackGroundColor(colorInt: Int)を利用してカラーを設定。
ここで注意するべき点はsetBackGroundColor(colorInt: Int)の引数で必要なのはcolorIntに対し、リストから取得しているのはcolorResouce。
その為ResourceCompatクラスのgetColor(res: Resouces, colorResource: Int, theme: Theme)を用いてcolorResourceからColorIntに変更を行ってsetBackGroundColor(colorInt: Int)に背景色のセットを行う。
// TextViewにStyleを反映
TextViewCompat.setTextAppearance(text, style)
// ColorResourceをcolorIntに変更し、Layoutに背景色を反映
constraintLayout.setBackgroundColor(ResourcesCompat.getColor(resources, colorResource, null))
以上で今回作成したアプリは完成。
実装手順と詳細の一覧コードを下記に載せておく。
package com.example.samplerandamstyleapplication
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat
import androidx.core.widget.TextViewCompat
import androidx.fragment.app.Fragment
import java.util.*
class MainFragment : Fragment() {
companion object {
private const val CORRECTION_STYLE_NUMBER = 1
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val constraintLayout = view.findViewById<ConstraintLayout>(R.id.constraintLayout)
val text = view.findViewById<TextView>(R.id.randomColorText)
val button = view.findViewById<Button>(R.id.changeRandomButton)
val stylesList = listOf(R.style.text1, R.style.text2, R.style.text3)
val colorList = listOf(R.color.purple_500, R.color.teal_200, R.color.white)
button.setOnClickListener {
// ランダムインスタンス生成
val random = Random()
// nextInt()を使用して 0 ~ (スタイルの要素数 - 1) の間でランダムな数値を一つ取り出す
val randomNumber = random.nextInt(stylesList.size)
// 取得してきたランダムな値をリストの要素番号の指定に利用して、styleと背景色を決定する
val style = stylesList[randomNumber]
val colorResource = colorList[randomNumber]
// +1の補正をかけてスタイルの番号を表示する
text.text = (randomNumber + CORRECTION_STYLE_NUMBER).toString()
// TextViewにStyleを反映
TextViewCompat.setTextAppearance(text, style)
// ColorResourceをcolorIntに変更し、Layoutに背景色を反映
constraintLayout.setBackgroundColor(ResourcesCompat.getColor(resources, colorResource, null))
}
}
}
Android:今後の自己学習
今後学習予定
MVVMアーキテクチャーでRetrofitを使ってAPIを叩く(Moshi, Rx, OkHttp, Retrofitの使用あり) = https://tech.mti.co.jp/entry/2020/03/31/163321
MVVM学習 = https://qiita.com/s_emoto/items/08e46b18f72c3b303b12
RestAPI(WebApi学習) = https://qiita.com/masato44gm/items/dffb8281536ad321fb08
Retrofit詳細 = https://shim0mura.hatenadiary.jp/entry/2015/11/24/143000
デザインパターン「Factory Method」= https://qiita.com/shoheiyokoyama/items/d752834a6a2e208b90ca
Android:エラー発生時から原因調査・解決方法について
原因調査手順
1.ログを見る
----------
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.sampleweathersamplejavaapplication, PID: 8670
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sampleweathersamplejavaapplication/com.example.sampleweathersamplejavaapplication.MainActivity}: android.view.InflateException: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Error inflating class androidx.appcompat.widget.ActionBarContainer
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: android.view.InflateException: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Error inflating class androidx.appcompat.widget.ActionBarContainer
Caused by: android.view.InflateException: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Error inflating class androidx.appcompat.widget.ActionBarContainer
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:852)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1004)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at android.view.LayoutInflater.inflate(LayoutInflater.java:479)
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:905)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:815)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:703)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:195)
at com.example.sampleweathersamplejavaapplication.MainActivity.onCreate(MainActivity.java:12)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.NoSuchMethodError: No static method metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; in class Ljava/lang/invoke/LambdaMetafactory; or its super classes (declaration of 'java.lang.invoke.LambdaMetafactory' appears in /apex/com.android.art/javalib/core-oj.jar)
at androidx.core.view.ViewCompat.
----------
上記のようなエラーログが表示された場合
- 長い英文で表示されているが基本的には各ブロックの始まりを見れば良い。
----------
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sampleweathersamplejavaapplication/com.example.sampleweathersamplejavaapplication.MainActivity}: android.view.InflateException: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Binary XML file line #27 in com.example.sampleweathersamplejavaapplication:layout/abc_screen_toolbar: Error inflating class androidx.appcompat.widget.ActionBarContainer
----------
多くの場合この始まりに、最終結果(概要)->過程(詳細)の順でエラーが起きた原因が記載されている。
「java.lang.RuntimeException」 -> 「例外が発生した」
「Unable to start activity ComponentInfo」-> 「Activityを開始する事が出来ない」
「android.view.InflateException」-> 「インフレートで例外が発生した」
等「:」区切りで詳細が記載されているので知りたい所まで原因を追う
2.該当箇所を調べる
原因が分かったら原因が該当しそうな箇所を知らべにいく。
エラーログに青文字リンクがある場合、自分で記載したコードの中でエラーが発生した事を示しているのでその箇所を重点的に見る。
3.それでもわからなければGoogle検索
エラーメッセージで検索をかける。
この際概要ではなく一番奥の詳細エラーメッセージで検索をかけた方が良い。
Android: API実装について
今回APIのサンプル実装を行った
使用したプラグイン(プラグインとは? -> ソフトウェアの拡張用に作られたモジュール(部品)の事)
- Kapt(Kotlin-Annontation-Process-Tools) = アノテーション(先頭が@で始まるもの)を使ってコードを自動生成出来るようになる
使用したライブラリ一覧(ライブラリとは? -> 便利なモジュール群(複数の部品)の集まりの事)
- Moshi = JSON形式のデータを読み込んで、JavaObject(Entityクラス等)に変換するもの。これがないとアプリ側で、APIを叩いてきた結果を扱う事が困難になる。
- Rx = 非同期処理でリアクティブプログラミングをするためのライブラリ。リアクティブプログラミングとはデータが流れてくる事(ストリーム)に着目し、
- Retrofit = API Clientを作成するのに用いる
- OkHttp = Http通信を用いる際に使う