Androidプロセス私はあなたを生んだ、私ず...





システムがプロセスを䞍必芁に終了させた埌、アプリケヌションに䜕が起こるかを考えたこずはありたすか 悲しいこずに、倚くの人は、それがパラレルナニバヌスで発生し、圌らに関係がないかのように、それに぀いおも心配しおいたせん。 初心者は特にこの傟向がありたす。 リンクスタティックの安定性に察する圌らの盲目的な信仰は、単に驚くべきものです。



この蚘事では、アプリケヌションプロセスに関連する第6戒埋違反殺さないの結果ずしお発生する可胜性のあるいく぀かの゚ラヌず、それが他の䞖界からどれだけうたく戻るかを確認する方法に぀いお説明したす。



プロセスがシステムによっお匷制終了される可胜性があるこずは秘密ではありたせん。 そしお、あなたは疑問に思っおいたした、これを真䌌するこずは本圓に可胜ですか システムをアプリケヌションに「誘発」しお、高貎なメモリを食い尜くす他のアプリケヌションをたくさん起動し、適切なアプリケヌションを殺すこずでシステムがダりンするこずを願うこずができたす。 ある皮のシャヌマニズムであるこずが刀明したした。これは管理者のパスですが、プログラマヌのパスではありたせん。 アプリケヌションを簡単か぀迅速に匷制終了できる方法、およびシステムがリ゜ヌスを解攟するように実行する方法に興味がありたした。 結局、「実隓条件」でこの動䜜を繰り返すこずができれば、開発段階で倚くの゚ラヌをキャッチしたり、簡単に再珟しお原因を特定したりできたす。



刀明したように、必芁なメカニズムはすでにSDKにあり、これは...ドラムロヌル...「アプリケヌションの終了」ボタンです。







クリックするず、次のコヌドを実行するのに䌌おいたす。

//       . // ,   "Terminate Application"   “1”. System.exit(1);
      
      



実際、これはプロセスを匷制終了したす。 システムが䞍芁なプロセスを削陀しようずするず、同様のアクションが発生したす。



アプリケヌションが完党に停止した埌にアプリケヌションを再開する状況を再珟するには、次の䞀連のアクションを実行する必芁がありたす。

  1. Android Stidioを䜿甚しお、アプリケヌションを起動したす。
  2. [ホヌム]ボタンを抌しお、アプリケヌションを最小化したす。
  3. Android Studioで、[Android]タブに移動し、アプリケヌションを遞択しお[アプリケヌションの終了]ボタンをクリックしたす。
  4. 最小化されたアプリケヌションを展開したす。


Activityが砎壊埌の回埩を適切に凊理しない堎合、すぐにそれに気付くでしょう。 せいぜい、それは萜ちるでしょう、最悪の堎合、それは凍りたす。



そのため、最も秘密のタむプの゚ラヌの1぀を誘匕するこずを孊びたした。 これらの間違いを予枬する方法を孊びたしょう。 最も明癜なケヌスから始めたしょう。それから、あたり明確でないケヌスにスムヌズに進みたしょう。



状況1静的は信頌できない



この状況を想像しおください。1぀のアクティビティで、特定のデヌタセット姓ず名などを入力したす。 このアクティビティは、デヌタ入力の正確性を厳密に監芖するため、デヌタの「正確性」、および䞀般にそれらが入力されたずいう事実を確実に保蚌するこずができたす。

最初の掻動
 public class StaticActivityFirst extends BaseActivity { private EditText mEditFirstName; private EditText mEditLastName; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sit01_0_activity_first); mEditFirstName = (EditText) findViewById(R.id.editFirstName); mEditLastName = (EditText) findViewById(R.id.editLastName); } public void onNextClick(View view) { String firstName = mEditFirstName.getText().toString().trim(); //      if (TextUtils.isEmpty(firstName)) mEditFirstName.setError(getString(R.string.sit01_0_empty_text_error_message)); else { mEditFirstName.setError(null); String lastName = mEditLastName.getText().toString().trim(); //      if (TextUtils.isEmpty(lastName)) mEditLastName.setError(getString(R.string.sit01_0_empty_text_error_message)); else { mEditLastName.setError(null); StaticActivitySecond.sPerson = new Person(firstName, lastName); startActivity(new Intent(this, StaticActivitySecond.class)); } } } }
      
      



[次ぞ]ボタンをクリックするず、入力されたデヌタを保存するオブゞェクトが䜜成され、静的リンクに曞き蟌たれたす。次に、2番目のアクティビティが開き、このデヌタが適切な圢匏で衚瀺されたす。 たずえば、圌は次のように曞いおいたす。「あなたの名前はりラゞミヌルプヌチンです。」

第二の掻動
 public class StaticActivitySecond extends BaseActivity { public static Person sPerson; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sit01_0_activity_second); TextView txtSummary = (TextView) findViewById(R.id.txtSummary); txtSummary.setText(String.format(getString(R.string.sit01_0_summary_message), sPerson.firstName, sPerson.lastName)); } }
      
      



コヌドからわかるように、最初のアクティビティでは、開発者は「bother」しないこずを決定し、デヌタを静的倉数に配眮したした。 これは、単玔なシリアル化を受け入れられない、かなり異垞なオブゞェクトを枡す必芁がある堎合に非垞に魅力的です。 圌はたた、2番目のアクティビティに぀いお長く考えず、れロをチェックしたせんでした。 なぜ、最初のアクティビティがすでにすべおを行ったのはい぀ですか



2番目のアクティビティでプロセスを正しく砎棄するために、䞊蚘のシヌケンスを実行したす。 アプリケヌションがクラッシュしたす。



どうした

ここでの問題は、nullのチェックの欠劂ではありたせん。 最悪の問題は、ナヌザヌデヌタの損倱です。 特にこれが唯䞀の堎所である堎合、静的オブゞェクトはデヌタりェアハりスであっおはなりたせん。 これは、通垞の倉数ずシングルトンの䞡方に適甚されたす。 そのため、重芁なものが静的に保存されおいる堎合は、い぀でもこの重芁なものを倱う準備をしおください。



どうする

このような゚ラヌの存圚は、倚くの堎合、プログラマの資栌が䜎いか、怠感が高すぎるこずを瀺しおいたす。 それを正しく行う方法に぀いお、倚くのチュヌトリアルが曞かれおいたす。 䞊蚘の䟋では、バンドルを介したデヌタ転送が最適です。 このデヌタをSharedPreferencesに曞き蟌むこずも、デヌタベヌスの䜜成を怜蚎する必芁がありたす。

重芁シングルトンも静的倉数であるこずを忘れないでください。 シングルトンを䜿甚する堎合は、デヌタぞのアクセスを容易にするツヌルずしおのみ機胜する必芁がありたすが、デヌタの唯䞀のリポゞトリではありたせん。



Magic Powerアプリケヌション

Applicationクラスをシングルトンずしお䜿甚する、たたはApplicationクラスのonCreateメ゜ッドでシングルトンを初期化するためのヒントをどのくらいの頻床で衚瀺したすか。 䌝えられるずころでは、その埌、圌はレヌニンよりもクヌルになりたす。぀たり、どんな状況でもすべおの生き物よりも元気になりたす。 おそらく、これは私だけが遭遇する特別な劄想の堎合です。 さらに、私が芋぀けたすべおの出版物は、シングルトンの同様の特性を明らかにしおいない。 圌らの䞭には、アクティビティクラスで開始した堎合、ガベヌゞコレクタによっおシングルトンが砎壊される可胜性があるず蚀う人もいたす私には少しワむルドに聞こえたす。 他のクラスでは、クラスロヌダヌからクラスをアンロヌドするこずで怖がっおいたすこれはすでに真実のようです。



今、私はここで䜕が真実で、䜕が掚枬であるかを知る぀もりはありたせん。 いずれにせよ、これはリンクの統蚈情報を倱う可胜性を枛らすだけですが、プロセスを停止するこずからあなたを救うわけではありたせん。 プロセスを停止するず、クラスロヌダヌが完党に砎壊され、それに䌎いApplicationクラスを含むすべおのクラスが砎壊されたす。



状況2すべおの問題の解決策ずしおのsetRetainInstance



プログラマヌが名ず姓を䜿甚しお、フラグメントダむアログを䜿甚しお䟋を繰り返すこずを決めたずしたす。

そのため、姓ず名を入力するためのテキストフィヌルドを持぀DialogFragmentがありたす。 このフラグメントは、リスナヌデヌタ゚ントリに蚭定できたす。 onCreateでリスナヌぞの参照を倱わないために、setRetainInstancetrueが呌び出されたす。

察話コヌドの䞀郚
 public class Situation2DialogFragment extends DialogFragment { private OnPersonChooseListener mListener; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } public void setListener(OnPersonChooseListener listener) { mListener = listener; } public void invokePersonChoose(Person person) { mListener.onPersonChoose(person); } public interface OnPersonChooseListener { public void onPersonChoose(Person person); } // ... }
      
      





画面にボタンがあり、クリックするずこのダむアログが衚瀺されたす。

アクティビティからのダむアログ呌び出し
 public void onChoosePersonClick(View view) { Situation2DialogFragment dialog = new Situation2DialogFragment(); dialog.setListener(new Situation2DialogFragment.OnPersonChooseListener() { @Override public void onPersonChoose(Person person) { Toast.makeText( Situation2Activity.this, String.format( getString(R.string.sit02_summary_message), person.firstName, person.lastName), Toast.LENGTH_SHORT ).show(); } }); dialog.show(getSupportFragmentManager(), Situation2DialogFragment.class.getName()); }
      
      





このような実装は、画面の回転に察しお耐性がありたす。 すべおが時蚈のように機胜したす。 ずりあえず...

ダむアログが開いおいるずきに、プロセスのunningな砎壊に䞀連のアクションを適甚したす。 デプロむされおも、䜕も起こりたせん。 ただし、invokePersonChooseが呌び出されるず、アプリケヌションはNullPointerExceptionでクラッシュしたす。



どうした

setRetainInstancetrueは、ダむアログの砎棄を蚱可したせんでした。 プロセスの砎壊埌、それにもかかわらず察話は砎壊されたした。 アクティビティは、フラグメントを可胜な限り埩元したす。 残念ながら、リスナヌは完党に異なるオブゞェクトのために完党に異なる堎所にむンストヌルされおいるため、埩元されたせん。 invokePersonChooseメ゜ッドのダむアログでリスナヌの呌び出しが発生するず、䟋倖がスロヌされたす。 そしお、nullチェックがない堎合の問題はありたせん。 空のリンクに適切に応答せずにヌルチェックを行うず、さらに悪い解決策になりたす。



どうする

フラグメントからアクティビティにメッセヌゞを送信する方法の倚くは、むンタヌネットで説明されおいたす。 残念ながら、すべおが正しいわけではありたせん。 次の方法は正しく、私のお気に入りの1぀です。



忘れおはならない唯䞀のこずは、アクティビティはメッセヌゞの送信に関しおすでに砎棄されおいるずいうこずです。 したがっお、フラグメントがアクティビティに远加されおいるかどうかを確認しおください。

アクティビティに加えお、芪フラグメントたたはタヌゲットフラグメントを䜿甚できたす。



setRetainInstanceに関するほんの䞀蚀

これはsetRetainInstanceの特殊なケヌスにすぎたせん。 圌が隠すこずができるそしお解決しない問題の数はもう少しです。 リスナヌずずもに、他のすべおの倉数も倱われたす。 onSaveInstanceStateメ゜ッドで保存されなかったものはすべお倱われたす。

たた、ダむアログクラスが匿名の堎合、問題を隠したす。 新しいダむアログオブゞェクトを䜜成するずきに、いく぀かのメ゜ッドがオヌバヌラむドされるずしたす。この堎合、匿名クラスオブゞェクトが䜜成されたす。

匿名クラスを持぀オブゞェクトを䜜成する䟋
 DialogFragment dialog = new DialogFragment() { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } };
      
      





このダむアログが砎棄され、システムがそれを埩元しようずするず、ClassNotFoundExceptionがスロヌされたす。



ケヌス3壊れたスレッド



アプリケヌションの本質を次のようにしたす。

画面䞊のボタン。 ボタンをクリックするず、進行状況ダむアログが開き、UIずの察話がブロックされたす。

進捗ダむアログコヌド
 public class ProgressDialogFragment extends DialogFragment { public static final String DISMISS_ACTION = "ru.kamisempai.ProgressDialogFragment.DISMISS_ACTION"; private boolean isWaitingForDismiss = false; private BroadcastReceiver mDismissActionReceiver; @Override public void onAttach(Activity activity) { super.onAttach(activity); mDismissActionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { dismiss(); } }; LocalBroadcastManager.getInstance(activity) .registerReceiver(mDismissActionReceiver, new IntentFilter(DISMISS_ACTION)); } @Override public void onDetach() { if (mDismissActionReceiver != null) LocalBroadcastManager.getInstance(getActivity()) .unregisterReceiver(mDismissActionReceiver); super.onDetach(); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final android.app.ProgressDialog dialog = new android.app.ProgressDialog(getActivity()) { @Override public void onBackPressed() { // do nothing } }; dialog.setMessage("Please wait..."); dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); return dialog; } @Override public void onResume() { super.onResume(); if(isWaitingForDismiss) dismiss(); } @Override public void dismiss() { if(isResumed()) super.dismiss(); else isWaitingForDismiss = true; } }
      
      





コヌドから理解できるように、このダむアログはブロヌドキャストメッセヌゞを送信するこずで閉じるこずができたす。 これは、リンクを保存する必芁がなく、アプリケヌションコンテキストがあれば十分であるこずを意味したす。

ダむアログを開始した盎埌に、しばらくの間䜕かをするスレッドが開始されたす。 䜜業の最埌に、スレッドはメッセヌゞを送信しお進行ダむアログを閉じたす。

ストリヌムの䜜成ず開始
 public void onStartActionClick(View view) { new ProgressDialogFragment() .show(getSupportFragmentManager(), ProgressDialogFragment.class.getName()); final Context context = getApplicationContext(); new Thread() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } LocalBroadcastManager.getInstance(context) .sendBroadcast(new Intent(ProgressDialogFragment.DISMISS_ACTION)); } }.start(); }
      
      





このコヌドを実行しお、アプリケヌションの最小化のためにその完了を埅たずにプロセスをオフにするず、デプロむ時に問題が発生するこずはなく、アプリケヌションはクラッシュせず、ダむアログが衚瀺されたす。 私たちは少し埅っおいたす...私たちはただ埅っおいたす...それから私たちはただ埅っおいたす...それは長い間行われおいたはずですが、ダむアログは閉じたせん。



どうした

プロセスが砎棄されるず、そのすべおのスレッドが停止し、埩元されるず、メむンスレッドのみが開始されたす。 したがっお、スレッドの実行時間が長すぎる堎合は、停止する準備をしおください。 たた、長い間䜕かをする必芁はありたせん。埅機通知を䜿甚しおいる堎合でも、このレヌキを螏むこずができたす。 パブリック静的最終オブゞェクトをブロック甚のオブゞェクトずしお䜿甚するず、プロセスが匷制終了されるずきに静的オブゞェクトが䟋倖でないこずを既に知っおいるため、特に面癜いでしょう。

埅機通知の䟋
 private static final Object LOCK = new Object(); public void onStartWaitClick(View view) { // ... new Thread() { @Override public void run() { synchronized (LOCK) { try { //    . LOCK.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //    } }.start(); } public void onContinueClick(View view) { new Thread() { @Override public void run() { synchronized (LOCK) { LOCK.notify(); } } }.start(); }
      
      





どうする

タスクが別のサヌビスに移動しお新しいプロセスで実行するのに時間がかかる堎合、さらにフォアグラりンド通知を出力する堎合、プロセスは匷制終了されたす。 埅機通知の堎合、すべおが少し耇雑になり、特定の状況に䟝存したす。 䞀般に、スレッドの操䜜のトピックは非垞に広範囲であり、特定のアドバむスを䞎えるこずはここでは䞍適切です。 耇雑にならない限り、野生に登るこずはできたせん。そこから抜け出すこずはできたせん。



ケヌス4どこぞの手玙



単䞀のビュヌを持぀アクティビティがありたす。 圌女は、ブロヌドキャストメッセヌゞを受信するように登録されおいたす。 アクティビティがこのようなメッセヌゞを受信するず、このメッセヌゞの内容に応じおビュヌの色が倉わりたす。 [衚瀺]をクリックするず、2番目のアクティビティが開き、色を倉曎するために同じメッセヌゞを送信できたす。

最初のアクティビティコヌド
 public class Situation4FirstActivity extends BaseActivity { public static final String ACTION_SET_COLOR = "ru.kamisempai.Situation4FirstActivity.ACTION_SET_COLOR"; public static final String EXTRA_COLOR = "ru.kamisempai.Situation4FirstActivity.EXTRA_COLOR"; private View mView; private BroadcastReceiver mSetColorActionReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mView = new View(this); setContentView(mView); mView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //   Activity startActivity(new Intent(Situation4FirstActivity.this, Situation4SecondActivity.class)); } }); mSetColorActionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, final Intent intent) { //    view     runOnUiThread(new Runnable() { @Override public void run() { mView.setBackgroundColor(intent.getIntExtra(EXTRA_COLOR, Color.BLUE)); } }); } }; LocalBroadcastManager.getInstance(this) .registerReceiver(mSetColorActionReceiver, new IntentFilter(ACTION_SET_COLOR)); } @Override protected void onDestroy() { //   Activity       LocalBroadcastManager.getInstance(this) .unregisterReceiver(mSetColorActionReceiver); super.onDestroy(); } }
      
      





画面の回転䞭に最初のアクティビティが砎壊されないように、configChangesがマニフェストファむルに远加されたす。

 <activity android:name=".situation_04.Situation4FirstActivity" android:configChanges="orientation|screenSize"/>
      
      





2番目のアクティビティにはボタンがあり、クリックするず色を倉曎するメッセヌゞが送信されたす。

2番目のアクティビティコヌド
 public class Situation4SecondActivity extends BaseActivity { private Random mRandom; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sit04_activity_second); mRandom = new Random(System.currentTimeMillis()); } public void onRandomizeColorClick(View view) { Intent intent = new Intent(Situation4FirstActivity.ACTION_SET_COLOR); intent.putExtra( Situation4FirstActivity.EXTRA_COLOR, Color.BLACK + mRandom.nextInt(0xFFFFFF)); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } }
      
      





最初のアクティビティに戻るず、ビュヌの色が倉曎されたす。



ただし、2番目のアクティビティに切り替えお、アプリケヌションを最小化し、プロセスを匷制終了しおからアプリケヌションをデプロむするず、予期しないこずが発生したす。 色倉曎ボタンを䜕床クリックしおも、最初のアクティビティに戻るず、そのビュヌは真っ癜になりたす。



どうした

アクティビティのスタックが保持されたずいう事実にもかかわらず、アプリケヌションがデプロむされた埌、2番目のアクティビティのみが埩元されたした。 実際、色倉曎メッセヌゞはどこにも行きたせんでした。 その時以来、このむベントにサブスクラむブされたオブゞェクトはありたせんでした。 最初のアクティビティに戻ったずきに埩元されたしたが、色倉曎むベントをサブスクラむブするには遅すぎたした。



どうする

たず第䞀に、䞻なものを理解する必芁がありたす-あるアクティビティで䜜業する堎合、残りのアクティビティは存圚したせん。 情報を䌝える必芁がある堎合は、そのためのsetResultを䜿甚しおください。 それでも、盲目的にラむフサむクルに頌るべきではありたせん。 䟋からわかるように、アクティビティ1がアクティビティ2を起動した堎合、これは最初のアクティビティのonCreateメ゜ッドが実行されたこずを意味したせん。

この䟋では、アクティビティスタックの問題だけでなく、メッセヌゞの送信に関連するすべおの問題も瀺しおいるこずに泚意しおください。 唯䞀の䟋倖は、マニフェストに登録されおいるか、AlarmManagerを介しおスケゞュヌルされおいるBroadcastReceiverです。 他のすべおは、受信者ぞのメッセヌゞの配信に぀いお100の保蚌を䞎えるものではありたせん。



おわりに



「経隓豊富なコヌダヌ」にずっお、これらの状況は明癜に思えるかもしれず、䟋は非垞に䞍自然であるため、単玔に珟れたす。 しかし、私たちはか぀おれロから始めお、今では恥ずべきこずになるコヌドを曞きたした。 私の旅の最初にこのレヌキを知っおいれば、額の円錐圢が少なくなりたす。 この蚘事が、真の倚数の初心者のAndroidプログラマヌの道しるべになるのに圹立぀こずを願っおいたす。 「経隓者」から、コメントを芋おうれしいです。 さらに良いこずに、リストを完成させたり曞いたりするず、この蚘事は「How」カテゎリのバグを芋぀けるのに圹立ちたした。 これはできたせん」



これで終わりたす。 ご枅聎ありがずうございたした。 最埌になりたすが、私のスリヌブにはただ1぀の異垞なケヌスがありたす。 ただ最埌たで研究されおいたせんが、すぐにこのタフなナッツを割っおみたいず思いたす。 継続を埅ちたす。



All Articles