2015-06-15 33 views
22

Mam główną funkcję i wewnątrz niej ładuję fragment A. Z FragmentA, wywołuję aktywność w Google EarthPicker używając startActivityforResult w następujący sposób.Funkcja Android parentActivity nie jest odtwarzana po uruchomieniu startActivityForResult.

PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); 
Intent intent = builder.build(getActivity()); 
getActivity().startActivityForResult(intent,PLACE_PICKER_REQUEST); 

Ale kiedy wybrać miejsce, onActivityResult (zarówno w Fragmenta lub główną działalność) nie jest nazywany coraz. W rzeczywistości moja aplikacja ulega zniszczeniu po wywołaniu funkcji startActivityForResult.

Zgodnie z moim rozumowaniem, Android powinien odtworzyć wywołującą aktywność, jeśli nie jest dostępna w pamięci. Ale tak się nie dzieje. Nawet naCreate nie jest wywoływana wewnątrz MainActivity.

Czy ktoś mógłby mi powiedzieć, dlaczego tak się zachowuje, czy też czegoś brakuje?

Teraz zamiast akcji PlacePicker, próbowałem używać innej aktywności w tej samej aplikacji.

Powiedzmy mam MainActivity z FragmentA loaded.I apeluję SubActivity z startActivityForResult z FragmentA .Teraz podczas powrotu z SubActivity, wyjściach aplikacji. Włączyłem Dont keep activities w moim urządzeniu, aby przetestować ten konkretny scenariusz. Widzę, że MainActivity ulega zniszczeniu, gdy przenoszę się do SubActivity. Ale po powrocie z SubActivity, Android nie odtwarza MainActivity (nawet onCreate nie otrzymuje wywołania. Aplikacja właśnie wychodzi).

+0

* "moja aplikacja ulega zniszczeniu po wywołaniu funkcji startActivityForResult." Czy masz na myśli awarię? – codeMagic

+0

Czy w pliku manifestu podano filtr zamiany? Jest to potrzebne, aby twoja aktywność mogła zostać odtworzona. – Mann

+0

@codeMagic Nie ulega awarii. Po wywołaniu funkcji startActivityForResult, widzę metodę onDestory w MainActivity, która jest wywoływana. I kiedy wybieram miejsce z uruchomionej aktywności, zwraca. Ale moja aplikacja nie odtwarza . jeśli wybiorę miejsce wystarczająco szybko (przed moją MainActivity) ulegnie zniszczeniu), onActivityResult jest poprawnie wywoływany. – Renjith

Odpowiedz

7

Wydaje się dość nietypowe, aby system Android wyczyścił działanie w opisany sposób, ale w takim przypadku aktywność powinna zostać przywrócona. Android nie powinien niszczyć działania, chyba że zadzwonisz pod numer finish() lub coś spowoduje, że działanie zakończy się przedwcześnie.

Jeśli odnieść się do diagramu aktywności cyklu życia:

W scenariusza opisanego pierwszą działalność powinna zadzwonić OnStop, ale nie onDestroy, a następnie po powrocie z drugiego aktywności powinien zadzwonić onStart jeszcze raz.

I stworzył bardzo prostą aplikację do testowania scenariusza opisanego, który zawierał następujące elementy:

  • Istnieją 2 czynności, FirstActivity i SecondActivity
  • FirstActivity posiada przycisk, po kliknięciu przycisku go rozpoczyna SecondActivity z startActivityForResult()
  • działalności imprezach cyklu życia są rejestrowane za pomocą ActivityLifecycleCallbacks w klasie niestandardowej aplikacji
  • w FirstActivity onActivityResult dodatkowo wyjść do l og gdy zostanie wywołany

Oto, co jest wyświetlane:

Aplikacja jest uruchamiana (FirstActivity jest stworzony i uruchomiony i widoczny):

FirstActivity onCreate 
FirstActivity onStart 
FirstActivity onResume 

naciśnięciu przycisku, aby rozpocząć SecondActivity:

FirstActivity onPause 
SecondActivity onCreate 
SecondActivity onStart 
SecondActivity onResume 
FirstActivity onSaveInstanceState 
FirstActivity onStop 

Uwaga, onDestroy nie zostanie wywołany.

Teraz naciśnij przycisk Wstecz i powrócić do pierwszego działalności:

SecondActivity onPause 
FirstActivity onStart 
FirstActivity onActivityResult 
FirstActivity onResume 
SecondActivity onStop 
SecondActivity onDestroy 

Tył Przycisk wywołuje finish na SecondActivity więc jest zniszczony

Teraz gdybym naciśnij ponownie FirstActivity zostanie także zakończona, powodując wywołanie onDestroy.

FirstActivity onPause 
FirstActivity onStop 
FirstActivity onDestroy 

Możesz zobaczyć, że ten przykład został dokładnie zastosowany do diagramu cyklu życia. Działania są niszczone tylko po naciśnięciu przycisku Wstecz, co powoduje, że aktywność wywołuje finish().

Wspomniał Pan (i), że próbowałeś włączyć opcję "Nie zatrzymuj aktywności" w opcjach dewelopera, możemy powtórzyć powyższy eksperyment z włączoną opcją i zobaczyć, co się stanie. Właśnie dodania odpowiednich zdarzeń cyklu życia, aby zapisać powtarzanie wszystkiego, co powyżej:

Po naciśnięciu przycisku w pierwszej działalności, aby rozpocząć drugą działalność:

... 
SecondActivity onResume 
FirstActivity onSaveInstanceState 
FirstActivity onStop 
FirstActivity onDestroy 

Zgodnie z oczekiwaniami, działalność została zniszczona to czas. To, co dzieje się podczas przejścia z powrotem do pierwszego aktywności znowu:

SecondActivity onPause 
FirstActivity onCreate 
FirstActivity onStart 
FirstActivity onActivityResult 
FirstActivity onResume 
... 

Tym razem onCreate został powołany ponownie, ponieważ system nie miał zatrzymany wersję pierwszej czynności, aby ponownie uruchomić. Nadal wywoływano onActivityResult(), niezależnie od tego, czy działanie musiało zostać odtworzone.

To dodatkowo potwierdza, że ​​coś w pierwszej aktywności musi wywoływać finish() lub powodować awarię. Jednak bez zobaczenia twojego prawdziwego kodu jest to przypuszczenie.

Wreszcie, aby utrzymać stan, czy działalność ma z jakiegoś powodu potrzebujesz, aby odtworzyć, można zastąpić onSaveInstanceState() i dodawać żadnych informacji o stanie do wiązki:

protected void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    outState.putString(MY_STRING_KEY, "my string value"); 
} 

Kiedy działalność jest odtworzony, będziesz dostać pakiet z powrotem w onCreate która powinna zawierać wszystko, co zapisano:

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    ... 
    if (savedInstanceState != null) { 
     // Restore previous state 
    } 
} 
+0

Dokładnie testuję ten sam przypadek odtworzenia działania (zobacz moją edycję). Problem jest nawet na MetodaCreate nie jest wywoływana podczas zwracania. – Renjith

+2

Dlaczego jest to zaakceptowana odpowiedź? Nie odpowiada na pytanie imo. –

+0

Co powiesz na wywołanie funkcji onDestroy podczas wywoływania? –

18

może się to zdarzyć z różnych powodów, ale mam nadzieję, że jest to rzadkie zjawisko. System operacyjny zniszczy działanie po uruchomieniu w tle, jeśli potrzebuje odzyskać zasoby, co jest bardziej prawdopodobne w przypadku urządzeń o mniejszej pamięci i mocy obliczeniowej.

Ustawienie to jest dobrym sposobem na przetestowanie tego scenariusza, aw takim przypadku występują inne problemy, nawet jeśli odtwarzanie aktywności/fragmentu zostanie ponownie utworzone. Po włączeniu tego ustawienia Aktywność i Fragmenty zostają zniszczone po wyświetleniu PlacePicker, a gdy pojawia się onActivityResult(), nie ma prawidłowego Kontekstu, ponieważ Aktywność i Fragment są wciąż w trakcie ponownego tworzenia.

Znalazłem się przeprowadzając kontrolowane badanie przy ustawieniu wyłączone, a następnie włączone ustawienie, a następnie patrząc na wyniki

kładę logujących się w każdym zwrotnego cyklu życia na aktywny i fragment w celu aby zorientować się, co się dzieje podczas tych połączeń.

Oto pełna klasa użyłem że obejmuje zarówno aktywność i fragment:

public class MainActivity extends AppCompatActivity { 

    MyFragment myFrag; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     Log.d("PlacePickerTest", "Activity onCreate"); 

     myFrag = new MyFragment(); 

     setContentView(R.layout.activity_main); 
     if (savedInstanceState == null) { 
      getSupportFragmentManager().beginTransaction() 
        .add(R.id.container, myFrag) 
        .commit(); 
     } 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 

     Log.d("PlacePickerTest", "Activity onResume"); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 

     Log.d("PlacePickerTest", "Activity onPause"); 
    } 

    @Override 
    protected void onDestroy() { 
     Log.d("PlacePickerTest", "Activity onDestroy"); 
     super.onDestroy(); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     getMenuInflater().inflate(R.menu.menu_main, menu); 
     return true; 
    } 

    @Override 
    public void onActivityResult (int requestCode, int resultCode, Intent data) { 
     super.onActivityResult(requestCode, resultCode, data); 

     Log.d("PlacePickerTest", "Activity onActivityResult requestCode:" + requestCode); 

     if (requestCode == 199){ 
      //process result of PlacePicker in the Fragment 
      myFrag.processActivityResult(data); 
     } 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     int id = item.getItemId(); 
     if (id == R.id.action_settings) { 
      //open PlacePicker from menu item 
      myFrag.startPlacePicker(); 
      return true; 
     } 

     return super.onOptionsItemSelected(item); 
    } 

    /** 
    * Fragment containing a map and PlacePicker functionality 
    */ 
    public static class MyFragment extends Fragment { 

     private GoogleMap mMap; 
     Marker marker; 

     public MyFragment() { 
     } 

     @Override 
     public View onCreateView(LayoutInflater inflater, ViewGroup container, 
           Bundle savedInstanceState) { 
      View rootView = inflater.inflate(R.layout.fragment_main, container, false); 

      Log.d("PlacePickerTest", "Fragment onCreateView"); 

      return rootView; 
     } 


     @Override 
     public void onResume() { 
      super.onResume(); 

      Log.d("PlacePickerTest", "Fragment onResume"); 

      setUpMapIfNeeded(); 
     } 

     @Override 
      public void onPause() { 
      super.onPause(); 

      Log.d("PlacePickerTest", "Fragment onPause"); 
     } 

     @Override 
     public void onDestroy() { 
      Log.d("PlacePickerTest", "Fragment onDestroy"); 
      super.onDestroy(); 
     } 

     private void setUpMapIfNeeded() { 
      // Do a null check to confirm that we have not already instantiated the map. 
      if (mMap == null) { 
       // Try to obtain the map from the SupportMapFragment. 
       mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)) 
         .getMap(); 
       // Check if we were successful in obtaining the map. 
       if (mMap != null) { 
        setUpMap(); 
       } 
      } 
     } 

     private void setUpMap() { 

      // Enable MyLocation Layer of Google Map 
      mMap.setMyLocationEnabled(true); 
      mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); 
      mMap.getUiSettings().setZoomControlsEnabled(true); 
      mMap.getUiSettings().setMyLocationButtonEnabled(true); 
      mMap.getUiSettings().setCompassEnabled(true); 
      mMap.getUiSettings().setRotateGesturesEnabled(true); 
      mMap.getUiSettings().setZoomGesturesEnabled(true); 

     } 

     public void startPlacePicker(){ 
      int PLACE_PICKER_REQUEST = 199; 
      PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); 
      //Context context = getActivity(); 
      try { 
       Log.d("PlacePickerTest", "Fragment startActivityForResult"); 
       getActivity().startActivityForResult(builder.build(getActivity()), PLACE_PICKER_REQUEST); 
      } catch (GooglePlayServicesRepairableException e) { 
       e.printStackTrace(); 
      } catch (GooglePlayServicesNotAvailableException e) { 
       e.printStackTrace(); 
      } 
     } 

     public void processActivityResult (Intent data) { 

      if (getActivity() == null) return; 

      Log.d("PlacePickerTest", "Fragment processActivityResult"); 


      //process Intent...... 
      Place place = PlacePicker.getPlace(data, getActivity()); 
      String placeName = String.format("Place: %s", place.getName()); 
      String placeAddress = String.format("Address: %s", place.getAddress()); 

      LatLng toLatLng = place.getLatLng(); 

      // Show the place location in Google Map 
      mMap.moveCamera(CameraUpdateFactory.newLatLng(toLatLng)); 
      mMap.animateCamera(CameraUpdateFactory.zoomTo(15)); 

      if (marker != null) { 
       marker.remove(); 
      } 
      marker = mMap.addMarker(new MarkerOptions().position(toLatLng) 
        .title(placeName).snippet(placeAddress) 
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA))); 

     } 
    } 
} 

Oto wynikające dzienniki, które dają wgląd w to, co cyklu callbacks nazywane są w trakcie procesu w normalnych okolicznościach:

D/PlacePickerTest﹕ Activity onCreate 
D/PlacePickerTest﹕ Fragment onCreateView 
D/PlacePickerTest﹕ Activity onResume 
D/PlacePickerTest﹕ Fragment onResume 
D/PlacePickerTest﹕ Fragment startActivityForResult 
D/PlacePickerTest﹕ Fragment onPause 
D/PlacePickerTest﹕ Activity onPause 
D/PlacePickerTest﹕ Activity onActivityResult requestCode:199 
D/PlacePickerTest﹕ Fragment processActivityResult 
D/PlacePickerTest﹕ Activity onResume 
D/PlacePickerTest﹕ Fragment onResume 

Więc, jak widać onDestroy() nigdy nie jest wywoływana i onPause() i onResume() nazywane są zarówno na aktywny i fragment.

Oto wynik wizualnie:

PlacePicker

Następnie po zebraniu miejsce:

place shown on map

Potem włączona Do not keep Activities pod Opcje programistyczne w ustawieniach, a prowadził ten sam test .

Są wynikające kłody:

D/PlacePickerTest﹕ Activity onCreate 
D/PlacePickerTest﹕ Fragment onCreateView 
D/PlacePickerTest﹕ Activity onResume 
D/PlacePickerTest﹕ Fragment onResume 
D/PlacePickerTest﹕ Fragment startActivityForResult 
D/PlacePickerTest﹕ Fragment onPause 
D/PlacePickerTest﹕ Activity onPause 
D/PlacePickerTest﹕ Activity onDestroy 
D/PlacePickerTest﹕ Fragment onDestroy 
D/PlacePickerTest﹕ Activity onCreate 
D/PlacePickerTest﹕ Fragment onCreateView 
D/PlacePickerTest﹕ Activity onActivityResult requestCode:199 
D/PlacePickerTest﹕ Activity onResume 
D/PlacePickerTest﹕ Fragment onResume 

Tak, można zobaczyć zarówno aktywny i Fragment są niszczone, gdy PlacePicker jest widoczny, a po tym miejscem jest odbierany w PlacePicker, nigdy wykonanie kodu dostał do wpisu dziennika Fragment processActivityResult, a aplikacja nigdy nie pokazała wybranego miejsca na mapie.

To właśnie z powodu zerowej czeku kontekstu:

if (getActivity() == null) return; 

Log.d("PlacePickerTest", "Fragment processActivityResult"); 

//process Intent...... 
Place place = PlacePicker.getPlace(data, getActivity()); 

Więc wywołanie onActivityResult() ma przyjść, ale robi to w tym samym czasie, że aktywność i Fragment trafiają ponownie utworzone, a potrzebujesz poprawnego kontekstu, aby zadzwonić pod numer PlacePicker.getPlace(data, getActivity());.

Dobrą wiadomością jest to, że większość użytkowników końcowych nie ma włączonej opcji Do not keep Activities i przez większość czasu działanie nie zostanie zniszczone przez system operacyjny.

+0

Może się to zdarzyć, jeśli system operacyjny usunie działanie wywołujące funkcję startActivityForResult. –

+0

@JawadLeWywadi Masz rację, czytając to ponownie Zdałem sobie sprawę, że ta odpowiedź miała pewne problemy. Po prostu edytowane, myślę, że teraz jest lepiej. Dzięki! –

+0

powinno to być poprawną odpowiedzią, zwłaszcza na wskazówkę dotyczącą ustawień "Nie utrzymuj aktywności przy życiu" ... – Opiatefuchs

Powiązane problemy