[解決済み] さらに理解を深める setRetainInstance(true)
質問
何
まさに
を呼び出したらどうなるのでしょうか?
setRetainInstance(true)
を呼び出すと
Fragment
? ドキュメントはほとんど存在せず、これは非常に重要な機能のように思われます。具体的には、私はこのシーケンス(私が作ったもの)がどれくらい真であるかを知りたいのです。
- ユーザーがデバイスを回転させる。
- フラグメントが切り離され
Activity
とFragment.onDetach()
が呼び出されます。- アクティビティは破棄される。
Activity.onDestroy()
が呼び出されます。- は
Activity
javaオブジェクトは(可能であればGCによって)削除されます。- 新しい
Activity
java オブジェクトが作成され、そのコンストラクタ、およびonCreate()
が呼び出されます。- で
Activity.onCreate()
のどちらかです。setContentView(...)
で、フラグメントを含むレイアウトを設定するか、あるいはFragmentTransaction
を使用してフラグメントを追加します。- 本当に自信がないのですが、androidは賢いので古いフラグメントを見つけて
Fragment.onAttach()
を呼び出して を呼び出して、それを新しいActivity
- 次に(あるいはその前に?)
Activity.onResume()
が呼び出されます。
というのは正しいのでしょうか?Androidは賢いので、たとえ私が明示的に
FragmentTransaction.add(new MyFragment(), ...)
を使ったとしても、Android は古いフラグメントを見つけることができるのでしょうか?もしそうなら、どうすれば
別の
フラグメントを
onCreate()
? このようにする必要があるのでしょうか?
if (getSupportFragmentManager().findFragmentByTag("foo") == null)
{
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(new FooFragment(), "foo").commit();
}
どのように解決するのですか?
Android のドキュメントには有用な情報が含まれていますが、悲しいことに、そのどれもが
setRetainInstance()
. から
フラグメントについてのページ
注意: 各フラグメントは、アクティビティが再開されたときにシステムがフラグメントを復元するために使用できる一意の識別子を必要とします。 アクティビティが再開されたときに、システムがフラグメントを復元するために使用できる一意の識別子が必要です(そして、この識別子を使用して 削除などのトランザクションを実行するためにフラグメントをキャプチャするために使用することができます。 削除などのトランザクションを実行するためにフラグメントをキャプチャするために使用することができます)。フラグメントの ID を提供するには、3 つの方法があります。
- android:id属性に一意のIDを指定する。
- android:tag属性に一意な文字列を指定します。
- 前の2つのどちらも提供しない場合、システムはコンテナビューのIDを使用します。
このことは、もしあなたが
setContentView(R.layout.whatever)
で
Activity.onCreated()
で、そのレイアウトに含まれるフラグメントは
setRetainInstance(true)
を含む場合、アクティビティが再作成されるとき、そのIDまたはタグを使用して再び検索されます。
次に、UIレスフラグメントについては、以下のように記述されています。
UIを持たないフラグメントを追加するには、アクティビティからフラグメントを追加します。 add(Fragment, String) を使用して追加します(フラグメントに一意の文字列 "tag" を指定します)。 を指定します)。これにより、フラグメントが追加されますが アクティビティ・レイアウトのビューと関連付けられていないため、フラグメントは onCreateView()への呼び出しを受け取りません。したがって、その メソッドを実装する必要はありません。
そしてdocsは非常に良い例へのリンクを張っています -。
FragmentRetainInstance.java
へのリンクがあり、私はあなたの便宜のためにそれを以下に再現しました。これはまさに私が質問で答えだと推測したことを行っています (
if (...findFragmentByTag() == null) { ...
).
最後に、どのような関数が呼び出されるかを正確に確認するために、自分自身のテストアクティビティを作成しました。これは、縦向きで開始して横向きに回転させたときに出力されます。コードは以下の通りです。
(読みやすくするために少し編集してあります)
TestActivity@415a4a30: this()
TestActivity@415a4a30: onCreate()
TestActivity@415a4a30: Existing fragment not found.
TestFragment{41583008}: this() TestFragment{41583008}
TestFragment{41583008}: onAttach(TestActivity@415a4a30)
TestFragment{41583008}: onCreate()
TestFragment{41583008}: onCreateView()
TestFragment{41583008}: onActivityCreated()
TestActivity@415a4a30: onStart()
TestFragment{41583008}: onStart()
TestActivity@415a4a30: onResume()
TestFragment{41583008}: onResume()
<rotate device>
TestFragment{41583008}: onPause()
TestActivity@415a4a30: onPause()
TestFragment{41583008}: onStop()
TestActivity@415a4a30: onStop()
TestFragment{41583008}: onDestroyView()
TestFragment{41583008}: onDetach()
TestActivity@415a4a30: onDestroy()
TestActivity@415a3380: this()
TestFragment{41583008}: onAttach(TestActivity@415a3380)
TestActivity@415a3380: onCreate()
TestActivity@415a3380: Existing fragment found.
TestFragment{41583008}: onCreateView()
TestFragment{41583008}: onActivityCreated()
TestActivity@415a3380: onStart()
TestFragment{41583008}: onStart()
TestActivity@415a3380: onResume()
TestFragment{41583008}: onResume()
Android のドキュメントが誤っていることに注意してください:UI のないフラグメントである
は
への呼び出しを受け取ります。
onCreateView()
を返すのは自由ですが
null
.
のソースコード
TestActivity
/
TestFragment
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.concentriclivers.ss.R;
// An activity for understanding Android lifecycle events.
public class TestActivity extends Activity
{
private static final String TAG = TestActivity.class.getSimpleName();
public TestActivity()
{
super();
Log.d(TAG, this + ": this()");
}
protected void finalize() throws Throwable
{
super.finalize();
Log.d(TAG, this + ": finalize()");
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d(TAG, this + ": onCreate()");
TextView tv = new TextView(this);
tv.setText("Hello world");
setContentView(tv);
if (getFragmentManager().findFragmentByTag("test_fragment") == null)
{
Log.d(TAG, this + ": Existing fragment not found.");
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(new TestFragment(), "test_fragment").commit();
}
else
{
Log.d(TAG, this + ": Existing fragment found.");
}
}
@Override
public void onStart()
{
super.onStart();
Log.d(TAG, this + ": onStart()");
}
@Override
public void onResume()
{
super.onResume();
Log.d(TAG, this + ": onResume()");
}
@Override
public void onPause()
{
super.onPause();
Log.d(TAG, this + ": onPause()");
}
@Override
public void onStop()
{
super.onStop();
Log.d(TAG, this + ": onStop()");
}
@Override
public void onDestroy()
{
super.onDestroy();
Log.d(TAG, this + ": onDestroy()");
}
public static class TestFragment extends Fragment
{
private static final String TAG = TestFragment.class.getSimpleName();
public TestFragment()
{
super();
Log.d(TAG, this + ": this() " + this);
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d(TAG, this + ": onCreate()");
setRetainInstance(true);
}
@Override
public void onAttach(final Activity activity)
{
super.onAttach(activity);
Log.d(TAG, this + ": onAttach(" + activity + ")");
}
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
Log.d(TAG, this + ": onActivityCreated()");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.d(TAG, this + ": onCreateView()");
return null;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, this + ": onViewCreated()");
}
@Override
public void onDestroyView()
{
super.onDestroyView();
Log.d(TAG, this + ": onDestroyView()");
}
@Override
public void onDetach()
{
super.onDetach();
Log.d(TAG, this + ": onDetach()");
}
@Override
public void onStart()
{
super.onStart();
Log.d(TAG, this + ": onStart()");
}
@Override
public void onResume()
{
super.onResume();
Log.d(TAG, this + ": onResume()");
}
@Override
public void onPause()
{
super.onPause();
Log.d(TAG, this + ": onPause()");
}
@Override
public void onStop()
{
super.onStop();
Log.d(TAG, this + ": onStop()");
}
@Override
public void onDestroy()
{
super.onDestroy();
Log.d(TAG, this + ": onDestroy()");
}
}
}
のソースコード
FragmentRetainInstance.java
のソースコード(API 16時点)です。
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.app;
import com.example.android.apis.R;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
/**
* This example shows how you can use a Fragment to easily propagate state
* (such as threads) across activity instances when an activity needs to be
* restarted due to, for example, a configuration change. This is a lot
* easier than using the raw Activity.onRetainNonConfiguratinInstance() API.
*/
public class FragmentRetainInstance extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// First time init, create the UI.
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(android.R.id.content,
new UiFragment()).commit();
}
}
/**
* This is a fragment showing UI that will be updated from work done
* in the retained fragment.
*/
public static class UiFragment extends Fragment {
RetainedFragment mWorkFragment;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);
// Watch for button clicks.
Button button = (Button)v.findViewById(R.id.restart);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mWorkFragment.restart();
}
});
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Check to see if we have retained the worker fragment.
mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");
// If not retained (or first time running), we need to create it.
if (mWorkFragment == null) {
mWorkFragment = new RetainedFragment();
// Tell it who it is working with.
mWorkFragment.setTargetFragment(this, 0);
fm.beginTransaction().add(mWorkFragment, "work").commit();
}
}
}
/**
* This is the Fragment implementation that will be retained across
* activity instances. It represents some ongoing work, here a thread
* we have that sits around incrementing a progress indicator.
*/
public static class RetainedFragment extends Fragment {
ProgressBar mProgressBar;
int mPosition;
boolean mReady = false;
boolean mQuiting = false;
/**
* This is the thread that will do our work. It sits in a loop running
* the progress up until it has reached the top, then stops and waits.
*/
final Thread mThread = new Thread() {
@Override
public void run() {
// We'll figure the real value out later.
int max = 10000;
// This thread runs almost forever.
while (true) {
// Update our shared state with the UI.
synchronized (this) {
// Our thread is stopped if the UI is not ready
// or it has completed its work.
while (!mReady || mPosition >= max) {
if (mQuiting) {
return;
}
try {
wait();
} catch (InterruptedException e) {
}
}
// Now update the progress. Note it is important that
// we touch the progress bar with the lock held, so it
// doesn't disappear on us.
mPosition++;
max = mProgressBar.getMax();
mProgressBar.setProgress(mPosition);
}
// Normally we would be doing some work, but put a kludge
// here to pretend like we are.
synchronized (this) {
try {
wait(50);
} catch (InterruptedException e) {
}
}
}
}
};
/**
* Fragment initialization. We way we want to be retained and
* start our thread.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Tell the framework to try to keep this fragment around
// during a configuration change.
setRetainInstance(true);
// Start up the worker thread.
mThread.start();
}
/**
* This is called when the Fragment's Activity is ready to go, after
* its content view has been installed; it is called both after
* the initial fragment creation and after the fragment is re-attached
* to a new activity.
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Retrieve the progress bar from the target's view hierarchy.
mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
R.id.progress_horizontal);
// We are ready for our thread to go.
synchronized (mThread) {
mReady = true;
mThread.notify();
}
}
/**
* This is called when the fragment is going away. It is NOT called
* when the fragment is being propagated between activity instances.
*/
@Override
public void onDestroy() {
// Make the thread go away.
synchronized (mThread) {
mReady = false;
mQuiting = true;
mThread.notify();
}
super.onDestroy();
}
/**
* This is called right before the fragment is detached from its
* current activity instance.
*/
@Override
public void onDetach() {
// This fragment is being detached from its activity. We need
// to make sure its thread is not going to touch any activity
// state after returning from this function.
synchronized (mThread) {
mProgressBar = null;
mReady = false;
mThread.notify();
}
super.onDetach();
}
/**
* API for our UI to restart the progress thread.
*/
public void restart() {
synchronized (mThread) {
mPosition = 0;
mThread.notify();
}
}
}
}
関連
-
[解決済み] FragmentのsetRetainInstance(boolean)を理解する。
-
[解決済み] ArrayAdapter<myClass> の使用方法
-
[解決済み] プログラムでソフトキーボードを開く
-
[解決済み] getApplication()、getApplicationContext()、getBaseContext()、someClass.thisの違いと使い分け。
-
[解決済み] これはどういうことですか?失敗 [INSTALL_FAILED_CONTAINER_ERROR]?
-
[解決済み] AsyncTaskを複数回実行する
-
[解決済み] 文字サイズとアンドロイドの画面サイズの違い
-
[解決済み] APKが署名済みかデバッグビルドかを確認するには?
-
[解決済み] アダプタからActivityメソッドを呼び出す
-
[解決済み] BottomNavigationViewを新しいNavControllerで使用する際に、フラグメントを生かす方法はありますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] FragmentのsetRetainInstance(boolean)を理解する。
-
[解決済み] プログラムでソフトキーボードを開く
-
[解決済み] CardView layout_width="match_parent "が親のRecyclerViewの幅と一致しない。
-
[解決済み] Androidのソースコードにある@hideの意味とは?
-
[解決済み] 「KotlinとAndroidで「パラメータTを推測するのに十分な情報がありません。
-
[解決済み] バイト配列の画像ファイルをビットマップに変換するには?
-
[解決済み] Gmailの3分割アニメーションシナリオの完全動作サンプル?
-
[解決済み] TabLayoutに対応したandroidデザインでタブテキストのフォントを変更する
-
[解決済み] Android Studioの「未実装メソッドの追加」機能
-
[解決済み] EditTextの右側のDrawableにonClickListenerを設定する [重複] [重複