AsyncTaskLoader базовый пример. (Android)

Я использую Loader в своем приложении и на основании результата, который я получаю по запросу, который я выполняю для COntacts с помощью этого Loader, я выполняю некоторые вычисления и сохраняю их обратно в Sqlite DB. Я хочу, чтобы эта операция была асинхронной, однако я путаюсь с использованием задачи Async, поскольку у меня много разных типов данных для возврата, или я должен использовать простой обработчик или AsyncTaskLoader, я хочу, чтобы он был простым, поскольку я новичок в Погрузчики. Я пытался найти примеры AsyncTaskLoader, но, похоже, наука о ракетах - базовый и простой функциональный пример любого из трех в контексте моего сценария - был бы очень полезен.

6 ответов

Решение

Если вы хотите использовать AsyncTaskLoader, вот хороший пример для вас.

РЕДАКТИРОВАТЬ: Я решил сделать более простое решение (на основе этого репо):

public abstract class AsyncTaskLoaderEx<T> extends AsyncTaskLoader<T> {
    private static final AtomicInteger sCurrentUniqueId = new AtomicInteger(0);
    private T mData;
    public boolean hasResult = false;

    public static int getNewUniqueLoaderId() {
        return sCurrentUniqueId.getAndIncrement();
    }

    public AsyncTaskLoaderEx(final Context context) {
        super(context);
        onContentChanged();
    }

    @Override
    protected void onStartLoading() {
        if (takeContentChanged())
            forceLoad();
        //this part should be removed from support library 27.1.0 :
        //else if (hasResult)
        //    deliverResult(mData);
    }

    @Override
    public void deliverResult(final T data) {
        mData = data;
        hasResult = true;
        super.deliverResult(data);
    }

    @Override
    protected void onReset() {
        super.onReset();
        onStopLoading();
        if (hasResult) {
            onReleaseResources(mData);
            mData = null;
            hasResult = false;
        }
    }

    protected void onReleaseResources(T data) {
        //nothing to do.
    }

    public T getResult() {
        return mData;
    }
}

Использование:

в вашей деятельности:

        getSupportLoaderManager().initLoader(TASK_ID, TASK_BUNDLE, new LoaderManager.LoaderCallbacks<Bitmap>() {
            @Override
            public Loader<Bitmap> onCreateLoader(final int id, final Bundle args) {
                return new ImageLoadingTask(MainActivity.this);
            }

            @Override
            public void onLoadFinished(final Loader<Bitmap> loader, final Bitmap result) {
                if (result == null)
                    return;
                //TODO use result
            }

            @Override
            public void onLoaderReset(final Loader<Bitmap> loader) {
            }
        });

внутренний статический класс или нормальный класс:

private static class ImageLoadingTask extends AsyncTaskLoaderEx<Bitmap> {

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

    @Override
    public Bitmap loadInBackground() {
        //TODO load and return bitmap
    }
}

Обновление: начиная с библиотеки поддержки 27.1.0, все немного изменилось (ссылка здесь):

В версии 27.1.0 onStartLoading() вызывается при каждом запуске Activity. Поскольку вы вызываете deliveryResult() в onStartLoading(), вы запускаете onLoadFinished(). Это работает как задумано.

Вы должны удалить свой вызов метода deliveryResult() из onStartLoading(), так как он не нужен (загрузчики уже доставляют результаты, вычисленные в loadInBackground(), без дополнительной работы, необходимой с вашей стороны).

Я обновил код выше для этого изменения.

Вот пошаговое руководство для реализации AsyncTaskLoader, или проверить эту же статью на Medium

  1. Воплощать в жизнь LoaderManager.LoaderCallbacks<String> на MainActivity и создать static int для уникальной идентификации вашего загрузчика и создания ключа String для передачи URL-адреса строки вашему загрузчику

    public class MainActivity extends AppCompatActivity 
                 implements LoaderManager.LoaderCallbacks<String>{
        public static final int OPERATION_SEARCH_LOADER = 22;
        public static final String OPERATION_QUERY_URL_EXTRA = "query";
    //...}
    
  2. Override onCreateLoader, onLoadFinished а также onLoaderReset функции внутри MainActivity

    @Override
    public Loader<String> onCreateLoader(int id, final Bundle args) {
        //Here we will initiate AsyncTaskLoader
        return null;
    }
    
    @Override
    public void onLoadFinished(Loader<String> loader, String operationResult) {
        //Think of this as AsyncTask onPostExecute method, the result from onCreateLoader will be available in operationResult variable and here you can update UI with the data fetched.
        Log.d("MAINACTIVITY","result : "+ operationResult);
    }
    
    @Override
    public void onLoaderReset(Loader<String> loader) {
        //Don't bother about it, Android Studio will override it for you
    }
    
  3. внутри onCreateLoader() вернуть новый AsyncTaskLoader<String> как анонимный внутренний класс с this как параметр конструктора и переопределить loadInBackground & onStartLoading внутри анонимного внутреннего класса

    @Override
    public Loader<String> onCreateLoader(int id, final Bundle args) {
        return new AsyncTaskLoader<String>(this) {
            @Override
            public String loadInBackground() {
                //Think of this as AsyncTask doInBackground() method, here you will actually initiate Network call
                return null;
            }
    
            @Override
            protected void onStartLoading() {
               //Think of this as AsyncTask onPreExecute() method,start your progress bar,and at the end call forceLoad(); 
               forceLoad();
            }
        };
    }
    
  4. внутри loadInBackground сделайте сетевой вызов, используя HTTPUrlConnection или OKHttp или все, что вы используете.

     @Override
        public String loadInBackground() {
            String url = args.getString(OPERATION_QUERY_URL_EXTRA);//This is a url in string form 
            if (url!=null&&"".equals(url)) {
                return null;//if url is null, return
            }
            String operationResult="";
            try {
                operationResult = NetworkUtils.getResponseFromHttpUrl(url);//This just create a HTTPUrlConnection and return result in strings
            } catch (IOException e) {
                e.printStackTrace();
            }
            return operationResult;
        }
    
  5. внутри onCreate инициализируйте загрузчик с OPERATION_SEARCH_LOADER в качестве идентификатора, null для пакета и это для контекста

    getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
    
  6. Теперь вызывайте этот метод, когда и где вы хотите запустить загрузчик

    private void makeOperationSearchQuery(String url) {
    
        // Create a bundle called queryBundle
        Bundle queryBundle = new Bundle();
        // Use putString with OPERATION_QUERY_URL_EXTRA as the key and the String value of the URL as the value
        queryBundle.putString(OPERATION_QUERY_URL_EXTRA,url);
        // Call getSupportLoaderManager and store it in a LoaderManager variable
        LoaderManager loaderManager = getSupportLoaderManager();
        // Get our Loader by calling getLoader and passing the ID we specified
        Loader<String> loader = loaderManager.getLoader(OPERATION_SEARCH_LOADER);
        // If the Loader was null, initialize it. Else, restart it.
        if(loader==null){
            loaderManager.initLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
        }else{
            loaderManager.restartLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
        }
    }
    

Уолла, вы сделали, просто чтобы напомнить вам NetworkUtils.getResponseFromHttpUrl(url); моя пользовательская функция, которая принимает строку конвертировать в URL который в свою очередь используется для создания HTTPUrlConnection

Начиная с Honeycomb и библиотеки v4 совместимости можно использовать AsyncTaskLoader, Из того, что я понимаю, AsyncTaskLoader может выжить через изменения конфигурации, как переворачивание экрана. Но используя AsyncTask Вы можете испортить изменения конфигурации.

Ключевая информация: AsyncTaskLoader это подкласс Loader, Этот класс выполняет ту же функцию, что и AsyncTask, но немного лучше, он также может быть полезен при обработке изменений конфигурации (ориентация экрана).

Очень хороший пример и объяснение приведены здесь. http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html

У Google есть довольно хороший пример непосредственно в Документах API. Шаблоны Android Design предоставляют более подробную информацию и обоснование загрузчиков.

Этот урок, безусловно, поможет вам. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html

Мне нравится этот краткий пример AsyncTask и AsyncTaskLoader.

class FooLoader extends AsyncTaskLoader {
   public FooLoader(Context context, Bundle args) {
      super(context);
      // do some initializations here
   }
   public String loadInBackground() {
      String result = "";
      // ...
      // do long running tasks here
      // ...
      return result;
   }
} 


class FooLoaderClient implements LoaderManager.LoaderCallbacks {
   Activity context;
   // to be used for support library:
   // FragmentActivity context2;
   public Loader onCreateLoader(int id, Bundle args) {
      // init loader depending on id
      return new FooLoader(context, args);
   }
   public void onLoadFinished(Loader loader, String data) {
      // ...
      // update UI here
      //
   }
   public void onLoaderReset(Loader loader) {
      // ...
   }
   public void useLoader() {
      Bundle args = new Bundle();
      // ...
      // fill in args
      // ...
      Loader loader = 
         context.getLoaderManager().initLoader(0, args, this);
      // with support library: 
      // Loader loader = 
      //    context2.getSupportLoaderManager().initLoader(0, args, this);
      // call forceLoad() to start processing
      loader.forceLoad();
   }
}

Упростить трудно, может быть

  private void loadContent() {
    getLoaderManager().initLoader(1000, new Bundle(), 
      new LoaderManager.LoaderCallbacks<List<String>>() {

      @Override
      public Loader<List<String>> onCreateLoader(int id, Bundle args) {
        return new AsyncTaskLoader<List<String>>(MainActivity.this.getApplicationContext()) {

          @Override
          public List<String> loadInBackground() {
            Log.i("B", "Load background data ");
            ArrayList<String> data = new ArrayList<>();
            for (int i = 0; i < 5000; i++) {
              data.add("Data." + i + " " + System.currentTimeMillis());
            }
            try {
              Thread.sleep(5000);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
            return data;
          }
        };
      }

      @Override
      public void onLoadFinished(Loader<List<String>> loader, List<String> data) {
        Log.i("B", "Here are your data loaded" + data);
        if (!loader.isAbandoned()) {
          mAdapter.setData(data); // Read also about RecyclerView
        }
      }

      @Override
      public void onLoaderReset(Loader<List<String>> loader) {
        Log.i("B", "Loader reset");
      }
    }).forceLoad();
  }

  @Override
  protected void onDestroy() {
    // Abandon the loader so that it should not attempt to modify already dead GUI component
    getLoaderManager().getLoader(1000).abandon();
    super.onDestroy();
  }

Сделайте эту часть своей деятельности. Образец имитирует задержку, но позволяет легко распознавать новые записи, поскольку они будут иметь различный суффикс метки времени. Конечно, вам также нужен RecyclerView для отображения данных, ответ на этот вопрос кажется очень хорошим.

Загрузчик в этом примере является внутренним классом, который хранит ссылку на родительское действие. Это должен быть внешний статический класс без такой ссылки в производстве.

Я предпочитаю использовать Bolts-Android. это очень просто.

https://github.com/BoltsFramework/Bolts-Android

Task.callInBackground(new Callable<Void>() {
  public Void call() {
    // Do a bunch of stuff.
  }
}).continueWith(...);
Другие вопросы по тегам