Android Volley Loader. Движение в сторону библиотеки

от автора

Дальнейшие эксперименты по скрещиванию Volley и Loader привели меня к мысли о создании библиотеки. Чтобы вызывался Loader с параметрами и в одну строку. На подобии как это реализовано в Picaso. После пары вечеров что-то получилось…

JSON

{ "1":{"name":"Samsung","price":51200.6}, "2":{"name":"Lg","price":5400.6}, "3":{"name":"Alcatel","price":4500.6}, "4":{"name":"iPhone","price":4800.3}, "7":{"name":"iPad","price":2850.1} } 

Data

public class GoodsItem {     String name;     float price; } 

Loader

    private String url = "http://192.168.1.103/shop.json";     private static final int LOADER_GOODS_ID = 1;     Map<Integer, GoodsItem> mGoodsMap = new HashMap<Integer, GoodsItem>();     @Override     protected void onCreate(Bundle savedInstanceState) {         ...         FeedLoader.with(this).addLoader(LOADER_GOODS_ID, url, HashMap.class, new DelivererFeedLoader.Listener<Map<Integer, GoodsItem>>() {             @Override             public void onResponse(int loaderId,                  Map<Integer, GoodsItem> goodsMap) {                 mGoodsMap = goodsMap;                 for (Map.Entry<Integer, GoodsItem> entry : mGoodsMap.entrySet()) {                     Log.d(TAG , "Goods item : " + entry.getKey() + " : " + entry.getValue());                 }             }             @Override             public void onErrorResponse(VolleyError data) {                 Log.d(TAG , "onErrorResponse :" + data);             }         }).start(LOADER_GOODS_ID, this); 

Доступ к библиотеке предоставлен через простой Singleton. Здесь все просто, как в Picaso

FeedLoader.with(this)

public static FeedLoader with(Context context) {     if (singleton == null) {         synchronized (FeedLoader.class) {             if (singleton == null) {                 singleton = new FeedLoader(context);             }         }     }     return singleton; } 

Добавление Типов и Листнера делается через addLoader:
loaderId — ID лоадера, просто число
url — Адрес feed
loaderClazz — Это тип класса, который хотим распарсить
DelivererFeedLoader.Listener — Callback через который вернутся данные или ошибка

public FeedLoader addLoader(int loaderId, String url, Class<?> loaderClazz, DelivererFeedLoader.Listener callback){     delivererFeedLoader.loaderClazzMap.put(loaderId, loaderClazz);     delivererFeedLoader.callBackMap.put(loaderId, callback);     delivererFeedLoader.urlFeeds.put(loaderId, url);     return singleton; } 

Запускаем Loader через start(…):

public void start(int loaderId, final Context context) {     Bundle bundle = new Bundle();     bundle.putParcelable("delivererFeedLoader", delivererFeedLoader);      ((FragmentActivity)context).getSupportLoaderManager().             restartLoader(loaderId, bundle, callback); } 

Собственно четыре файла реализации Alfa

FeedLoader.java

package net.appz.feedloader;  import android.content.Context; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader;  /**  * Created by App-z.net on 04.04.15.  */ public class FeedLoader {      private static volatile FeedLoader singleton = null;      private Context context = null;     private boolean DEBUG = true;     private String TAG = getClass().getSimpleName();      private DelivererFeedLoader delivererFeedLoader = new DelivererFeedLoader();      public FeedLoader(Context context) {         this.context = context;     }      LoaderManager.LoaderCallbacks<Object> callback = new LoaderManager.LoaderCallbacks<Object>() {         @Override         public Loader<Object> onCreateLoader(int id, Bundle args) {             return new FeedLoaderWrapper(context , args);         }          @Override         public void onLoadFinished(Loader<Object> loader, Object data) {             delivererFeedLoader.callBackMap.get(loader.getId()).onResponse(loader.getId(), data);         }          @Override         public void onLoaderReset(Loader<Object> loader) {          }     };      public FeedLoader addLoader(int loaderId, String url, Class<?> loaderClazz, DelivererFeedLoader.Listener callback){         delivererFeedLoader.loaderClazzMap.put(loaderId, loaderClazz);         delivererFeedLoader.callBackMap.put(loaderId, callback);         delivererFeedLoader.urlFeeds.put(loaderId, url);         return singleton;     }      public void start(int loaderId, final Context context) {         Bundle bundle = new Bundle();         bundle.putParcelable("delivererFeedLoader", delivererFeedLoader);          ((FragmentActivity)context).getSupportLoaderManager().                 restartLoader(loaderId, bundle, callback);     }      public static FeedLoader with(Context context) {         if (singleton == null) {             synchronized (FeedLoader.class) {                 if (singleton == null) {                     singleton = new FeedLoader(context);                 }             }         }         return singleton;     } } 

FeedLoaderWrapper.java

package net.appz.feedloader;  /**  * Created by App-z.net on 05.04.15.  */  import android.content.Context; import android.os.Bundle; import android.support.v4.content.Loader; import android.util.Log;  import com.android.volley.RequestQueue; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.Volley;  /**  *  * @param <D>  */ class FeedLoaderWrapper<D> extends Loader<D> {      private boolean DEBUG = true;     private String TAG = getClass().getSimpleName();      private DelivererFeedLoader delivererFeedLoader = new DelivererFeedLoader();      private RequestQueue requestQueue;      /**      * Stores away the application context associated with context.      * Since Loaders can be used across multiple activities it's dangerous to      * store the context directly; always use {@link #getContext()} to retrieve      * the Loader's Context, don't use the constructor argument directly.      * The Context returned by {@link #getContext} is safe to use across      * Activity instances.      *      * @param context used to retrieve the application context.      */     public FeedLoaderWrapper(Context context, Bundle bundle) {         super(context);         delivererFeedLoader = bundle.getParcelable("delivererFeedLoader");         requestQueue = Volley.newRequestQueue(context);         // run only once         onContentChanged();     }      /**      * Get Data      */     private void doRequest(Class<?> clazz) {         String urlFeed = delivererFeedLoader.urlFeeds.get(getId());         final GsonRequest gsonRequest = new GsonRequest(urlFeed,                 clazz,                 null,                 new Response.Listener<D>() {                     @Override                     public void onResponse(D data) {                         deliverResult(data);                     }                 }, new Response.ErrorListener() {             @Override             public void onErrorResponse(VolleyError volleyError) {                 if (volleyError != null)                     //deliverResult(null);                     if (DEBUG) Log.e(TAG, "volleyError: " + volleyError.getMessage());                         delivererFeedLoader.callBackMap.get(getId()).onErrorResponse(volleyError);             }         });         requestQueue.add(gsonRequest);     }      @Override     protected void onStartLoading() {         if (takeContentChanged())             forceLoad();     }      @Override     protected void onStopLoading() {         if (DEBUG) Log.i(TAG, "Loader onStopLoading()");         requestQueue.cancelAll(this);         super.onStopLoading();     }      @Override     protected void onReset() {         if (DEBUG) Log.i(TAG, "Loader onReset()");         requestQueue.cancelAll(this);         super.onReset();     }      @Override     public void onForceLoad() {         super.onForceLoad();          String urlFeed = delivererFeedLoader.urlFeeds.get(getId());         if (DEBUG) Log.d(TAG, "Loader onForceLoad() : feedUrl = " + urlFeed);         doRequest(delivererFeedLoader.loaderClazzMap.get(getId()));     } } 

DelivererFeedLoader.java

package net.appz.feedloader;  import android.os.Parcel; import android.os.Parcelable; import com.android.volley.VolleyError; import java.util.HashMap;  /**  * Created by App-z.net on 05.04.15.  */ public class DelivererFeedLoader implements Parcelable {     HashMap<Integer, Class> loaderClazzMap = new HashMap<>();     HashMap<Integer, Listener> callBackMap = new HashMap<>();     HashMap<Integer, String> urlFeeds = new HashMap<>();      public DelivererFeedLoader(){}      protected DelivererFeedLoader(Parcel in) {         loaderClazzMap = (HashMap) in.readValue(HashMap.class.getClassLoader());         callBackMap = (HashMap) in.readValue(HashMap.class.getClassLoader());         urlFeeds = (HashMap) in.readValue(HashMap.class.getClassLoader());     }      @Override     public int describeContents() {         return 0;     }      @Override     public void writeToParcel(Parcel dest, int flags) {         dest.writeValue(loaderClazzMap);         dest.writeValue(callBackMap);         dest.writeValue(urlFeeds);     }      @SuppressWarnings("unused")     public static final Parcelable.Creator<DelivererFeedLoader> CREATOR = new Parcelable.Creator<DelivererFeedLoader>() {         @Override         public DelivererFeedLoader createFromParcel(Parcel in) {             return new DelivererFeedLoader(in);         }          @Override         public DelivererFeedLoader[] newArray(int size) {             return new DelivererFeedLoader[size];         }     };      public interface Listener<D>{         void onResponse(int loaderId, D data);         void onErrorResponse(VolleyError data);     } } 

GsonRequest.java

package net.appz.feedloader;  import com.android.volley.AuthFailureError; import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.toolbox.HttpHeaderParser; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException;  import java.io.UnsupportedEncodingException; import java.util.Map;  /**  * Created by App-z.net on 29.03.15.  */ public class GsonRequest<T> extends Request<T> {     private final Gson gson = new Gson();     private final Class<T> clazz;     private final Map<String, String> headers;     private final Response.Listener<T> listener;      /**      * Make a GET request and return a parsed object from JSON.      *      * @param url URL of the request to make      * @param clazz Relevant class object, for Gson's reflection      * @param headers Map of request headers      */     public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,                        Response.Listener<T> listener, Response.ErrorListener errorListener) {         super(Method.GET, url, errorListener);         this.clazz = clazz;         this.headers = headers;         this.listener = listener;     }      @Override     public Map<String, String> getHeaders() throws AuthFailureError {         return headers != null ? headers : super.getHeaders();     }      @Override     protected void deliverResponse(T response) {         listener.onResponse(response);     }      @Override     protected Response<T> parseNetworkResponse(NetworkResponse response) {         try {             String json = new String(                     response.data,                     HttpHeaderParser.parseCharset(response.headers));             return Response.success(                     gson.fromJson(json, clazz),                     HttpHeaderParser.parseCacheHeaders(response));         } catch (UnsupportedEncodingException e) {             return Response.error(new ParseError(e));         } catch (JsonSyntaxException e) {             return Response.error(new ParseError(e));         }     } } 

Проект на GitHub
RoboSpice

// TO DO

  • Пока нет проверок на ошибки добавляемых параметров в FeedLoader
  • Остановка Loader
  • Rename DelivererFeedLoader to DispatcherLoader
Стали бы вы пользоваться такой библиотекой?

Проголосовало 26 человек. Воздержалось 7 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

ссылка на оригинал статьи http://habrahabr.ru/post/254989/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *