Android 3.0 에서 도입 한 비동기 로드 메커니즘 Loader

Loader 는 구 글 이 안 드 로 이 드 3.0 에 도입 한 비동기 로드 메커니즘 으로 데이터 비동기 로드 를 Activity 나 Fragment 에 표시 할 수 있 으 며 사용 자 는 데이터 의 수명 주 기 를 관리 하지 않 고 Loader 메커니즘 에 맡 길 수 있다.
Loader 의 장점 사용 하기
만약 우리 가 네트워크 에서 데 이 터 를 가 져 올 필요 가 있다 면,일반적인 방법 은 하위 스 레 드 Thread+Handler 를 사용 하거나 AsyncTask 를 사용 하여 처리 하 는 것 입 니 다.
Thread+Handler 방법 은 간단 하고 직관 적 이지 만 번 거 로 울 수 있 습 니 다.Handler 서브 클래스 를 스스로 실현 하고 스 레 드 를 만 들 며 Handler 의 생명 주 기 를 관리 해 야 합 니 다.
AsyncTask 는 스 레 드 와 Handler 를 스스로 관리 할 필요 가 없 이 간단 합 니 다.그러나 AsyncTask 의 라 이 프 사이클 을 관리 하려 면 Activity 종료 시 상황 을 처리 해 야 합 니 다.그렇지 않 으 면 이상 이나 메모리 유출 이 발생 할 수 있 습 니 다.
Loader 를 사용 하면 스 레 드 와 Handler 의 생 성 과 소각 에 관심 을 가 질 필요 도 없고 데이터 의 전체 생명 주 기 를 스스로 관리 할 필요 도 없습니다.Loader 체 제 는 자동 으로 우 리 를 도와 처리 할 것 입 니 다.우리 가 유일 하 게 처리 해 야 할 것 은 데이터 자체 다.
Loader 에서 사용 하 는 절차:
Fragment Activity 나 Fragment 를 만 들 고 LoaderManager 의 인 스 턴 스 를 가지 고 Loader 를 실현 합 니 다.데이터 원본 에서 돌아 온 데 이 터 를 불 러 와 LoaderManager 를 실현 합 니 다.LoaderCallbacks 인터페이스 에서 데 이 터 를 보 여 주 는 데이터 원본,예 를 들 어 ContentProvider,서버 에서 보 낸 데이터 등 몇 가지 관련 된 LoaderManager 를 실현 합 니 다.
Loader 인 스 턴 스 를 관리 하고 Fragment Activity 나 Fragment 와 연결 합 니 다.
Activity 나 Fragment 에 유일한 LoaderManager 인 스 턴 스 가 있 습 니 다.
하나의 LoaderManager 인 스 턴 스 는 여러 개의 Loader 인 스 턴 스 를 관리 할 수 있 습 니 다.
Fragment Activity 나 Fragmeng 에서 getSupportLoaderManager()를 사용 하여 LoaderManager 인 스 턴 스 를 가 져 올 수 있 습 니 다.
initLoader()또는 restartLoader()방법 으로 데 이 터 를 불 러 올 수 있 습 니 다.

//0,    ID,       , Loader     
//null, Bundle  ,   Loader      
//LoaderCallbacks,LoaderManager Loader      ,        LoaderManager.LoaderCallbacks
getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<D>());
LoaderManager.LoaderCallbacks
Loader Manager 는 Loader 의 다양한 상황 에 대한 리 셋 인터페이스 로 세 가지 리 셋 방법 을 포함 합 니 다.
onCreateLoader(int,Bundle)
Loader 대상 을 직접 만들어 야 합 니 다.int 는 Loader 의 유일한 표지 이 고 Bundle 은 Loader 의 구조 적 매개 변수 이 며 비어 있 습 니 다.

...
new LoaderManager.LoaderCallbacks<String>() {
      @Override
      public Loader<String> onCreateLoader(int id, Bundle args) {
        return new MyLoader();
      }
      ...
}
onLoadFinished(Loader,D)
LoaderManager 가 데 이 터 를 불 러 왔 을 때 이 방법 을 되 돌려 줍 니 다.사용자 에 게 UI 로 데 이 터 를 보 여 줍 니 다.D 는 범용 이 고 실제 상황 에 따라 필요 한 데이터 형식 으로 설정 합 니 다.initLoader()LoaderCallbacks인자 의 범 형 과 같은 유형 입 니 다.

new LoaderManager.LoaderCallbacks<String>() {
      ...
      @Override
      public void onLoadFinished(Loader<String> loader, String data) {
          show(data);
      }
      ...
}
onLoaderReset(Loader)
이전에 만 든 Loader 인 스 턴 스 가 리 셋 되 었 을 때 이 방법 을 되 돌려 줍 니 다.이 때 는 관련 데 이 터 를 제거 해 야 합 니 다.

new LoaderManager.LoaderCallbacks<String>() {
      ...
      @Override
      public void onLoaderReset(Loader<String> loader) {
          show(null);
      }
      ...
}
 Loader
데이터 원본 에서 데 이 터 를 가 져 오고 데 이 터 를 불 러 옵 니 다.추상 류 로 서 하위 클래스 를 스스로 실현 해 야 합 니 다.
공식 적 으로 이 루어 진 두 가지 키 를 사용 하거나
AsyncTask Loader(이 를 계승 할 때 구 덩이 를 만 날 수 있 습 니 다.아래 분석 참조)
비동기 처리 데이터 가 져 오기 CursorLoader
ContentProvider 가 되 돌려 준 데 이 터 를 처리 하여 AsyncTaskLoader 가 만난 구덩이
먼저 MyAsyncTaskLoader 를 사용자 정의 하고 AsyncTaskLoader 를 계승 하면 매개 변 수 를 Context 로 하 는 구조 방법 과 loadInBackground()추상 적 인 방법 을 실현 해 야 합 니 다.

//  AsyncTaskLoader ,              ,    String
public class MyAsyncTaskLoader extends AsyncTaskLoader<String>{

  public MyAsyncTaskLoader(Context context) {
    super(context);
  }

  @Override
   public String loadInBackground() {
    //    
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    //        
    return new String("MyAsyncTaskLoader Test Result");
  }  
}
FragmentActivity 만 들 기

public class BaseActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks{
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.base_activity_layout);
//    addFragment();
    log("onCreate");
    loadData();
  }

  protected void loadData(){
    Log.e(getClassName(),"call");
    getSupportLoaderManager().initLoader(0, null, this);
  }

  protected String getClassName(){
    return getClass().getSimpleName();
  }

  @Override
  public Loader onCreateLoader(int id, Bundle args) {
    Log.e(getClassName(),"onCreateLoader");
    return new MyAsyncTaskLoader(BaseActivity.this);
  }

  @Override
  public void onLoadFinished(Loader loader, Object data) {
    Log.e(getClassName(),"data:"+data);
  }

  @Override
  public void onLoaderReset(Loader loader) {

  }
}
실행 중 로그 값 이 onCreate,call,onCreateLoader 로 인쇄 되 어 있 는 것 을 발 견 했 습 니 다.예상 되 는 MyAsyncTaskLoader Test Result 는 출력 되 지 않 았 습 니 다.즉,onLoadFinished 는 반전 되 지 않 았 습 니 다.디 버 깅 결과 MyAsyncTaskLoader 의 loadInBackground()방법 도 실행 되 지 않 았 습 니 다.
이게 어떻게 된 거 죠?
그러면 소스 코드 를 볼 수 밖 에 없습니다.여기 서 사용 하 는 것 은 모두 슈퍼 port-v4 가방 입 니 다.
AsyncTask Loader 소스 코드 를 보면 loadInBackground()방법 이 abstract 형식 임 을 알 수 있 습 니 다.호출 된 곳 은 LoadTask 라 는 내부 클래스 에 있 습 니 다.

//    ModernAsyncTask    AsyncTask

final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
    ....
    @Override
    protected D doInBackground(Void... params) {
       ...
        D data = AsyncTaskLoader.this.onLoadInBackground();
       ...
    }
   .....
  }
또한 AsyncTask Loader 의 전역 변수 입 니 다.

public abstract class AsyncTaskLoader<D> extends Loader<D> {
....
volatile LoadTask mTask;
....
}
mTask 실례 화 및 실 행 된 곳 은 onForceLoad()방법 에서

...
  @Override
  protected void onForceLoad() {
    ...
    mTask = new LoadTask();
    ...
    executePendingTask();
  }
  ...
  void executePendingTask() {
    ...
      if (mUpdateThrottle > 0) {
        ...
          mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
          return;
        }
      }
      ...
      mTask.executeOnExecutor(mExecutor, (Void[]) null);
    }
  }
mHandler.postAtTime 이나 mTask.execute OnExecutor 두 곳 이 바로 Task Loader 를 실행 하 는 곳 이 고 doInBackground()방법 으로 호출 됩 니 다.
이 쯤 에서 사용자 정의 MyAsyncLoader 의 loadInBackground()가 실행 되 지 않 았 다 고 추측 할 수 있 습 니 다.그러면 onForceLoad()도 실행 되 지 않 았 을 것 입 니 다.
이 단 서 를 따라 이 onForceLoad()가 어디에서 호출 되 었 는 지 찾 아 보 세 요.AsyncLoader 의 부모 클래스 Loader 의 forceLoad()에서 호출 된 것 으로 밝 혀 졌 습 니 다.

public class Loader{
...
  public void forceLoad() {
    onForceLoad();
  }
...
}
그리고 주석 을 보 니 이 방법 은 loader 가 시 작 될 때 만 호출 할 수 있 을 뿐 실 마 리 를 찾 지 못 했 습 니 다.

갑자기 CursorLoader 에 문제 가 없 는 것 같 습 니 다.forceLoad()가 호출 되 었 는 지 찾 아 보 니 역시 있 었 습 니 다!onStart Loading()이라는 방법 에 있 습 니 다.그리고 여기 서 만 호출 됩 니 다!

public class CursorLoader extends AsyncTaskLoader<Cursor> {
  ...
  @Override
  protected void onStartLoading() {
    if (mCursor != null) {
      deliverResult(mCursor);
    }
    if (takeContentChanged() || mCursor == null) {
      forceLoad();
    }
  }
  ...
}
그럼 이 걸 따라 해 볼 게 요.정말 되 는 지 아 닌 지.MyAsyncLoader 의 코드 수정 은 다음 과 같 습 니 다.

//  AsyncTaskLoader ,              ,    String
public class MyAsyncTaskLoader extends AsyncTaskLoader<String>{

  public MyAsyncTaskLoader(Context context) {
    super(context);
  }
  
  //       
  @Override
  protected void onStartLoading() {
    forceLoad();
  }

  @Override
   public String loadInBackground() {
    //    
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    //        
    return new String("MyAsyncTaskLoader Test Result");
  }  
}
실행 해 보 니 정말 출력 이 가능 합 니 다!문 제 는 해 결 된 것 같 습 니 다.
 
마지막 행동 출력 결과
문 제 는 해결 되 었 습 니 다.하지만 의문 이 있 습 니 다.이 onStart Loading()은 어디에서 호출 되 었 습 니까?아무래도 소스 코드 를 봐 야 겠 어 요.
getSupportLoaderManager().initLoader(0,null,this)를 분석 한 결과 마지막 으로 onStartLoading()으로 호출 되 는 것 으로 나 타 났 다.
요약 하면 다음 과 같 습 니 다.원본 코드 를 대조 하여 볼 수 있 습 니 다.

LoaderManager     LoaderManagerImpl
init()       LoaderInfo info
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);    createAndInstallLoader   
mCallbacks.onLoadFinished(loader, data);    onLoadFinished   
createLoader(id, args, callback)    createLoader   
installLoader(info);    installLoader   
info.start();    start   
mLoader.startLoading();    startLoading   
onStartLoading();

좋은 웹페이지 즐겨찾기