- 投稿日:2020-10-23T20:16:01+09:00
【超初心者向け】Android入門 EditText編
はじめに
前回はButtonを解説したので、今回はEditTextについて解説していきたいと思います。
記事の対象
- Androidアプリ開発を始めたばかりの人
- 前回の記事を読んだ方
EditText
EditTextとは、入力可能なテキストフィールドです。
1. EditTextを配置していく
activity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="30dp" android:layout_marginTop="30dp" android:layout_marginEnd="30dp" app:layout_constraintTop_toBottomOf="@id/textView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="Click!" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/editText" /> </androidx.constraintlayout.widget.ConstraintLayout>前回のTextViewとButtonの間にEditTextを入れていきます。
2. 実行
一旦このまま実行していきましょう。
下線が付いているところがEditTextです。このまま入力するだけだとつまらないですよね。
なので今回はボタンを押したら上のTextViewに入力したテキストを反映させるようにしましょう。
3. MainActivity.ktでEditTextを認識させる
MainActivitypackage com.example.helloworld import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.EditText import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.textView) val editText = findViewById<EditText>(R.id.editText) val button = findViewById<Button>(R.id.button) button.setOnClickListener { textView.text = editText.text } } }val editText = findViewById<EditText>(R.id.editText)でActivityにEditTextを認識させます。
4.ボタンをクリックした時の動作を記述する
button.setOnClickListener { textView.text = editText.text }textViewにeditTextの入力値を代入します。
5.実行!
EditTextに何かしら入力してから、ボタンをクリックしてみてください!
そうするとEditTextの入力値がTextViewに反映されてると思います。6.まとめ
今回はEditTextの使い方、というかEditTextを使ってTextViewに入力を反映させる方法を解説しました。
今更ですが、このシリーズは最終的にTodoアプリを作っていきたいと思っていますので最後までお付き合いいただけたらと思います。
- 投稿日:2020-10-23T19:13:38+09:00
MutableLiveData は双方向データバインディングにも使える
データバインディングを利用して EditText に入力した内容を ViewModel に通知する処理を実装していました。
<EditText android:id="@+id/form_user_name_edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{viewmodel.userName}" />FormViewModel.ktclass FormViewModel : ViewModel() { var userName = ObservableField<String>() }上記の実装だと、
FormViewModel
からuserName
を変更すると View に反映されますが、逆に画面からテキストを入力してもFormViewModel
のuserName
に反映されません。
つまり、ViewModel -> View は対応しているが、View -> ViewModel には対応していません。双方向データバインディング
上記の問題を解決するためには、「双方向データバインディング」にする必要があります。
名前の通り、ViewModel -> View だけでなく、View -> ViewModel にも対応するための仕組みです。対応方法
それぞれ以下のように変更します。
<EditText android:id="@+id/form_user_name_edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@={viewmodel.userName}" />xml の方は
@
と{
の間に=
を追加するだけです。FormViewModel.ktclass FormViewModel : ViewModel() { val userName = MutableLiveData<String>() }ViewModel の方は
FormViewModel
のuserName
をMutableLiveData<String>
に変更するだけです。
また、MutableLiveData を使わない、従来型の方法(公式サイト記載)もあります。FormViewModel.ktimport com.example.simplemvvmapp.BR class FormViewModel : BaseObservable() { @Bindable var userName: String = "" set(value) { field = value notifyPropertyChanged(BR.userName) } }ですが MutableLiveData を使う方が簡単に書けますし、ライフサイクルにも考慮してくれるので、MutableLiveData を使うことをお勧めします。
まとめ
MutableLiveData は双方向データバインディングにも使えるということがわかりました。
普通にデータバインディング以外にも使えるのでもっと積極的に使っていきたいです。参考 URL
- 投稿日:2020-10-23T17:26:05+09:00
音声認識
音声認識を取り入れたアプリを開発した時にいろいろ詰まったことがあったので備忘録。
何を使った??
まず音声認識をするのに使ったのはSpeechRecognizer!
Androidの音声認識をやるにはこれが基本みたいです。やりたいこと
常に音声を拾いたい!拾ったら画面に認識した言葉を文字列として表示したい!
詰まったところ
ひとつめ
いざ音声認識をしてみたのですが、一回拾ったらその次の言葉を拾ってくれない。。
ええええ、なんで?となって調べまくりました。
連続して音声を拾うには、音声の取得が終わったところでまたSpeechRecognizer#startListening()
を呼べばいいそう!
音声認識した文字列を受け取った先でstartListeningするためにリスナーを用意しました。
エラーの時も処理を継続させたかったので、onError
時にもリスナーをよんでいます!ざっくりですが、こんな感じ。
▼SpeechRecognizerのコールバック用クラス
class SpeechRecognizerListener(private val listener: SpeechResponseListener) : RecognitionListener { // 音声結果を返却する interface SpeechResponseListener { fun onResultsResponse(speechText: String) } ・・・略・・・ // ネットワークまたは、音声認識エラー override fun onError(error: Int) { listener.onResultsResponse("") } // 認識結果 override fun onResults(bundle: Bundle?) { ・・・略・・・ // 結果を返却する if (speechText.isNullOrEmpty()) { listener.onResultsResponse("") } else { listener.onResultsResponse(speechText) } } }▼リスナー先
override fun onResultsResponse(speechText: String) { if (speechRecognizer != null) { // SpeechRecognizerをdestroyする speechRecognizer!!.destroy() } setupSpeechRecognizer() setupRecognizerIntent() speechRecognizer.startListening(intent) muteBeepSoundOfRecorder(true, context) }二回目以降の
startListening()
を呼び出す前に、destroy()
しないとうまくいかないことがあるみたいなので、nullじゃなかったらdestroy
するようにしてます。これで、連続して音声認識してくれることに成功しました。
ふたつめ
音声認識中になんだか音がピコンピコンうるさい。。
startListening
する時にmuteBeepSoundOfRecorder
というとこで音ミュートにしたりしてるのになぜ?!と詰まる。▼実際のイメージ
鳴り続ける音。 pic.twitter.com/beI7ruMiY6
— tg_kondo (@KondoTg) October 20, 2020これじゃぴこぴこうるさいですよね。
でもこれを追加しただけで音が鳴り止んだんです。am.adjustStreamVolume(AudioManager.STREAM_SYSTEM, AudioManager.ADJUST_MUTE, 0)
AudioManager.STREAM_MUSIC
に対してだけしか操作してなかったんですけど、AudioManager.STREAM_SYSTEM
が必要だったんですねえ。笑▼STREAM_SYSTEMを入れた後のイメージ
音が止んだ! pic.twitter.com/jNhp8t2cuz
— tg_kondo (@KondoTg) October 20, 2020難しいなあ。
- 投稿日:2020-10-23T14:41:02+09:00
Android Navigationでアニメーション付きで画面遷移させる
config.xmlとアニメーション用のxmlを作成する
main/res/values/config.xml<?xml version="1.0" encoding="utf-8"?> <resources> <integer name="config_transitionAnimTime">400</integer> </resources>res/anim/slide_in_right.xml<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="50%p" android:toXDelta="0" android:duration="@integer/config_transitionAnimTime" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="@integer/config_transitionAnimTime" /> </set>res/anim/slide_out_left.xml<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="0" android:toXDelta="-50%p" android:duration="@integer/config_transitionAnimTime" /> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="@integer/config_transitionAnimTime" /> </set>res/anim/slide_in_left.xml<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="-50%p" android:toXDelta="0" android:duration="@integer/config_transitionAnimTime" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="@integer/config_transitionAnimTime" /> </set>res/anim/slide_out_right.xml<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="0" android:toXDelta="50%p" android:duration="@integer/config_transitionAnimTime" /> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="@integer/config_transitionAnimTime" /> </set>navigationファイルでアニメーションを設定する。
res/navigation/nav_graph.xml<action android:id="@+id/action_hogeFragment_to_fugaFragment" app:destination="@id/fugaFragment" app:enterAnim="@anim/slide_in_right" app:exitAnim="@anim/slide_out_left" app:popEnterAnim="@anim/slide_in_left" app:popExitAnim="@anim/slide_out_right" />
- 投稿日:2020-10-23T14:08:44+09:00
【備忘録】プロジェクト作成
新規プロジェクト作成
※著者のAndrid Studioは日本語化されています。
- プロジェクト構成
- 名前 変更は面倒なのでよく考える
- パッケージ名 所持ドメインの逆順が一般的?
- 保存ロケーション 保存先どこでも
- 言語(JAVA or Kotlin) 今回はJAVA
- 最小SDK 使用する環境に合わせて選択
- Use legacy~ とりあえずチェック
[完了]を推すとプロジェクトが作られる
戻る
- 投稿日:2020-10-23T13:32:07+09:00
【備忘録】業務用Andoridアプリ開発への挑戦
前提情報
挨拶
乱筆乱文にて失礼いたします
業務改善のためAndroidアプリの開発に挑戦することに
開発手順を検討しつつ進める所存
都合により細部をぼかすことがあるはご容赦いただきたし
内容は随時追加予定選定経緯
- 紙に記入→PCへ入力の業務が非常に多く
データ化されていない情報も多い- PC台数が少なく調べものもままならない
- ネットワーク環境が弱いためオフラインでも使える
→Androidでアプリ開発が最適では
投稿者スペック
- システム管理者3年目(2020/10現在)
- Android使用経験なし
- Java経験なし
環境確認
- Windows10 Pro + Java7
- Android Studio 4.0
- サーバー + SQL Server + Tomcat7
- Androidタブレット(Android 9 Pie)
開発手順
要件定義
- ターゲット補足
- 理想形の構築
- 現状の確認
- すり合わせ
Androidアプリ
- プロジェクト作成(Android Studio)
- 基本情報設定(gradle,manifests)
- GUI作成(activity_.xml)
- SQLite関連
- http通信関連(okhttp3)
WebAPI
- Tomcat7関連
- http通信関連
- 帳票印刷
- 投稿日:2020-10-23T13:14:37+09:00
【未経験の方必見】アプリエンジニアが狙い目な理由
こんにちは。
この記事では、
「なぜアプリエンジニアが狙い目なのか」
「なぜアプリエンジニアが少ないのか」
にフォーカスを置いて書いていきます。なぜアプリエンジニアが狙い目なのか
理由は簡単です。
母数が少ないからです。
この理由から、需要に対しての供給が足りていません。
経験者の採用を諦め、未経験者の教育に力を入れている会社もあるようです。実際、弊社では全ポジションを募集していますが、
アプリエンジニアの面談回数はサーバーサイドやフロントに比べてかなり少ないです。サーバーサイドやフロントに対してアプリエンジニアは
大体5分の1〜10分の1ほどしか応募がきません。
アプリエンジニアの中でもAndroidエンジニアは特に、iOSエンジニアの10分の1ほどしか
来ないため、Androidエンジニアの希少性はかなり強いと言えます。なぜアプリエンジニアが少ないのか
Twitterを通していろいろな意見を見たり、実際に勉強会で交流を通して意見を聞いてきた中で、
アプリエンジニアが少ない理由がいくつか見えてきました。苦手意識
初学者の人はまず、「どの言語をやるか」「どのポジションをやるか」
について選択が迫られます。
この時、「フロントの方がすぐに結果が見えてわかりやすい」
という意見がよく見られました。また、「プログラミングの勉強を始めるならまずHTML・CSS」
という暗黙の何かがあるように思えました。
(「HTML・CSSはプログラミング言語じゃない」という気持ちはわかります)
そこから派生してJavascript・jQuery・Vue.jsなどフロントエンジニアの道を歩む人が
多いのではないでしょうか。アプリエンジニアを勧める環境が少ない
初学者の言語選択に対して、「PHP×Javascript」「Rails」
を勧める環境が多いように感じられました。
特に「PHP×Javascript」は、「初学者が最短で仕事につくのに最適だ」「一番簡単だ」
と勧める人をよく見かけました。Railsが多い理由はスクールの影響が大きいかと思います。
確かにわかりやすいですし、レール(rail)にのっとった書き方ができるため初学者に優しいかもしれませんが、
逆に言えば経験者が爆速開発するためのフレームワークで少数精鋭でやるものだと言う声もあり、
初学者に本当に優しいのかと聞かれると
はっきりとYesと言えない言語です。これらから分かるように、初学者にとってウケがいい言語は「手頃(そう)な言語」で、
またウケを狙った(?)スクールによってRails学習者が増え、
バランスが偏ってしまっているのではないかと推測しました。iOSエンジニアはMacBookが必要
正確に言えば、MacBookがなくてもOSをぶち込めれば良いのですが、
いきなりの初学者がそんなことを思いつきませんし、できません。
勉強を始めるためにMacBookを買うと言うフィルターがあるために、
アプリエンジニアの頭数が少ないのだと推測しました。Androidユーザーが少ない
アプリエンジニアの道に進んだとしても、iOSかAndroidかの分岐があります。
ここでは、多くの人がiOSエンジニアを選択することでしょう。
なぜなら日本では、世界でも稀にiPhoneユーザーが多いためです。
Android端末を使ったことがないのにAndroidの勉強をする人はかなり稀です。結局のところアプリエンジニアは少ない
これらの理由から、エンジニア市場には偏りが見られます。
iOSエンジニアをやるにしても高価なMacbookが必要で、
Androidエンジニアへの道も国内OSシェアからその道に進む人があまりいません。サーバー・インフラ>>アプリエンジニア(iOS >> Android)
のような構図が見られるため、ただでさえアプリエンジニアは狙い目であるのに
Androidエンジニアとなれば更に母数が減るのです。これからプログラミングの勉強を始めようと考えている方がいましたら、
ぜひアプリエンジニアを検討してみてはいかがでしょうか。誤解してはいけない
誤解してはいけないのが、「供給が足りないのだから簡単なんだ!」と言うことです。
あくまでも一定レベルのスキルは必要であり、それなりの勉強時間が必要になります。
はっきり言ってエンジニアになるには大変です。
数ヶ月でなれる人は天才だと思います。最後まで記事を読んでいただきありがとうございました。
- 投稿日:2020-10-23T12:40:46+09:00
【超初心者向け】Android入門 Button編
はじめに
前回Android入門 TextView編をやったので今回はButtonに関して解説していきたいと思います。
記事の対象
- Androidアプリ開発初心者
- Androidをこよなく愛す人
- 前回の記事を読んだ方
Buttonとは
その名の通りボタンです。
押して何かしらのアクションを起こすものですね。1. activity_main.xmlにButtonを追加する。
activity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="Click!" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textView" /> </androidx.constraintlayout.widget.ConstraintLayout>Buttonの中に書いてあることは最初は無視でいいです。
簡単に説明すると、Buttonがどのくらいの大きさで(layout:width, layout_height)、上にどれだけ余白をつけて(layout_marginTop)、ボタンのテキストはどうするか(text)、 どの位置に配置するか(layout_constraintHogetHoge)です。興味がある方は色々試してみてくださいねー!
2. MainActivity.ktでButtonを定義する
MainActivity.ktclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.textView) textView.text = "こんにちは、世界" val button = findViewById<Button>(R.id.button) } }前回の続きからなので、textViewが定義されたままですね。
その下にボタンでボタンを定義していきましょう
勘がいい方はお気づきかと思いますが、findViewByIdメソッドがxml上で定義したidを元にViewを見つけてくれるんですね!
3.クリックリスナーを定義する
クリックリスナーというのはボタンをクリックした時にどんな動作をさせるかというものです。
今回はボタンをクリックしたらtextViewの文字がClicked!になるようにしましょう。先ほどのval button = ~~の下に以下の一文を加えてください
button.setOnClickListener { textView.text = "Clicked!" }4.実行
さぁ実行してみましょう!
どうでしょう?ボタンをクリックしたらこんにちは、世界がClicked!になりましたね。
5.まとめ
今回はButtonの解説をしていきました。
上記の通りにやっても動かない等あれば気軽にコメントやTwitterのDMに質問ください!
- 投稿日:2020-10-23T12:40:24+09:00
【超初心者向け】Android入門 TextView編
はじめに
前回Android入門 Hello World編を書いたので、今回はHello World以外の出力をしてみたいと思います。
記事の対象
- Android入門者
- Android開発してみたい人
- 前回の記事を読んだ方
TextViewとは
TextViewはテキスト表示するためのViewです。
前回の続きから書いていきましょう。
1. activity_main.xmlを確認
左側のディレクトリ構成を確認します。
app/res/layoutの中にactivity_main.xmlがあることがわかりますね。
基本的にレイアウトファイルはapp/res/layout下に配置していきます。2. xmlを見てみる
上記の画像の右上のタブが今はDesignが指定されていますが、これをSplitにします。
この画面になっていると思います。
このTextViewの中にIDを付与してきます。
activity_main.xml<TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />3. MainActivityを見てみる
app/java/{package_name}を見てみるとMainActivity.ktがあることがわかります。
こんな感じですね。
このonCreateメソッドの中で、Viewをいじっていきます。MainActivity.ktpackage com.example.helloworld import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.textView) textView.text = "こんにちは、世界" } }val textView = findViewById<TextView>(R.id.textView)この一文でxmlで定義したTextViewをActivity側で認識させます。
textView.text = "こんにちは、世界"この一文で認識したTextViewに新しい文を追加します。
4. 実行
さぁ、実行してみましょう!
スタートボタンを押すとどうでしょう。
Hello Worldがこんにちは、世界に変わってますね。
まとめ
今回はTextViewを使って新しい文を表示させました。
動的に新しい文を表示させたい場合はこんな感じにすればOKですね。
- 投稿日:2020-10-23T12:39:55+09:00
【超初心者向け】Android入門 Hello World編
はじめに
今回は超初心者向け、AndroidでHello worldを出力するだけの記事です。
記事の対象
- Androidアプリ開発をしたことがない人
- Androidに挑戦してみたい人
- Hello World大好きな人
環境構築
Android Studioをインストールするだけです。
初めてのAndroidアプリ
1. Create New Projectを選択します。
2. Empty Activityを選択しNextを押下
3. アプリ名を決めます
Name: アプリ名(今回はHelloWorldにしました)
Package Name: パッケージ名(無視でいいです)
Save Location: 保存場所
Language: 言語(Kotlin or Java)
Minimum SDK: デフォルトで構いません。これらが終わったらFinishを押下します。
4. Androidアプリの雛形が完成します
5. Android端末を持っている方
端末によって違いますが、設定->デバイス情報といくと下の方にビルド番号というのがあるので、それを5回ほどタップします。
すると、「開発者オプションが有効になりました」という文言と共に、開発者オプションの設定ができるようになります。開発者オプションを開き、USBデバッグをONにし、PCと繋げると、、、
ここのスマホアイコンのところに自分の端末が表示されます。
あとはスタートボタン(再生ボタンみたいなやつ)を押すだけ!!Hello Worldが表示されます。
6. Android端末を持っていない方
右から3番目、AVD Managerを開きます。
Create Virtual Deviceをクリックします。
適当な端末を選び、適当なOSバージョンを選びます。
そうすると、PC上で動作してくれる仮想端末が完成します。あとはスタートボタン(再生ボタンみたいなやつ)を押すだけ!!
Hello Worldが表示されます。
7. 完成
非常に簡単ですね!!
よくわからない環境構築をしなければHello Worldすら表示させてくれないとなると、初心者の方は心が折れそうになります。
ですが、AndroidアプリはAndroid StudioをインストールするだけでHello Worldを表示してくれます。まとめ
ここまで記事を書きながら10分でHello Worldを表示させることができました。(インストール時間は除く)
この記事通りにやってもできない方はQiitaのコメントか、TwitterのDMに連絡いただければ解決しますので気軽に相談してください!
- 投稿日:2020-10-23T11:41:25+09:00
【Android】APKをデコンパイルしてみる
はじめに
最近Android開発を始めて、絶賛Kotlin勉強中の者です。今回ソースコードを難読化してbuildすることがあり、本当に難読化できているのかを確かめてみたかったので、デコンパイルすることにしました。ただ、なかなか思うようにデコンパイルができなかったので、成功した方法を備忘録として残しておきます。
今回は、下図の通り、build前のソースコードをKotlinで書いたので、
.kt
ファイルをbuildして.apk
ファイルになったAndroidのコードをデコンパイルし、.java
ファイルを作成しました。環境・バージョン
- macOS Catalina バージョン 10.15.6
失敗した方法
調べるとよく出てくる
dex2jar
とjad
を使う方法です。
この方法が定番なのかなと思ったので、この方法でデコンパイルをやってみましたが、わたしの場合上手くいきませんでした。
dex2jar
やファイルを展開する手順を踏むことで、.class
ファイルが生成されます。
手順詳細はこちらへ。その後、
jad
を使って.class
を.java
にデコンパイルします。$ jad hoge.classすると、次のようなエラーが出て、デコンパイルが進まなくなってしまいました。
Bad CPU type in executable考えられる原因
調べた限り、確実な情報は出てこなかったので、推測です。
- 現在のMac OSでは64bitのアプリケーションしか動作しないが、jadは32bitのツール?
- jadは少し前からアップデートが止まっているみたい?
上記みたいなところが原因かなと思います。
きっと他にツールがあるはず、諦めます!成功した方法
jadx
を使う方法を使うと簡単にデコンパイルすることができました。
この方法はJDKが入っていなくても実行できます。インストール
macだとHomebrewを使って簡単にインストールすることができます。
$ brew install jadx実行
実行はこのコマンドのみ!
特にPATHを通すこともなく、どこからでも開くことができます。$ jadx-gui実行すると、下記のような画面が開くので、デコンパイルしたいapkファイルを開きます。
これでデコンパイルは完了です。
非常に簡単ですね!実行結果
元のコード(.kt)
package com.example.testqiita import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var message = findViewById<TextView>(R.id.text) val buttonEnglish = findViewById<Button>(R.id.button_en) val buttonJapanese = findViewById<Button>(R.id.button_jp) buttonEnglish.setOnClickListener { message.setText("Hello World!") } buttonJapanese.setOnClickListener { message.setText("こんにちは 世界!") } } }デコンパイルしたコード(.java)
ファイルを開くと、下記のようなディレクトリ構成で出力されます。
MainActivity
は下記のように3つのファイルに分かれていますが、同じ処理が読み取れることがわかります。MainActivitypackage com.example.testqiita; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import kotlin.Metadata; import kotlin.jvm.internal.Ref; @Metadata(bv = {1, 0, 3}, d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\u0007"}, d2 = {"Lcom/example/testqiita/MainActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_debug"}, k = 1, mv = {1, 1, 16}) /* compiled from: MainActivity.kt */ public final class MainActivity extends AppCompatActivity { /* access modifiers changed from: protected */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((int) R.layout.activity_main); Ref.ObjectRef message = new Ref.ObjectRef(); message.element = (TextView) findViewById(R.id.text); ((Button) findViewById(R.id.button_en)).setOnClickListener(new MainActivity$onCreate$1(message)); ((Button) findViewById(R.id.button_jp)).setOnClickListener(new MainActivity$onCreate$2(message)); } }MainActivity\$onCreate\$1package com.example.testqiita; import android.view.View; import android.widget.TextView; import kotlin.Metadata; import kotlin.jvm.internal.Ref; @Metadata(bv = {1, 0, 3}, d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\u0010\u0000\u001a\u00020\u00012\u000e\u0010\u0002\u001a\n \u0004*\u0004\u0018\u00010\u00030\u0003H\n¢\u0006\u0002\b\u0005"}, d2 = {"<anonymous>", "", "it", "Landroid/view/View;", "kotlin.jvm.PlatformType", "onClick"}, k = 3, mv = {1, 1, 16}) /* compiled from: MainActivity.kt */ final class MainActivity$onCreate$1 implements View.OnClickListener { final /* synthetic */ Ref.ObjectRef $message; MainActivity$onCreate$1(Ref.ObjectRef objectRef) { this.$message = objectRef; } public final void onClick(View it) { ((TextView) this.$message.element).setText("Hello World!"); } }MainActivity\$onCreate\$2package com.example.testqiita; import android.view.View; import android.widget.TextView; import kotlin.Metadata; import kotlin.jvm.internal.Ref; @Metadata(bv = {1, 0, 3}, d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\u0010\u0000\u001a\u00020\u00012\u000e\u0010\u0002\u001a\n \u0004*\u0004\u0018\u00010\u00030\u0003H\n¢\u0006\u0002\b\u0005"}, d2 = {"<anonymous>", "", "it", "Landroid/view/View;", "kotlin.jvm.PlatformType", "onClick"}, k = 3, mv = {1, 1, 16}) /* compiled from: MainActivity.kt */ final class MainActivity$onCreate$2 implements View.OnClickListener { final /* synthetic */ Ref.ObjectRef $message; MainActivity$onCreate$2(Ref.ObjectRef objectRef) { this.$message = objectRef; } public final void onClick(View it) { ((TextView) this.$message.element).setText("こんにちは 世界!"); } }おわりに
いろいろ躓きましたが、結果的に簡単にデコンパイルできることがわかりました。こんなに簡単にソースの中身を見ることってできるんですね。びっくりです。同時に、難読化や堅牢化の必要性も改めて感じました。
堅牢化できるツールってすごく高価なようですね。。なので、リバースエンジニアリングされることを想定して被害が最小限で済むような設計をする必要がありますし、そのあたりは今後勉強しないといけないところだなと思いました。ちなみに、Windowsでもjadxは使えるそうです!(Windows環境がなかったので、試せていません。)
失敗した方法(dex2jar、jad)・手順詳細
それぞれのツールのインストール、デコンパイルの方法の詳細を一応残しておきます。
dex2jar
dex2jarのインストールとPATHの設定を行います。
このツールは.dex
を.jar
(.class
を圧縮したファイル)に変換するために使うものです。$ brew install dex2jar $ echo 'export PATH="$PATH:/usr/local/Cellar/dex2jar/2.0/bin"' >> ~/.bash_profile $ source ~/.bash_profilejad
jadのインストールを行います。
このツールは.class
を.java
に変換するために使うものです。インストールに必要なcaskは、brewに標準で組み込まれるようになったそうです。
$ brew install homebrew/cask/jadターミナルでJDKを使えるようにする
d2j-dex2jar
を使うにあたりJDKをインストールする必要があるようです。ただ、今回はJDKをOracleのサイトからインストールするのではなく、Android studioで使っているJDKをターミナルで使えるようにするために、PATHを通しました。$ echo 'export PATH=$PATH:/Applications/"Android Studio.app"/Contents/jre/jdk/Contents/Home/bin' >> ~/.bash_profile $ echo 'export JAVA_HOME=/Applications/"Android Studio.app"/Contents/jre/jdk/Contents/Home' >> ~/.bash_profile $ source ~/.bash_profileデコンパイル
apkファイルを展開します。
$ unzip app-debug.apk
d2j-dex2jar
を使って生成されたclasses.dex
を.jar
に変換します。$ d2j-dex2jar classes.dex
classes-dex2jar.jar
が生成されるので、展開します。$ unzip classes-dex2jar.jar(もともと難読化されているのかを確認していたので、気づかなかったのですが、このあたりで難読化設定を入れておくと
MainActivity.class
が見つかるのですが、入れてないと見つからないという不思議な状況になっていました。)
とりあえず続きを進めてみます。
jad
を使って.class
を.java
にデコンパイルを行います。$ jad hoge.classこの手順で、本来はデコンパイルが完了するはずです。
参考文献
- 投稿日:2020-10-23T10:03:21+09:00
Android で Youtube の動画を再生する[Kotlin]
つくるもの
YoutubeAPI を使って動画再生するためには、そもそも端末に Youtube アプリが入っている必要があるとかで、Youtube アプリを必要としない Youtube 動画プレイヤーを実装する。
現在は権利フリーの動画しか再生できず、おそらく大半の動画の再生に失敗してしまう。
(どうすればここの問題を解決できるのか知っている方がいれば教えていただきたいです…)再生できることが確認できている動画
https://www.youtube.com/get_video_info?video_id=rvkxtVkvawc
これが再生できなければ実装側のバグです。今回やったこと
できることは AndroidでYoutubeの動画を再生する とほぼ同じ。
このコードを参考に、下記の内容を追加実装した。
- Kotlin化
- ExoPlayer を中心に各ライブラリの更新
- Youtube動画ID から直接再生
方法
ここのソースで動かせる
https://github.com/tktcorporation/ExoPlayerYoutubeSampleそこそこボリューミーになりそうなので、解説は 筆が乗れば or 要望があれば 追加します…
参考
https://qiita.com/TaigaNatto/items/384318fb73b4f4712f6e
https://qiita.com/TaigaNatto/items/d3ff98961c346098eaf7