Android 응용 프로그램 에서 Fragment 구성 요 소 를 사용 하 는 문제 및 해결 방안 요약

10372 단어 AndroidFragment
Fragment 의 주요 의 미 는 Activity 와 연 결 된 라 이 프 사이클 리 셋 을 제공 하 는 것 이다.
Fragment 는 Activity 의 보기 단계 에 View 를 추가 하지 않 아 도 됩 니 다.특정한 모듈 이 Activity 의 생명주기 리 셋 을 받 아야 할 때 Fragment 를 통 해 이 루어 지 는 것 을 고려 할 수 있 습 니 다.
예 를 들 어 DialogFragment 는 show 방법 으로 Dialog(이 키 Window 는 Activity 의 보기 등급 에 있 지 않 습 니 다)를 표시 합 니 다.화면 을 돌 릴 때 DialogFragment 는 onDestroy View 를 이용 하여 dismiss Dialog 를 리 셋 한 다음 Activity 를 재 구축 한 후에 DialogFragment 는 onStart 리 셋 을 이용 하여 Dialog 를 표시 합 니 다.
물론,우 리 는 UI 가 전혀 없 는 Fragment 를 만 들 수 있 습 니 다.예 를 들 어 BackgroundWorker Fragment 는 onResume 에서 Task 를 실행 하고 onPause 에서 Task 를 일시 정지 할 수 있 습 니 다.
프 래 그 먼 트 수명 주기
먼저 기초 지식 을 돌 이 켜 보면 Fragment 의 생명 주기 도 는 다음 과 같다.
2016511113051864.png (317×847)
설명:전체적으로 말 하면 Fragment 와 Activity 의 생명주기 가 유사 하 다.주의해 야 할 것 은 Activity 에 비해 onAttach(),onDetch(),onCreateView()와 onDestroy View()라 는 몇 가지 반전 함수 가 많 습 니 다.하지만 온 레 스타 트()가 빠 졌 다.
Fragment 의 생명 주 기 는 매우 복잡 하 며 다음 과 같은 몇 가지 상황 으로 나 뉜 다.
  • XML 의탭 을 통 해 실례 화 된 경우,첫 번 째 로 받 은 리 셋 은 onInflate
  • 입 니 다.
  • setRetainInstance(true)가 있 으 면 Activity 재 구축 시 Fragment 의 onDestroy 와 Activity 재 구축 후 Fragment 의 onCreate 리 셋 이 호출 되 지 않 습 니 다.(스 택 에 추 가 했 든 안 했 든)
  • 현재 Fragment A 를 표시 하고 Fragment Transaction.replace()를 실행 하면 Fragment A 는 onPause()->onStop()->onDestroy View()->onDestroy()->onDetach()를 실행 하고 Fragment Transaction.replace().addToBackStack()을 실행 하면 Fragment A 는 onPause()->onStop()->onDestroy View()
  • 를 실행 합 니 다.
  • Fragment Transaction.hide()는 onPause()가 아 닌 onHiddenChanged()
  • 를 촉발 합 니 다.
  • Fragment Transaction.detach(),onPause()->onStop()->onDestroy View(),주의:onDestroy()와 onDetach()가 호출 되 지 않 습 니 다
  • FragmentTransaction
  • Fragment 에 대한 작업 은 모두 Fragment Transaction 을 통 해 이 루어 집 니 다.하나의 Fragment Transaction 은 하나 이상 의 작업 을 포함 할 수 있 습 니 다.commt 또는 commt Allowing StateLoss 를 통 해 제출 합 니 다.이 Fragment Transaction 이 스 택 에 가입 하면 스 택 에서 나 올 때 이 Transaction 의 모든 작업 이 취 소 됩 니 다
  • .
  • commt 방법 은 비동기(handler post 에 해당 하 는 message 에서 MainLooper 와 연 결 된 Message quue)입 니 다.즉시 Transaction 작업 을 수행 하려 면 execute PendingTransactions()
  • 를 호출 할 수 있 습 니 다.
  • Fragment Transaction 의 commt 방법 과 Fragment Manager 의 popBackStack 방법 은 모두 비동기 적 이어서 호출 자 에 게 많은 불편 을 가 져 왔 습 니 다.execute PendingTransactions()방법 을 호출 하여 즉시 실행 할 수 있 지만 왜 기본적으로 비동기 적 입 니까?(저 는 Transaction 을 제출 하면 Fragment 의 생명주기 방법 이 실 행 될 수 있 고 심지어 여러 개의 리 셋 이 실 행 될 수 있 기 때 문 이 라 고 생각 합 니 다.만약 에 Fragment 가 이 리 셋 에서 새로운 Transaction 을 제출 하면 현재 Transaction 의 상 태 를 파괴 할 수 있 습 니 다.예 를 들 어 이것 은 pop 작업 입 니 다)
  • Can not perform this action after onSaveInstanceState
    Fragment 를 사용 하 는 과정 에서 Activity 의 onSave InstanceState 방법 이 호출 된 후에 commt 나 popBackStack 을 조작 하여 발생 하 는 crash 를 자주 만 날 수 있 습 니 다.
    onSave InstanceState 방법 이후 의 작업 상 태 를 잃 어 버 릴 수 있 기 때문에 Android framework 는 기본적으로 이상 을 던 집 니 다.
    commt 방법 에 있어 서 이 이상 을 피 하 는 것 은 간단 합 니 다.commt Allowing StateLoss 방법 을 사용 하면 됩 니 다.그러나 popBackStack 과 popBackStack Immediate 도 state(checkStateLoss)를 검사 합 니 다.특히 주의해 야 할 것 은 Activity 의 onBackPressed 방법 입 니 다.
    
    public void onBackPressed() {
      if (!mFragments.popBackStackImmediate()) {//  
        supportFinishAfterTransition();
      }
    }
    
    onBackPressed 가 onSaved InstanceState 이후 에 호출 되면 crash 입 니 다.
    onBackPressed 호출 시기:
    *targetSdkVersion<=5,onKeyDown 에서 호출
    *targetSdkVersion>5,onKeyUp 에서 호출
    onSaved InstanceState 호출 시기(호출 되면):
    *반드시 온 스 톱 전에
    *onPause 이전 일 수도 있 고,onPause 와 onStop 사이 일 수도 있 습 니 다.
    주의해 야 할 것 은:onSaved InstanceState 방법 이 반드시 호출 되 는 것 은 아 닙 니 다.Activity 가 특정한 이유 로 Framework 에 의 해 소각 되 고 그 후에 다시 만들어 야 하 는 경우 에 만 호출 이 필요 합 니 다.(예 를 들 어 회전 화면 이나 메모리 가 부족 해서 스 택 에 있 는 일부 Activity 를 회수 합 니 다)
    예:
    *Activity A 가 프론트 에 있 을 때 화면 이 잠 길 때 까지 어 두 워 집 니 다.그러면 A 의 onSaved InstanceState 가 호출 됩 니 다.
    *Activity A start Activity B,Activity A 의 onSaved InstanceState 가 호출 됨
    *Activity A 는 리 턴 키 나 finish 호출 로 이전 화면 으로 되 돌아 갑 니 다.그러면 A 의 onSaved InstanceState 는 호출 되 지 않 습 니 다.
    따라서 onBackPressed 가 onSaved InstanceState 방법 이후 에 호출 되면 반드시 crash 가 발생 합 니 다.해결 방법 은 주로 두 가지 가 있 습 니 다.
    Activity 의 onSaved InstanceState()방법 을 다시 쓰 고 슈퍼 호출 을 설명 합 니 다.
    이러한 방법 은 crash 를 피 할 수 있 지만 전체 Activity 의 상 태 를 잃 어 버 릴 수 있 습 니 다.DialogFragment 의 경우 정상 적 인 상황 에서 표 시 된 DialogFragment 는 회전 화면 Activity 를 다시 만 든 후에 우리 가 처리 할 필요 가 없습니다.Dialog 는 자동 으로 표 시 됩 니 다(DialogFragment.onStart(참조).그러나 Activity 의 onSaved InstanceState()방법 을 설명 한 후에 Fragment 상 태 를 잃 어 버 립 니 다.Activity 가 다시 만 들 면 Dialog 도 다시 표시 되 지 않 습 니 다.
    더 좋 고 일반적인 방법:commt,popBackStack,onBackPressed 방법 을 호출 하기 전에 onSaved InstanceState()방법 이 실행 되 었 는 지 판단 하고 onResume 방법 이 실행 되 지 않 았 습 니 다.그렇지 않 으 면 직접 조작 합 니 다.그렇지 않 으 면 pending 대기 열 에 가입 하여 onResume Fragments 나 onPost Resume 을 기다 린 후에 실행 합 니 다.
    메모:onResume 에서 동작 하지 마 십시오.이 때 Fragment Manager 의 mStateSaved 는 여전히 true 일 수 있 기 때 문 입 니 다.(실행 순서 가 onSaved InstanceState()->onPause()->onResume()또는 onPause()->onSaved InstanceState()->onResume())))
    예 를 들 면:
    
    public void onDataReceived() {
      if(isStateSaved()) {//isStateSaved() BaseActivity  
        addPendingFragmentOperation(new Runnable() {
          @Override
          public void run() {
            getSupportFragmentManager().popBackStackImmediate();
          }
        });
      } else {
        getSupportFragmentManager().popBackStackImmediate();
      }
    }
    
    @Override
    protected void onPostResume() {
      super.onPostResume();
      if(pendingFragmentOperation != null && !pendingFragmentOperation.isEmpty()) {
        for(Runnable operation : pendingFragmentOperation) {
          operation.run();
        }
        pendingFragmentOperation.clear();
      }
    }
    
    
    startActivityForResult
    requestCode 의 사용 가능 한 구간:
    1.Activity: [Integer.MIN_VALUE, Integer.MAX_VALUE]
    (1)requestCode 의 값 이[Integer.MIN 에 있 을 때VALUE,-1]구간 에서 효 과 는 startActivity()와 마찬가지 로 onActivity Result()리 셋 을 받 지 않 습 니 다.
    (2)내 장 된 Fragment 는 requestCode 를 사용 할 수 있 는 구간 과 Activity 가 같 습 니 다.
    2.support 라 이브 러 리:Fragment,그리고 Fragment Activity:[-1,65535]
    (1)requestCode==-1,효 과 는 startActivity()와 마찬가지 로 onActivity Result()리 셋 을 받 지 못 합 니 다.
    (2)requestCode 재[Integer.MINVALUE,-2]또는[65536,Integer.MAXVALUE]사이 에 이상 이 발생 합 니 다(requestCode 는 16 비트 이하 만 사용 가능)
    권장:requestCode 의 수 치 는[-1,65535]사이 로 통일 적 으로 제한 합 니 다.
    모자이크 프 래 그 먼 트
    우선 내장 Fragment 를 사용 하지 말 라 는 것 이다.
    포 함 된 Fragment 에서 startActivity ForResult()를 사용 할 때 발생 하 는 문제:
    모든 Fragment 에서 onActivity Result()를 받 을 수 없습니다.
    어떤 level 1 의 Fragment 가 onActivity Result()를 받 았 습 니 다.
    아무튼 startActivity ForResult()를 시작 하 는 내장 Fragment 는 onActivity Result()의 리 셋 을 받 지 않 을 것 입 니 다.
    이 유 는 다음 과 같다.(위 에서 말 한 requestCode 참조)
    Fragment Activity.startActivity FromFragment()는 requestCode 를 변경 하여 Fragment Manager 에 있 는 index 를 높 은 16 비트 로 저장 하고,낮은 16 비트 는 Fragment 에서 사용 할 수 있 는 requstCode 로 저장 합 니 다.Fragment Activity.onActivity Result()에 서 는 높 은 16 비트 에 따라 Fragment Manager 에서 해당 하 는 Fragment 를 찾 은 다음 낮은 16 비트 의 값 을 requstCode 로 합 니 다.Fragment.onActivity Result()를 호출 합 니 다.
    그러면 requestCode 에 하나의 index,즉 root Fragment Manager 의 Fragment index 만 저장 할 수 있 습 니 다.따라서 위 에 열거 한 상황 이 나타 납 니 다.
  • 포 함 된 Fragment 가 childFragment Manager 에 있 는 index 가 rootFragment Manager 의 모든 index 보다 클 때 rootFragment Manager 는 이 index 에 대응 하 는 Fragment 를 찾 을 수 없 기 때문에 Fragment 가 없 으 면 onActivity Result()
  • 를 받 을 수 있 습 니 다.
  • child Fragment Manager 에 포 함 된 index 가 rootFragment Manager 의 모든 index 보다 작 을 때 rootFragment Manager 에 속 하 는 Fragment 는 onActivity Result()
  • 를 받 습 니 다.
  • 어쨌든 Fragment 가 있어 도 onActivity Result()를 받 을 수 있 습 니 다.그것 은 요청 한 내장 Fragment
  • 가 아 닌 꼭대기 층 의 한 Fragment 입 니 다.
    해결 방안:
  • 내장 Fragment 사용 하지 않 음:)
  • 여전히 requestCode 를 이용 하여 낮은 16 비트 로 나 누 었 습 니 다.그 중에서 높 은 8 비트 는 child Fragment Manager 의 index 를 저장 하고 낮은 8 비트 는 Child Fragment 에 남 겨 두 었 습 니 다.(끼 워 넣 은 등급 이 깊 지 않 으 면 이 방안 은 좋 습 니 다.등급 이 깊 으 면 Fragment 에 남 겨 진 requestCode 의 사용 가능 한 값 구간 은 매우 제한 되 어 있 습 니 다)
  • Android 4.2(Api 17)이후 내 장 된 Fragment 와 ChildFragment Manager 를 사용 할 수 있 습 니 다.내 장 된 Fragment 는 requestCode 의 높 은 16 비트 를 통 해 index 를 기록 할 필요 가 없습니다.프레임 워 크 가 Fragment.startActivity ForResult()를 받 았 을 때 이 Fragment 의 표지(android:fragment:${parentIndex}:${my Index})를 기록 하고 result 를 보 낼 때,이 표지 에서 그 Fragment 를 찾 을 수 있 습 니 다.따라서 ChildFragment 에서 onActivity Result()를 받 지 못 하 는 문제 가 발생 하지 않 습 니 다.Activity.dispatch Activity Result()
  • 를 참고 하 십시오.
    Tips
    개발 시 Fragment 관련 디 버 깅 정 보 를 열 수 있 습 니 다.
    
    FragmentManager.enableDebugLogging(BuildConfig.DEBUG);
    Activity onResume    ,Fragment onResume     .
    protected void onPostResume() {
      super.onPostResume();
      mHandler.removeMessages(MSG_RESUME_PENDING);
      onResumeFragments();
      mFragments.execPendingActions();
    }
    protected void onResumeFragments() {
      mFragments.dispatchResume();
    }
    
    Fragment 의 onResume 이 모두 실 행 된 후에 어떤 작업 을 수행 해 야 한다 면 onPostResume()방법 을 다시 쓸 수 있 습 니 다.슈퍼.onPostResume()을 호출 해 야 합 니 다.
    1.IllegalStateException(Fragment not attached to Activity)의 질문
    이 이상 한 상황 은 Fragment 에서 비동기 작업 을 시작 한 다음 리 소스 와 관련 된 작업(getString(...)이나 startActivity(...)와 같은 작업 을 수행 하 는 것 입 니 다.그러나 이 때 Fragment 는 detach 에 의 해 실행 되 었 을 수 있 습 니 다.따라서 이 작업 을 수행 하기 전에 isAdded()를 판단 해 야 합 니 다.
    메모:여 기 는 isDetached()를 사용 하여 판단 하지 마 십시오.Fragment 가 detach 에 당 한 후에 도 isDetached()방법 은 false 로 돌아 갈 수 있 기 때 문 입 니 다.
    2.Fragment A 가 replace 되 어 detach 가 되 었 다 면,isDetached()는 false 로 되 돌아 갑 니 다.
    3.Fragment A 에 대응 하 는 Fragment Transaction 이 스 택 에서 나 와 detach 가 되면 isDetached()는 true 로 돌아 갑 니 다.
    
    final public Resources getResources() {
      if (mHost == null) {
        throw new IllegalStateException("Fragment " + this + " not attached to Activity");
      }
      return mHost.getContext().getResources();
    }
    public void startActivity(Intent intent, @Nullable Bundle options) {
      if (mHost == null) {
        throw new IllegalStateException("Fragment " + this + " not attached to Activity");
      }
      mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);
    }

    좋은 웹페이지 즐겨찾기