ログインが必要なアプリケーションを作成しています。メイン アクティビティとログイン アクティビティを作成しました。
メイン アクティビティonCreate
メソッドに次の条件を追加しました。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
loadSettings();
if(strSessionString == null)
{
login();
}
...
}
onActivityResult
ログインフォームが終了したときに実行されるメソッドは次のようになります。
@Override
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case(SHOW_SUBACTICITY_LOGIN):
{
if(resultCode == Activity.RESULT_OK)
{
strSessionString = data.getStringExtra(Login.SESSIONSTRING);
connectionAvailable = true;
strUsername = data.getStringExtra(Login.USERNAME);
}
}
}
問題は、ログイン フォームが 2 回表示されることがある (login()
メソッドが 2 回呼び出される) ことと、電話のキーボードをスライドするとログイン フォームが再度表示されることです。問題は変数にあると思われますstrSessionString
。
ユーザーがすでに正常に認証された後にログインフォームが表示されないようにするために、変数をグローバルに設定する方法を知っている人はいますか?
ベストアンサー1
この回答は、Android が比較的新しく、Android 開発にはまだ十分に確立されていない領域が数多くあった 2009 年に書いたものです。この投稿の最後に長い補足を追加し、いくつかの批判に対処し、Application をサブクラス化するのではなくシングルトンを使用することに対する私の哲学的な意見の相違を詳しく説明しました。自己責任でお読みください。
元の回答:
より一般的な問題は、複数のアクティビティとアプリケーションのすべての部分にわたって状態を保存する方法です。静的変数 (たとえば、シングルトン) は、これを実現するための一般的な Java の方法です。ただし、Android では、状態をアプリケーション コンテキストに関連付ける方がよりエレガントな方法であることがわかりました。
ご存知のとおり、各アクティビティはコンテキストでもあり、これは最も広い意味でその実行環境に関する情報です。アプリケーションにもコンテキストがあり、Android はそれがアプリケーション全体で単一のインスタンスとして存在することを保証します。
これを実現するには、独自のサブクラスを作成します。android.app.アプリケーション、そしてマニフェストのアプリケーション タグでそのクラスを指定します。これで、Android は自動的にそのクラスのインスタンスを作成し、アプリケーション全体で使用できるようになります。メソッドcontext
を使用して、どこからでもアクセスできますContext.getApplicationContext()
(には、まったく同じ効果を持つActivity
メソッドも用意されていますgetApplication()
)。以下は、非常に単純化された例ですが、注意点があります。
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
これは、静的変数またはシングルトンを使用する場合と本質的に同じ効果がありますが、既存の Android フレームワークに非常にうまく統合されます。これはプロセス間では機能しないことに注意してください (アプリが複数のプロセスを持つ珍しいアプリの 1 つである場合)。
上記の例から注目すべき点があります。代わりに次のようなことをしたとします。
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
これで、この遅い初期化 (ディスクへのアクセス、ネットワークへのアクセス、ブロックするものなど) は、Application がインスタンス化されるたびに実行されます。これはプロセスに対して 1 回だけなので、いずれにせよコストを支払う必要があると考えるかもしれません。たとえば、Dianne Hackborn が以下で述べているように、バックグラウンド ブロードキャスト イベントを処理するためだけにプロセスをインスタンス化することは完全に可能です。ブロードキャスト処理でこの状態が必要ない場合は、複雑で遅い一連の操作を無駄に実行した可能性があります。ここでは、遅延インスタンス化が重要です。以下は、Application を使用する少し複雑な方法ですが、最も単純な用途以外ではより理にかなっています。
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
ここでは、よりエレガントなソリューションとしてシングルトンを使用するよりもアプリケーションのサブクラス化を好みますが、状態をアプリケーション サブクラスに関連付けることによるパフォーマンスとマルチスレッドの影響をまったく考慮しないよりも、本当に必要な場合に開発者がシングルトンを使用する方がよいでしょう。
注 1:また、anticafe がコメントしたように、アプリケーションのオーバーライドをアプリケーションに正しく関連付けるには、マニフェスト ファイルにタグが必要です。詳細については、Android のドキュメントを参照してください。例:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
注 2: user608578 は、ネイティブ オブジェクトのライフサイクルの管理でこれがどのように機能するかを以下で質問しています。私は Android でのネイティブ コードの使用についてまったく詳しくなく、それが私のソリューションとどのように相互作用するかについて回答する資格がありません。誰かがこれに答えを持っている場合は、その人をクレジットし、最大限の可視性を得るためにこの投稿に情報を掲載したいと思います。
補遺:
一部の人が指摘しているように、これは永続的な状態に対するソリューションではありません。これは、元の回答でもっと強調すべきだったかもしれません。つまり、これは、アプリケーションの存続期間にわたって永続化されることを意図したユーザーやその他の情報を保存するためのソリューションではありません。したがって、アプリケーションがいつでも強制終了されることなどに関連する以下のほとんどの批判は無意味であると考えます。ディスクに永続化する必要のあるものはすべて、アプリケーションのサブクラスを通じて保存されるべきではないからです。これは、一時的で簡単に再作成できるアプリケーション状態 (ユーザーがログインしているかどうかなど) と、本質的に単一インスタンス (アプリケーション ネットワーク マネージャーなど) (シングルトンではありません!) であるコンポーネントを保存するためのソリューションです。
デイアーマンは興味深いことを指摘してくれた。レト・マイヤーとダイアン・ハックボーンとの会話アプリケーションサブクラスの使用は推奨されず、シングルトンパターンが推奨される。Somatik も以前、この性質のことを指摘していたが、当時は気付いていなかった。Reto と Dianne は Android プラットフォームの保守に携わっているため、彼らのアドバイスを無視することはお勧めできない。彼らの言うことは正しい。アプリケーションサブクラスよりもシングルトンを優先するという意見には反対したい。反対する理由として、私は次の概念を利用する。シングルトンデザインパターンに関するStackExchangeの説明、この回答で用語を定義する必要がないようにするためです。先に進む前にリンクをざっと読むことを強くお勧めします。ポイントごとに:
Dianne は、「Application からサブクラス化する理由はありません。シングルトンを作成するのと何ら変わりありません...」と述べています。この最初の主張は誤りです。これには 2 つの主な理由があります。1) Application クラスは、アプリケーション開発者にとってより優れたライフタイム保証を提供します。つまり、アプリケーションのライフタイムを持つことが保証されます。シングルトンは、アプリケーションのライフタイムに明示的に結び付けられているわけではありません (事実上は結び付けられていますが)。これは、平均的なアプリケーション開発者にとっては問題にならないかもしれませんが、これは Android API が提供すべき契約の種類であり、関連データのライフタイムを最小限に抑えることで、Android システムにはるかに高い柔軟性を提供すると主張します。2) Application クラスは、アプリケーション開発者に状態の単一インスタンス ホルダーを提供します。これは、状態のシングルトン ホルダーとは大きく異なります。違いの一覧については、上記のシングルトンの説明リンクを参照してください。
Dianne は続けて、「...将来、アプリケーション オブジェクトが独立したアプリケーション ロジックであるべきものが絡み合った大きな混乱状態になっていることに気付いて後悔する可能性が高い」と述べています。これは確かに間違いではありませんが、これはアプリケーション サブクラスよりもシングルトンを選択する理由にはなりません。Diane の議論には、シングルトンの使用がアプリケーション サブクラスよりも優れているという理由がまったく示されていません。彼女が証明しようとしているのは、シングルトンの使用はアプリケーション サブクラスよりも悪くないということだけですが、これは間違っていると思います。
彼女は続けて、「そして、これは、これらのものをどのように管理すべきか、つまり、オンデマンドで初期化すべきかという点に自然につながります」と述べています。これは、Application サブクラスを使用してオンデマンドで初期化できない理由はないという事実を無視しています。ここでも、違いはありません。
Dianne は最後に、「フレームワーク自体には、ロードされたリソースのキャッシュ、オブジェクトのプールなど、アプリ用に維持されるすべての小さな共有データ用のシングルトンが大量にあります。これは非常にうまく機能します。」と述べています。私は、シングルトンの使用がうまく機能しない、または正当な代替手段ではないと主張しているわけではありません。シングルトンは、アプリケーションのサブクラスを使用するほど Android システムとの強力な契約を提供しない、さらに、シングルトンを使用すると一般的に柔軟性のない設計になり、簡単に変更できず、将来的に多くの問題につながると主張しています。私の意見では、Android API が開発者アプリケーションに提供する強力な契約は、Android を使用したプログラミングの最も魅力的で楽しい側面の 1 つであり、開発者による早期の採用につながり、Android プラットフォームを今日の成功に導きました。シングルトンの使用を提案することは、暗黙的に強力な API 契約から離れることであり、私の意見では、Android フレームワークを弱体化させます。
Dianne も下記でコメントしており、Application サブクラスを使用することのもう 1 つの欠点として、パフォーマンスの低いコードの作成が促進されるか、または容易になる可能性があると述べています。これはまさにその通りで、私はここでパフォーマンスを考慮することの重要性と、Application サブクラス化を使用している場合は正しいアプローチを取ることを強調するためにこの回答を編集しました。Dianne が述べているように、プロセスがバックグラウンド ブロードキャスト イベント用にのみロードされている場合でも、プロセスがロードされるたびに (アプリケーションが複数のプロセスで実行されている場合は一度に複数回実行される可能性があります)、Application クラスがインスタンス化されることを覚えておくことが重要です。したがって、Application クラスは、処理を行う場所としてではなく、アプリケーションの共有コンポーネントへのポインターのリポジトリとして使用することが重要です。
先ほどの StackExchange リンクから引用した、シングルトンの欠点のリストを以下に示します。
- 抽象クラスまたはインターフェースクラスを使用できない。
- サブクラス化できない。
- アプリケーション全体の結合度が高い(変更が困難)
- テストが難しい (ユニットテストでフェイク/モックできない);
- 可変状態の場合、並列化が困難です (大規模なロックが必要)。
そして私自身の意見を追加します:
- 不明瞭で管理不能なライフタイム コントラクトは、Android (または他のほとんどの) 開発には適していません。