20191004のAndroidに関する記事は10件です。

ADB備忘録

エンジニア一年生のバッチファイル第二弾

あらすじ

なんでもやりますから仕事をくださいと言った。
アンドロイドのお仕事があるよと言われてありがたく頂戴した。
いわゆるキッティングというやつで、そこそこ量もあったので、せっかくだから自動化した。

※なお本稿の筆者は今年の八月に初めてエンジニアとしての勉強を始めました。
誤認識等あれば後学のためご指摘いただければ幸いです。


前提

  • Android端末は[設定]-[システム]-[ビルド番号]連打を済ませて開発オプションを表示してある やり方(Qiita内リンク)
  • [開発オプション]をオンにして、[USBデバッグ]のスイッチをオンにした状態で、データ転送可能なUSBケーブルを使ってPCに接続している
  • アプリのインストールをさせる場合、APKというやつを抜き出してPC側に持っている必要がある:やり方:なお筆者はコマンドプロンプトさんでgrepを使う方法?と困惑した挙句 >> package.txt として書き出してエディタの検索機能に拾っていただきました。
    • あとそういえば引っこ抜いてくるときは「base.APK」的な名前だったんですけど実際APKリストに載ってた名前が違ってたので「base.APK」をバックアップとしてコピー&リネームでリストと同じ名前にしたやつを流し込みましたが、正常にインストール、動作しているもようです。

使ったものたち

バッチ側
- set :準備 / set /p check=""でpause代わりに
- %~dp0 :準備で使った / 現在地の取得
- pushd / popd :証跡pngの保存先とAPKの保存先を行ったり来たりした
- timeout :処理が走りがち(端末側の画面遷移が遅かった)だったので止めた
- echo :ログファイル作るときに echo f | echo piyo >> ~.log
- call :タスクキルの操作をひとまとめにして呼んでた
- if( else ) :インストールちゃんと終わったかどうかとか判別してた
- goto :「間違いなくこの処理をしてほしい」時に指定して飛ばした

adb側
- "shell"がつくやつ
- am start :アプリを立ち上げた
- input( tap/keyevent/swipe/text ) :Android機の画面操作をした
- screencap :Android機でスクショを撮った
- rm :↓のpullはコピーなのでAndroid機にデータが残る のでrmで消した
- "shell"がつかないやつ
- pull :screencapで撮ったスクショをPCに引っ張った
- install :PC側で持っているAPKを指定してAndroid機にインストールさせた
- devices :PCと接続されている端末のチェックをした

気が狂ったポイント

startで[設定]を呼ぶとする。
立ち上がった時点でkeyevent20を送ったときの動きと、自動化したときにstart→keyevent20の流れで送った時の挙動が違う。

しんどかったからtapで座標指定した。

構成

  • 準備
    • @echo off:実質呪文
    • set here=%~dp0:実質呪文
    • %here%は~~/imakoko/のように末尾に/がついているので、気になる時は%here:~0,-1%とかするといいかもしれない。→参照 バッチ:文字列の加工
    • set PATH=%PATH%;%here%-adbのセットアップファイルを置いてるとこ-
    • pushd "%here%"
    • この後Android機に流し込むAPKファイルが同じフォルダ内にあるので現在地をはっきりさせておきたかった&&後で戻ってきたかったのでcdじゃなくてpushdにした
    • adb devices
    • ↑でパスを通したのがちゃんと通っているか(通っていなければエラー出る)&&セットアップ対象のAndroid端末がちゃんとコマンドを受け付けるかどうか

地味に困ったのはAndroid端末にコマンドを送るときにドライバが必要だったこと。
自宅PCなら余裕でやったけれども会社のPCでは怖い。そういうものだと思う。
今回はメーカの公式HPからドライバをDLできたので非常に助かりました。


  • 処理
    • adb shell am start -a android.intent.action.MAIN -n com.android.settings/.Settings:設定を開く(※いまひとつ理解はできていない)
    • -a:って書いてあったのでactionって書いてあるとこを書いた
    • -n:って書いてあった。「何をするどこを開きたいの」って聞かれているところだと解釈している([設定]以下のメニューとかたしかここで直で指定できた)
    • adb shell input ( tap/keyevent/text ):画面操作用
    • タップするときは座標を指定する。[設定]-[開発オプション]-[タップ位置を表示]的なメニューをオンにすると画面上に出る。左上がx,y/0,0と思う。
    • textでマルチバイト文字(日本語だいたいそう)を指定するとえらいことになるらしいのでやめた方がいいと思った。
    • 参考までに Windowsでadbコマンドを使用する際の注意adbコマンドを使ってマルチバイトテキストをスマホに送る方法Android 端末にadbで日本語を送りたい /筆者はビビりなので、特に必要に駆られもしなかったということもありADBKeyboardを試していません。
    • adb shell input swipe
    • 始点と終点を始x y 終x yの順番で書く。スワイプにかける時間を指定するときは始x y 終x y 時間の順番で書く。ここで時間を何千とかにするとアイコンを握ったままスワイプさせて動かすとかもできた。時間の単位はミリ秒。
    • adb shell screencap -p :証跡管理用
    • やり方(Qiita内リンク):SDCard以下を指定すると権限がない旨のエラーが発生したのでSDCard内に保存し削除した。-pは拡張子を「png」で保存するオプション。-pを外しても、保存の時に/piyo.png と指定したら問題なくpngで保存されるらしい。【参考】screencapに対する考察 - adbでスクリーンキャプチャを取る方法(Qiita内リン ク)

  • 分岐
    • APKインストールの時
    • 成功したら「Success」と標準出力に出てくるので、adb install -r | find /c "Success" で if %ERRORLEVEL%==0 ( インストールが完了したときの処理 ) else ( インストールが完了しなかったときの処理 )にした。
    • この時の分岐は「やれって言ったことだけやってほしい」のでgotoで確実に処理を飛ばすことにした。ラベル読み込みで処理遅延が出るという記事(Qiita内リンク)を見かけたが、現時点での筆者の技量では速度はさほど重視されていないため。
    • タスクキルの時
    • 厳密に言えば分岐ではないがcallでラベルを呼んできた。[設定]の中でいろんな項目の設定をしたいが[戻る]を使うと処理や画面、メニューの表示形式がブレる、というような現象が発生していたため。
    • callで呼ぶラベルの末尾にはexit /bを使うこと。でないと帰ってこなくなってバッチごと終わる。uleFone触ったときはタスクキル後に「deep cleaning」かなんかの表示が出てたのでtimeoutを入れた。

参考資料

一年生が頑張って解説をしようするより資料を列挙した方が後々よいと思う。

  • input keyevent
    • keyeventの一覧。19~23あたりと4、61、66は頻繁に使った。

メモ

  • setでpathを通すとコマンドプロンプトを落とした後はpath追加前の状態に戻る → %PATH%の中身を他の処理に持ち越すことがない → 安心
  • Wi-Fi経由での端末への接続 → adb connect 
    • 次回試せるときが来たら同一ネットワークに乗せなければならないのかどうか、Android端末側は4G回線でもいいのかどうか は確認したい
    • ポートを開ける必要があるかもしれない
    • adb tcpip 5555 | findstr /b C:/"restarting"
    • ※"restarting"はtcpipのコマンドが正常終了したときに標準出力される
  • adb shell input KeyEvent ~のやつはKEYCODE_TAB的な奴で指定しようというのが当初の目論見だったが、そんなコマンドはない的なエラーとヘルプが出たので数字で指定をした
  • 今回はやらなかったが順次実行なら&&でコマンドをつなげばできるらしい
  • アプリの実行はrun-asでもできるらしい(インストールさせずにテスト的に動かすとかそういうやつだろうかと思いつつ試していないので試してみたい)
  • アプリのアンインストールはuninstall [-k] packageでできる。-kオプションはキャッシュ等を保持するオプションなので、不要なら外して全部まるごと消せばよい
  • みんな大好きGET EVENTはadb shell getevent -plでやる
  • 例のdd【参考】(Qiita内)
    • > 操作の受け取り(入力)を操作(出力)に引き継ぐ
    • > adb shell "dd if=/dev/input/event5 of=/sdcard/sample2"
    • > adb shell "dd if=/sdcard/sample2 of=/dev/input/event5"
    • 尚例のddを使うには→getevemtでdev\input~~に書き出す→どっかしらのテキストファイルにリダイレクト→リダイレクト先のテキストファイルを開き、中身をバイナリに変換して配列化し、sendeventの位置として送る必要があるらしい。
    • ファイル→バイナリ:certutil -encodehex [コマンド] <変換元ファイル名> <変換先ファイル名>
    • バイナリ→ファイル:certutil -decodehex [コマンド] <変換元ファイル名> <変換先ファイル名>
    • > getevent /dev/input/event1 > /sdcard/event.txt
    • >  ([Ctrl]+[C]を押下すると記録を終了する)
    • > cat /sdcard/event.txt|while read a;do set $a;sendevent /dev/input/event1 $((0x$1)) $((0x$2)) $((0x$3));done
    • 【参考】
    • 【参考2】
  • adbは接続してしまった後はUNIXベースの技術であるので、UNIXのコマンドが使えることも多いらしい ※ただし筆者はUNIXと聞いたらRedhatとかCentOSが出てきて「それLINUXや」と一人芝居をする程度の人間 【参考】

ふりかえり

  • 各コマンドのオプション等を理解する間もなく一日で組んで二日目にテストしながら実機作業だったので、動いたけど本当に正しいのかがわからない
    • 次回までにリファレンスを呼んでみたりQiitaを散歩したりで勉強したい
  • Qiitaでみんなが言ってるなーと思ってはいたけどinputでの操作がだいぶ遅い
    • keyeventでバンバン羅列して送ったら歯抜けになってあらぬところを操作しはじめたりもしたので、なるほど遅いのにもきっと理由があるのだろう、とは思った。それにしてもたしかに遅い。しかしddとかその辺は一日では組めないと思って諦めた。これもできれば次回までに情報集めておきたい
  • とはいっても次回がいつ来るかの予定は未定なのでとりあえず案件的には無事終了してよかったなというきもち。次の仕事がほしい(来ない)
  • 複数の端末を接続して、例えば「端末ごとの名前のついたフォルダにスクショを引っ張ってくる」というような画一ではない動作をさせた場合、処理が混線してえらいことになるらしいと聞いてビビってやらなかった。実機がいっぱいあるうちに試しておけばよかった

自分用まとめなので、使ったものたちに書いてるけど準備とか分岐とかそこへんで出てきてないやつとかもあります。追記のご希望等なにかあればご連絡ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webプログラマがアプリ開発(ios,android)

  • swift Objective-cは敷居が高い。
  • 使い慣れたVS Codeでコーディングがしたい。
  • はじめは簡単なアプリ作りたい

そんな方にReact Native
※少しReactかじった方が良いかも

環境

  • mac
  • XCode シュミレーター必須

React Nativeのはじめ

公式チュートリアル

Expoの方ではなく、「React Native CLI Quickstart」を実施

  • homebrewで各インストール
brew install yarn
brew install node
brew install watchman
brew tap AdoptOpenJDK/openjdk
brew cask install adoptopenjdk8
  • npmでreact-native-cliをグローバルへインストール
npm install -g react-native-cli
  • ソースを置くフォルダでinitして実行
react-native init AwesomeProject

cd AwesomeProject
react-native run-ios

Reactのモジュールが無いよとビルドエラーになった場合、以下のコマンドで、モジュール取得して再ビルドしたらいけた。

cd ios
pod install

こちら参考

動いたあとは、App.jsを修正していくことでアプリの開発が行える。 簡単

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mac で Android エミュレーター の hosts を変更する方法

エミュレーターのリストを表示

$ ~/Library/Android/sdk/emulator/emulator -list-avds
Pixel_3a_XL_API_28_9.0_

エミュレーターを書き込みモードで起動

$ ~/Library/Android/sdk/emulator/emulator -avd Pixel_3a_XL_API_28_9.0_ -writable-system

上記コマンドを実行するとエミュレーターが起動する。

既にエミュレーターが起動している場合は

emulator: WARNING: System image is writable
emulator: ERROR: Running multiple emulators with the same AVD is an experimental feature.
Please use -read-only flag to enable this feature.

こんなエラーが表示されるので終了してからコマンドを実行する。

注意

ここで起動するエミュレーターは 「Play Store」ではない ものでないとダメらしい。

root を取得

$ ~/Library/Android/sdk/platform-tools/adb root
restarting adbd as root

もし「Play Store」のエミュレーターを起動していた場合は

adbd cannot run as root in production builds

こんなエラーが出る。

再マウント

$ ~/Library/Android/sdk/platform-tools/adb remount
remount succeeded

エミュレーターの hosts ファイルを取得

$ ~/Library/Android/sdk/platform-tools/adb pull /system/etc/hosts ~/Desktop/
/system/etc/hosts: 1 file pulled. 0.0 MB/s (79 bytes in 0.002s)

Android Studio の Device File Explorer でエミュレーター内のファイルを確認すると
/etc/hosts も存在するが /system/etc/hosts へのリンクが貼ってあるだけ。

hosts ファイルを編集

127.0.0.1       localhost
::1             ip6-localhost

10.0.2.2 変更するドメイン ← 追記

ローカルへ接続する場合、 10.0.2.2 でないとダメらしい。

hosts ファイルを転送

$ ~/Library/Android/sdk/platform-tools/adb push ~/Desktop/hosts /system/etc/hosts
/Users/*****/Desktop/hosts: 1 file pushed. 0.0 MB/s (79 bytes in 0.011s)

もし書き込みモードでエミュレーターを起動していない場合は

adb: error: failed to copy '/Users/*****/Desktop/hosts' to '/system/etc/hosts': remote couldn't create file: Read-only file system

こんなエラーが出る。

表示されるか確認する

以上で hosts が変更されたハズ。

Chrome などでちゃんと表示されるか確認する。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android Q でアプリを強制的に再起動する方法

概要

Android Q では、従来よく使われていた AlarmManager によるアプリ強制再起動が使えなくなりました。
この記事ではその代替手段である Activity によるアプリ強制再起動について説明します。

問題

従来、アプリを強制的に再起動するために次のような方法がありました。

  1. AlarmManager を使ってメインの Activity を数秒後に起動するように設定
  2. アプリのプロセスを Kill する。

具体的には次のようなコードです。

private void restartApp() {
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent pi = PendingIntent.getActivity(this, 0, intent,
            PendingIntent.FLAG_ONE_SHOT);
    AlarmManager am =
            (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    // 1秒後に MainActivity を起動
    am.setExact(AlarmManager.RTC, System.currentTimeMillis() + 1000, pi);
    // 現在のプロセスを Kill する
    android.os.Process.killProcess(android.os.Process.myPid());
}

この方法は Android Q では使えなくなりました。
この制限は Android Q で動くすべてのアプリに適用されます。targetSdkVersion は関係ありません。

これは『Android Q におけるバックグラウンドでの Activity 起動の制限』が原因です。

Android Issues にこの問題に関するチケットが起票されています。これに対し、公式は「Android Q では AlarmManager による再起動は利用できなくなった」とコメントしています

解決方法

Android Q からは Activity によるアプリ強制再起動が利用できます。

概要は次の通りです。

  1. 再起動を実行する RestartActivity を定義する
  2. RestartActiivty が別プロセスで起動するように宣言する
  3. メインプロセスから RestartActivity を起動させる
  4. RestartActivity で再起動処理をする
    1. RestartActivity が起動したらメインプロセスを Kill する
    2. メインプロセスを Kill したら、MainActivity を起動する
    3. RestartActivity を終了する

RestartActivity を定義する

RestartActivity を定義します。
RestartActivity を起動する Intent にメインプロセスの PID を保持しておきます。

package com.x.androidapprestart;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class RestartActivity extends Activity {
    public static final String EXTRA_MAIN_PID = "RestartActivity.main_pid";

    public static Intent createIntent(Context context) {
        Intent intent = new Intent();
        intent.setClassName(context.getPackageName(), RestartActivity.class.getName());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // メインプロセスの PID を Intent に保存しておく
        intent.putExtra(RestartActivity.EXTRA_MAIN_PID, android.os.Process.myPid());
        return intent;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ... (省略) ...     
    }
}

別プロセスで起動するように宣言する

AndroidManifest.xml に RestartActivity の宣言を追加します。
この時、別プロセスで起動させるために android:process に任意のプロセス名を指定します。

<activity
  android:name=".RestartActivity"
  android:excludeFromRecents="true"
  android:exported="false"
  android:launchMode="singleInstance"
  android:process=":restart_process"
  android:theme="@android:style/Theme.Translucent.NoTitleBar">
</activity>

RestartActivity を起動する

終了したいタイミングで RestartActivity を起動します。

private void restartApp() {
    Context context = getApplicationContext();
    Intent intent = RestartActivity.createIntent(context);
    // RestartActivity を起動(AndroidManifest.xml での宣言により別プロセスで起動する
    context.startActivity(intent);
}

再起動処理をする

1. メインプロセスを Kill する
RestartActivity の onCreate メソッドでメインプロセスを Kill します。
この時、PID は Intent で渡された値を利用します。

2. MainActivity を起動する
メインプロセスを Kill したら、再起動したい Activity(今回のケースでは MainActivity)を起動します。

3. RestartActivity を終了する
RestartActivity とそのプロセスを終了します。

これでアプリが再起動されるはずです。

以下に RestartActivity の onCreate のコードを記載します。

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   // 1. メインプロセスを Kill する
   Intent intent = getIntent();
   int mainPid = intent.getIntExtra(EXTRA_MAIN_PID, -1);
   android.os.Process.killProcess(mainPid);

   // 2. MainActivity を再起動する
   Context context = getApplicationContext();
   Intent restartIntent = new Intent(Intent.ACTION_MAIN);
   restartIntent.setClassName(context.getPackageName(), MainActivity.class.getName());
   restartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   context.startActivity(restartIntent);

   // 3. RestartActivity を終了する
   finish();
   android.os.Process.killProcess(android.os.Process.myPid());
}

すべてのコード

すべてのコードは github にありますので参考にしてください。

余談

この方法は Android 版 Chrome でも使われているようです。
Chrome にも BrowserRestartActivity というクラスがあり、ChromeLifetimeController から呼ばれています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】Android アプリの基本:ブロードキャスト

ブロードキャストとは

  • 送り先を指定せず不特定多数の相手に投げるメッセージ。
  • ブロードキャストは インテントとして 投げられる。

サービスとは

  • UIをもたず、バックグラウンドで処理を実行するコンポーネント。

コンテンツプロバイダとは

  • 他アプリに対してアプリ内の データを公開するコンポーネント。
  • 公開できる主なデータは「DBテーブルデータ」「ファイルデータ」。

コンテンツリゾルバとは

  • コンテンツURIから Android システム内の対象となるコンテンツプロバイダを探し、そのプロバイダが提供するデータのアクセスを行う。

コンテントURIとは

  • コンテンツプロバイダが提供するデータを指し示すURI
  • 書き方:  content://{authority}/{path}

    • authority: プロバイダを識別するための名前。
      • プロバイダクラスの「完全修飾クラス名を小文字」にしたものを付けるのが推奨されている。 例) コンテンツプロバイダが「org.sample.MyProvider」 だった場合org.sample.myprovider  
    • プロバイダが提供するデータの中のどのデータなのかを識別する名前。
    • aaa/bbb/ccc のように階層形式で付けられ、各部分を「セグメント」という。
      例1) hoge テーブルの全レコード content://org.sample.myprovider/hoge
      例2) hoge テーブルの ID=10 のレコード content://org.sample.myprovider/hoge/10

インテントとは

  • Android システムに送る「何をしたいか」のメッセージ

インテントに設定できる情報

  1. 呼び出し対象を絞り込む条件 (必須)
    • コンポーネント名
    • アクション名
    • カテゴリ
    • データ
  2. 呼び出し対象に送るデータ
    • エクストラ
    • フラグ(インテントフラグ)

インデントの種類

  • 明示的インテント

    • 呼び出し対象を一意に指定するインテントのこと。
    • 絞り込み条件でいうと「コンポーネント名」を指定したインテント。
    • 例:アクティビティ名を指定しインテントを生成

      Intent intent = new Intent(context, FooActivity.class);
      
  • 暗黙的インテント

    • 呼び出し対象が必ずしも一意にならないインテントのこと。
    • 絞り込み条件でいうと「アクション名」「カテゴリ」「データ」を指定したインテント。
    • 例:アクション名とURIを指定してインテントを生成

      Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
      

インテントフラグとは

  • インテントによって起動されるアクティビティの起動方法を指定するもの

主要なインテントフラグ

  • FLAG_ACTIVITY_CLEAR_TOP
    • 起動するアクティビティが現在のスタック内に存在する時、そのアクティビティから上の全アクティビティをスタックから削除する。
  • FLAG_ACTIVITY_SINGLE_TOP
    • スタックの先頭が同じアクティビティの場合に新しくアクティビティを生成せず再利用
  • FLAG_ACTIVITY_NEW_TASK
    • 新しいタスクを作り、そのルートアクティビティとして起動
  • FLAG_ACTIVITY_MULTIPLE_TASK
    • new_taskと一緒に使用する
  • FLAG_ACTIVITY_NO_HISTORY"
    • タスクのスタックに保持されない。
    • ユーザが戻るボタンでこのフラグを指定したアクティビティに戻ってきた時、スタックに保存されていないため、結果的にスキップされる。

インテントフィルタとは

  • インテントの絞り込み条件(アクション、カテゴリ、データ)のヒット対象になるかをコンポーネントに定義したもの。
    (インテントを受けることができる「アクティビティ」「サービス」「ブロードキャストレシーバ」の3つのコンポーネントに設定できる。)
  • Android マニフェストの各コンポーネント定義 < activity >, < service >, < receiver > の子要素として定義する。

エクストラとは

  • 呼び出し対象に渡せる Bundle 形式のデータ。
  • Bundle クラス:キーと値のセットを保持するクラス(Map と似たようなもの)

エクストラの格納と取り出し方

  • データ格納する送信側

    // インテントのインスタンス生成する
    Intent intent = new Intent(this, FooActivity);
    // エクストラにデータを格納する
    intent.putExtra("key1", 10);
    intent.putExtra("key2", "abc");
    startActivity(intent);
    
  • データを取り出す受信側

    // インテントのエクストラからデータ取り出し
    int val1 = getIntent().getIntExtra("key1", 0);
    String val2 = getIntent().getStringExtra("key2");
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LineSDK入れ込みでハマった話

経緯

  • LineSDKでログイン認証することに
  • とりあえずSDKを入れて、ID設定して、コード書いてみた
  • Unity上だと動作チェックできないのでUnityCloudBuildでiOS, Androidビルド
  • >> ビルドエラー! <<

原因1:対応バージョン見てなかった

LineSDKは Android API 17(Android4.2)以上、 iOS 10.0以上対応 です。
これをビルド時の対応バージョン修正することで、Androidはビルドできるようになりました。
だからReadmeは読めとあれほど(ry

原因2:Library Search Paths

iOSが相変わらずビルドできない上に、よく見るあのエラーが発生していることを確認。

Undefined symbols for architecture arm64

そう、やつです。
エラー文だけではどこが悪いのか全然分からないやつです。
CocoaPodの設定が悪いのかなど散々迷走したあげく、Swift関連のライブラリが軒並みリンク失敗して入れ込めてないことに気づきました。

参考:https://stackoverflow.com/questions/52536380/why-linker-link-static-libraries-with-errors-ios

参考リンク先で言われているように、Unityの OnPostprocessBuild内にて、以下を追加することでiOSビルドができるようになりました。
project.AddBuildProperty (target, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)");

感想

今回、UnityCloudBuildを使ったことで、より問題を複雑に考えて迷走しました。
原因はどっちも基本的な部分にあったので、一旦落ち着いてローカル環境でも同じ問題が起きるかなどきちんと確かめていくと絞り込みやすいかなと思いました。
あと、Readmeは読もう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チュートリアルだと気づけないAndroidアプリ開発を始める上で必要になる知識

概要

Android Kotlin codelab courseでKotlinに関する知識やAndroidのコードに関する知識はかなり手に入れることができますが、
実際に開発を始める上で必要になる知識があると感じているので、それをまとめて紹介しようと思います。

Android Studioを効率的に使う

知っておけば楽になったのに、、というのが結構たくさんあります。

なぜかビルドできない、importできないときの1次対応

  • File - Sync Project with Gradle FIlesしてみる
  • ダメならFile - Invalidate Caches and Restartを試す
  • ダメならプロジェクトをimportしなおす。ここから一度プロジェクトを消せます

image.png

CMD + SHIFT + A(Win: Ctrl + Shift + A): Actionの検索

※ CMD = Command
このコマンドを入力すると、Android Studio上でできることが全て表示され、検索することができます。まず最初にこのショートカットだけ覚えれば検索結果にそれを行うショートカットも表示されるため、何でもできるようになります
リネームなどのリファクタリングしたいとか、プロジェクトのSyncをしたい(Gradle sync)したいとか、いちいちこの項目はFileのメニューに入っていて、、とか探すの大変なので、いろんな何かがしたいときに、Actionを検索することができます。
image.png

SHIFT + SHIFT: すべての場所から検索

何かを探したい!というときに、ファイルもクラス名もメソッド名も全部探せるのがこのコマンドです。
ただ、このコマンドはすべての場所から検索するので、多少ラグがある場合があります。そこが気になってきたらファイルの検索(CMD+SHIFT+O(Win:Ctrl + Shift + N))や、シンボルの検索(CMD+OPTION+O(Win:Ctrl + Alt + Shift + N))を使い分けて覚えていくといいでしょう。

CMD + クリック(または CMD + B): 参照ジャンプ(宣言に(直接)移動)によって、移動する

(WindowsではControl + クリックなど)
コードを読んでいるときは、このファイルの実装ってどうなっているんだろう。と飛びたくなることがよくあります。また呼び出し元を検索したくなることもよくあります。そんなときはCMDを押しながらクリック、またはCMD + Bで飛ぶことができ、また宣言上で、CMD + Bで呼び出し元も検索できます。cmdb.gif

メモリを設定しよう

大きいアプリではデフォルトの1280MBではAndroid Studioのメモリサイズが足りず、フリーズして辛いことがあります。
自分が使っているPCのスペックのメモリサイズを確認して、Help - Edit Custom VM Optionsでメモリサイズを変更しておきましょう。
image.png

-Xmx4G

Project viewによるプロジェクト表示を使う

デフォルトではAndroid viewという表示になっています。この表示はシンプルにファイルを表示してくれますが、実際のフォルダ構造と違うので、実際にファイルを置いたりするときに手間取ったりします。Project viewを使うことで、実際のフォルダ構造と同じ形で表示してくれるので、トラブルを回避しやすいです。
image.png

デバッグとデバッグ時にコード実行

デバッグでとめたいときにこのあたりをクリックするとブレークポイントをはることができます。
image.png
image.png
この状態でこの虫ボタン?を押すことで実行時にそこを通ったときに止めることができます。
image.png
image.png

またアプリを普通に起動している状態で、途中でデバッガーをつけたくなった場合はこのボタンを押すことで、アプリの再起動無しでデバッガーをつけることができます。
image.png

止めたときに変数の中身が見たくなるかもしれません。そのときはAltを押しながら変数をクリックするとその変数の中身をみることができます。
test.gif
また計算機ボタンを押すことで、ブレークポイントで任意のプログラムを実行することができます。
calc.gif

Gradleによるビルド

Android Studioからのビルドは内部的にGradleというビルドシステムを使っています。
コンソールから以下のように./gradlew tasksを実行すると、以下のように表示されます。
このリストによって何ができるのかがわかります。

$ cd android-sample-project
$ ./gradlew tasks
> Task :tasks

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for the base and test modules
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assemble main outputs for all the variants.
assembleAndroidTest - Assembles all the Test applications.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.

例えば一番上のandroidDependenciesを実行してみしょう。以下のように依存しているライブラリが表示されます。このようにビルドを実行することでAndroid Studioではアプリをビルドしています。

./gradlew androidDependencies
> Task :model:androidDependencies
debug
debugCompileClasspath - Dependencies for compilation
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.20@jar
+--- org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.10.0@jar
+--- org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.20@jar
+--- org.jetbrains.kotlin:kotlin-stdlib:1.3.20@jar
+--- org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.10.0@jar
+--- org.jetbrains.kotlin:kotlin-stdlib-common:1.3.20@jar
+--- org.jetbrains:annotations:13.0@jar
\--- com.soywiz:klock-jvm:1.1.0-eap-100@jar
...

具体的には./gradlew assembleDebug (./gradlew asDeなど省略可)のようにすることでアプリのバイナリ(apkファイル)を作ることができます。

まとめ

初心者に教えるときに効率的になる、ハマりがちなポイントを軽くお教えしました。
何かありましたらまた追記します。またこういうポイントも!みたいな所があれば気軽にコメントしてください! :pray:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android] permission一覧の取得方法

結論

Androidシステムで、定義されているpermissionを確認する方法はいくつか用意されているが、
少なくともAndroid8.1では、下記のどちらかの方法を使うべき。

$ adb shell dumpsys package permissions
Permissions:
  Permission [com.google.android.apps.nexuslauncher.permission.READ_SETTINGS] (878caf2):
    sourcePackage=com.google.android.apps.nexuslauncher
    uid=10099 gids=null type=0 prot=signature|privileged
    perm=Permission{2705543 com.google.android.apps.nexuslauncher.permission.READ_SETTINGS}
    packageSetting=PackageSetting{8a0e7c0 com.google.android.apps.nexuslauncher/10099}
  Permission [android.permission.REAL_GET_TASKS] (19f5cf9):
    sourcePackage=android
    uid=1000 gids=null type=0 prot=signature|privileged
    perm=Permission{c4a43e android.permission.REAL_GET_TASKS}
    packageSetting=PackageSetting{df7243c android/1000}
...
$ adb shell cat /data/system/packages.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="29" databaseVersion="3" fingerprint="google/sdk_gphone_x86/generic_x86:10/QPP6.190730.005.B1/5775370:userdebug/dev-keys" />
    <version volumeUuid="primary_physical" sdkVersion="29" databaseVersion="3" fingerprint="google/sdk_gphone_x86/generic_x86:10/QPP6.190730.005.B1/5775370:userdebug/dev-keys" />
    <permission-trees>
        <item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
    </permission-trees>
    <permissions>
        <item name="com.google.android.apps.nexuslauncher.permission.READ_SETTINGS" package="com.google.android.apps.nexuslauncher" protection="18" />
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />="com.google.android.apps.nexuslauncher" protection="18" />
...

NGな方法

Android Developerには、下記のように記載がある。
しかし、少なくともAndroid8.1では、全てのpermissionは出力されないので注意。

設定アプリ、またはシェルコマンド adb shell pm list permissions を使って現在システムに定義されているパーミッションをご覧いただけます。

なお、adb shell cmd package list permissionsも、内部実装は共通なので、同じくすべてのpermissionは出力されない。

何が違うのか

まず、pm list permissionsは、permission groupに属さないpermissionしか表示しない実装になっている。
(ちなみに、pmコマンドのマニュアルでは、「既知のすべてのパーミッションを出力します。」となっている)

例えば、CALL_PHONE permissionは、PHONE groupに属しているので、表示されない。

-gオプションをつけることで、既知のpermission groupそれぞれに対して、所属するpermissionをすべて出力することができるが、これも完璧ではない。
permissionの定義がイケてなくて、存在しないpermission groupに属していることにしてしまっている場合、リストアップされない。

具体的には、android.permission.DOWNLOAD_WITHOUT_NOTIFICATIONというpermissionは
android.permission-group.NETWORKというグループに属すると定義されているが、
android.permission-group.NETWORKが定義されていないため、pm list permissions -gでは表示されない。

一方でdumpsys package permissionsは、
素直に/data/system/packages.xmlの中のpermissions要素をリストアップしているため、同じ問題が発生しない。

なお実装は、以下のあたり。

pm list permissions: frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.javaのrunListPermissions, doListPermissions
dumpsys package permissions: frameworks/base/services/core/java/com/android/server/pm/Settings.javaのdumpPermissionsLPr

おわりに

PackageManagerはdumpsysとpm(cmd)が両方似たような領域でがんばっていて、どちらを使うべきかいちいち悩む。。。
今回のpermissionに関しても、網羅性という観点ではdumpsys package permissions一択だが、
pm list permissionsには-fオプションをつけることでlabelとdescriptionを出力してくれるという便利な機能があり、
dumpsysだけ使えりゃいい、というわけでもない。
なんでこんなことになってしまっているのか。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】Android アプリの基本:アクティビティ

アクティビティの作り方

  1. Activity クラスを継承したクラスを作る。
  2. UIを定義したレイアウトファイルを用意。
  3. onCreate() メソッドを定義。
  4. onCreate() 内で setContentView() を使ってレイアウトファイルのビュー定義をアクティビティに追加。

アクティビティの起動

  1. インテントをインスタンス化する。

    Intent intent = new Intent(this, xxxActivity.class);
    
  2. インテントを引数にして、startActivity()メソッドを実行する。

    startActivity(intent);
    

アクティビティの起動メソッド 2種

  • startActivity():アクティビティを起動するのみ。
  • startActivityForResult():アクティビティを起動し、それが終了した際に起動元に結果を返す。

アクティビティの終了

  • finish()メソッドで終了する。
    【注意】finish()でアクティビティは閉じられるが、そのアクティビティが直ぐに破棄される(onDestroy()が呼ばれる)わけではない。アクティビティ破棄がスケジュールされてそれが処理されて onDestory() となる。

アクティビティ間でのデータ受け渡し(例:AからBへ)

  1. インテントのインスタンス生成
  2. B を起動するインテントのエクストラに渡したいデータをセットする。
  3. B を起動する。
    ※ 起動には startActivity()/startActivityForResult() どちらを用いてもよい。
  4. B にて、 Activity#getIntent() でインテントを取得する
  5. そこからデータ(エクストラ)を取り出す。(データ型に合わせ、getXXExtraを使う)

B(終了)→Aの遷移で、BからAのアクティビティへのデータ渡し

  1. B の起動に startActivityForResult() を用いる。
  2. B を終了する前に返したいデータをエクストラに設定したインテントを作りActivity#setResult() でそのインテントをセット。
  3. A に戻ると A 側で onActivityResult() メソッドが自動で呼ばれ、その引数で B で setResult() したインテントが渡されるのでそこでデータを受け取る。

アクティビティが再生成される状況(2つ)

  • 端末の回転などのコンフィグレーションが変更されたとき。
  • アクティビティをバックグラウンドにし、メモリ不足などでアクティビティが破棄され、そのあとフォアグラウンドに戻したとき。

再生成後のデータの変化

  • 再生成後、フィールド値は消える。
  • ビューの値:消えない
    標準ビューは再生成で状態を復元するようなメソッドがある。

アクティビティの状態を保存・復元の方法

  • 保存

    • onSaveInstanceState() が onStop() の前に必ず呼ばれる。
    • onSaveInstanceState() の引数に渡される Bundle (outState) にセットすることで行う。
  • 復元

    • onRestoreInstanceState() は onResume() の前だが、再生成時のときしか呼ばれない。
    • onRestoreInstanceState() または onCreate() の引数の Bundle (savedInstanceState) から取り出して行う。
    • 再生成ではない場合、onCreate() の savedInstanceState は null が渡される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】Android アプリの基本

プロジェクトの主要なフォルダ構成

  • プロジェクト名/ プロジェクトのルート
    • app/ サブプロジェクトのルート
      • libs/ サブプロジェクトのjar配置場所
      • src/
        • androidTest/ テストコード
        • main/
          • java/ Javaソースコード
          • res/ リソース
          • AndroidManifest.XML

4つのアプリケーションコンポーネント

  • アプリケーションコンポーネントとは Android アプリの必要不可欠な構成要素。

  • <アプリケーションコンポーネント 4種>

    • アクティビティ
      UI を持ち、ユーザーが操作できる画面を提供する。
    • サービス
      UI を持たず、バックグラウンドで処理を実行する。
    • コンテンツプロバイダ
      アプリ内のデータを他アプリに公開する。
    • ブロードキャストレシーバ
      ブロードキャスト(システム全体に送ることができるメッセージ)を受け取る事ができる。

アクティビティとは

  • UIを持ち、1つの画面を表すコンポーネント。
  • フォアグラウンドで実行状態、バックグラウンドで停止状態のように、状態によるライフサイクルを持つ。
  • アクティビティ自体は「Javaクラス」で、そこにUI部品を追加することで画面が作られる。
  • UI部品 2種類で以下の通り。
    • ビュー
      テキスト、ボタン、画像などの UI として形状を持つ部品。
    • レイアウト
      UI としての形状を持たずビューをグループ化し表示位置を制御する部品。
  • UI 部品を定義したレイアウトファイルからビューをインスタンス化して、アクティビティに追加できる。

アクティビティの「ライフサイクル」

  • ライフサイクル
    アクティビティの「生成 → 表示 → 破棄」のサイクル
  • ライフサイクルメソッド
    ・ライフサイクル状態に応じて呼ばれるメソッド。

アクティビティのライフサイクル図

アクティビティの生成から、破棄するまでの過程

    +---→ 実行中 ----+
                   ↓
    onResume ←-------- onPause
    (触れる)           (触れなくなる)
    ↑                      ↓
    onStart ←--- onRestart ---- onStop
    (見える)  (見えないから見える)  (見えなくなる)
    ↑                              ↓
    onCreate ←----------------------- onDestroy
    (生成される)                       (破棄される)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む