Attached file: file_105_87.txt
¶Attached file: file_105_86.txt
¶Attached file: file_105_85.txt
¶Attached file: file_105_84.txt
¶Attached file: file_105_83.txt
Androidアプリの開発がAndroid Studioのみで公式サポートされるようになってから、ライブラリがjarからaarで配布されることが多くなり、ライブラリ構成も今までよりも小さな機能単位で分割されるようになりました。
Android Studioで開発する場合はソフト側で依存関係の解決してくれるので特に問題は無いのですが、ANEを作るのはとても大変になり、もはや手作業で依存関係を調整するのは現実的ではなくなってきました。
そこで依存関係を調べてダウンロードし、展開して名前を変更してANEを作りやすくするツールを作ったので、その時の知見をここに書きます。
ツール自体は汎用性がなく公開するに耐えるような物ではないので公開する予定はありませんが、その時に利用した各種仕様や注意事項をまとめています。
自分でツールを作るのが最善の方法か分かりませんし、例えばAndroid Studioの使い方に精通すれば簡単に依存性に基づいてライブラリをダウンロードできるかもしれません。ただ、同じような問題に悩んでいる人の役に立つ可能性もあると思いこの記事を公開します。
ライブラリの依存関係はAndroid Studioの機能でもできるのですが、それを見ながら一つずつダウンロードするのは面倒なので、pomファイルを利用しました。
Android StudioはApache Mavenというプロジェクト管理ツールを使っており、それが使用する依存関係を記載したxmlファイルがpomファイルです。
例えばGoogleのライブラリについては、
「http://maven.google.com/「グループパス」/「ライブラリ名」/「バージョン」/「ライブラリ名」-「バージョン」.pom」
にアクセスすればpomファイルが取得できます。
つまり「play-services-ads」のバージョン「17.2.0」なら、
http://maven.google.com/com/google/android/gms/play-services-ads/17.2.0/play-services-ads-17.2.0.pom
となります。google以外が提供しているライブラリも、Mavenを利用する物なら「http://maven.google.com/」の部分をgradleに記載するURLに変更すればpomファイルが取得可能です。
pomファイルの内容は例えば
<?xml version='1.0' encoding='UTF-8'?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.google.android.gms</groupId> <artifactId>play-services-ads</artifactId> <version>17.2.0</version> <packaging>aar</packaging> <dependencies> <dependency> <groupId>com.android.support</groupId> <artifactId>customtabs</artifactId> <version>26.1.0</version> <scope>compile</scope> <type>aar</type> </dependency> <dependency> <groupId>com.google.android.gms</groupId> <artifactId>play-services-ads-base</artifactId> <version>[17.2.0]</version> <scope>compile</scope> <type>aar</type> </dependency> 以下略
といったものになっており、「dependencies」の部分に依存関係が記載されています。
記載されている依存関係は直接の子だけです。孫の依存関係は記載されている子ライブラリのpomファイルを参照しなければなりません。つまり、ツールを作成する場合は再帰的にpomファイルを取得して解析しなければなりません。
pomファイルの詳しい仕様はhttps://maven.apache.org/pom.htmlで公開されています。また、実際にファイルを見るとだいたいどのような事が書いてあるか分かると思います。そこで、わかりにくい部分や注意すべき所だけ解説します。
そのライブラリがどのようなファイル形式で配布されているかです。省略されているとjarファイルです。ライブラリのファイルをダウンロードするときに必要になります。全てのライブラリがaar形式で配布されているわけではないので注意が必要です。
dependencyのversionに時々ついている[](角括弧)は正確にそのバージョンでなければならない事を意味します。他に「[1.0,2.0)」となっていれば、1.0以上、2.0未満を表すなど、数学の数値の範囲を表す記号と同様のルールがあります。特にそういった記号がなければ推奨されるバージョンを指します。
dependencyの中に「exclude」というタグがあり、ライブラリ名が記載されていることがあります。その場合はそのライブラリはさらにその下の階層の依存性から取り除かなければなりません。この処理を忘れるとライブラリ同士のバージョンなどが衝突しやすくなります。
依存関係が分かれば次はそれをダウンロードします。そのURLはGoogleなら
https://dl.google.com/dl/android/maven2/「ライブラリのグループパス」/「ライブラリ名」/「バージョン」/「ライブラリ名」-「バージョン」.「拡張子」
です。ここで「拡張子」はpomファイルのpackagingで指定された物です。
このあたりをおさえておけば、pomファイルから依存関係を割り出してダウンロードするツールが作れると思います。
ライブラリをダウンロードしたら、それをzip解凍して必要なファイルを取り出さなければなりません。
Androidで使うaarファイルの構成物の仕様は以下のページにまとまっています。
https://developer.android.com/studio/projects/android-library?hl=ja#aar-contents
多くのライブラリでは
AndroidManifest.xml classes.jar /res/
をANEやAirアプリに取り込む必要があります。また、
/assets/
も必要となることがあります。他の「/libs/name.jar」や「/jni/abi_name/name.so」も必要となる可能性がありますが、私は扱ったことがないのでここでは省略します。
AndroidManifest.xmlの「manifest」の「package」属性にパッケージ名が記載されています。これはANEを作成するときのplatform.xmlで指定する「packagedResource」の下の「packageName」になります。例えば、play-services-adsならAndroidManifest.xmlは
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.google.android.gms.ads.impl" > <uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.customtabs" /> </manifest>
となっているので、パッケージ名は「com.google.android.gms.ads.impl」となります。
また、このAndroidManifest.xmlの内容は最終的なapkのAndroidManifest.xmlに統合されます。特に「uses-permission」や「activity」がよく記述されているので、忘れずにAirをビルドするときのapp.xmlの「manifestAdditions」の下に付け加えなければなりません。また、異なるライブラリのAndroidManifest.xmlに全く同じ内容の項目がある場合には一つにまとめる必要があります。
実際にどのような項目が必要でどう統合されるかを確かめるなら、Android Studioで実際にライブラリを含むapkファイルを作成して、それをzip解凍すると統合されたAndroidManifest.xmlが得られます。ただし、このAndroidManifest.xmlはバイナリファイルになっているので、例えば https://qiita.com/red12sparrow11/items/59962c8b065860944670を参考にするなどしてテキストに戻す必要があります。またこのAndroidManifest.xmlは変数が展開されてしまっているので、これをそのままapp.xmlに書くのはよくありません。参照している値が変わったときに同期がとれなくなるからです。しかし、どういった項目が必要かの参考にはなります。
/res/はANEの作成ではplatform.xmlのpackagedResourceのfolderNameで指定する物で、パッケージ名と組にして管理する必要があります。わかりやすい名前に変更しておいた方が良いでしょう。中身が空の時も多く、その時はplatform.xmlに記載する必要はありません。
定数や画像などのAndroidのリソースは多言語化、多解像度対応が容易なRクラスを利用するために/res/に入れるのが普通です。しかし、直接ファイルをあつかいたい場合にはリソースを/assets/に入れることがあります。この中にファイルがあれば、それらはANE作成時ではなく、Airアプリ作成時に取り込んでおく必要があります。Animateの「Air for Android設定」の「含めるファイル」の一番上の階層に入れておけば、apkを書き出すとその中に自動的に「assets」というディレクトリが作成されてそこに格納されます。
例えば取り込むaarファイルを展開した/assets/以下に「test.png」というファイルが含まれていたら、以下のようにします。
ライブラリアップデートでANEを作り替えるときは、Airの方での取り込みも修正しないといけないわけです。忘れやすいところですから注意してください。
よって上側確率から横軸の値を求めるisfを使えば
¶95%信頼区間は平均値の周りに確率の和が0.95となる範囲を取り出すことになるので、上側および下側確率が0.025になる部分以外とも考えられます。
¶一般に100(1-a)%の信頼区間の係数を求めるならnorm.isf(q=a/2)と書けます。
¶95%信頼区間は平均値の周りに確率の和が0.95となる範囲を取り出すことになるので、上側または下側確率が0.025になる部分以外とも考えられます。
¶95%信頼区間は平均値の周りに確率の和が0.95となる範囲を取り出すことになるので、確率分布の一方の端の確率の和が0.025になる部分以外とも考えられます。
統計解析の本の最後によく標準正規分布表が載っています。 これで正規分布に関する問題を解くこともできますが、「21世紀にもなって表とか引きたくない」と思う事もよくあります。しかも本によって上側確率だったり0からの累積確率だったりまちまちです。 表の使い方覚えるぐらいなら、統計ソフトやプログラムの統計ライブラリの使い方を覚えた方が後々役に立ちそうで、あまり学ぶモチベーションがあがらないことがあります。
そういうときに、pythonが入ったパソコンがあれば、scipyを使って計算できます。 scipyはpipがあればコマンドラインで
pip install scipy
か
pip3 install scipy
などを実行すればインストールできます。
横軸の値から上側確率を出すには
>>> from scipy.stats import norm >>> norm.sf(x=1.233) 0.10878788208591617
とします。
横軸の値から下側確率を出すには
>>> from scipy.stats import norm >>> norm.cdf(x=1.233) 0.8912121179140838
とします。
確率の定義から、この二つを加えると1になります。
>>> norm.cdf(x=1.233) + norm.sf(x=1.233) 1.0
なので、どちらか覚えれば十分です。 ただし、1からsfを引くよりは直接cdfを使った方が精度は良くなる可能性があります。
累積確率から横軸の値を出すには
>>> from scipy.stats import norm >>> norm.ppf(q=0.9) 1.2815515655446004
とします。
上側確率から横軸の値を出すには、
>>> norm.isf(q=0.9) -1.2815515655446004
とします。
標準正規分布の性質から、累積確率の結果とは符号が逆転します。 よってこれもどちらか覚えれば十分です。
normは標準正規分布だけではなく、一般の正規分布にも対応しています。
一般の正規分布で使う時はパラメータに loc, scaleを加えます。 それぞれ平均と標準偏差です。 ただ、標準化すれば済むのでこのページでは省略します。
詳しくはこちらでscipy.stats.norm。
95%信頼区間を求めるときに使う係数、「1.96」を求めることを考えます。 95%信頼区間は平均値の周りに確率の和が0.95となる範囲を取り出すことになるので、上側および下側確率が0.025になる部分以外とも考えられます。
よって上側確率から横軸の値を求めるisfを使えば
>>> norm.isf(q=0.025)
結果:
1.9599639845400543
を得ます。
同様に90%信頼区間の係数なら
>>> norm.isf(q=0.05)
結果:
1.6448536269514726
となります。
一般に100(1-a)%の信頼区間の係数を求めるならnorm.isf(q=a/2)と書けます。
しかし、ちょっと分布の値を参照してみたい場合などには対話モードですぐに使えるので便利です。
¶カイ二乗分布はカイ二乗検定などでよく使う確率分布です。しかし、検定自体はPythonならscipy.stats.chi2_contingencyなどで行えますし、他の統計ソフトでも実装されているので、そちらを使うべきです。
Pythonでカイ二乗分布の値を求めるなら、SciPyのscipy.stats.chi2が利用できます。
カイ二乗分布はカイ二乗検定などでよく使う確率分布です。しかし、検定自体はPythonならscipy.stats.chi2_contingencyなどで行えますし、他の統計ソフトでも実装されているので、そちらを使うべきです。
しかし、ちょっと分布の値を参照してみたい場合などには対話モードですぐに使えるので便利です。
横軸の値から下側確率を求めるにはcdfを使います。
from scipy.stats import chi2 chi2.cdf(x = 2,df = 4)
結果
0.2642411176571154
xが横軸の値、dfが次元です。
横軸の値から上側確率を求めるにはsfを使います。
from scipy.stats import chi2 chi2.sf(x = 2, df = 4)
結果:
0.7357588823428847
確率の定義より、上の結果を加えると1になります。
そのため、例えば上側確率を求めるのに1 - chi2.cdfとしても良いですが、誤差がでることもあるようです。
カイ二乗検定自体が通常近似的な物なのであまりないとは思いますが、ごくわずかな誤差が気になる場合は上記二つを使い分ける必要があります。
そうでないなら、どちらかだけ覚えて1から引けば良いでしょう。
カイ二乗検定でよく使う、累積確率から横軸の値を求めるのは、ppfを使います。
from scipy.stats import chi2 chi2.ppf(q = 0.95,df = 4)
結果
9.487729036781154
備忘録的なメモ
]]>しかし、もしもっと大量のデータを扱うなら、NumPyにも同様の関数があり、より高速なのでそちらを使うのがおすすめです。
¶上の例のように、1000個ぐらいのデータなら、スペックの低いパソコンでもほぼ一瞬で答えが出ます。
しかし、もしもっと大量のデータを扱うなら、NumPyにも同様の関数がありより高速なので、そちらを使うのがおすすめです。
¶また、母分散(pvariance)や標本標準分散(variance)も全く同様に求められます。
¶乱数を使っているので値は実行するたびに変化しますが、平均10、標準偏差2.5に近い値が出ています。
¶母標準偏差はデータの個数で割るもの、標本標準偏差はデータの個数から1減じたもので割る方です。 詳細は略しますが、標本から母集団の統計量を推測するときには標本標準偏差を使うべきとされています。
Pythonには標準でstatisticsという数理統計関数ライブラリが用意されています。
それを使うと、平均や標準偏差、中央値などさまざまな統計量が簡単に計算できます。
標準偏差は母標準偏差(pstdev)と標本標準偏差(stdev)が両方用意されています。 母標準偏差はデータの個数で割るもの、標本標準偏差はデータの個数から1減じたもので割る方です。 詳細は略しますが、標本から母集団の統計量を推測するときには標本標準偏差を使うべきとされています。
「Python/正規分布に従う乱数を生成する」で紹介した「random.gauss」で、平均10、標準偏差2.5の正規分布に従うランダムなデータを1000個作成し、その平均と標準偏差を求めると以下のようになります。
import statistics import random m = 10 v = 2.5 n = 1000 data = [] for j in range(n): data.append(random.gauss(m,v)) print("平均\t\t",statistics.mean(data)) print("母標準偏差\t",statistics.pstdev(data)) print("標本標準偏差\t",statistics.stdev(data))
結果:
平均 9.961114639666533 母標準偏差 2.4024438913751966 標本標準偏差 2.403646014988765
乱数を使っているので値は実行するたびに変化しますが、平均10、標準偏差2.5に近い値が出ています。
また、母分散(pvariance)や標本標準分散(variance)も全く同様に求められます。
上の例のように、1000個ぐらいのデータなら、スペックの低いパソコンでもほぼ一瞬で答えが出ます。
よって、両方覚えなくてもどちからだけ知っておけば十分です。ただし、1から引くと精度は少しだけ落ちることがあるようなので、非常に細かい精度を問題にするなら上側確率や下側確率を直接求めた方が無難です。
¶25〜30程度までは差が大きく減少し、その後はほぼ横ばいになることが分かります。
¶自由度1では不自然な値になっていますが、現実にはそんな標本数は使わないので無視すると、25〜30程度までは差が大きく減少し、その後はほぼ横ばいになることが分かります。
¶自由度1や2では不自然な値になっていますが、現実にはそんな標本数は使わないので無視すると、25〜30程度までは差が大きく減少し、その後はほぼ横ばいになることが分かります。
¶自由度100あたりでようやく1パーセントを切ります。
t分布は母集団が正規分布していて、かつその標準偏差が未知の場合、少ない標本からもとの分布の平均値を推定するときに使われる確率分布です。
統計の教科書などの最後によく数表が載っていますが、値が結構とびとびだったりして使いにくいことがよくあります。こういうことはコンピューターにやらせた方が早くて正確な結果が得られます。
Pythonでt分布をあつかうにはscipyを使います
(注意 :Python/標準正規分布と同様の内容なので、こちらを読んでからの方が理解しやすいかも知れません。)
自由度10のt分布の横軸の値から上側確率を出すには
from scipy.stats import t t.sf(x=1.233,df=10)
結果
0.12288719525159397
とします。xが横軸の値、dfが自由度です。
自由度10のt分布の横軸の値から下側確率を出すには
from scipy.stats import t t.cdf(x=1.233,df=10)
結果
0.877112804748406
確率の定義から、この二つを加えると1となります。
t.sf(x=1.233,df=10) + t.cdf(x=1.233,df=10)
結果
1.0
よって、両方覚えなくてもどちからだけ知っておけば十分です。ただし、1から引くと精度は少しだけ落ちることがあるようなので、非常に細かい精度を問題にするなら上側確率や下側確率を直接求めた方が無難です。
累積確率から横軸の値を出すには
t.ppf(q=0.9,df=10)
結果
1.3721836411102866
とします。
上側確率から横軸の値を出すには、
t.isf(q=0.9,df=10)
結果
-1.3721836411102866
とします。
t分布の性質より、これらは符号が反転します。 よってどちらか覚えれば十分です。
t分布は自由度、つまり標本の数が多くなると正規分布に近づくとされています。
そのため、通常は標本の数が多いときは正規分布を使い、少ないときにしかt分布は使いません。
この「少ない」の具体的な数は、要求され精度によって異なるでしょうが、私の手持ちの書籍などによるとだいたい25〜30未満を意味するようです。
では、実際に自由度によってどの程度の差が出るのかをPythonで計算して、この値の妥当性を見てみます。
実際によく使いそうな確率0.95の横軸の値の比を計算してみると以下のようになりました。
t.isf(q=0.95,df=10) / norm.isf(q=0.95)
結果
1.1018981221872615
t.isf(q=0.95,df=20) / norm.isf(q=0.95)
結果
1.0485542389065539
以下同様にして
1.038475845670062
1.031861356263898
1.0093508011061045
自由度10では10パーセントあった差が、自由度25〜30で差は3〜4パーセントになります。
自由度100あたりでようやく1パーセントを切ります。
自由度が10から30へ変化したときの減少幅の方が30から100までの減少幅よりはるかに大きく、30前後を自由度の区切りと考えるのが自然そうに思えます。
実際、上の計算を自由度1〜100まで行ってグラフにすると以下のようになりました。
25〜30程度までは差が大きく減少し、その後はほぼ横ばいになることが分かります。
注意:この例はPython2系のコードです。 Python3以上で実行する場合は「print i」を「print(i)」にしてください。以下の例も同様です。
¶print i,w
Python3での結果
インデックスをつけてループするには、組み込み関数「enumerate」を使います。
s = "Test." for i, w in enumerate(s): print(i,w)
結果:
0 T 1 e 2 s 3 t 4 .
例3:辞書型だとキーでループすることになります。
回数を指定してループする場合は、「Python/回数を指定してループ」も参照してください。
Pythonのfor文は配列などをループ処理するときに使う制御文です。
例1
for i in [1,2,3]: print i
結果
1 2 3
注意:この例はPython2系のコードです。 Python3以上で実行する場合は「print i」を「print(i)」にしてください。以下の例も同様です。
例2
for s in "abc": print s a b c
例3:辞書型だとキーでループすることになります。
d = {"a":"1","b":"2","c":"3"} for k in d: print k,d[k]
結果
a 1 c 3 b 2
inの後は上の例のように、イテレータブルオブジェクトと呼ばれる数え上げたり列記したりできるデータの型を指定します。
Pythonのfor文ではelse文も書けます。
for i in range(3): print i else: print "loop end"
Python3での結果
0 1 2 loop end
上の例のように、ループが終了するとelseが実行されます。
もしループをbreakして最後まで実行されなかった場合はelseは実行されません。
「こちら」でも詳しく説明しています。
また、Flag型はフラグの状態をわかりやすく出力してくれるのでデバッグの時便利です。
¶フラグが少なく処理が簡単な場合は真偽型boolをそのまま使うか、いくつかまとめたクラスを作って使う方が楽です。
¶ただし、現在のようにメモリも大容量でCPUも速くなった時代にフラグ処理が少し軽くなる程度の工夫が必要かどうかはちょっと微妙な気もします。特にPythonのようなスクリプト言語は他の部分が遅いのでフラグがボトルネックにはなりにくく、ビットフラグをよく使うのはCなどのコンパイル言語というイメージです。そういった言語で使い慣れている人にはFlagやIntFlagは使いやすいでしょう。
¶ただし、現在のようにメモリも大容量でCPUも速くなった時代にフラグ処理が少し軽くなる程度の工夫が必要かどうかはちょっと微妙な気もします。特にPythonのようなスクリプト言語は他の部分が遅いので、ビットフラグをよく使うのはCなどのコンパイル言語というイメージです。そういった言語で使い慣れている人にはFlagやIntFlagは使いやすいでしょう。 フラグが少ない場合は真偽型boolをそのまま使うか、いくつかまとめたクラスを作って使う方が楽です。
¶注意:FlagやIntFlagは列挙型Enumの一種です。Enumを知らない場合はPython/列挙型を先に読むのを推奨します。
昔からあるプログラムの書き方で、ビット毎にフラグを設定して一つの変数に複数のフラグを保存する方法があります。
ビット単位で管理するのでメモリが節約できます。また一般にビット演算は高速なのでプログラムの高速化も狙えます。
これと同じようなことを実現するPythonのクラスがFlagです。
例えば、ゲームで状態異常を定義したいとして、次のようなクラスを定義します。
from enum import Flag class PlayerStatus(Flag): SLEEP = 1 #睡眠 POISON = 2 #毒 PARALYSIS = 4 #麻痺
ここで1,2,4は二進数で書けば1,10,100の事で、それぞれ1ビット目、2ビット目,3ビット目をフラグとして使っています。
このように手で書くこともできますが、Python3.6で追加されたautoという関数を使うと自動で二のべき乗を割り振ってくれます。
from enum import Flag ,auto class PlayerStatus(Flag): SLEEP = auto() POISON = auto() PARALYSIS = auto() for s in PlayerStatus: print(s.name,s.value)
結果:
SLEEP 1 POISON 2 PARALYSIS 4
これの良いところは一つの変数に複数のフラグを設定できることです。 例えば、睡眠かつ毒状態のステータスは
status = PlayerStatus.SLEEP | PlayerStatus.POISON
と書きます。これを表示させると、
print(status)
結果:
PlayerStatus.POISON|SLEEP
というふうに、実際にフラグが複数設定されています。 また、Flag型はフラグの状態をわかりやすく出力してくれるのでデバッグの時便利です。
フラグが立っているかどうかは「&」でチェックします。
status = PlayerStatus.SLEEP | PlayerStatus.POISON if status & PlayerStatus.SLEEP: print("SLEEP!") if status & PlayerStatus.POISON: print("POISON!") if status & PlayerStatus.PARALYSIS: print("PARALYSIS!")
結果:
SLEEP! POISON!
フラグをオフにするには以下のように「&」と「~」(否定)を組み合わせます。
status = PlayerStatus.SLEEP | PlayerStatus.POISON status = PlayerStatus.SLEEP & (~PlayerStatus.POISON) if status & PlayerStatus.SLEEP: print("SLEEP!") if status & PlayerStatus.POISON: print("POISON!") if status & PlayerStatus.PARALYSIS: print("PARALYSIS!")
結果:
POISON!
また、Flag型はFlag型同士でなければ演算できないことに注意してください。 Intと演算したいときには次のIntFlag型を使います。
IntFlag型はFlag型とほぼ同じ機能がありますが、Flag型とは異なり整数としても扱えます。
つまり、Flag型では次の処理はエラーとなります。
status = PlayerStatus.SLEEP | 2 print(status)
Flag型での結果:
Traceback (most recent call last): File "enumflgs.py", line 50, in <module> status = PlayerStatus.SLEEP | 2 TypeError: unsupported operand type(s) for |: 'PlayerStatus' and 'int'
PlayerStatus.POISON|SLEEP
直接数値が使えないのでFlag型の方がマジックナンバーが発生しにくいという利点があります。
Flagはビット演算でのフラグの管理を使いやすくするクラスです。 IntFlagはそのFlagを整数としても扱えるようにした物です。
フラグがたくさんあって整理が難しい場合や、少しでも処理を高速化したい場合には便利なクラスです。
ただし、現在のようにメモリも大容量でCPUも速くなった時代にフラグ処理が少し軽くなる程度の工夫が必要かどうかはちょっと微妙な気もします。特にPythonのようなスクリプト言語は他の部分が遅いのでフラグがボトルネックにはなりにくく、ビットフラグをよく使うのはCなどのコンパイル言語というイメージです。そういった言語で使い慣れている人にはFlagやIntFlagは使いやすいでしょう。
フラグが少なく処理が簡単な場合は真偽型boolをそのまま使うか、いくつかまとめたクラスを作って使う方が楽です。
ここに書くと長くなるのでページを分けてPython/FlagとIntFlagで説明しています。
¶必要であれば、後述のIntEum型なら整数との比較が可能です。
¶めんどうなようですが、こうなっているとせっかく定義したEnum型を使わずに手で直接値を指定してしまうという、いわゆるマジックナンバーの問題をある程度予防してくれるというメリットがあります。
¶めんどうなようですが、こうなっているとせっかく定義したEnum型を使わずに手で直接値を指定してしまうのをある程度予防してくれるというメリットがあります。
¶例えば、ゲームのプログラムで今どの画面かを識別するために以下のような変数を定義したとします。
Pythonの列挙型enumは何かひとまとまりの定数を定義するときに便利な型で、Python3.4から導入されました。
例えば、ゲームのプログラムで今どの画面かを識別するために以下のような変数を定義したとします。
SCREEN_TITLE = 1 #タイトル画面 SCREEN_PLAYING = 2 #プレイ中画面 SCREEN_GAME_OVER = 3 #ゲームオーバー画面 SCREEN_TRANSITION = 4 #画面遷移中
このままでもプログラムできますが、もう少しまとめてあつかいたい、と考える人もいるでしょう。 また、ただの変数なのでうっかり書きかえないかも心配です。
これをenumを使えば、以下のように書き直せます。
from enum import Enum class Screen(Enum): TITLE = 1 PLAYING = 2 GAME_OVER = 3 TRANSITION = 4
使う時は
Screen.PLAYING
などとすれば参照できます。
比較するときはメンバー同士で行う必要があります。
print(Screen.TITLE == 1) print(Screen.TITLE == Screen.TITLE)
結果:
False True
Enumは他の型の値と直接比較できないので注意してください。 めんどうなようですが、こうなっているとせっかく定義したEnum型を使わずに手で直接値を指定してしまうという、いわゆるマジックナンバーの問題をある程度予防してくれるというメリットがあります。
必要であれば、後述のIntEum型なら整数との比較が可能です。
この記事では数値で定義しましたが、文字列で定義することも可能です。
また、これらは実質定数として扱えます。 例えば、
Screen.GAME_OVER = 2
とすると
Traceback (most recent call last): File "enumtest.py", line 10, in <module> Screen.GAME_OVER = 2 File "/usr/lib/python3.6/enum.py", line 363, in __setattr__ raise AttributeError('Cannot reassign members.') AttributeError: Cannot reassign members.
というような実行時エラーが出ます。
また、
screen = Screen.PLAYING print(screen)
とすると、
Screen.PLAYING
とクラスとメンバー名が出力されるのでデバッグの時にわかりやすくて便利です。
nameとvalueというプロパティを使えばメンバー名と値も参照できます。
print(Screen.GAME_OVER.name,Screen.GAME_OVER.value)
結果:
GAME_OVER 4
ループ処理もできます。 このとき、順番は定義順であることが保証されています。
for s in Screen: print(s)
結果:
Screen.TITLE Screen.PLAYING Screen.GAME_OVER Screen.TRANSITION
となります。
このようにenumを使うと定数をまとめてあつかいやすくなります。
さらに以下のような便利な機能があります。
Python3.6から自動で番号をつけてくれるautoという関数が加わりました。
from enum import Enum, auto class Screen(Enum): TITLE = auto() PLAYING = auto() GAME_OVER = auto() TRANSITION = auto()
とすれば自動で番号をつけてくれます。
例えば上の例でタイトル画面の前にというロード画面を付け加えたい、順番的にできればそれを表す定数は1にしたい、となったときに、手で番号をつけると今までの定義を全て手で1ずらさねばなりません。
class Screen(Enum): LOADING = 1 TITLE = 2 #ここから下全て1ずらした PLAYING = 3 GAME_OVER = 4 TRANSITION = 5
enumのメンバー数が多いと編集が大変になります。 autoを使っておけば自動で番号をつけてくれるので「LOADING = auto()」という一行を付け加えるだけでよく、手間が省けます。
class Screen(Enum): LOADING = auto() #この行だけ増やした TITLE = auto() PLAYING = auto() GAME_OVER = auto() TRANSITION = auto()
また、enum型は値が一意になる事は保証していません。 次のようなコードは問題なく実行されます。
class Test(Enum): TEST1 = 1 TEST2 = 1 TEST3 = 1
しかし、実際には一意な値をつけたいときが多いでしょう。 手で定義するとどうしても書き間違いの可能性が出てきます。 その時は「@unique」というデコレータを使えば一意であることが保証されます。
@unique class Test(Enum): TEST1 = 1 TEST2 = 1 TEST3 = 1
とすると、実行時エラーになります。
しかし、autoを使っておけば、自動的に値は一意になります。 とりあえず値はなんでもいいという場合にはautoを使うのがおすすめです。
autoを使いつつ、特定の値を同じにしたければ以下のようにします。
class Test(Enum): TEST1 = auto() TEST2 = auto() TEST3 = auto() TEST2_ALT = TEST2 print(Test.TEST2_ALT,Test.TEST2_ALT.value)
結果:
Test.TEST2 2
Enumとほぼ同じですが、整数や他のIntEnum型との直接の比較が可能になっています。
ほとんどの場合Enumで十分ですが、外部から入力された値に名前をつけたい、他のプログラムとデータのやり取りをするときに独自の名前をつけたい、計算結果に名前をつけたい、といった場合に、その値が整数なら簡単に比較できるようになるので便利です。
Enumにビット演算子を適用可能にしてフラグとして使いやすくした物です。 ここに書くと長くなるのでページを分けてPython/FlagとIntFlagで説明しています。
https://dl.google.com/dl/android/maven2/「ライブラリのグループパス」/group-index.xml
「https://dl.google.com/dl/android/maven2/「ライブラリのグループパス」/group-index.xml」 https://dl.google.com/dl/android/maven2/master-index.xml
「ライブラリのグループパス」の部分は、
https://dl.google.com/dl/android/maven2/master-index.xml
に一覧がありますが、基本的にはパッケージ名の「.」を「/」にしたものです。
例えばGoogle Play Servicesならパッケージ名が「com.google.a
ndroid.gms」なので「com/google/android/gms」となります。
https://dl.google.com/dl/android/maven2/「ライブラリのグループパス」/「ライブラリ名」/「バージョン」/「ライブラリ名」-「バージョン」.aar
ただし、単純に最新バージョンをダウンロードすると、依存関係などで動かない可能性があるので、バージョンは自分で指定した方が良いかも知れません。
¶with open(l+".aar", 'wb') as save_file: with open("support-v4.aar", 'wb') as save_file:
Android Studioで、以前はSDKマネージャーで「Google Repository」をチェックしておけば自動で使っているライブラリのaarファイルなどがダウンロードされていました。しかしAndoid Studioのアップデートによりリポジトリのダウンロードがされなくなりました。 Android Studioを使って開発している人は、Gradleファイルなどをちゃんと設定しておけば勝手に依存関係を解決してくれるのでなんの問題もありません。しかし、それ以外のツールで開発している場合は必要なライブラリのaarファイルをダウンロードしなければならないときがあります。
Android Studioで、以前はSDKマネージャーで「Google Repository」をチェックしておけば自動で使っているライブラリのaarファイルなどがダウンロードされていました。しかしAndoid Studioのアップデートによりリポジトリのダウンロードがされなくなりました。
Android Studioを使って開発している人は、Gradleファイルなどをちゃんと設定しておけば勝手に依存関係を解決してくれるのでなんの問題もありません。しかし、それ以外のツールで開発している場合は必要なライブラリのaarファイルをダウンロードしなければならないときがあります。
そのダウンロード方法をご紹介します。
まず、ダウンロードしたライブラリのバージョンを調べます。
グーグルのサイトにビルドツールMaven用のバージョンが一覧になっているxmlファイルがあるので、それを使うと便利です。
https://dl.google.com/dl/android/maven2/「ライブラリのグループパス」/group-index.xml
にアクセスすると、利用可能なバージョンが一覧となっています。
https://dl.google.com/dl/android/maven2/master-index.xml
「ライブラリのグループパス」の部分は、
https://dl.google.com/dl/android/maven2/master-index.xml
に一覧がありますが、基本的にはパッケージ名の「.」を「/」にしたものです。
例えばGoogle Play Servicesならパッケージ名が「com.google.a
ndroid.gms」なので「com/google/android/gms」となります。
よってGoogle Play Servicesでは次のxmlファイルにバージョンが一覧となっています。
https://dl.google.com/dl/android/maven2/com/google/android/gms/group-index.xml
ここでダウンロードしたいライブラリのバージョンを選びます。
次に実際のarrファイルの場所は
https://dl.google.com/dl/android/maven2/「ライブラリのグループパス」/「ライブラリ名」/「バージョン」/「ライブラリ名」-「バージョン」.aar
となります。
例えば、ライブラリ「play-services-basement」のバージョン「16.2.0」なら、次のurlからダウンロードできます。
https://dl.google.com/dl/android/maven2/com/google/android/gms/play-services-basement/16.2.0/play-services-basement-16.2.0.aar
手でurlを書き直してダウンロードするのは少し面倒なので、よく使うライブラリならスクレイピングツールを作っておくと便利です。
例えば「play-services-ads-lite,play-services-ads,play-services-base,play-services-basement,play-services-tasks,support-v4」の最新バージョンのurlを表示し、ダウンロードして実行したディレクトリに保存するPython3のスクリプトは以下のようになります。
from bs4 import BeautifulSoup import requests version_url = "https://dl.google.com/dl/android/maven2/com/google/android/gms/group-index.xml" html = requests.get(version_url) bf = BeautifulSoup(html.text,"lxml") libs = ["play-services-ads-lite","play-services-ads","play-services-base","play-services-basement","play-services-tasks"] for l in libs: vs = bf.find(l)["versions"].split(",") lv = vs[len(vs)-1] durl="https://dl.google.com/dl/android/maven2/com/google/android/gms/%s/%s/%s-%s.aar" % (l,lv,l,lv) print(durl) arr_file = requests.get(durl) with open(l+".aar", 'wb') as save_file: save_file.write(arr_file.content) version_url = "https://dl.google.com/dl/android/maven2/com/android/support/group-index.xml" html = requests.get(version_url) bf = BeautifulSoup(html.text,"lxml") vs = bf.find("support-v4")["versions"].split(",") lv = vs[len(vs)-1] durl="https://dl.google.com/dl/android/maven2/com/android/support/support-v4/%s/support-v4-%s.aar" % (lv,lv) print(durl) arr_file = requests.get(durl) with open("support-v4.aar", 'wb') as save_file: save_file.write(arr_file.content)
ただし、単純に最新バージョンをダウンロードすると、依存関係などで動かない可能性があるので、バージョンは自分で指定した方が良いかも知れません。
こちらの記事は以下のサイトを参考にしました。
]]>アップデートにより、ローカルにaarファイルが自動でダウンロードされなくなり、以下の方法は使えなくなりました。Android/Google Play Servicesなどのaarファイルをダウンロードするを参考にしてGoogleのサイトからダウンロードしてください。
アップデートにより、ローカルにarrファイルが自動でダウンロードされなくなり、以下の方法は使えなくなりました。Android/Google Play Servicesなどのaarファイルをダウンロードするを参考にしてGoogleのサイトからダウンロードしてください。
アップデートにより、ローカルにarrファイルが自動でダウンロードされなくなり、以下の方法は使えなくなりました。Android/Google Play Servicesなどのarrファイルをダウンロードするを参考にしてGoogleのサイトからダウンロードしてください。
アップデートにより、ローカルにarrファイルが自動でダウンロードされなくなり、以下の方法は使えなくなりました。Googleのサイトから直接ダウンロードする方法があるのでそれについては後日記事を公開します。
サポートライブラリのバージョンについてはAir SDKの対応しているバージョンとあわせる必要があります。詳しくは「Android向けANE作成で、サポートライブラリを取り込むときにエラーが出る場合の対処方法」を参照のこと。
アップデートにより、ローカルにarrファイルが自動でダウンロードされなくなり、以下の方法は使えなくなりました。
-----ここから-----
---ここまでは現在は使えない-----
AdmobのANEを制作するのに一番やっかいなのが、依存するライブラリを探して取り込むところです。
以前Eclipseでの開発ではライブラリがプロジェクトとして配布され、一つにまとまっていたため、探す必要はなかったのですが、Android Studioからは複数のライブラリが自動で取り込まれるようになったため、どれが必要で、それがどこにあるかを探さねばなりません。
Admobで使われているライブラリは、これで間違いない、という保証はないのですが、 プロジェクトの中間ファイルや、他の人の制作したANEなどを見る限り以下のもののようです。
あらゆる広告で動くのか、逆に余分に取り込みすぎていないのかは正直わからないのですが、とりあえず、これを取り込むと動きます。
サポートライブラリのバージョンについてはAir SDKの対応しているバージョンとあわせる必要があります。詳しくは「Android向けANE作成で、サポートライブラリを取り込むときにエラーが出る場合の対処方法」を参照のこと。
アップデートにより、ローカルにaarファイルが自動でダウンロードされなくなり、以下の方法は使えなくなりました。Android/Google Play Servicesなどのaarファイルをダウンロードするを参考にしてGoogleのサイトからダウンロードしてください。
-----ここから-----
これらのライブラリは、Android SDKの中、support-v4は「sdk/extras/android/m2repository/com/android/support/support-v4」に、他は「sdk/extras/google/m2repository/com/google/android/gms/」 にあります。
必要なのはjarファイルなのですが、全てaarファイルになっているので一つずつ解凍して、中からjarファイルを取り出さなければなりません。また、取り出したjarファイルの名前は全てclasses.jarなので名前も変更しなければなりません。
とても面倒なので、私は以下のshellを作って自動化しています。
BASE_DIR="YOUR_SDK_ROOT/sdk/extras/google/m2repository/com/google/android/gms/" DIRS="play-services-ads-lite play-services-ads play-services-base play-services-basement play-services-tasks" ANE_WORK_DIR="YOUR_ANE_WORK_DIR" JAR_FILES="" for d in $DIRS do AAR_FILE=`ls $BASE_DIR$d/1*/*.aar | sort -nr |sed -n -e 1p` FILE_NAME=`basename $AAR_FILE` cp $AAR_FILE ./ yes|unzip $FILE_NAME RES_DIR_NAME=`echo $FILE_NAME | sed -e 's/.aar/_res/'` JAR_FILE_NAME=`echo $FILE_NAME | sed -e 's/.aar/.jar/'` if [ -e classes.jar ] ; then mv classes.jar $ANE_WORK_DIR$JAR_FILE_NAME fi if [ -e res ] ; then mv res $RES_DIR_NAME fi JAR_FILES=$JAR_FILES" "$JAR_FILE_NAME done echo '---------------------------------------------------' for n in $JAR_FILES do echo '<packagedDependency>'$n'</packagedDependency>' done
ver 10代から検索して最新の物を検索しています。ver 20が出たら、修正の必要があります。
android-support-v4は一つだけなので手でやっています。
---ここまでは現在は使えない-----
また、このときbasememtを展開して出てくるresフォルダ内のvalues/values.xmlの中の「google_play_services_version」は後で使うのでメモしておいてください。例えば、ver9.2.1なら
<integer name="google_play_services_version">9256000</integer>
となっています。
このようにして、以下のjarファイルをすべて用意してください。
バージョン番号部分は適宜読み替えてください。
extension.xmlはこちらとほとんど同じです。
<?xml version="1.0" encoding="utf-8" standalone="no"?> <extension xmlns="http://ns.adobe.com/air/extension/22.0"> <id>ANEAdmob</id> <versionNumber>1.0.0</versionNumber> <platforms> <platform name="Android-ARM"> <applicationDeployment> <nativeLibrary>classes.jar</nativeLibrary> <initializer>jp.njf.aneadmob.ANEAdmobFREExtension</initializer> </applicationDeployment> </platform> </platforms> </extension>
今回はandroidのライブラリの取り込み用のxmlファイルが一つ必要です。 内容は以下の通りです。
<platform xmlns="http://ns.adobe.com/air/extension/22.0"> <packagedDependencies> <packagedDependency>android-support-v4.jar</packagedDependency> <packagedDependency>play-services-ads-lite-9.2.1.jar</packagedDependency> <packagedDependency>play-services-ads-9.2.1.jar</packagedDependency> <packagedDependency>play-services-base-9.2.1.jar</packagedDependency> <packagedDependency>play-services-basement-9.2.1.jar</packagedDependency> <packagedDependency>play-services-tasks-9.2.1.jar</packagedDependency> </packagedDependencies> </platform>
ファイル名はなんでも良いのですが、「platform-android.xml」としておきます。 名前空間のバージョン(ここでは「22.0」)はextension.xmlのものと同じでないといけないので注意してください。
通常なら、これに加えてaarの展開で出たリソースファイルも指定するのですが、admobの広告に関しては、使っているリソースが一つだけ、マニフェストファイルで使う「@integer/google_play_services_version」のみです。なので、リソースファイルを取り込まなくても、マニフェストファイルにすでにメモしてもらった「google_play_services_version」の数字(ここの例なら「9256000」)を書き込めば良いだけです。そうすれば、リソースファイルの取り込みは必要ありません。
しかし、全ての広告でリソースを使っていないのか、将来的にも使わないのかは正直分かりません。 気になるのでちゃんと取り込みたい、という人はこちらの「AndroidでのAdmobのANEの作成/リソースを取り込む」を参照してください。
リソースの取り込みは経験的に問題が生じやすいので、ここでは取り込まない方針でANEを作ります。
----アップデートにより、このセクションの手順は意味がなくなったので飛ばしてください-----
特に難しい部分はないはずです。分からない部分があれば「AndroidでのANEの作成/Android側の作成」を参照してください。
¶jarファイルの取り出しについては、こちらの記事と同じです。
¶こちらも分からない部分があれば「AndroidでのANEの作成/Android側の作成」を参照してください。
まず、通常のAndroidアプリで広告を表示できないとANEを作成することはできません。ここではAndroidアプリでAdmobの広告を表示し、そこから必要なファイルを抜き出す方法を紹介します。
こちらの内容は「AndroidでのANEの作成/Android側の作成」にそった方法で行いますので、そちらも参考にして下さい。
また、こちらではAdmobの広告IDを使いますので、あらかじめテスト用のAndroidのアプリの登録とそのID、バナー広告用のID、インタースティシャル広告のIDを用意しておいて下さい。
最初にAndroid Studioでプロジェクトの作成を行います。
名前は好きな物でかまいません。
次のターゲットは「Phone and Tablet」として、Minimum SDKは特に理由が無ければAPI10にしておくと良いでしょう。
次のActivityは余計なものが無い方がやりやすいので「Empty Activity」にしておきます。
Activityの名前はそのままMainActivityにしておきます。
「Finish」ボタンを押すとプロジェクトが作成されます。
動作確認を行うためのテスト用ボタンを配置します。
「AndroidでのAdmobのANEの作成」でも説明したように、このANEには以下の機能があります。
このうち最初の「各種IDの設定」については内部的に一度行うだけのもので、インタラクティブである必要はありません。
よって残りの4つについて、それぞれの機能を確認できるように4つボタンを配置します。
ボタン配置方法の詳細はこちらの記事でご紹介しているので、ここでは結果だけを表示します。
ちょっとずれていますが、Androidのレイアウト調整は結構面倒なのでテスト用と割り切って細かな配置などにはこだわらないでおくのがお勧めです。
それぞれ以下のようにテキストとIDを割り振っています。
次にテスト用のActivityなどと主処理を分離するために、ライブラリを作成します。
File->New->New Moduleを選択します。
Android Libraryを選びます。
名前はANEAdmobとしておきます。
Finishボタンを押せばライブラリが作成されます。
----アップデートにより、このセクションの手順は意味がなくなったので飛ばしてください-----
(この部分については公式ページも参考にして下さい。)
Admobの表示にはGoogle Play Servicesが必要です。それを使えるようにするために、まずAndroid Studioで「Tools->Android->SDK Manager」を選択し、さらにSDK Toolsタブを選び、「Google Play Services」と「Google Repository」にチェックを入れます。
すると次回起動時などにそれらも自動更新されます。
すぐにインストールやアップデートしたい場合は、ウインドウ下部に「Launch Standalone SDK Manager」というリンクがあるので、そこからスタンドアロン版SDKマネージャーを立ち上げてインストールまたはアップデートして下さい。
次にaneadmobのライブラリの中のbuild.gradleのdependenciesに「play-services-ads」を加えます。
例えばver.9.2.1なら
compile 'com.google.android.gms:play-services-ads:9.2.1'
とします。
または
compile 'com.google.android.gms:play-services-ads:9.+'
とすると、ver.9で一番新しいものが使われます。
ただし、ANEの製作では後でGoogle Play Servicesのバージョン番号を使うので、この記法よりもバージョンを明記しておいたほうがトラブルが起こりにくく、こちらの「+」を使った表記はあまりおすすめしません。(マイナーバージョンの違いのみなのでさほど問題は起こらないとは思いますが。)
build.gradleはあちこちにありますが、aneadmobの下のものなので間違えないようにして下さい。 するとdependencies部分はバージョン部分を除いて以下のようになります。
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.google.android.gms:play-services-ads:9.2.1' }
あとは「Sync Now」という表示が出ていると思うので、それをクリックしてgradleファイルの内容をプロジェクトに反映させれば、Admobの広告表示に必要なライブラリの取り込みができます。
広告表示の主処理となる部分を作っていきますが、あとでANEとして分離させないといけないので、サンプルなどにあるようにActivityに直接処理を書くわけにはいきません。admobaneライブラリの中に別クラスを作ってそこに主処理を書きます。
一方、Activityの方は、それを呼び出すテスト用のコードのみになるようにします。
まず 「aneadmob->src->main->java->jp.njf.admob」の下にANEAdmobというクラスを作成します。
前述しましたが、このANEには以下の機能を実装予定です。
最初の「各種ID」とは、AdmobのアプリケーションID、バナーID、インタースティシャルIDの3つです。
よってこのクラスには以下のpublicなメソッドが必要です。
メソッド名 | 引数 | 機能 |
setAppID | アプリケーションID(String型) | アプリケーションIDを設定する |
setBannerID | バナーの広告ID(String型) | バナーの広告IDを設定する |
setInterstitialID | インタースティシャルの広告ID(String型) | インタースティシャルの広告IDを設定する |
showBanner | なし | バナー広告の表示 |
hideBanner | なし | バナー広告の非表示 |
loadInterstitial | なし | インタースティシャル広告のロード |
showInterstitial | なし | インタースティシャル広告の表示 |
戻り値は全てなし、つまりvoid型メソッドです。
ここで「インタースティシャル広告のロード」については説明が必要かも知れません。インタースティシャル広告は全画面で表示され、場合によっては動画なども含まれるため、データ容量が大きく、ネットから広告データをロードするのに時間がかかります。 それを素早く表示するためには、あらかじめロードしておいて、それが完了していたら表示する、という手順をふむことになります。ロードの直後にすぐ表示しようとすると、ロードが終わっていないのでたいてい失敗します。
例えば、画面遷移5回毎に表示するなら一回目でロードし、4回も画面遷移すれば多分ロードは終わっているでしょうから、5回目の画面遷移で広告を表示する、といったふうに使います。そのため、ロードと表示のメソッドは分けておく必要があるのです。
ではクラスの詳細を解説します。 まずコンストラクタは広告の初期化で必要なactivityを引数とします。
public ANEAdmob(Activity activity){ this.activity = activity; }
次にアプリケーションのIDを設定するメソッドです。これは広告を表示する前であることはもちろん、他のIDを設定する前に必ず一度実行する必要があります。
public void setAppID(String appID){ MobileAds.initialize(activity, appID); }
次にバナーのIDを設定する関数です。 バナーの初期化も行い、かつアプリの下の中心に表示するようにしています。
public void setBannerID(String bannerID){ if(bannerView == null) { bannerView = new AdView(activity); bannerView.setAdUnitId(bannerID); bannerView.setVisibility(AdView.GONE); bannerView.setAdSize(AdSize.BANNER); FrameLayout.LayoutParams adLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); adLayout.gravity = Gravity.BOTTOM | Gravity.CENTER; FrameLayout.LayoutParams adParams = adLayout; activity.addContentView(bannerView, adParams); bannerView.setBackgroundColor(Color.BLACK); } }
バナーが二つ表示されるとやっかいなので、一度しか初期化できないようにif文でくくられています。 ここでsetBackgroundColorを使って背景色を黒にしています。 これはまれに広告のロードが終わっても画像が表示されず、透明のままになることがあり、するとユーザーの誤クリックを誘発しやすくなることを防ぐためです。 誤クリックが多いと、Admobはアカウント停止になる場合もあります。 Admobは特にバナーの規約が厳しいので、アプリのどこにどう表示するかは必ず規約を確認して下さい。 詳しくは「AdMob実装時の注意点」でも書いています。
バナーの表示と非表示はバナーの更新を止めて、viewのvisibilityを切り替えるだけです。
public void showBanner(){ if(bannerView.getVisibility() == AdView.VISIBLE){ return; } bannerView.setVisibility(AdView.VISIBLE); bannerView.resume(); bannerView.loadAd(makeAdRequest()); }
public void hideBanner(){ if(bannerView.getVisibility() == AdView.GONE){ return; } bannerView.pause(); bannerView.setVisibility(AdView.GONE); }
通常、バナー広告は数十秒〜数分ごとに更新されます。 AdViewのpauseメソッドはこの更新を止めます。 バナーを消すと更新は不要なので呼び出しています。
アプリがバックグラウンドにまわったときも、余計な負荷を除くため、このpauseメソッドを呼び出し、再び前にまわったらresumeメソッドを呼び出すことをお勧めします。
ここの例では、単にバックグラウンドでhideBannerを、前にきたらshowBannerを呼び出すと良いでしょう。
ここでmakeAdRequestメソッドは広告用のリクエストを作るメソッドです。
private AdRequest makeAdRequest() { return new AdRequest.Builder().build(); }
一行しかありませんが、インタースティシャル広告でも使うのでメソッドにしています。また、AdRequestの作り方を
return new AdRequest.Builder().addTestDevice("デバイスID").build();
とするとテスト用の広告が表示されるようになります。 「デバイスID」の部分はlogcatに
I/Ads: Use AdRequest.Builder.addTestDevice("デバイスID") to get test ads on this device.
といった感じで表示されるので探してみて下さい。
Admobは開発者が自ら広告をタップしたり、開発時に実際の広告を表示するのを禁止しています。 現実的には少々テストで表示したり、一度や二度ほどタップしたからいきなり問題が生じることはありませんが、何度も表示したり誤タップしてしまうとアカウント停止などのなんらかのペナルティがある可能性があります。 そのため、テスト時にはこのようにテスト用デバイスを設定することをお勧めします。 また、その時のためにこのようにAdRequestを作成するメソッドは別に用意しておいた方が、一カ所直せば良いので便利です。
インタースティシャルの方は簡単です。
public void setInterstitialID(String interstitialID){ interstitial = new InterstitialAd(activity); interstitial.setAdUnitId(interstitialID); }
public void loadInterstitial(){ interstitial.loadAd(makeAdRequest()); }
public void showInterstitial(){ if(interstitial.isLoaded()){ interstitial.show(); } }
isLoadedはロード完了かどうかを判定するメソッドです。
インタースティシャルは規約的にも注意するのは表示させすぎないぐらいで、それほど難しい部分はありません。
次にANEAdmobクラスをAndroidでテストします。 まずappの下のbuild.gradleに次の一行を加えます。
compile project(':aneadmob')
さらにapp/src/main/AndroidManifest.xmlに次のパーミッションを加えます。
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
同じくAndroidManifest.xmlのapplicationタグの中に次を入れます。
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<activity android:name="com.google.android.gms.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" android:theme="@android:style/Theme.Translucent" />
あとはonCreateメソッドにボタンを押したときの処理を入れます。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); aneAdmob = new ANEAdmob(this); aneAdmob.setAppID(APP_ID); aneAdmob.setBannerID(BANNER_ID); aneAdmob.setInterstitialID(INTER_ID); Button button = (Button) findViewById(R.id.showBannerBtn); assert button != null; button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { aneAdmob.showBanner(); } } ); button = (Button) findViewById(R.id.hideBannerBtn); assert button != null; button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { aneAdmob.hideBanner(); } } ); button = (Button) findViewById(R.id.loadInterstitialBtn); assert button != null; button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { aneAdmob.loadInterstitial(); } } ); button = (Button) findViewById(R.id.showInterstitialBtn); assert button != null; button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { aneAdmob.showInterstitial(); } } ); }
これでテスト可能です。
これで動かないと当然ながらANEにしたところで動きません。ANEへの変換は手間がかかるので、作業を能率化するにはこの段階で入念にテストしておく必要があります。
Android上で問題なく実行されたら、ANEにするための処理を入れます。
こちらを参考にFlashRuntimeExtensions.jarをaneadmobの下のlibに配置します。
最終的にaneadmobのbuild.gradleのdependencies以下に次の一行が挿入されていればOKです。
compile files('libs/FlashRuntimeExtensions.jar')
次にActionScriptから呼び出される部分を作ります。
呼び出しには次の二つのクラスが必要です。
名前 | 機能 | インターフェイス |
ANEAdmobFREExtension | 呼び出しの定義と振り分け処理 | FREExtension |
ANEAdmobFREFunction | 実際に呼び出される関数 | FREFunction |
ここではこれらのクラスをjp.njf.aneadmobパッケージとして制作します。
ここで呼び出されるクラスの設計には二つの選択肢があります。 呼び出される関数はFREFunctionをimplementsしておくと、その中のcallメソッドが呼ばれます。
よって呼び出しメソッドの数だけクラスを作るか、一つのクラスを作って内部的に振り分けるか、という設計があり得ます。
前節のように、このANEには7つのメソッドがあるので、7つもクラスを作るのは面倒です。よって一つのクラスを作って第一引数を使って処理の分岐を行うようにします。そのクラスが上記のANEAdmobFREFunctionで、メソッドの数だけクラスがないのはそのためです。
ただし、メソッドごとにクラスを作った方が呼び出しエラーなどがFlash側のtraceメッセージなどと一緒に表示されるため、デバッグは楽です。自分で振り分けする場合は、もちろん自分でエラー処理もしなくてはなりません。
このように一長一短ありますので、場合に応じて設計を変えて下さい。
まずANEAdmobFREExtensionクラスのcreateContextメソッドです。"admob"という名前で呼び出せるようにしておきます。
@Override public FREContext createContext(String s) { FREContext context = new FREContext() { @Override public Map<String, FREFunction> getFunctions() { Map<String, FREFunction> result = new HashMap<String, FREFunction>(); result.put("admob", new ANEAdmobFREFunction()); return result; } @Override public void dispose() { } }; return context; }
特に難しい部分はないはずです。分からない部分があれば「AndroidでのANEの作成/Android側の作成」を参照してください。
次にANEAdmobFREFunctionのcallメソッドです。 第一引数をメソッドの振り分けに、第二引数をパラメータとして使っています。
public FREObject call(FREContext freContext, FREObject[] freObjects) { String method = ""; String prm = ""; try { method = freObjects[0].getAsString(); } catch (Exception e) { Log.e("njf.jp.aneadmob", "ANEAdmobFREFunction:can't parse : arg 0"); } try { prm = freObjects[1].getAsString(); } catch (Exception e) { Log.w("njf.jp.aneadmob", "ANEAdmobFREFunction:can't parse : arg 1"); } if(aneAdmob == null){ aneAdmob = new ANEAdmob(freContext.getActivity()); } if(method.equals("setAppID") && !prm.equals("")){ aneAdmob.setAppID(prm); }else if(method.equals("setBannerID") && !prm.equals("")){ aneAdmob.setBannerID(prm); }else if(method.equals("setInterstitialID") && !prm.equals("")){ aneAdmob.setInterstitialID(prm); }else if(method.equals("showBanner")){ aneAdmob.showBanner(); }else if(method.equals("hideBanner")){ aneAdmob.hideBanner(); }else if(method.equals("showInterstitial")){ aneAdmob.showInterstitial(); }else if(method.equals("loadInterstitial")){ aneAdmob.loadInterstitial(); }else{ Log.e("njf.jp.aneadmob", "ANEAdmobFREFunction:no method or invalid prm " + method); } return null; }
こちらも分からない部分があれば「AndroidでのANEの作成/Android側の作成」を参照してください。
jarファイルの取り出しについては、こちらの記事と同じです。
ツールバーの「Build->Rebuild Project」でビルドし直して、aneadmob-release.aarファイルを探し出し、zip解凍してください。必要なのは「classes.jar」のみです。
['ab21', 'Ab32', 'aB13']
ちなみに、一度リスト化して並べ替える方法がGoogle検索の上位に現れるのは日本語のサイトの検索結果です。英語のサイトの検索結果の上位サイトだとわざわざそんなことはしていません(2019年4月)。日本ローカルの謎の処理のようです。
¶ちなみに、一度リスト化して並べ替える方法がGoogle検索の上位に現れるのは日本語のサイトの検索結果です。英語のサイトの検索結果の上位サイトだとわざわざそんなことはしていません。日本ローカルの謎の処理のようです。
¶ちなみに、一度リスト化して並べ替える方法がGoogle検索の上位に現れるのは日本語のサイトの検索結果です。英語のサイトの検索結果の上位サイトだとわざわざそんなことはしていません。日本ローカルの勘違いだと思います。
¶ただし、キーではなく対応する値の方で並べ替えるなら一度リスト化した方が楽な場合があります。
リストの内容を変更する、いわゆる「破壊的」なソートするには、sortメソッドを使います。
original_list = [2,1,4,3,5] original_list.sort() print(original_list)
結果
[1, 2, 3, 4, 5]
リストの内容を変更せず、ソートされたコピーを作成するにはsorted関数を使います。
original_list = [2,1,4,3,5] sorted_list = sorted(original_list) print(original_list) print(sorted_list)
結果
[2, 1, 4, 3, 5] [1, 2, 3, 4, 5]
降順でソートするには引数に「reverse = True」を指定します。
original_list = [2,1,4,3,5] original_list.sort(reverse = True) print(original_list)
sorted関数についても同様です。
original_list = [2,1,4,3,5] sorted_list = sorted(original_list, reverse = True) print(original_list) print(sorted_list)
結果
[2, 1, 4, 3, 5] [5, 4, 3, 2, 1]
sorted関数は他のイテラブルにも使えます。例えば辞書型オブジェクトに使用するとキーを並べ替えたリストが得られます。
original_dict = {2:"B",4:"D",1:"A",3:"C"} sorted_dict_key = sorted(original_dict) print(original_dict) print(sorted_dict_key)
結果
{2: 'B', 4: 'D', 1: 'A', 3: 'C'} [1, 2, 3, 4]
対応する値を含まないキーだけ並び替えたリストが返ることに違和感があるかも知れませんが、実際にはこのやり方が合理的です。
なぜなら、もとの辞書型オブジェクトとキーを使えば対応する値が簡単にとれるからです。 例えば、上の例で並び替えたキーと対応する値を順番に表示するには以下のようにします。
for k in sorted_dict_key: print("key:%s, value:%s" % (k,original_dict[k]))
結果
key:1, value:A key:2, value:B key:3, value:C key:4, value:D
ところで、ネット上でPythonの辞書型オブジェクトのソートを検索すると、なぜか以下のような一度リストになおしてからソートする例が多く出てきます。
sorted_dict_list = sorted(original_dict.items()) print(sorted_dict_list)
結果
[(1, 'A'), (2, 'B'), (3, 'C'), (4, 'D')]
このやり方はあまりよくありません。
なぜなら、もとの辞書型オブジェクトの中と並べ替えたリストの中に同じ値が含まれており、データが二重化してしまうからです。
この方法では、メモリを無駄に消費するのはもちろん、ソートしてからキーに対応した値を変更しようとすると、プログラムの目的によっては、値の整合性をとるために並び替えたリスト(sorted_dict_list)と、もとの辞書型オブジェクト(original_dict)の値の両方を変更しなければならなくなります。
しかもリストの方は中がタプルのため値が変更できません。
そのため、もとの辞書型オブジェクトの値を変更した上で、キーでもう一度並べ替えをやりなおすか、タプルをリストに変換してキーを検索して値を変更する、といった処理が必要となります。
並べ替えたキーだけ保持する方法なら、もとの辞書型オブジェクトを変更するだけで良く、そのような処理は必要ありません。
ちなみに、一度リスト化して並べ替える方法がGoogle検索の上位に現れるのは日本語のサイトの検索結果です。英語のサイトの検索結果の上位サイトだとわざわざそんなことはしていません(2019年4月)。日本ローカルの謎の処理のようです。
sorted関数やリストのsortメソッドには「key」という引数があり、これを指定すると細かな並び替え条件を指定できます。
例えば、次のようなリストの並び替えを考えます。
original_list = ["Ab32","ab21","aB13"] sorted_list = sorted(original_list) print(sorted_list)
結果
['Ab32', 'aB13', 'ab21']
大文字の方が文字コードでは値が小さいため、上のような結果になります。
これを大文字小文字を無視して並べ替えるには以下のようにします。
sorted_list = sorted(original_list,key=str.lower) print(sorted_list)
結果
['aB13', 'ab21', 'Ab32']
str.lowerは引数を小文字にして返すメソッドです。 大文字小文字を無視するためにすべて小文字にしているわけです。 もちろん、全て大文字にするstr.upperメソッドを使っても同じ結果が得られます。
このようにkeyには何か関数やラムダ式を指定します。
指定した関数は引数として並べ替える要素が与えられ、値を返すとその結果に応じて並び替えらたリストなどがsortやsorted関数の結果となります。
例えば上のリストを4文字目で並べ替えるには以下のようにします。
sorted_list = sorted(original_list,key=lambda x: x[3]) print(sorted_list)
結果
['ab21', 'Ab32', 'aB13']
ここではラムダ式を使いましたが、「何番目の要素で並び替える」という処理はよく使うので、Pythonにはそれに対応した「itemgetter」というメソッドが用意されています。
from operator import itemgetter sorted_list = sorted(original_list,key=itemgetter(3)) print(sorted_list)
これで上のラムダ式と全く同じ結果が得られます。
こちらの方が記述が簡潔になり、かつ高速で動作します。
また、itemgetterは複数の引数を与えると、その順番に優先順位をつけてソートしてくれます。
例えば、一番目でまず並べ替え、同じ順位の間では四番目で並べ替えるには以下のようにします。
sorted_list = sorted(original_list,key=itemgetter(0,3)) print(sorted_list)
結果
['Ab32', 'ab21', 'aB13']
この例では文字列を使いましたが、リストやタプルなど、数字のインデックスでアクセスできるオブジェクトなら全く同様にソート可能です。
また、sorted関数だけではなくリストのsortメソッドにも同じ方法が使えます。
クラスオブジェクトのプロパティで並べ替えるには「attrgetter」を使います。
例えば、「name」というプロパティをもつオブジェクトのリストを並べ替えるのは以下のようなコードになります。
from operator import attrgetter 略 sorted_list = sorted(original_list, key=attrgetter('name'))