ジャンル不定の日記です。

openOrCreateDatabase

引き続きAndroidアプリの開発について勉強中で、SQLite関連をやってるが、わかりにくい・・・


android.database.sqliteの下にSQLiteOpenHelperというクラスがあって、このクラスを使って初期化するのが一般的らしいんだが、
オーバーライドしたクラスを作成する必要があるので、アクティビティが複数あるならSQLiteOpenHelperもpublicなクラスで用意しないといけない。
つまり、ソースファイルを分離しなきゃダメぽい。


他にSQLiteDatabaseクラスにopenOrCreateDatabaseというメソッドがあって、メソッド一発で必要なら作成してopenできる。
こっちの方が簡単そうなんだが、引数はDBファイル名じゃなくてpathを渡す必要があり、ファイル名だけを指定するとアプリ実行時にエラーになる。


更に他に、android.content.ContextWrapperクラスの下に同じ名前のopenOrCreateDatabaseメソッドがある。
こちらはpathではなくファイル名を指定して、2番目の引数にファイルのモードを指定する。
ファイルのモードはint型だが、android.contentで定数が定義されていて、
定数名

パーミッション
MODE_PRIVATE0
660
MODE_WORLD_READABLE1
664
MODE_WORLD_WRITEABLE2
666
(パーミッションはMODE_PRIVATEしか確認してないが、意味を考えるとそうなると思う)

android.content.ContextWrapperはandroid.app.Activityが継承しているので、Activityを継承したクラスで使用可。


openOrCreateDatabaseが2種類あって凄くわかりにくいが、アプリ用のDBを作成(open)するにはandroid.content.ContextWrapperの方のメソッドを使えば良い感じ。
android.content.ContextWrapperはActivityが継承しているので別途import不要だが、DBの操作自体はandroid.database.sqlite.SQLiteDatabase等で行うので、操作するのにimportは必要。


てな感じと思ったんだが、
DB作成時にtable作成とかもしなきゃいけないし、SQLiteOpenHelperならonCreateメソッドで作成時処理を書けるから、
やっぱSQLiteOpenHelper使ったほうが良さそうかもしれない・・・
もうちょい勉強の必要あるな。

複数アクティビティを持つアプリ

Androidのアプリでは画面を再描画する時に、現在のアクティビティを初期化して再描画するのではなく、画面ごとにアクティビティを用意するのが基本ぽい。
だから戻るボタンで前の画面に戻れるんだね。

というわけで、Androidアプリを作るには、複数アクティビティの使い方を知る必要があるようなので、
ボタンをクリックすると別のアクティビティに移動するアプリを作ってみた。

MainActivity.java
package com.example.hello;

import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button bt=new Button(this);
        bt.setText("Click");
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            }
        });
        setContentView(bt);
    }
}

前回の"Hello World"を表示するだけのコードからの変更点だけ赤文字にしてる。
最初に表示されるMainActivityでは、今度はTextViewの代わりにButtonウィジェットを使う。
android.widget.Buttonはandroid.widget.TextViewを継承しているので、文字列の設定とウィジェットの設置は同じなので赤文字にしてない。
前回と違うのは、クリックイベントの設定関連だけ。

setOnClickListenerでイベントの登録のようだが、これはandroid.widget.TextViewが継承しているandroid.view.Viewのメソッドなんで、ボタンじゃなくてTextViewにクリックイベントを設定することも出来そう?
プロジェクト作成時のコードにも「@Override」があるが、これは、既存メソッドを上書きしていることを明示しているということらしい。(書いておくとデバックがしやすいとか・・・)

setOnClickListenerの引数はOnClickListenerで、OnClickListenerのonClickメソッドにクリック時の処理を書く。
なんか気持ち悪い書き方だね・・・
覚えにくい・・・

今回はクリックされたら2つ目のアクティビティを起動するので、startActivityを使うが、startActivityの引数にはIntentを渡す。
Intentの作成方法は複数あるようだが、別のクラスのアクティビティを起動するには、ContextとClassの2つを渡してIntentを作成する。
Contextはwidget作成時にも使って、前回の時に単にthisじゃ問題起きる場面に遭遇しそうな気がしたが、さっそく遭遇した。
単にthisだとダメな場合は、「クラス名.this」で良いみたい。
Classの渡し方は、「クラス名.class」ぽい。


SecondActivity.java
package com.example.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class SecondActivity extends Activity{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv=new TextView(this);
        tv.setText("Hello, World");
        setContentView(tv);
    }
}

こちらがクリックされたら起動する2番目のアクティビティのソースコード。
最初、1つのソースコードで出来るんじゃ?と思ったんだが、publicじゃないと起動できず、Javaではpublicなclassはソースファイルの名前と同一じゃないとダメぽい。
というわけで、アクティビティが複数ある場合は、アクティビティの数だけソースファイルを分ける必要があるね。
めんどい・・・
こちらのコードは、前回のMainActivityと名前が違うだけで同じ。


AndroidManifest.xml(の一部)

<activity android:name="MainActivity" android:label="@string/app_name">
   <intent-filter>
     <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<activity android:name="SecondActivity" android:label="@string/app_name"></activity>

更に、AndroidManifest.xmlを修正してアクティビティを登録する必要がある。
プロジェクト作成時点で<activity>が1個あるが、それをコピーして名前を変更。
起動時に開始するわけではないので、<intent-filter>は不要って感じかな。


これで、エミュレーターにインストールして、予定通りボタンクリックで"Hello World"が表示されるアプリができた。

ウィジェットの使い方と複数アクティビティの使い方はわかったから、後は、
データ保存に、android.database.sqlite
HTTP通信に、android.net.http
HTTP以外の通信するなら、android.net
文字列操作するなら、android.text.TextUtils
HTMLアプリにするなら、android.webkit
辺りを調べれば良さそうかな。
AndroidのAPI覚えるのもめんどいし、android.webkitでできるようなことは webkit使っちゃった方が良いんじゃないかと思う。
そうすればAndroid以外に移植するのも簡単と思うし。

thisってなんだ?

Androidで"Hello World"アプリを作ってみた。

package com.example.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv = new TextView(this);
        tv.setText("Hello, World");
        setContentView(tv);
    }
}

赤文字以外の部分は前回の方法でプロジェクトを作成すると自動的に書かれるので、赤文字部分だけ追加。
文字を表示するだけの部品はTextViewウィジェットで、描画系部品はwidgetクラス以下にまとまっているようですね。

公式のAPIリファレンスを読めばわかる感じだが、APIの規模がでかい上に英語だから入門にはちょいきつい。
今回はまず"Hello World"ググッていろんなサイト参考にしたんだが、どのサイトも「new TextView(this);」のthisについて記載してないんだよね。
説明省略しちゃイカンところでしょ・・・

このthisは何?
ってことでわからないから調べたが、公式リファレンスには"TextView(Context context)"と記載されている。

今度はContextって何?
って感じだが、widget作成時はアクティビティの状態を渡す必要があり、渡すものがContext。
Contextを渡すのに省略してthisと書ける。
って感じかね。
Javaはよくわからないが、この場合のthisはMainActivityのことなのか?onCreateのことなのか?

習慣的にthisで開発してると問題に遭遇することもありそうな気がするが、
渡すべきものはContextって覚えとけば平気かな・・・


残りの2行は、
TextViewに文字列を設定。
アクティビティにTextViewを設置。
って感じで、リファレンス読むまでもなく雰囲気でわかるね。


コードはonCreateのブロック内に書いているが、名前がonから始まるからイベント的なものだろうという雰囲気があるが、
onCreateはアクティビティ初期化時に実行される。
他にonStart、onResume、onPause、onStop、onDestroyがあり、
開始時は、onCreate → onStart → onResume の順で処理される。
Androidのアプリは非表示にしてもスリープになるだけだが、
スリープ状態になる時は、onPause → onStop の順。
スリープした際にonStopが実行されるが、Stopする前にPauseでキャンセルされる場合があり、その場合はPauseに戻る。
Stopから復帰した場合はStartに戻る。
Destroyは正常終了だが、メモリ不足に陥るとPause又はStop状態でkillされる可能性がある。

スリープからの復帰時に何も変更しないなら、開始時の処理はonCreateだけで良さそう。
終了時に保存等の処理をする場合は、killされることも考えて、Pauseでやればいいのかな?