1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.example.android.supportv4.app; 18 19import com.example.android.supportv4.R; 20 21import android.support.v4.app.Fragment; 22import android.support.v4.app.FragmentActivity; 23import android.support.v4.app.FragmentManager; 24 25import android.os.Bundle; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.view.ViewGroup; 29import android.view.View.OnClickListener; 30import android.widget.Button; 31import android.widget.ProgressBar; 32 33/** 34 * This example shows how you can use a Fragment to easily propagate state 35 * (such as threads) across activity instances when an activity needs to be 36 * restarted due to, for example, a configuration change. This is a lot 37 * easier than using the raw Activity.onRetainNonConfiguratinInstance() API. 38 */ 39public class FragmentRetainInstanceSupport extends FragmentActivity { 40 @Override 41 protected void onCreate(Bundle savedInstanceState) { 42 super.onCreate(savedInstanceState); 43 44 // First time init, create the UI. 45 if (savedInstanceState == null) { 46 getSupportFragmentManager().beginTransaction().add(android.R.id.content, 47 new UiFragment()).commit(); 48 } 49 } 50 51 /** 52 * This is a fragment showing UI that will be updated from work done 53 * in the retained fragment. 54 */ 55 public static class UiFragment extends Fragment { 56 RetainedFragment mWorkFragment; 57 58 @Override 59 public View onCreateView(LayoutInflater inflater, ViewGroup container, 60 Bundle savedInstanceState) { 61 View v = inflater.inflate(R.layout.fragment_retain_instance, container, false); 62 63 // Watch for button clicks. 64 Button button = (Button)v.findViewById(R.id.restart); 65 button.setOnClickListener(new OnClickListener() { 66 @Override 67 public void onClick(View v) { 68 mWorkFragment.restart(); 69 } 70 }); 71 72 return v; 73 } 74 75 @Override 76 public void onActivityCreated(Bundle savedInstanceState) { 77 super.onActivityCreated(savedInstanceState); 78 79 FragmentManager fm = getFragmentManager(); 80 81 // Check to see if we have retained the worker fragment. 82 mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work"); 83 84 // If not retained (or first time running), we need to create it. 85 if (mWorkFragment == null) { 86 mWorkFragment = new RetainedFragment(); 87 // Tell it who it is working with. 88 mWorkFragment.setTargetFragment(this, 0); 89 fm.beginTransaction().add(mWorkFragment, "work").commit(); 90 } 91 } 92 93 } 94 95 /** 96 * This is the Fragment implementation that will be retained across 97 * activity instances. It represents some ongoing work, here a thread 98 * we have that sits around incrementing a progress indicator. 99 */ 100 public static class RetainedFragment extends Fragment { 101 ProgressBar mProgressBar; 102 int mPosition; 103 boolean mReady = false; 104 boolean mQuiting = false; 105 106 /** 107 * This is the thread that will do our work. It sits in a loop running 108 * the progress up until it has reached the top, then stops and waits. 109 */ 110 final Thread mThread = new Thread() { 111 @Override 112 public void run() { 113 // We'll figure the real value out later. 114 int max = 10000; 115 116 // This thread runs almost forever. 117 while (true) { 118 119 // Update our shared state with the UI. 120 synchronized (this) { 121 // Our thread is stopped if the UI is not ready 122 // or it has completed its work. 123 while (!mReady || mPosition >= max) { 124 if (mQuiting) { 125 return; 126 } 127 try { 128 wait(); 129 } catch (InterruptedException e) { 130 } 131 } 132 133 // Now update the progress. Note it is important that 134 // we touch the progress bar with the lock held, so it 135 // doesn't disappear on us. 136 mPosition++; 137 max = mProgressBar.getMax(); 138 mProgressBar.setProgress(mPosition); 139 } 140 141 // Normally we would be doing some work, but put a kludge 142 // here to pretend like we are. 143 synchronized (this) { 144 try { 145 wait(50); 146 } catch (InterruptedException e) { 147 } 148 } 149 } 150 } 151 }; 152 153 /** 154 * Fragment initialization. We way we want to be retained and 155 * start our thread. 156 */ 157 @Override 158 public void onCreate(Bundle savedInstanceState) { 159 super.onCreate(savedInstanceState); 160 161 // Tell the framework to try to keep this fragment around 162 // during a configuration change. 163 setRetainInstance(true); 164 165 // Start up the worker thread. 166 mThread.start(); 167 } 168 169 /** 170 * This is called when the Fragment's Activity is ready to go, after 171 * its content view has been installed; it is called both after 172 * the initial fragment creation and after the fragment is re-attached 173 * to a new activity. 174 */ 175 @Override 176 public void onActivityCreated(Bundle savedInstanceState) { 177 super.onActivityCreated(savedInstanceState); 178 179 // Retrieve the progress bar from the target's view hierarchy. 180 mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById( 181 R.id.progress_horizontal); 182 183 // We are ready for our thread to go. 184 synchronized (mThread) { 185 mReady = true; 186 mThread.notify(); 187 } 188 } 189 190 /** 191 * This is called when the fragment is going away. It is NOT called 192 * when the fragment is being propagated between activity instances. 193 */ 194 @Override 195 public void onDestroy() { 196 // Make the thread go away. 197 synchronized (mThread) { 198 mReady = false; 199 mQuiting = true; 200 mThread.notify(); 201 } 202 203 super.onDestroy(); 204 } 205 206 /** 207 * This is called right before the fragment is detached from its 208 * current activity instance. 209 */ 210 @Override 211 public void onDetach() { 212 // This fragment is being detached from its activity. We need 213 // to make sure its thread is not going to touch any activity 214 // state after returning from this function. 215 synchronized (mThread) { 216 mProgressBar = null; 217 mReady = false; 218 mThread.notify(); 219 } 220 221 super.onDetach(); 222 } 223 224 /** 225 * API for our UI to restart the progress thread. 226 */ 227 public void restart() { 228 synchronized (mThread) { 229 mPosition = 0; 230 mThread.notify(); 231 } 232 } 233 } 234} 235