20201016のAndroidに関する記事は8件です。

androidのアプリリンクで開く時にプロセスが2つ立ち上がったりジャンプ元のアプリ内で開く時の対処法

はじめに

アプリリンク(iOSでいうところのユニバーサルリンク)を導入すると、リンクを開く時に指定したアプリで開くことができるため、ユーザーに「どのアプリで開くのか」を選択してもらう必要がなくなりUXの向上になります。

しかし、androidではデフォルトの設定だと、アプリリンクを踏んだ時の動きがおかしくなります。
アプリリンクを踏むアプリをA、アプリリンクで開きたいアプリををアプリBとした時の理想の動きとしては

  • アプリBがバックグラウンドにいる
    • アプリAでアプリリンクを踏むとアプリBに遷移する
  • アプリBがkill状態
    • アプリAでアプリリンクを踏むとアプリBが起動して遷移する

ですが、androidのデフォルトの設定だと

  • アプリBがバックグラウンドにいる
    • アプリAでアプリリンクを踏むとアプリAのなかでアプリBが開いているパターン
    • アプリAでアプリリンクを踏むと、アプリBがもう1つ開かれて遷移するパターン
  • アプリBがkill状態
    • アプリAでアプリリンクを踏むとアプリAのなかでアプリBが開いているパターン

といった現象が発生します。起動しているアプリ一覧を見ると、同じアプリが複数立ち上がっているような状況で明らかに異常な状態です。

原因

androidManifest内でlaunchModeを設定していないため、デフォルトのstandardが適用されてしまっているから

対応

AndroidManifestのactivity内にlaunchMode="singleTask"を追加する

      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:windowSoftInputMode="adjustPan"
        android:screenOrientation="portrait"
        android:launchMode="singleTask">

launchModeについて

launchModeには4つある。
standard、singleTop、singleTask、singleInstance

standard

デフォルトの設定です。システムは常に、ターゲット タスク内でアクティビティの新しいインスタンスを作成し、そのインスタンスにインテントを渡します。

つまりジャンプ元のアプリの中で、アプリリンクで開きたいアプリを展開される。(タイトルの不具合)

singleTop

アクティビティのインスタンスがターゲット タスクの一番上に既に存在する場合は、システムはアクティビティの新しいインスタンスを作成せずに、onNewIntent() メソッドを呼び出して、インテントをそのインスタンスに渡します。

アプリリンクでアプリを開く時、
- すでにアプリが開いている時は、そのままジャンプする
- アプリが開いていない時は、ジャンプ元のアプリの中で開きたいアプリが展開される

singleTask

システムは新しいタスクのルートでアクティビティを作成し、そのアクティビティにインテントを渡します。ただし、アクティビティのインスタンスが既に存在する場合は、システムは新しいインスタンスを作成せずに、onNewIntent() メソッドを呼び出して、インテントを既存のインスタンスに渡します。

アプリリンク でアプリを開く時、

  • すでにアプリが開いている時は、そのままジャンプする
  • アプリが開いていない時は、新たにアプリを開いてジャンプする

singleInstance

インスタンスを保持しているタスクでシステムが他のアクティビティを起動しないことを除いて "singleTask" と同じです。アクティビティは常に、そのタスクの唯一のメンバーです。

singleTaskと同じ?要動作確認

備考

公式ではsingleTasksingleInstanceは推奨されていません。
しかし、同じアプリリンクでジャンプできるgoogle play storeの動きを見ると明らかにsingleTaskの動きであるため、アプリリンクを使う時はこのオプションは必須と考えても良いのではないでしょうか。

参考

https://developer.android.com/guide/topics/manifest/activity-element.html

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

雑魚競プロerがアプリ開発する(1週目)

1.はじめに

兼ねてから勉強してみたいと思っていたアプリ開発に着手し始めたので、今後の勉強の進捗について軽くまとめておきたいと思います。

2.自己紹介

1.専門は数学
2.半年前から競プロに取り組む。茶コーダー

3.ここ半年で学んだ技術

1.競技プログラミングで使うレベルのC++の知識
2.何を勉強すればいいのかわからなくてなんとなく学んだhtml,css,pythonの知識
3.TCP/IPの基礎知識、Linuxの基礎知識
4.Androidアプリ開発のための基本的な知識。基本的なアプリの作例をみて作成

4.今週学んだ知識

Kotlinの基本文法を公式のドキュメントを使って学んだ。
競プロのおかげで基本的な文法の流れはわかっていたので、型や繰り返し、Switchなどの知識は頭に入ってき易かったと思う。
流れとしては、基本的な文法をなんとなくドキュメントでさらった後にテキストエディタによって自分用にまとめた。
適宜ローカル開発環境を使って実際に動作しているところを確認した。
以下には今週まとめた分の一部を載せておく。

matome
/*
Kotlinの基本文法まとめ

開発環境
→ローカル開発環境とファイル転送アプリ、VSCodeを使用。
→ローカル環境には MyCentOSによる仮想サーバを使用
→Kotlin version 1.4.10-release-411 (JRE 1.8.0_265-b01)

ターミナル上でのコンパイラコマンド
→kotlinc ファイル名(.kt) -include-runtime -d ファイル名(.jar)
jarファイルの実行コマンド
→ava -jar ファイル名
 */

fun main(args: Array<String>) { 
  //String, char 
  var msg = "Hello World"
  println(msg)

  //Byte,Short,Int,Long Longの場合は文末にLをつるける必要がある
  val i: Int =100
  val l: Long = 44444444444L

  //Float,Double Floatは文末にFをつける
  val d: Double =224.543
  val f: Float = 12.423F

  //Boolean (true/false) 真理値の型
  val flag: Boolean = false

  //演算子 足し算 引き算 掛け算 割り算 余り
  //         + - * / %
 val x = 10
 println(x/3) //  小数点は切り捨てられる
 println(x/3.0) // double型で出したい場合はどちらかをdouble
 println(x % 3)

 var y = 5
 y++ //インクリメント y = y + 1
 println(y)
 y-- //ディクリメント y = y - 1
 println(y)

 var z = 4
 z += 12 //z = z + 12

 //AND OR NOT (論理演算子)
 //&& || !
 val flag =true
 println(!flag)

 //文字列の文中での展開
  println("hello" + "world")

  val name = "umemura"
  println("my name is $name")

  //数字の文中での展開
  println("my score is ${12 +32}")

  // \n: 改行
  // \t: タブ

  println("hello\n wor\tld"
  /*→出力
   hello
  wor   ld
 */

//if (条件式) {実行動作}←一行なら{}いらない
  //比較演算子 > >= < <= == !=
  val score = 85
  if (score > 80) {
    println("Great!")
  }
  else if (score > 60) {
    println("Good")
  }
  else {
    println("so so...")
  }
//結果 Great!
  val result = if (score > 80) "Great" else "so so..."
  println (result)
  // Great

 //when
  val num = 5

  /* when (num) {
    0 -> println("zero")
    1 -> println("one")
    2,3 -> println("Two or Three")
    in 4..10 -> println("many")
    else -> println("other")
  } */


  val result = when (num) {
    0 -> "zero"
    1 -> "one"
    2,3 -> "Two or Three"
    in 4..10 -> "many"
    else -> "other"
  }
  println(result)
}

5.今週のまとめ

有名なアプリ開発のエンジニアさんが「言語を学ぶより作って慣れろ」とおっしゃっていたので先週までは作例を作りつつ学んでいたのですが、自分にはその学び方は合っていなかったなと思いました。
やはり自分のような優秀でない層の人間にとっては、一歩一歩積み上げていくのが重要なのかもしれません・

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

雑魚競プロerがアプリ開発がする(1週目)

1.はじめに

兼ねてから勉強してみたいと思っていたアプリ開発に着手し始めたので、今後の勉強の進捗について軽くまとめておきたいと思います。

2.自己紹介

1.専門は数学
2.半年前から競プロに取り組む。茶コーダー

3.ここ半年で学んだ技術

1.競技プログラミングで使うレベルのC++の知識
2.何を勉強すればいいのかわからなくてなんとなくで学んだhtml,css,pythonの知識
3.TCP/IPの基礎知識、Linuxの基礎知識
4.Androidアプリ開発のための基本的な知識。基本的なアプリの作例をみて作成

4.今週学んだ知識

Kotlinの基本文法を公式のドキュメントを使って学んだ。
競プロのおかげで基本的な文法の流れはわかっていたので、型や繰り返し、Switchなどの知識は頭に入ってき易かったと思う。
流れとしては、基本的な文法をなんとなくドキュメントでさらった後にテキストエディタによって自分用にまとめた。
適宜ローカル開発環境を使って実際に動作しているところを確認した。
以下には今週まとめた分の一部を載せておく。

matome
/*
Kotlinの基本文法まとめ

開発環境
→ローカル開発環境とファイル転送アプリ、VSCodeを使用。
→ローカル環境には MyCentOSによる仮想サーバを使用
→Kotlin version 1.4.10-release-411 (JRE 1.8.0_265-b01)

ターミナル上でのコンパイラコマンド
→kotlinc ファイル名(.kt) -include-runtime -d ファイル名(.jar)
jarファイルの実行コマンド
→ava -jar ファイル名
 */

fun main(args: Array<String>) { 
  //String, char 
  var msg = "Hello World"
  println(msg)

  //Byte,Short,Int,Long Longの場合は文末にLをつるける必要がある
  val i: Int =100
  val l: Long = 44444444444L

  //Float,Double Floatは文末にFをつける
  val d: Double =224.543
  val f: Float = 12.423F

  //Boolean (true/false) 真理値の型
  val flag: Boolean = false

  //演算子 足し算 引き算 掛け算 割り算 余り
  //         + - * / %
 val x = 10
 println(x/3) //  小数点は切り捨てられる
 println(x/3.0) // double型で出したい場合はどちらかをdouble
 println(x % 3)

 var y = 5
 y++ //インクリメント y = y + 1
 println(y)
 y-- //ディクリメント y = y - 1
 println(y)

 var z = 4
 z += 12 //z = z + 12

 //AND OR NOT (論理演算子)
 //&& || !
 val flag =true
 println(!flag)

 //文字列の文中での展開
  println("hello" + "world")

  val name = "umemura"
  println("my name is $name")

  //数字の文中での展開
  println("my score is ${12 +32}")

  // \n: 改行
  // \t: タブ

  println("hello\n wor\tld"
  /*→出力
   hello
  wor   ld
 */

//if (条件式) {実行動作}←一行なら{}いらない
  //比較演算子 > >= < <= == !=
  val score = 85
  if (score > 80) {
    println("Great!")
  }
  else if (score > 60) {
    println("Good")
  }
  else {
    println("so so...")
  }
//結果 Great!
  val result = if (score > 80) "Great" else "so so..."
  println (result)
  // Great

 //when
  val num = 5

  /* when (num) {
    0 -> println("zero")
    1 -> println("one")
    2,3 -> println("Two or Three")
    in 4..10 -> println("many")
    else -> println("other")
  } */


  val result = when (num) {
    0 -> "zero"
    1 -> "one"
    2,3 -> "Two or Three"
    in 4..10 -> "many"
    else -> "other"
  }
  println(result)
}

5.今週のまとめ

有名なアプリ開発のエンジニアさんが「言語を学ぶより作って慣れろ」とおっしゃっていたので先週までは作例を作りつつ学んでいたのですが、自分にはその学び方は合っていなかったなと思いました。
やはり自分のような優秀でない層の人間にとっては、一歩一歩積み上げていくのが重要なのかもしれません・

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

Google Play Console の新しいUIでの時間指定公開とリリース手順

Play ConsoleのバージョンアップでUIが変わるので新しいUIでの手順をまとめておきます。
スクリーンショット 2020-10-16 17.02.58.png

時間指定公開

  • メニューの「公開の概要」から「管理」 -> 「管理対象の公開をオンにしました」を選択して保存 スクリーンショット 2020-10-16 16.34.38.png スクリーンショット 2020-10-16 16.35.40.png

リリース手順

  • メニューの「製品版」 -> 「新しいリリースを作成」 スクリーンショット 2020-10-16 17.46.14a.jpg
  • Android App BundleかAPKをアップロード(Android App Bundleの場合は署名が必要)
  • 含まれないAPKに前のAPKが入っているか確認する スクリーンショット 2020-10-16 17.47.32.jpg
  • リリース名とリリースノートを編集して「保存」 -> 「リリースのレビュー」 スクリーンショット 2020-10-16 17.48.03.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidアプリをGooglePlayConsoleで公開を止めている状態で、リリースの上書きができるか確認

前回からの引き続きで、 https://qiita.com/backgroundcolor/items/f1941a6b08eecbe3d0a7

公開を止めている状態で、リリースの上書きができるか確認

  1. ver16が公開準備完了状態
    スクリーンショット 2020-10-16 15.08.171.png

  2. 製品版にて、ver16が公開の準備完了状態となっているが、「新しいリリースを作成」
    スクリーンショット 2020-10-16 15.08.45.png
    3.ver17のファイルをアップし、リリースを作成。ver16は含まれないAPKに表示されている
    スクリーンショット 2020-10-16 15.13.42.png
    4.ver17が審査中と表示される
    スクリーンショット 2020-10-16 15.14.14.png
    5.「公開の概要」も審査中の変更にver17が表示される
    スクリーンショット 2020-10-16 15.14.55.png

  3. 少し待つと、製品版の変更の公開準備完了に、ver17のみ表示されるようになりました
    スクリーンショット 2020-10-16 17.31.00.png

  4. 「確認して公開」ボタンを押すとver17が製品版として公開されました

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

Android SIP API : REGISTER リクエストの有効期間と再送信

概要

SIP REGISTER リクエストの有効期間

REGISTER リクエストには有効期間 (Expires) を指定することができます。

REGISTER の有効期間に関する動作を簡単にまとめると以下のような感じです。

  • クライアントは、REGISTER リクエストの Expires に希望する期間を指定して、送信。
  • サーバーは、レスポンスにサーバー自身が希望する期間を指定して、送信。サーバーが指定した期間は、クライアントが希望した値かもしれないし、サーバーが希望する別の値に変更しているかもしれない。
  • クライアントは、レスポンスに書かれた期間を過ぎた後(または期間に近づいた際に)、 REGISTER リクエストを再送信することが必要。

Android SIP API の auto registration 機能

Android SIP API には auto registration 機能があり、この機能を有効にした場合は REGISTER リクエストの送信・再送信は android 側が行ってくれます。

auto registration を使用しない場合、SipManager.register(SipProfile localProfile, int expiryTime, SipRegistrationListener listener)SipSession.register(int expiryTime) を使用して REGISTER リクエストの送信を指示します。引数expiryTime に期間を指定できます。

問題

Android SIP API の auto registration を使用する場合、どのくらいの期間を指定しているのか、サーバーからの応答に指示された期間をどう処理しているのか、わからなかったためコードを調べました。

コードの調査

以下のサイトを使用しました。ブランチは android-7.0.0_r35 としました。

結論

結論を先に書きます。

auto registration 機能を使用する場合には

  • REGISTER リクエストには有効期間に 3600 秒を設定して送信している。
  • REGISTER リクエストのレスポンスに書かれた有効期間の60秒前に、REGISTER リクエストを再送する。
    • (レスポンスに書かれた有効期間が 3600 秒であれば、3600 - 60 秒後に再送信)
    • レスポンスに有効期間が書かれていない場合は、有効期間を3600秒として動作する。

REGISTER リクエストの送信に関するコード

1

REGISTER リクエストを作成しているのは、SipHelper.sendRegister(SipProfile userProfile, String tag, int expiry)
222 行 : 引数 expiry がリクエストにセットされる。

2

SipHelper.sendRegister の呼び出し元は、SipSessionImpl.readyForCall(EventObject evt)
引数expriy には、変数duration を指定している。
変数duration は、RegisterCommand.getDuration() の値がセットされている。

3

RegisterCommand.getDuration() は、インスタンス時の引数duration を返す。
RegisterCommand を作成しているのは、SipSessionGroup.register(int duration)
SipSessionGroup.register の引数duration を変更せず使用して、RegisterCommand をインスタンス化する。

4

SipSessionGroup.register の呼び出し元はいくつかあるが、auto registration 時は以下の2つ。

どちらも、定数EXPIRY_TIMEを指定している。EXPIRY_TIME は 3600。

REGISTER リクエストの応答処理に関するコード

1

レスポンスから Expires を取得しているのは、SipSessionImpl.getExpiryTime(Response response)
取得できなかった場合は、EXPRIY_TIME (3600) を返している。

2

SipSessionImpl.getExpiryTime(Response response) の呼び出し元は、SipSessionImpl.registeringToReady(EventObject evt)

getExpiryTime で取得した値は、SipSessionImpl.onRegistrationDone に渡す。

3

SipSessionImpl.onRegistrationDonemProxy.onRegistrationDone を呼ぶ。

mProxy は、setListener でセットされる。呼び出し元をたどると以下のように SipAutoReg クラスがある。
SipSessionImpl.setListener
new SipSessionImpl
SipSessionImpl.createSession
SipAutoReg.start

SipAutoReg.onRegistrationDone は、SipAutoReg.restartduration - MIN_EXPIRY_TIME を渡す。
MIN_EXPIRY_TIME は 60 。
(おそらく、期限が切れる60秒前に再送するため)

4

SipAutoReg.restart はタイマーを実行する。タイマーの期間は duration * 1000 ミリ秒後。
タイマーにより、SipAutoReg.run が実行され、mSession.register を実行。
(mSession.register ... REGISTER リクエストの送信指示)

参考

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

Associate Android Developer スタディガイド翻訳①

Associate Android Developer certificate

Google公式のAndroid開発者認定試験があるの、ご存知ですか。
Google Developers Certification : Associate Android Developer

あんまり普及してないのか、受験した方のブログなんかも少ないですね。

自分は太古の時代にAndroidアプリ開発をしていて、最近また何かやってみようとAndroid Studioをインストールしたものの何がなんだかわからず、とりあえず初心者として基本から勉強しようかと。
試験を受けるかはさておき、Androidの基礎を理解するのによさそうと思ってスタディガイドを読み始めたところです。
ただ漫然と読んでると目が滑るので、ノート取り代わりに翻訳してみることにしました。

※そもそも試験が英語のみでオンライン面接もあるそうなので、英語が全くダメだと取得は難しいと思われます

今回の翻訳はこちら
Study Guide : Android Core

Android Core

AndroidはLinuxベースで主にモバイル端末向けにデザインされたOSです。Anroidアプリはマルチタスクで、Java、Kotlin、またはC++で書くことができます。
AAD(訳注:アソシエイトAndroid開発者) 認定試験の準備のため、Android開発者は以下の必要があります:
- Androidシステムの構造を理解している
- Androidアプリの基礎的な構成について説明できる
- Androidアプリのビルド、実行方法を知っている
- ToastSnackbarを用いたポップアップで簡単なメッセージを表示できる
- Notificationsを使用してアプリUIの外側でメッセージを表示することができる
- アプリのローカライズ方法を理解している
- JobSchedulerを使用してバックグラウンド実行タスクをスケジュールできる

Resources

訳注:文中で触れられたリソースについてリンクが並んでます。リンク先ページは↑のURLリンクから本家で参照してください

Android Developers -> Toasts

 

Android Developers -> Snackbar

 

Android Developers -> Localize your app

  アプリのローカライズ方法

Android Developers -> Application fundamentals

  アプリケーションの基礎

Android Developers -> Create a notification

  Notification(通知)の作成

Android Developers -> AndroidX overview

  AndroidX概要

Android Developers -> Getting started with Jetpack

  JetPackを始めよう

Android Developers -> Android KTX (Kotlin)  

Codelabs -> Notifications

Codelabs -> JobScheduler

Codelabs -> WorkManager (Java)

Codelabs -> WorkManager (Kotlin)


今回はここまで。
Resourcesはもう少し色々読んでから追記します。

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

[Delphi] 10.4 移行で TakePhotoFromLibraryAction を使ってると落ちる罠

10.4.1

みなさん!Delphi 10.4.1 は楽しんでますか!
Community Edition が出てないから楽しめないって!?
わかる~
早く出してくれることを祈りましょう。

TakePhotoFromLibraryAction

さて、僕は 10.4.1 を楽しんでいるのですが、Android で、ちょっとした問題にぶつかりました。

それは、TakePhotoFromLibraryAction を使っていると、10.4 に移行したときにアプリが落ちてしまうという問題です。

TakePhotoFromLibraryAction はデバイスの写真を取ってくるアクションですが、ここで写真を選択して、アプリに戻ると直後に落ちます。

対処

10.4.1 で新規に作ったアプリでは落ちないので 10.3.3 から持ってきたファイルで 10.4.1 と何が違うのか diff を取ってみたところ原因がわかりました。

10.4 で新規に作った AndroidManifest.Template.xml には application タグの最後に

android:requestLegacyExternalStorage="true"

が挿入されていました。
そこで、10.3.3 から持ってきた AndroidManifest.Template.xml の application タグの最後に

AndroidManifest.Template.xml
    <application android:persistent="False" 
        android:restoreAnyVersion="False" 
        android:label="Project1" 
        android:debuggable="True" 
        android:largeHeap="False"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme"
        android:hardwareAccelerated="true"
        android:resizeableActivity="false"
        android:requestLegacyExternalStorage="true"> ←ここ!

追加したところ、写真を選んでも落ちなくなりました。

対象範囲別外部ストレージアクセス

Android 10 から「対象範囲別外部ストレージアクセス」(Scoped Strage)という機能が入りました。
これによりメディアファイル(写真や動画)などの取得方法が変りました。

写真を端末から選ぶ操作は普通は Scoped Strage の影響を受けないのですが、TakePhotoFromLibraryAction は受け取った写真データを一旦ファイルに保存し、後でそれを TBitmap.LoadFromFile を使って読み出す、というロジックになっています。

一旦ファイルに保存した時点で Scoped Strage の影響を受けることになり、TBitmap.LoadFromFile が例外を吐いて落ちていました。

requestLegacyExternalStorage

今回追加した requestLegacyExternalStorage は、旧式の外部ファイル管理方式を使うフラグで、これを付けると Scoped Strage は無効化されるため、上記の TakePhotoFromLibraryAction が動作します。

ですが、この救済措置が使えるのは Android 10 (API Level 29) までです。
TargetSDKVersion を Android 11 (API Level 30) にすると無視されます。

Delphi 10.4.1 は正式には Android 11 に対応していないため、現状はこの解決方法でも良いですが、次に出る 10.4.2 は Android 11 に対応して欲しいものです。

See also: サポートされているターゲット プラットフォーム

最後に

ちょっとした問題と書きましたが、いくつか要因を排除するために数日かかっています。
これが皆さんの移行の手助けになれば幸いです!

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