Дальнейшие эксперименты по скрещиванию 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
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
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; } }
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())); } }
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); } }
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)); } } }
// TO DO
- Пока нет проверок на ошибки добавляемых параметров в FeedLoader
- Остановка Loader
- Rename DelivererFeedLoader to DispatcherLoader
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
ссылка на оригинал статьи http://habrahabr.ru/post/254989/
Добавить комментарий