2015/03/09

[Java][Android]CharSequenceの話

例えばAlertDialogクラスのメソッドsetTitleは引数を1つ持っています。与えるべきなのはダイアログのタイトル部分に表示する文字列ですから型はStringかと思いきやCharSequenceというインターフェイスです。
よく見るとAndroidのクラスでStringを引数にするものは皆無といっていいのですが、普段意識することはあまりないと思います。
StringはCharSequenceを実装していますから、CharSequenceで宣言されている引数にはなんの問題もなくStringを使うことができます。なので意識していない人もすくなくないのではないかと思っています。
実際、J2EEで同じ役割を果たしているawtクラスの引数はStringです。普通のコーディングをしている方々ならStringで受けてくれた方がすっきりするのではないでしょうか。
(ちなみにCharSequenceは導入バージョンはJava1.4です。1.0から存在しているawtに採用されていないのは順番的に仕方ないと言えば仕方ないのですが…)

さて、CharSequenceはこんなインターフェイスです。

---

abstract charcharAt(int index)
Returns the character at index.
abstract intlength()
Returns the number of characters in this sequence.
abstract CharSequencesubSequence(int start, int end)
Returns a CharSequence from the start index (inclusive) to the end index (exclusive) of this sequence.
abstract StringtoString()
Returns a string with the same characters in the same order as in this sequence.

上から文字を取り出す、長さを求める、部分文字列を切り出す、そして文字列化する、というStringの基礎が明示されているだけです。
確かに表示するだけの文字列として最小限の命令セットにはなっています。
インターフェイス化はオブジェクト指向の基礎ですが、それだけではまだ、わざわざインターフェイスにする理由としては弱いと思っていました。
とはいえ現実問題インターフェイス化にはそれ相応のコストがかかります。JavaのStringはJava黎明期からコストパフォーマンスに非常に注意して作られたクラスです。普通に考えればダイレクトに使った方が実行効率はよいはずなのです。

StringサブクラスはUIを作っていればあちこちで使いたくなります。たった4つ、いや、toString()はObjectが持っていますから最低限3つの実装ではありますが、いちいち実装するのは上品ではありません。
なんでStringにしてくれなかったんだろう。そうしたらStringを継承するだけで済んだのに…

と思ってやっと気がつきました。理由は多分Stringがfinalだからです。
手前みそながら、前述のLabeledItemを設計した時です。

以下のコードはコンパイルできません。Stringがfinal宣言されているからです。

class SomeString extends String{

引数をinterfaceのCharSequenceにしておくことによって、機能拡張したStringぽいオブジェクトを引数に渡すチャンスを残した、と考えるべきでしょう。
おかげで私はLabeledItemを実装することができました。
awtは…多分、やらかしちゃったんでしょうね。
私はawtでがりがりUIを作った経験がないのでわかりませんが、黎明期のJavaコーダーさんのご意見を伺ってみたいものです。

ところでJavaには、CharSequenceを追加する他にもう一つの選択肢があったと思っています。
それは引数をObjectにすることです。
ObjectはtoString()を実装しています。つまりすべてのオブジェクトは文字として表すことができるように設計されています。個人的にはJavaの大変優れたデザインだと思っています。
Objectにしなかった理由は-これは本当に推測の域をでないのですが-JavaにおいてtoString()はデバッグ用という意思があったからではないかと思っています。
私はtoString()というデザインが好きな関係上、コードの中にUI上に表示するものとしてtoString()をオーバーライドすることがよくあるのですが、このスタイルはJavaとしてはアウトなのかもしれません。

さて、さらっと流しましたが、実はもうひとつの疑問がありました。
なんでStringはfinalなんでしょうか?
その話はこの次に。

2015/02/28

[Android]LabeledItem:RadioButtonを簡単に使うためのクラス


Stringについてのうんちくを使用と思ったのですが、書き始めるといろいろ裏を取らなければならないことがあり、そのためにはJavaの言語仕様やバーチャルマシン、Java Byte Codeの仕様まで読み込まなければならないと気が付いて気持ちが萎えています。
ネットに情報を上げている方々ってすごいですね、やっぱり。
一日考えたらいろいろ思いついたので追記しています。
ネットで発言はやっぱり難しい。

前に継承できるStringクラス、AbstractCharSequenceを紹介しました。

実は私は最終的にこのクラスを使用していません。
そのあとすぐに、もう少し便利で潰しが利くクラスを思いついたからです。

public class LabeledItem<T> implements CharSequence{
 private String label;
 private T value;

 public LabeledItem(String label, T value){
  this.label = label;
  this.value = value;
 }

 public T getValue(){
  return value;
 }

 @Override
 public String toString(){
  return label;
 }

 @Override
 public char charAt(int index) {
  return label.charAt(index);
 }

 @Override
 public int length() {
  return label.length();
 }

 @Override
 public CharSequence subSequence(int start, int end) {
  return label.subSequence(start, end);
 }

 public static int indexOf(LabeledItem<?>[]items, Object value){
  for(int i=0; i<items.length; ++i){
   if(value.equals(items[i].getValue())){
    return i;
   }
  }
  return -1;
 }
}

ジェネリッククラスになっていますので、データ型は自由に定義できます。こうするとAbstractCharSequenceを継承せずとも十分拡張性があるので、直接実装しています。
indexOfの第一引数はLabeledItemにしたかったのですが、文法エラーになります。
Tはインスタンスにかかる形容詞なのでstaticメソッドには使えません、とのこと。
型は指定しないがなんらかジェネリッククラス、という意味のにしてエラーは消えました。

ところで、LabeledItemをsetSingleChoiceItemsに適用するにはまだひと手間かかります。
第一引数に与えるためにLabeldItemの配列を作らなければならないのですが、素直に作った下記のクラスはコンパイルエラーとなります。

LabeledItem<Integer>[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),


ジェネリッククラスの配列は作ってはいけない模様。
釈然としないのですが、今は受け入れて回避策を練ってみました。

回避策1:CharSequenceの配列とする

CharSequence[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),

setSingleChoiceItemsの第一引数はCharSequence[]ですから、これで十分です。問題は選択された後、CharSequenceのインスタンスをLabeledItemにキャストしてあげる必要があり、ちょっとカッコ悪く、しかもワーニングになります。

回避策2:LabeledItemの配列とする
LabeledItem<?>[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),

型を不定にすると配列が作れるようになる、というのはやはり釈然としません。ここらへんの仕組みが血肉になっていないからなんだと思います。これもやはり選択された後にキャストが必要です。
なんとなく仕組みがわかってきました。そのうち解説しますが、このやり方はジェネリック文法の穴を突いた感じなのでよろしくないですね。

回避策3:LabeledItem<>を継承したクラスを作る
public class LabeledIntItem extends LabeledItem<Integer> {
 public LabeledIntItem(String label, Integer value) {
  super(label, value);
 }
}

LabeledIInttem[]items = new LabeledIntItem[](){
 new LabeledItem<Integer>("item1",123),


ただ1枚クラスを被せるだけでコンパイルを通ってしまうのもまた不思議です。これならキャストなしで使用できますが、せっかく流用できるように作ったジェネリッククラスをデコレートするクラスを作るというのが本末転倒です。
なんか違う方向に努力しているような気はしています。

回避策4:Object型の配列に格納する。

Object[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),

そもそもJavaの原始デザインにジェネリック型はありませんでした。コンテナクラスにはObject型で格納する、取り出す時にキャストするというのがルールだったのを思い出しました。
回避策1、2のようにワーニングすら出ませんからいっそすがすがしいと思います。

回避策5:コンテナクラスを使用する

  final List<LabeledItem<Integer>> items= new ArrayList<LabeledItem<Integer>>(){{
    new LabeledItem<Integer>("Zero", 0);
    new LabeledItem<Integer>("One", 1);
  }};

  .setSingleChoiceItems(items.toArray(new CharSequence[0]), 0, new OnClickListener(){

いろいろ考えた挙句、現在のJavaではこれが正解なのかな、と行きついた形です。
ジェネリッククラスで配列を作ることはできませんが、コンテナクラスを作ることはできます。これはCから受け継いだ配列という概念の限界を示しているのかもしれません。
上のように宣言すればコード上の扱いやすさは配列と大差ありません。
ポイントは、CharSequenceで扱われるときには正しくCharSequenceの配列として使用するというところでしょう。ここでジェネリッククラスの配列にしようとすると無限ループにはまります。

突っ込み歓迎します。

2015/02/22

[Java]extendsできるStringを作る

Stringがfinalで宣言されていて困ったので、自前で作ってみました。

最初に断っておきますが、Stringがfinalになっているのには相応の意味がありますので、理解してから、適切な使い方をしてください。

AndroidのAlertDialogで選択するアイテムを上品に作ろうとしていました。
setSingleChoiceItemsの第一引数は一覧に表示する文字列の配列です。普通はここにStringの配列を渡しますね。
私はここに選択するべきデータを与えたいと思いました。ラベルとデータで2つの配列を持つのは恰好わるいじゃないですか。

データのtoStringを目的に合わせて実装しておけばいいよね、と思っていたのですが、そう簡単にはいきませんでした。
データがCharSeqece(なんでStringじゃないの?の謎はまたあとで)を継承していないので適用できませんとのこと。toStringを持ってるんだからObjectでいいじゃん、とも思ったのですがこれもまた掘り下げると長くなりそうなのであとで。

それならStringを継承すればいいよね、と思ったら今度はStringがfinal宣言されているのでextendsできませんということ。これもまた理由があるのですが、長くなるのであとで。

素直にインターフェイスCharSequenceを実装すればいいんでしょうが、CharSequenceは複数のメソッドを持っていて実装はそこそこ面倒です。私の場合、同じ目的でいくつかのメニュにこの仕掛けを適用しようとしていましたので、データ毎にCharSequence実装するのは上品ではないなぁと思わざるを得ませんでした。

そこで、発想を転換して作ったのがこのAbstractCharSequenceです。

public class AbstractCharSequence implements CharSequence{
 @Override
 public char charAt(int index) {
  return toString().charAt(index);
 }

 @Override
 public int length() {
  return toString().length();
 }

 @Override
 public CharSequence subSequence(int start, int end) {
  return toString().subSequence(start, end);
 }
}

AbstractCharSequenceはGoFデザインパターンでいうところの典型的なAdapterです。
AbstractCharSequenceはtoString()で返される文字列を元に、CharSequenceを実装するという実に本末転倒なクラスです
表示に使う文字列はtoString()をオーバーライドして与えます。
toStringはデバッグ用、というのがJavaの基本概念のような気もしてます。このやり方は邪道かもしれません。参考まで。

public class AValue : extends AbstractCharSequence{

 private String label;
 private String value;

 public AValue(String label, String value){
  this.label = label;
  this.value = value;
 }

 @Override
 public String toString(){
  return label;
 }

 public String getValue(){
  return value;
 }
}

AbstractCharSequenceを実装したクラスをsetSingleChoiceItemsに与えることによって、データそのものを表示するインスタンスにできます。

最初にも書きましたが、この手法はわりとJavaという言語の設計思想の根幹に踏み込んだことをしています。今回のような明確な目的を持たずに乱用するとよくないことがおこるかもしれません。
少なくとも、AbstractCharSequenceは実体を持ったクラスなので、真にObjectmもしくはStringから継承されるべきクラスにのみ適用すべきです。ほかにも継承する候補があるクラスはそちらを優先すべきです。対応は簡単で、CharSequenceを継承、実装してください。

2015/02/21

[Android]有料版と無料版の切り替え

[2015/06/01追記]Android Studioでは下記手順のほとんどを無視して簡単にパッケージの切り替えができることがわかりました。また、やり方についてもベターな方法を編み出しました。Eclipseユーザー向けにこのページを残しておきますが、Android Studioユーザーは新しい方法を推奨します。

Google Playにアプリを公開するとき、有料版と無料版は別のアプリとして登録しなければなりません。
広告が入るくらいしか違いがないのだから簡単に切り替えたいですが、パッケージ名を変えるといろいろ祟りがあってイヤですよね。
Android開発し始めたばかりでまだツールの使い方や、AndroidManufestのいじり方もわからなかったころは本当に恐怖でした。

いくつかアプリを作ってみて、なんとなくコツが掴めてきたのでまとめておきます。

まず最初に、Eclipseはパッケージの切り替えについて効率の良いツールを提供してはいない、というのが私の結論です。上品プログラマーとしては、用意されたツールを使わない手はないとおもってあれこれ調べたのですが、あきらめざるを得ませんでした。
考えてみれば、パッケージはアプリの名前みたいなものですから、さくさく切り替えられてはたまらない、というのは理解できます。

AndroidManufest.xmlにかかれてるpackageを書き換えるとソースコード上に一気にコンパイルエラーが発生しますが、これはリソースIDを管理しているgen以下のパッケージ名がpackageに依存しているからです。
リソースIDを管理している<パッケージ名>+".R"というファイルのファイル名が変わるので、コード内のimport文をすべて書き換えるのは必須作業です。
逆に言うと、手間がかかる作業はこれだけで、あとは合理的に扱うことができます。

余談ですが、AndroidManufest.xmlに記述されるパッケージ名とアプリで使われているパッケージ名が同じである必要はないようです。わざわざ変える必要もないのですが、二つの間に関連性がないと気が付かないで嵌ったことがあるので、念のために。

以下は私がやっている手順です。

  1. 無料版用のパッケージを作ります。起動Activity、EclipseのウイザードがMainActivity.javaという名前で作るあれの格納されたパッケージと同列に”XXX_free”という名前で作っています。
  2. 新しく作ったパッケージに無料版に必要なクラスを移動します。上品プログラマーなら無料版と有料版のクラスを切り分けるためのデザインはできているはずです。最低限、起動Activty、例ではMainActivity.javaだけは複製する必要があります。起動時に有料版と無料版を切り替えるからです。単純に広告が入っているだけの違いなら起動Activityの複製だけで済むはずです。
  3. AndroidManufest.xmlの内容を書き換えます。
    - ?xmlタグのpackageをfree版のパッケージ名に切り替えます。例だとXXX_hureeです
    - 起動Activityをフリー版に切り替えます。intent-filterでが指定されているActivityです。これで無料版の起動Activityが呼ばれるようになります。
    - 無料版で必要なPermisionを追加します。広告を入れるなら、ネットワークなどの権限を足さなければなりません。逆に不要なPermissionは削除します。不要なPermissionが残っているアプリはセキュアではありませんし、マルウェアと思われかねません。PermissionはProguardも落としてくれません。
    - バージョン番号の更新が必要なら更新しておきましょう。上品プログラマーはバージョン番号を大事にします。
  4. String.xmlのapp_nameを無料版に書き換えます。多言語対応している場合はすべての言語で更新するのを忘れずに。
  5. Cleanしてからフルビルドします。このときアプリによっては大量のエラーが出ます。エラーの原因は自動生成されたクラスのパッケージ名が変わったからだけですから、あわてずにimport文の.Rで終わる一文を新しいパッケージ名に書き換えます。
  6. proguardをかけます。不要なクラスやライブラリはproguardが落としてくれるので、気にする必要はありません。proguardの設定は有料版と無料版の共用で構いません。
以上が私のやっている方法です。
一気に書き換えるツールを作ったら需要ありますかね。5分もかからないので手作業でやっていますが。


2015/01/14

[Java]Eclipseが起動しない

アプリネタを思いついたので久しぶりにEclipseを立ち上げたら起動しなくなっていました。
アイコンをクリックすると、少し間をあけてだらだらとエラーメッセージらしき文言が並んだダイアログが開くだけです。
なんとなく、Eclipse.iniの内容に似ているような気がします。
全部読んでも実がないので、検索してヒットしそうな文言を探してみました。

Java was started but returned exit code=13

その下はjavaw.exeまでのパスです。どうやらjavaw.exeがエラーで止まっている模様です。

面倒臭いけど再インストールすればなおるんじゃね?と思ってオフィシャルページに行ったら、Eclipse+ADTがなくなってAndroid Studioなんてものになってるじゃないですか。
これは焦りました。
あまりに焦って丸一日放置。

乗り換えのチャンス、と言えなくもないですが、過去の経験上こういう時、新しいことをしようと思うとはまっていくので、ぐっとこらえて現状解決を優先することにしました。

やり直せばなんとかなるんじゃね?と思ってeclipseを再インストール(というか、オリジナルを解凍して置き換え)してみたのですが、現象は変わりません。
Java側だと面倒くさくていやだなぁ

ググってみるといくつか同じ現象の報告がありました。
こちらのページが最も理路整然と説明してあります。

http://d.hatena.ne.jp/yhr/20141017/p1

要はJava 8にバージョンアップした時に余計なパスが加わって、64bitのeclipseが32bitのjavaw.exeを呼んでいるということ。
eclipse.iniに元の64bit版への絶対パスを指定してやればいいんですね。

…なんですが、私の環境を全検索しても64bit版javaw.exeってのが居ません。
今までどうやって起動してたんだい君は?

気持ちが悪かったのですが、64bit版JDKを再インストールしたところ、eclipse.iniの変更なしで起動するようになりました。

なにかやらかしたのかなぁと記憶をたどると、思い当たることが一つ。
なにかする度にJavaのアップデートしろしろ言われるので、年末の休みに入ったあたりでポチってしまったかもしれません。
Javaをバージョンアップした時に、古いものを削除しますか的なことを言われたような気もします。
きっとその時にアンインストールしちゃったんですね、そういうことにしておきましょう。

アップデートのトリガーになったブラウザが32bitアーキテクチャだから、という説明がありましたが、それにしても64bit版OSに32bit版を導入してしまう動きがおかしい気がします。

2014/10/12

Eclipseをインストール

2015年4月注記:
この記事を書いてから、Android開発環境が転換期を迎えている真っ最中だということに気が付きました
私のようにAndroid開発を主業務にしていない者は大きな流れに取り残されがちです
AndroidStudioがEclipseにきれいにとってかわるのか、それとも第三の選択肢が表れるのかは私が論ずるところではありませんが、下記に私が言ってしまったようにEclipse一択でなくなったことは事実のようです

こちらの記事は残しておきますが、別途Android Studioの初期設定、Eclipseとの比較記事なども順次上げていこうと思っています

-----------------------------------------------------------------------------------------------------------

必要な機材が揃ったら、次は開発環境を整えます
Androidの開発をするためのツールはいくつもありますが、私はEclipseを使っています
開発の情報もEclipse前提のものが多いので、ここは私を信じでEclipseにしておきませんか?

Eclipseはとても高性能な開発環境ですが、インターネットがあれば無料で手に入れることができます

Eclipseを使えるようにするには手順があります
こちらのページにとても丁寧に解説されていますので、参考にしてください

Tech Fun CC

と、他人の情報に降ってしまったのでは私が記事をかく意味がなくなってしまいますので、初心者の人が引っかかりやすいところを説明していきます

日本では個人用のPCはほぼWindowsなので、その前提でお話をさせていただきます
インストール前に調べておいてほしいことが2つだけあります

お使いのPCの1.OSのバージョンと2.CPUのビット数です

OSのバージョンはWindowsならXp、Vista、Windows 7、Windows 8等です
ビット数はハードに依存していて、32bitか64Bitのどちらかです
x86は32bit用のアーキテクチャ、つまり32bitと同義と考えていいです。なんでx32じゃないのかはまぁ理由があるんですが今はほっときましょう
64bit版はx64という名称になってます

この2つはインストールするときに聞かれることがあります
ご自分の使っているPCに合わせたものをインストールしてください

ざっくり以下の順番でインストールしていくことになります

1.Javaが実行できる環境(Java Development Kit,=JDK)のインストール
2.Eclipseが実行できる環境のインストール
3.EclipseでAndroidを開発するための環境のインストール

1.はEclipseがJavaで動くアプリなので必要です。2はEclipseは汎用の開発環境なのでAndroid用にカスタマイズする必要があるのです
「なんでこんなに面倒くさい手順を踏まなければならないの?」と思う方はいて当然ですが、無料のツールなのでそこは我慢しましょう
それでもAndroidデベロッパーの需要が増えたからでしょう、Andridパッケージなんていうのも提供されるようになって随分簡単になったんですよ、これでも

Getting the Android SDK

インターネット上の情報は古くなっていきますから、あるページに書いてある情報が合わないことがままあります(このページも含めてね)
情報は新しい方がおおむね正しいです
ネット上にリリースされているものにはバージョンが付いているのが普通ですので、比べて確認する習慣をつけましょう
ネット上の情報の真偽新旧を見極めるのは個人の責任ですし、それがスキルです

ADTという言葉は覚えておくといいと思います
ADTはAndroid Development Tool(アンドロイド デベロップメント ツール)の略で、EclipseでAndroidを開発するためのプラグインです
プラグインとは、あるアプリに機能を追加する別のアプリ、と覚えてください
Eclipseは普通のJavaを開発するツールなのですが、ADTをプラグインすることでAndroid Javaの開発ができるようになります
ADTにさらにプラグインするツールもたくさん存在しています
くりくりは上のごく標準的な開発環境で開発できますが、これからいろんなこと、新しいことをしようとしたときにはADTを使った機能拡張をする必要があることがあります

次はEclipseを使って最初のアプリを作ってみましょう

2014/10/08

Androidアプリを作るはじめの一歩ー必要なもの

くりくりを公開して一日経ちました
ダウンロード数0...
 無料だからFACEBOOKやTwitterでつながってる人が少しくらいダウンロードしてくれると思ったのに、結構厳しいもんですね、世の中って

気を取り直して、Androidアプリの作り方を順番に説明していきます
対象はこのブログを見ることができる…つまり何等かインターネットに接続できるデバイスは持っている人
日本語がわかる人
中学生程度の算数、具体的には一次方程式と座標の概念がわかっている人
アルファベットが読める人、としておきます
もっといろいろわかる人は読み飛ばしてください

まずは始める前にどうしても必要なもの、こればかりはなければどうしようもないっていうものを紹介します

パソコン
Widows7が動くものがいいです。Xpでもきっと動くけど、サポート終了してるので。Linuxでもいいんですが、日本でLinuxのパソコン持ってる人はもう私よりパソコン詳しいんで説明聞かなくていいです
とりわけ高性能なものは必要ありません

インターネット
開発ツールも、技術情報もインターネット上のものが無料で使えます。逆に言うとインターネットにつながっていなければ相当の金額を積んでも必要なものは手に入りません。そういう時代なんです
開発につかうパソコンが自由に接続できるインターネット契約は必須です
かなり巨大なデータもありますので、早いに越したことはないですが、無理をすることもありません
最近は学校や自宅に普通にありますよ、ね?

それから、あるといいもの

Android端末
iPhoneはダメだよw
開発ツールにはエミュレータが付いているので、最悪なくても動作確認はできますが、実機の方が動作が軽いし、なにより実機上で動いた方が楽しいです
これも最新である必要はありませんが、新しい方がいろいろできることが増えます
くりくりはAndroid3.2以上、API 13以上でないと動作しません
 Androidのバージョンにはこれまたいろいろな蘊蓄がありますが、今の段階では大きい方ができることが多いみたいだ、程度の理解で結構です
自分の持っている端末のバージョンはアプリメニューの中にある端末の設定から端末情報>Androidバージョンで確認できます
極端に言うと、アプリが必要としないなら電話やネットワークの契約をしていない端末でも問題ありません
タブレットはいろいろ例外があって思わぬトラブルにあったりしますが、使用は可能です

USBケーブル
ミニというやつです。PCにつないでapkをダウンロードしたり、操作したりします
電源専用のケーブルは使えません。通信用というのを入手してください
通信用ならどんなものでもいいはずなんですが、私が前に使ってた端末はUSBジャックが接触不良になって交換しました。その時お店の人に「端末メーカーの作った正規品を使ったほうがいいですよ」と言われました。参考まで


AndroidやJavaの本があると嬉しい気がしますが、実際のところインターネットだけで足りてしまうものです

思ったより少ないとは思いませんか?
特にAndroid開発をしよう!って思わなくても、大人なら大抵持ってるものですよね?
端末は、型落ちしたものをネットオークションなどで落札して、電話契約なしでつかうなら本当に安く手にはいります
まだ電話の契約ができない学生さんは親御さんを口説き落としましょう