ログインが必要なアプリケーションを作成しています。メインアクティビティとログインアクティビティを作成しました。
メインアクティビティの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
が問題になることです。
ユーザーが既に認証に成功した後にログインフォームが表示されないようにするために、変数globalを設定する方法を知っている人はいますか?
私はこの答えをAndroidが比較的新しくなった'09年に書き直しました。Android開発には確立されていない分野が数多くありました。私はこの記事の最後に長い補遺を追加して、いくつかの批判に対処し、アプリケーションをサブクラス化するのではなくシングルトンを使用することについての哲学的意見の不一致について詳述しました。あなた自身の責任でそれを読んでください。
元の答え:
あなたが遭遇しているより一般的な問題はいくつかの活動とあなたのアプリケーションのすべての部分に渡って状態を保存する方法です。静的変数(例えば、シングルトン)はこれを達成するための一般的なJavaの方法です。しかし私は、Androidにおけるよりエレガントな方法はあなたの状態をアプリケーションコンテキストと関連付けることであることを発見しました。
ご存じのとおり、各アクティビティはコンテキストでもあります。これは、最も広い意味での実行環境に関する情報です。あなたのアプリケーションにもコンテキストがあり、Androidはそれがあなたのアプリケーション全体で単一のインスタンスとして存在することを保証します。
これを行う方法は、 Android.app.Application の独自のサブクラスを作成してから、そのクラスをマニフェストのapplicationタグに指定することです。これでAndroidは自動的にそのクラスのインスタンスを作成し、それをアプリケーション全体で利用できるようにします。 Context.getApplicationContext()
メソッドを使用して、どのcontext
からでもアクセスできます(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;
}
}
これで、この遅い初期化(ディスクへのアクセス、ネットワークへのアクセス、ブロックなど)は、アプリケーションがインスタンス化されるたびに実行されます。あなたは、おそらく、これはプロセスのために一度だけであると思うかもしれません、そして、私はとにかくコストを払わなければならないでしょうね?たとえば、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();
...
}
}
ここではより洗練された解決策としてシングルトンを使用するよりもアプリケーションサブクラス化を好みますが、パフォーマンスとアプリケーションサブクラスを関連付けることによるマルチスレッドの意味をまったく考えないよりも、開発者はシングルトンを使用します。
NOTE 1:また、アンチセーフのコメントとして、アプリケーションオーバーライドをアプリケーションに正しく結び付けるには、マニフェストファイルにタグが必要です。繰り返しますが、詳細についてはAndroidのドキュメントを参照してください。例:
<application
Android:name="my.application.MyApp"
Android:icon="..."
Android:label="...">
</application>
注2: user608578では、これがネイティブオブジェクトのライフサイクルの管理とどのように連携するのかを以下に尋ねています。私は、Androidでネイティブコードを使うのを少しも速くしていませんし、それが私のソリューションとどのように相互作用するのかに答えることはできません。誰かがこれに答えを持っているならば、私は彼らを信用して最大の可視性のためにこのポストに情報を入れても構わないと思っています。
補遺:
何人かの人々が指摘したように、これはpersistent状態の解決策ではnotであり、おそらく私が最初の答えでもっと強調しておくべきものです。すなわちこれは、アプリケーションの存続期間を通じて持続することを目的とした、ユーザーまたはその他の情報を保存するためのソリューションではありません。したがって、ディスクに永続化する必要があるものはすべてApplicationサブクラスを介して格納しないでください。これは、一時的で簡単に再作成可能なアプリケーションの状態(たとえば、ユーザーがログインしているかどうか)およびシングルインスタンスのコンポーネント(たとえば、アプリケーションネットワークマネージャ)を保存するためのソリューションです(NOT singleton! ) 本来は。
Dayermanは、Applicationサブクラスの使用がシングルトンパターンのために推奨されていない興味深い Reto MeierとDianne Hackbornとの会話 を指摘するほど親切にしています。 Somatikはまた私がその時それを見なかったけれども、この性質の何かを以前に指摘した。 Androidプラットフォームを維持する上でのRetoとDianneの役割のために、私は忠実に彼らのアドバイスを無視することを勧めません。彼らが言うことは行きます。私は、アプリケーションのサブクラスよりもシングルトンを好むことに関して表明された意見に同意しません。私の意見の相違には、私は このStackExchangeのシングルトンデザインパターンの説明 で説明されている概念を利用するつもりで、この答えの中で用語を定義する必要はありません。続行する前に、リンクをスキップすることを強くお勧めします。ポイントバイポイント:
「アプリケーションからサブクラス化する理由はありません。シングルトンを作成するのと同じことです。」最初の主張は正しくありません。これには主に2つの理由があります。 1)Applicationクラスはアプリケーション開発者にとってより良い生涯保証を提供します。アプリケーションの有効期間があることが保証されています。シングルトンはアプリケーションの有効期間に厳密には結び付けられていません(効果的ですが)。これは平均的なアプリケーション開発者にとっては問題にならないかもしれませんが、これはまさにAndroid APIが提供すべき契約の種類であり、関連する製品の寿命を最小化することでAndroidシステムにはるかに高い柔軟性を提供します。データ。 2)Applicationクラスは、アプリケーション開発者に状態の単一インスタンス保持者を提供します。これは、シングルトン状態保持者とは大きく異なります。違いの一覧については、上記のシングルトンの説明リンクを参照してください。
「...あなたのアプリケーションオブジェクトが、独立したアプリケーションロジックであるべきもののこの大きな絡み合った混乱になっているのを見いだしたとき、ダイアン氏は続けます。これは確かに不正確ではありませんが、これはSingleton over Applicationサブクラスを選択する理由ではありません。 Dianeの主張はいずれも、Singletonを使用することがApplicationサブクラスよりも優れているという理由を提供していません。彼女が確立しようとしているのは、Singletonを使用することがApplicationサブクラスより悪くないということです。
彼女は続けて、「そしてこれは、あなたがこれらのことをどうやって管理しなければならないかということをより自然に導きます。これは、Applicationサブクラスを使用してオンデマンドで初期化できない理由がないという事実を無視します。また違いはありません。
Dianneは、「フレームワーク自体に、アプリケーション用に保持している、ロードされたリソースのキャッシュ、オブジェクトのプールなど、すべての小さな共有データ用のシングルトンが何トンもあります」と述べています。私は、シングルトンを使用することがうまくいくことができないか、または合法的な代替手段ではないと主張していません。私は、シングルトンがアプリケーションサブクラスを使用するほどAndroidシステムとの強い契約を提供していないこと、さらにシングルトンを使用することは一般に柔軟性のない設計を指し示している。 IMHO、Android APIが開発者アプリケーションに提供する強力な契約は、Androidを使用したプログラミングの最も魅力的で魅力的な側面の1つであり、Androidプラットフォームを今日の成功に導いた開発者の早期採用につながりました。シングルトンの使用を提案することは、強いAPI契約から暗黙のうちに動いています、そして私の意見では、Androidフレームワークを弱めます。
Dianne氏は、Applicationサブクラスを使用することのもう1つのマイナス面を挙げて、以下のようにコメントしています。彼らは、パフォーマンスの低いコードを書くことを奨励または容易にするかもしれません。これは非常に本当です、そして私はここでperfを考慮し、あなたがApplicationサブクラス化を使っているなら正しいアプローチをとることの重要性を強調するためにこの答えを編集しました。 Dianneが述べているように、プロセスがバックグラウンドブロードキャストでのみロードされている場合でも、プロセスがロードされるたびにApplicationクラスがインスタンス化されることを覚えておくことが重要です。イベントしたがって、Applicationクラスは、処理を行う場所としてではなく、アプリケーションの共有コンポーネントへのポインタのリポジトリとして使用することが重要です。
以前のStackExchangeリンクから盗まれたように、私はあなたにシングルトンへの欠点の以下のリストを残します:
そして私自身を追加します。
このサブクラスを作成
public class MyApp extends Application {
String foo;
}
AndroidManifest.xmlにAndroid:nameを追加します。
例
<application Android:name=".MyApp"
Android:icon="@drawable/icon"
Android:label="@string/app_name">
Soonilによって提案されたアプリケーションの状態を保持する方法は優れていますが、欠点が1つあります。OSがアプリケーションプロセス全体を強制終了する場合があります。これはこれに関するドキュメントです - プロセスとライフサイクル 。
ケースを考えてみましょう - 誰かがあなたを呼んでいるのであなたのアプリはバックグラウンドに入ります(電話アプリは現在最前面にあります)。この場合、他の状況下で&&(上記のリンクを確認してください)により、OSがApplication
サブクラスインスタンスを含むアプリケーションプロセスを強制終了する可能性があります。その結果、状態は失われます。後でアプリケーションに戻ると、OSはそのアクティビティスタックとApplication
サブクラスインスタンスを復元しますが、myState
フィールドはnull
になります。
私の知る限りでは、州の安全性を保証する唯一の方法は、州を存続させるあらゆる種類の方法を使用することです。アプリケーションファイルまたはSharedPrefernces
にプライベートを使用します(最終的には、内部ファイルシステムのアプリケーションファイルにプライベートを使用します)。
ちょっと注意してください。
追加:
Android:name=".Globals"
あるいは、サブクラスの名前を existing <application>
タグに変更したものです。マニフェストに<application>
タグをもう1つ追加しようとしたところ、例外が発生しました。
アプリケーションタグの指定方法も見つけることができませんでしたが、多くのGooglingの後で、アプリケーションスタンザのデフォルトのアイコンとラベルに加えて、マニフェストファイルdocs:use Android:nameから明らかになりました。
Android:nameアプリケーション用に実装されたApplicationサブクラスの完全修飾名。アプリケーションプロセスが開始されると、このクラスはアプリケーションのコンポーネントの前にインスタンス化されます。
サブクラスはオプションです。ほとんどのアプリケーションには必要ありません。サブクラスが存在しない場合、Androidは基本のApplicationクラスのインスタンスを使用します。
このようなグローバル構造を持つネイティブメモリの収集を確実にすることはどうでしょうか。
アクティビティには破棄時に呼び出されるonPause/onDestroy()
メソッドがありますが、Applicationクラスには同等のものはありません。アプリケーションが強制終了されたとき、またはタスクスタックがバックグラウンドで実行されたときに、グローバル構造(特にネイティブメモリへの参照を含むもの)を適切にガベージコレクションするために推奨されるメカニズムは何ですか?
以下のように動作するアプリケーション名を定義する必要があります。
<application
Android:name="ApplicationName" Android:icon="@drawable/icon">
</application>
いくつかの変数がsqliteに格納されていて、あなたがあなたのアプリのほとんどの活動でそれらを使わなければならないならば。それからアプリケーションはそれを達成するための最善の方法かもしれません。アプリケーションの起動時にデータベースから変数を照会し、それらをフィールドに格納します。それからあなたはあなたの活動の中でこれらの変数を使うことができます。
正しい方法を見つけてください、そして最善の方法はありません。
上記で説明したように、OSは通知なしにAPPLICATIONを強制終了できます(onDestroyイベントはありません)ので、これらのグローバル変数を保存する方法はありません。
SharedPreferencesはあなたがCOMPLEX STRUCTURED変数を持っていることを除いては解決策になるかもしれません(私の場合私はユーザーが既に処理したIDを格納するために整数配列を持っていました)。 SharedPreferencesの問題点は、値が必要になるたびにこれらの構造を格納したり取り出したりするのが難しいということです。
私の場合は、背景変数がSERVICEで、この変数をそこに移動できるようにしました。また、サービスにonDestroyイベントがあるので、これらの値を簡単に保存できます。
この種の状態を格納するための静的フィールドを持つことができます。または、リソースBundleに配置して、そこからonCreate(Bundle savedInstanceState)に復元します。 Androidアプリで管理されるライフサイクルを完全に理解していることを確認してください(たとえば、キーボードの向きが変わるとlogin()が呼び出される理由)。
DO N'Tマニフェストファイルで別の<application>
タグを使用します。既存の<application>
タグに1つの変更を加え、この行を追加しますAndroid:name=".ApplicationName"
where、ApplicationName
は、作成しようとしているサブクラス(グローバルの保存に使用)の名前になります。
したがって、最終的にマニフェストファイルのONE AND ONLY<application>
タグは次のようになります。
<application
Android:allowBackup="true"
Android:icon="@mipmap/ic_launcher"
Android:label="@string/app_name"
Android:theme="@style/Theme.AppCompat.NoActionBar"
Android:name=".ApplicationName"
>
これを行うには、2つの方法があります。
共有設定を使用する
アプリケーションクラスの使用
例:
class SessionManager extends Application{
String sessionKey;
setSessionKey(String key){
this.sessionKey=key;
}
String getSessisonKey(){
return this.sessionKey;
}
}
以下のようにMainActivityにログインを実装するために上記のクラスを使うことができます。コードは次のようになります。
@override
public void onCreate (Bundle savedInstanceState){
// you will this key when first time login is successful.
SessionManager session= (SessionManager)getApplicationContext();
String key=getSessisonKey.getKey();
//Use this key to identify whether session is alive or not.
}
この方法は一時的な保存に有効です。メモリが不足しているため、オペレーティングシステムがアプリケーションを強制終了することになるかどうかは、まったくわかりません。アプリケーションがバックグラウンドで実行中のメモリを多く必要とする他のアプリケーションをナビゲートしているときは、オペレーティングシステムがバックグラウンドよりもフォアグラウンドプロセスを優先するため、アプリケーションは強制終了されます。そのため、ユーザーがログアウトするまではアプリケーションオブジェクトはnullになります。したがって、このために私は上記で指定された第二の方法を使用することをお勧めします。
共有設定を使用する.
String MYPREF="com.your.application.session"
SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
//Insert key as below:
Editot editor= pref.edit();
editor.putString("key","value");
editor.commit();
//Get key as below.
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
String key= getResources().getString("key");
intents、Sqlite、またはShared Preferencesを使用できます。ドキュメント、写真、ビデオなどのメディアストレージに関しては、代わりに新しいファイルを作成できます。
活動中の結果が再開前に呼び出されます。ログインチェックを再開し、secomdアクティビティが肯定的な結果を返したら、2回目のログインをブロックできます。毎回履歴書が呼び出されるので、最初に呼び出されないという心配はありません。
class GlobaleVariableDemo extends Application {
private String myGlobalState;
public String getGlobalState(){
return myGlobalState;
}
public void setGlobalState(String s){
myGlobalState = s;
}
}
class Demo extends Activity {
@Override
public void onCreate(Bundle b){
...
GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
String state = appState.getGlobalState();
...
}
}
サブクラス化のアプローチはBARACUSフレームワークによっても使用されています。私の観点からは サブクラス化 アプリケーションはAndroidのライフサイクルで動作することを意図していました。これが any Application Containerの動作です。グローバルを持つ代わりに、このコンテキストにBeanを登録して、コンテキストで管理可能なクラスにBeeingされるようにします。注入されたBeanインスタンスはすべて実際にはシングルトンです。
あなたがそんなに多くを持つことができるなら、なぜ手動作業をするのですか?
Application
クラスを拡張するクラスを作成してから、そのクラスのフィールドとして変数を宣言し、それに対するgetterメソッドを提供することができます。
public class MyApplication extends Application {
private String str = "My String";
synchronized public String getMyString {
return str;
}
}
そして、あなたのアクティビティでその変数にアクセスするために、これを使います:
MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();