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 17 package com.example.android.supportv4.app; 18 19 import com.example.android.supportv4.R; 20 21 import android.support.v4.app.Fragment; 22 import android.support.v4.app.FragmentActivity; 23 import android.support.v4.app.FragmentManager; 24 25 import android.os.Bundle; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.View.OnClickListener; 30 import android.widget.Button; 31 import 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 */ 39 public 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 public void onClick(View v) { 67 mWorkFragment.restart(); 68 } 69 }); 70 71 return v; 72 } 73 74 @Override 75 public void onActivityCreated(Bundle savedInstanceState) { 76 super.onActivityCreated(savedInstanceState); 77 78 FragmentManager fm = getFragmentManager(); 79 80 // Check to see if we have retained the worker fragment. 81 mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work"); 82 83 // If not retained (or first time running), we need to create it. 84 if (mWorkFragment == null) { 85 mWorkFragment = new RetainedFragment(); 86 // Tell it who it is working with. 87 mWorkFragment.setTargetFragment(this, 0); 88 fm.beginTransaction().add(mWorkFragment, "work").commit(); 89 } 90 } 91 92 } 93 94 /** 95 * This is the Fragment implementation that will be retained across 96 * activity instances. It represents some ongoing work, here a thread 97 * we have that sits around incrementing a progress indicator. 98 */ 99 public static class RetainedFragment extends Fragment { 100 ProgressBar mProgressBar; 101 int mPosition; 102 boolean mReady = false; 103 boolean mQuiting = false; 104 105 /** 106 * This is the thread that will do our work. It sits in a loop running 107 * the progress up until it has reached the top, then stops and waits. 108 */ 109 final Thread mThread = new Thread() { 110 @Override 111 public void run() { 112 // We'll figure the real value out later. 113 int max = 10000; 114 115 // This thread runs almost forever. 116 while (true) { 117 118 // Update our shared state with the UI. 119 synchronized (this) { 120 // Our thread is stopped if the UI is not ready 121 // or it has completed its work. 122 while (!mReady || mPosition >= max) { 123 if (mQuiting) { 124 return; 125 } 126 try { 127 wait(); 128 } catch (InterruptedException e) { 129 } 130 } 131 132 // Now update the progress. Note it is important that 133 // we touch the progress bar with the lock held, so it 134 // doesn't disappear on us. 135 mPosition++; 136 max = mProgressBar.getMax(); 137 mProgressBar.setProgress(mPosition); 138 } 139 140 // Normally we would be doing some work, but put a kludge 141 // here to pretend like we are. 142 synchronized (this) { 143 try { 144 wait(50); 145 } catch (InterruptedException e) { 146 } 147 } 148 } 149 } 150 }; 151 152 /** 153 * Fragment initialization. We way we want to be retained and 154 * start our thread. 155 */ 156 @Override 157 public void onCreate(Bundle savedInstanceState) { 158 super.onCreate(savedInstanceState); 159 160 // Tell the framework to try to keep this fragment around 161 // during a configuration change. 162 setRetainInstance(true); 163 164 // Start up the worker thread. 165 mThread.start(); 166 } 167 168 /** 169 * This is called when the Fragment's Activity is ready to go, after 170 * its content view has been installed; it is called both after 171 * the initial fragment creation and after the fragment is re-attached 172 * to a new activity. 173 */ 174 @Override 175 public void onActivityCreated(Bundle savedInstanceState) { 176 super.onActivityCreated(savedInstanceState); 177 178 // Retrieve the progress bar from the target's view hierarchy. 179 mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById( 180 R.id.progress_horizontal); 181 182 // We are ready for our thread to go. 183 synchronized (mThread) { 184 mReady = true; 185 mThread.notify(); 186 } 187 } 188 189 /** 190 * This is called when the fragment is going away. It is NOT called 191 * when the fragment is being propagated between activity instances. 192 */ 193 @Override 194 public void onDestroy() { 195 // Make the thread go away. 196 synchronized (mThread) { 197 mReady = false; 198 mQuiting = true; 199 mThread.notify(); 200 } 201 202 super.onDestroy(); 203 } 204 205 /** 206 * This is called right before the fragment is detached from its 207 * current activity instance. 208 */ 209 @Override 210 public void onDetach() { 211 // This fragment is being detached from its activity. We need 212 // to make sure its thread is not going to touch any activity 213 // state after returning from this function. 214 synchronized (mThread) { 215 mProgressBar = null; 216 mReady = false; 217 mThread.notify(); 218 } 219 220 super.onDetach(); 221 } 222 223 /** 224 * API for our UI to restart the progress thread. 225 */ 226 public void restart() { 227 synchronized (mThread) { 228 mPosition = 0; 229 mThread.notify(); 230 } 231 } 232 } 233 } 234