【Android開発】Can’t toast on a thread that has not called looper.prepare()エラーの解決策

表題のエラーの解決策です。

原因としては、本来Toastはメインスレッドでのみ実行できる仕様ですが、サブスレッドで実行してしまっていることです。

解決策はHandlerを使ってメインスレッドで実行するように変更すればOKです。
以下、具体例です。

修正前

1
2
3
4
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
Toast.makeText(getContext(), "エラーメッセージ", Toast.LENGTH_LONG).show()
});

修正前

1
2
3
4
5
6
7
final Handler handler = new Handler();
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
handler.post(() -> {
Toast.makeText(getContext(), "エラーメッセージ", Toast.LENGTH_LONG).show()
});
});

handler.postの処理はnew Handler()したスレッドで実行されるのでnew Handler()はメインスレッドで実行する必要があります!

Android開発は非同期処理が鬼門ですよね、、

おわり

【Android】Spinnerの初期表示時にonItemSelectedが呼ばれちゃう対処法

Spinnerの初期表示時にonItemSelectedが呼ばれちゃう対処法です。
初回に呼ばれるまでfocusableをfalseにしておき、初回に呼ばれたらtrueにする作戦です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HogeFragment extends BaseFragment implements AdapterView.OnItemSelectedListener {

public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
spinner.setFocusable(false);
})

@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// 画面表示時にonItemSelectedが呼ばれちゃう対処法
Spinner spinner = mRoot.findViewById(R.id.spinner_id);
if (!spinner.isFocusable()) {
spinner.setFocusable(true);
return;
}
}
}

以上です!

【Android】画像タップで拡大表示。さらにピンチインアウト可能にする。

これは何

Android 開発で ImageView をタップしたら画像を拡大表示し、それをピンチインアウトでズームできるようにする方法です。

利用ライブラリ

https://github.com/davemorrissey/subsampling-scale-image-view

ライブラリインストール方法は上にあるので割愛します

修正内容

1
2
3
4
5
6
7
8
9
10
// 拡大対象のImageViewにタップ時のリスナーをセット
mImageView.setOnClickListener(v -> {
SubsamplingScaleImageView imageViewEnlarged = new SubsamplingScaleImageView(getContext());
Bitmap bitmap = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
imageViewEnlarged.setImage(ImageSource.bitmap(bitmap));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(imageViewEnlarged);
builder.setNegativeButton(R.string.label_close, null);
builder.show();
});

てか Android 開発、情報少なすぎません?泣
ただ画像をタップして拡大表示してピンチインアウトするだけのことがなんでこんなに情報がないのか?
なかなかライブラリに辿り着けなくて、結構ハマっちゃいました、、
ツライけどお互い頑張りましょう、、w

参考

[Android]ImageView の scaleType を秒で決める
[Android]メモリリークを起こさずに大きな画像を表示させるライブラリ「Subsampling Scale Image View」

【Android】BuildConfigが見つからないエラー

BuildConfig が見つからないエラーになった時の解決策です

  1. Android Studio >ファイル>キャッシュの破棄>再起動
  2. Android Studio >ビルド>プロジェクトをクリーンにする
  3. Android Studio >ビルド>プロジェクトの再ビルド

参考

BuildConfig not getting created correctly (Gradle Android)

【Android】ProGuardからR8に変更したらアプリが落ちるようになった原因と解決策

事象

ProGuard から R8 に乗り換えた後、アプリをビルドして起動したところ、
いくつかの画面がクラッシュしてアプリが落ちる不具合が発生しました

原因

GSON を利用していた場合、文字列をそのまま利用する必要があるが、難読化されてしまったため

解決策

proguard-project.txt に以下のコードを追加する

1
2
3
4
5
### for Gson in R8
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep,allowobfuscation @interface com.google.gson.annotations.SerializedName

これ結構重要なところなのに情報が少なすぎてビビる、、
Android 開発の情報もっと増えてくれ〜

参考

R8 互換性 FAQ

【Android】aabファイルをリバースエンジニアリングしてみた

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# abb→apk
brew install bundletool
bundletool build-apks --mode=universal --bundle=./app-release.aab --output=./app-release.apks
unzip app-release.apks

# apk→dex
unzip universal.apk

# dex→jar
# dexをjarに変換するためのツールをダウンロードする
# https://sourceforge.net/projects/dex2jar/files/
chmod 777 dex2jar-2.0/d2j-dex2jar.sh
./dex2jar/d2j-dex2jar.sh classes.dex

# jar→class
unzip classes-dex2jar.jar
# classファイルの中身が見れるツールをダウンロード
wget https://github.com/java-decompiler/jd-gui/releases/download/v1.6.6/jd-gui-1.6.6.jar
java -jar jd-gui-1.6.6.jar

# あとは見たいclassファイルを選択すればOK

こんな感じ

良いね!

参考
Android の APK を逆コンパイルする
Java Decompiler project の JD-GUI で JAR ファイルからソースコードを生成する

AndroidアプリでiBeaconを受信する

利用ライブラリ

https://github.com/AltBeacon/android-beacon-library

設定を変更

app/build.gradle に以下を追加

1
2
3
dependencies {
implementation 'org.altbeacon:android-beacon-library:2+'
}

受信コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import org.altbeacon.beacon.*
import kotlin.collections.ArrayList

class HogeActivity : AppCompatActivity(), BeaconConsumer {
private val TAG: String = "HogeActivity"
// ビーコンマネージャ宣言
private lateinit var mBeaconManager: BeaconManager
// iBeaconのデータを認識するためのParserフォーマット
private val IBEACON_FORMAT: String = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"

private val mRegion = Region("unique-id-001", null, null, null)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_Hogee)
// ビーコンマネージャのインスタンス作成
mBeaconManager = BeaconManager.getInstanceForApplication(this)
// iBeaconのフォーマットをParserへ渡す
mBeaconManager.beaconParsers.add(BeaconParser().setBeaconLayout(IBEACON_FORMAT))
}

override fun onPause() {
super.onPause()
mBeaconManager.unbind(this)
Log.d(TAG, "ビーコンサービスを解除した")
}

override fun onResume() {
super.onResume()
mBeaconManager.bind(this)
Log.d(TAG, "ビーコンサービスを起動した")
}

override fun onBeaconServiceConnect() {

// 初期化。これがないと画面を2回以上開いた時に2重でデータを受信してしまう
mBeaconManager.removeAllMonitorNotifiers()
mBeaconManager.removeAllRangeNotifiers()
mBeaconManager.rangedRegions.forEach {region ->
mBeaconManager.stopRangingBeaconsInRegion(region)
}

mBeaconManager.addMonitorNotifier(object : MonitorNotifier {
// ビーコン信号の領域内へ侵入した際に呼ばれる
override fun didEnterRegion(region: Region) {
Log.d(TAG, "ビーコンが近くにいる!ビーコンが発信するデータの受信を開始する")
}

// ビーコン信号の領域外へ退出した際に呼ばれる
override fun didExitRegion(region: Region) {
Log.d(TAG, "ビーコンが近くにいなくなった。。ビーコンが発信するデータの受信を停止する")
}

// 領域への侵入/退出のステータスが変化した際に呼ばれる
// 一見didEnterRegionやdidExitRegionで十分に見えるが、実際にはこっちしか呼ばれないことがある
override fun didDetermineStateForRegion(state: Int, region: Region) {
Log.d(TAG, "ビーコンの侵入/退出ステータスが変化: $state")
try {
if (state == 1) {
mBeaconManager.startRangingBeaconsInRegion(region)
} else {
mBeaconManager.stopRangingBeaconsInRegion(region)
}
} catch (e: RemoteException) {
Log.d(TAG, "didDetermineStateForRegion e = " + e.message)
}
}
})

try {
mBeaconManager.startMonitoringBeaconsInRegion(mRegion)
Log.d(TAG, "ビーコンが近くにいるかどうかの監視を開始した。")
} catch (e: RemoteException) {
Log.e(TAG, "Exception", e)
e.printStackTrace()
}

mBeaconManager.addRangeNotifier(RangeNotifier { beacons, region ->
// ビーコンにデータが入っていない場合はbeaconsは[]となる
// また、設定から位置情報を許可した状態でない場合もbeaconsは[]となる
Log.d(TAG, "ビーコンデータ:${beacons}")
for (beacon in beacons) {
Log.d(TAG, "UUID:" + beacon.id1 + ", major:" + beacon.id2
+ ", minor:" + beacon.id3 + ", Distance:" + beacon.distance
+ ",RSSI" + beacon.rssi)
}
})
}
}

その他

検索したら一番上に出てくる qiita の記事とかだと removeAllMonitorNotifiers がなかったりして動かなかくて結構ハマった。。
以下の参考記事のおかげで解決した!
ありがてえ!

参考

[Android]AltBeacon を使って iBeacon を受信する

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×