怎样使用ListView实现一个带有网络请求,解析,分页,缓存的公共的List页面来大大的提高工作效率

在寻常的开发中常常会有非常多列表页面。每做一个列表页就须要创建这个布局文件那个Adapter适配器文件等等一大堆与之相关的附属的不必要的冗余文件。

假设版本号更新迭代比較频繁,如此以往,就会使项目project变得无比庞大臃肿。

假设看过这篇文章或者在使用过这样的方式之后呢,全部的工作都能够被压缩成仅仅有两个文件,一个JAVA文件一个XML布局文件。

并且代码还少少的。

咱们来看看实际情况:

寻常的一个列表页面的生成须要下面文件:

  • 一个Activity文件。有时候可能还会忘记注冊
  • 一个包括上下拉刷新控件以及无数据时提示的布局文件
  • 一个Listview的item的布局文件
  • 一个Adapter适配器文件
  • 一个须要被解析的Bean文件

当然在Activity中还须要处理下面功能:

  • 数据解析
  • 分页载入
  • 数据缓存
  • 网络请求

当然,以上功能能够通过频繁的复制粘贴来实现,可是这会给以后的维护留下不少不小的坑,比方突然须要更换Listview为Recyclerview,你是不是顿时就想哭了呢?是不是就得一个个文件去改呢?如今不用了,改俩地方即可了。

如今你可能想知道一个公共的List页有什么特点呢?

  • 无需再关心网络请求、数据解析、分页、缓存等同样的功能
  • 不须要写那么多的同样的布局文件,仅仅用写那些不同的item布局文件就能够。仅仅须要关心你关心的
  • 仅仅会有一个Adapter适配器,一个ViewHolder存在,Activity也能够仅仅有一个
  • 可复用性超强,不管是Activity中展示,还是在被要求放在ViewPager中显示都没问题
  • 大大减小项目的project文件数量。提高编译速度,不用再把一天的时间都浪费在编译时间上
  • 提高你的工作效率。不用再复制粘贴,那个时间没有这个快。仅仅用实现你的getView方法就能够
  • 降低维护成本。假设某一天须要在网络请求加上某个參数,曾经的方法须要改无数个地方,而如今仅仅用改一个地方就OK,假设如探须要更改上下拉刷新控件,比方须要将XListView改成PullToRefreshListView。你是不是就苦逼了?非常多地方都须要跟着改,如今不用了,仅仅用动一个地方全都OK
  • 还有非常多我一时间想不起来等你去发掘的功能

好。BB了这么多,究竟是怎么实现和怎么使用呢?容我慢慢道来:

好,先来看看使用起来有多便捷:

/**
 * 演示样例代码,将关键的部分放在fragment中。不管是viewpager还是Activity。还是其他容器,都能够将fragment嵌入当中显示
 *
 * @author Sahadev
 *
 */
public class ExampleFragment extends SuperAbstListFragment<ExampleBean> {

	public static AbstListFragment getInstance(String requestUrl) {
		AbstListFragment fragment = new ExampleFragment();
		Bundle bundle = new Bundle();
		bundle.putString(AbstListFragment.URL, requestUrl);
		fragment.setArguments(bundle);
		return fragment;
	}

	@Override
	public Type getInstanceType() {
		// 返回须要实例化的对象类型
		return new TypeToken<List<ExampleBean>>() {
		}.getType();
	}

	/**
	 * 须要实例化的类,这里仅用一个属性做样例
	 *
	 * @author Work
	 *
	 */
	public static class ExampleBean implements Serializable {

		/**
		 *
		 */
		private static final long serialVersionUID = 7624541082621792974L;

		@SerializedName("title")
		public String title;

	}

	//在这里完毕数据绑定就能够了,支持链式调用
	@Override
	public void setView(ViewHolder viewHolder, ExampleBean t) {
		viewHolder.setText(R.id.title, t.title);
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

	}
}

这是实际执行效果图:只通过短短的几行代码就能够实现强大的功能。是不是非常方便?

好,效果先看到了,接下来描写叙述一下是怎样完毕这么多功能的:

当然,子类写的代码少,那说明父类已经帮它完毕了不少功能,所以咱们先看看总体的文件夹结构:

这个图可能画的有些毛糙,还是再用小文来简述一下,BaseFragment含有一些主要的功能,比方高速弹出一个toast,显示一个等待对话框等待,它还有子类经常使用的一些属性,activity。LayoutInflater,ImageLoader 等等:

/**
 * 基本类。提供一些经常使用的主要的方法属性供子类使用
 *
 * @author Sahadev
 *
 */
public class BaseFragment extends Fragment {
	/**
	 * 图片载入工具
	 */
	protected ImageLoader mImageLoader;
	/**
	 * 等待对话框
	 */
	private LoadingDialog mLoadingDialog;
	/**
	 * 布局填充器
	 */
	protected LayoutInflater mInflater;
	/**
	 * context
	 */
	protected Activity mContext;

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		mContext = activity;
		mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		// 在此处初始化mImageLoader,mLoadingDialog等属性
		mLoadingDialog = LoadingDialog.getInstance(mContext);
		// imageLoader属性可在自己定义的Application中设置全局的单例。由自己定义Application暴露接口获取单例,比方
		// mImageLoader = CustomApplication.getImageLoaderInstance();
	}

	/**
	 * 吐司
	 *
	 * @param message
	 */
	protected void toast(String message) {
		Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
	}

	/**
	 * 显示等待对话框。显示默认文本
	 */
	protected void showLoadingDialog() {
		if (mLoadingDialog != null) {
			mLoadingDialog.show();
		}
	}

	/**
	 * 显示等待对话框,显示传入的文本
	 *
	 * @param message
	 */
	public void showLoadingDialog(String message) {
		if (mLoadingDialog != null) {
			mLoadingDialog.setMessage(message);
			mLoadingDialog.show();
		}
	}

	/**
	 * 关闭等待对话框
	 */
	protected void dismissLoadingDialog() {
		if (mLoadingDialog != null) {
			mLoadingDialog.dismiss();
		}
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		dismissLoadingDialog();
	}

}

AbstListFragment则是咱们项目的关键部分了。它集成了界面生成、空数据展示界面、网络请求及分页请求。网络请求回调,item点击回调。界面主动刷新广播接收器等功能,

能够使用户自定义适配器:

/**
 * 含有ListView的Fragment
 * 抽取公共的含有ListView的Fragment。此Fragment已经包含主要的下拉刷新,网络载入,分页载入等公共功能,仅仅须要关心实现 推荐使用
 * {@link #SuperAbstListFragment}实例化子类方式參见{@link #ExampleFragment}
 *
 * @author Sahadev
 *
 */
public abstract class AbstListFragment extends BaseFragment implements OnItemClickListener, OnClickListener,
		Listener<JSONObject>, ErrorListener, OnRefreshListener2<ListView> {
	protected PullToRefreshListView mListView;
	protected ImageView emptyView;
	private AnimationDrawable rocketAnimation;
	private View rootView;
	protected int page = 0;

	public static final String URL = "ABST_LIST_FRAGMENT_URL";
	public static final String NEED_REFRESH_BROADCAST_RECEIVER = "NEED_REFRESH_BROADCAST_RECEIVER";

	/**
	 * 请求的链接地址
	 */
	protected String requestUrl;

	/**
	 * 由子类实现。安全传參
	 *
	 * @param requestUrl
	 * @return
	 */
	public static AbstListFragment getInstance(String requestUrl) {
		return null;
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		if (rootView == null) {
			rootView = inflater.inflate(R.layout.activity_list_layout, container, false);
			emptyView = (ImageView) rootView.findViewById(R.id.empty_view);
			mListView = (PullToRefreshListView) rootView.findViewById(R.id.list);
			mListView.setEmptyView(emptyView);
			mListView.getRefreshableView().setLayoutTransition(new LayoutTransition());

			mListView.setOnRefreshListener(this);
			mListView.setOnItemClickListener(this);
			mListView.getRefreshableView().setOnItemClickListener(this);

			emptyView.setOnClickListener(this);

			Bundle bundle = getArguments();
			if (bundle != null) {
				requestUrl = getArguments().getString(AbstListFragment.URL);
			}
		}

		ViewGroup parent = (ViewGroup) rootView.getParent();
		if (parent != null) {
			parent.removeView(rootView);
		}
		return rootView;
	}

	@Override
	public void onStart() {
		super.onStart();
		if (mListView != null) {
			mListView.setRefreshing(false);
		}
	}

	@Override
	public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
		page = 0;
		getData(requestUrl + "&p=" + page);// 这里能够加入分页和其他请求server所须要的必要參数。比方token或者其他什么的,所以在传入的地方仅仅用传入必要的參数就OK
	}

	@Override
	public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
		page++;
		getData(requestUrl + "&p=" + page);
	}

	protected void getData(String requestUrl) {
		if (isNeedLoadDataFromNet()) {
			if (page == 0) {
				// 能够在这里设置载入动画
				emptyView.setImageResource(R.drawable.loading_animation);// R.drawable.loading_animation代表动画资源
				rocketAnimation = (AnimationDrawable) emptyView.getDrawable();
				rocketAnimation.start();
			}
			RequestUtils.requesGet(requestUrl, this, this);
		}
	}

	/**
	 * 这种方法用于返回是否是从网络载入,有些数据是须要从本地载入的。这个 方法就能够由子类来控制详细是什么
	 *
	 * @return
	 */
	protected boolean isNeedLoadDataFromNet() {
		return true;
	}

	@Override
	public void onResponse(JSONObject response) {
		// 设置请求完成之后的状态
		rocketAnimation.stop();
		emptyView.setImageResource(R.drawable.nocontent);
		mListView.onRefreshComplete();
	}

	@Override
	public void onErrorResponse(VolleyError error) {
		// 设置请求完成之后的状态
		rocketAnimation.stop();
		emptyView.setImageResource(R.drawable.nocontent);
		toast("咦?网络状况貌似出了点问题.");
		mListView.onRefreshComplete();
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		// 当点击无数据提示的时候又一次载入
		case R.id.empty_view:
			mListView.setRefreshing();
			break;
		default:
			break;
		}
	}

	private BroadcastReceiver receiver;

	@Override
	public void onResume() {
		super.onResume();
		receiver = new NeedRefreshBroadcastReceiver();
		IntentFilter filter = new IntentFilter(NEED_REFRESH_BROADCAST_RECEIVER);
		filter.addCategory(Intent.CATEGORY_DEFAULT);
		mContext.registerReceiver(receiver, filter);
	}

	@Override
	public void onPause() {
		super.onPause();
		mContext.unregisterReceiver(receiver);
	}

	/**
	 * 主动刷新广播接收器,当数据发生改变的时候(比方加入或者删除)主动刷新
	 *
	 * @author Work
	 *
	 */
	private class NeedRefreshBroadcastReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			mListView.setCurrentMode(Mode.PULL_FROM_START);
			mListView.setRefreshing(false);
		}

	}

}

SuperAbstListFragment<T> 是对父类AbstListFragment的进一步抽象。它里面集成了一个适配器与一个万能的ViewHolder,使子类仅仅用

实现几个主要的方法就能够,比方要解析的类型、当item点击之后的处理方式、数据与界面怎样绑定等等,来看看这个

类都有什么:

/**
 * 抽象的AbstListFragment中间层,具有更强大的功能
 *
 * @author Work
 *
 */
public abstract class SuperAbstListFragment<T> extends AbstListFragment {
	protected AbstBaseAdapter<T> adapter;

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		try {
			// 假设有些Adapter中不满足实际情况的话,能够使用反射来实例化
			// adapter = (AbstBaseAdapter<T>)
			// getAdapterClass().getConstructor(Context.class).newInstance(mContext);
			adapter = new SuperAdapter(mContext);
			mListView.setAdapter(adapter);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 万能适配器,它仅仅是个中间件
	 *
	 * @author Work
	 *
	 */
	public class SuperAdapter extends AbstBaseAdapter<T> {

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

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			return SuperAbstListFragment.this.getView(position, convertView, parent);
		}
	}

	/**
	 *
	 *
	 * @param position
	 * @param convertView
	 * @param parent
	 * @return
	 */
	public View getView(int position, View convertView, ViewGroup parent) {
		// 这里使用的是万能的ViewHolder
		ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent, R.layout.fragment_list_item, position);// 这一行能够进一步的抽取到父类中

		T t = adapter.getData().get(position);

		setView(viewHolder, t);

		return viewHolder.getConvertView();
	}

	/**
	 * 绑定数据,使用户真正关心的仅仅有他们想要关心的
	 *
	 * @param viewHolder
	 * @param t
	 */
	public abstract void setView(ViewHolder viewHolder, T t);

	/**
	 * 假设单单的getView方法不满足需求的话。能够通过自己定义Adapter的方法来实现,该方法用来返回须要实例化的Adapter的类名
	 *
	 * @return
	 */
	public Class<?> getAdapterClass() {
		return null;
	}

	/**
	 * 须要解析的数据类型是一个对象还是对象的集合,由这个返回
	 *
	 * @return
	 */
	public abstract Type getInstanceType();

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.sahadev.general_assembly.base.AbstListFragment#onResponse(org.json
	 * .JSONObject) 当网络请求成功之后回调该方法,開始解析数据
	 */
	@Override
	public void onResponse(JSONObject response) {
		super.onResponse(response);
		if (response != null && response.optBoolean("success")) {
			Gson gson = new Gson();
			List<T> datas = gson.fromJson(response.optJSONArray("data").toString(), getInstanceType());
			initAdapter(datas);
		}
	}

	/**
	 * 数据解析完成之后刷新数据
	 *
	 * @param list
	 */
	protected void initAdapter(List<T> list) {
		if (page == 0) {
			adapter.addFirstPageData(list);
		} else {
			adapter.addOtherPageData(list);
		}
	}

}

通过以上几个类的不断抽取。当最后在使用的时候。实现类仅仅用简单的几行代码就能够完毕非常多非常多的功能。怎么样,是不是非常easy?

接下来简介一下怎样在各种页面仅仅用一个Activity来装载不同页面的Fragment呢:

/**
 * 含有ListFragment的Activity
 *
 * @author 尚斌
 *
 */
public class IncludeListFragmentActivity extends FragmentActivity {
	private String mFragmentClass = "x.x.x.x.x.x";
	private String mRequestUrl = "http://www.baidu.com";
	private String title = "标题没有定义";

	public static final String TITLE = "TITLE";
	public static final String CLASS = "CLASS";
	public static final String URL = "URL";

	/**
	 * @param context
	 * @param fragmentClass
	 *            须要实例化的Fragment的包名
	 * @param requestUrl
	 *            该Fragment内部的请求地址
	 * @return
	 */
	public static Intent getIntent(Context context, String fragmentClass, String requestUrl, String title) {
		Intent intent = new Intent(context, IncludeListFragmentActivity.class);
		Bundle bundle = new Bundle();
		bundle.putString(TITLE, title);
		bundle.putString(CLASS, fragmentClass);
		bundle.putString(URL, requestUrl);
		intent.putExtras(bundle);
		return intent;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		super.onCreate(savedInstanceState);

		Intent intent = getIntent();
		Bundle bundle = intent.getExtras();
		mFragmentClass = bundle.getString(CLASS);
		mRequestUrl = bundle.getString(URL);
		title = bundle.getString(TITLE);

		// 设置标题
		setTitle(title);

		// 设置布局文件
		setContentView(R.layout.activity_include_list_fragment);

		try {
			Class<BaseFragment> newInstance = (Class<BaseFragment>) Class.forName(mFragmentClass);

			Method method = null;
			BaseFragment fragment = null;

			method = newInstance.getMethod("getInstance", String.class);
			fragment = (BaseFragment) method.invoke(null, mRequestUrl);

			if (fragment != null) {
				getSupportFragmentManager().beginTransaction().add(R.id.container, fragment).commit();
			} else {
				throw new Exception("You must be have a named getInstance method!");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

就是这个文件。能够通过传入的标题,须要实现的Fragment类,须要请求的地址来生成多种多样的界面,所以在实现子类的时候每一个子类都须要重写public static AbstListFragment getInstance(String requestUrl) 方法以供外部能够调用到它。

说了这么多,在这里面事实上是没有缓存的。事实上这个公共的项目与缓存关系是不大的。既然提到就说一下是怎么实现的。在项目开发的时候非常多时候都用到了第三方网络框架,也是有源代码的,这里就用Volley举个栗子:

在Volley请求的时候,在请求的基类方法中,依据请求的URL去数据库中寻找,数据库这里推荐使用xUtils提供的数据库存储。假设没找到,则调用网络请求,在网络请求成功回调的部分将请求的数据存入数据库。以便第二次查找。基本思路就是这样:

使用代码举例:

网络请求部分:

	public JsonRequest(int method, String url, Map<String, String> body, Listener<T> listener,
			ErrorListener errorListener, boolean isNeedCache, Type type) {
		super(method, url + "&token=token" + "&vid=vid"), errorListener);
		mListener = listener;
		mRequestBody = null;
		mType = type;
		if (isNeedCache && !(检查网络是否可用)) {
			url += "&token=token" + "&vid=vid";

			try {

				//使用自己定义的方式去数据库中查找,这里使用的是xUtils举例:

				List<Cache> datas = xUtils
						.getInstance()
						.getDbUtils()
						.findAll(
								Selector.from(Cache.class).where("requestUrl", "=", url).orderBy("time", false)
										.limit(1));

				if (!netAccessed && datas != null & listener != null) {
					for (Cache cache : datas) {
						//假设查找成功就进行回调
						listener.onResponse((T) new JSONObject(cache.jsonString));
					}
				}

			} catch (DbException e) {
				e.printStackTrace();
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}
		bodyMap = body;

	}

网络回调部分。将server数据存储:

@Override
	protected void deliverResponse(final T response) {
		if (response != null
				&& (response.getClass().equals(String.class) || response.getClass().equals(JSONObject.class))) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					Cache cache = new Cache(mUrl, response.toString());

					try {
						//使用xUtils进行存储
						xUtils.getInstance().getDbUtils().save(cache);
					} catch (DbException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}

		netAccessed = true;

		if (mListener != null && (假设网络可用)) {
			if (mType != null) {
				try {
					String simString1 = response.getClass().getName();
					String simString2 = mType.toString();
					//有多种类型回调,假设仅仅是回调String类。则调用下面
					if (simString2.contains(simString1)) {
						mListener.onResponse(response);
					} else {
						mListener.onResponse(null);
					}
				} catch (Exception e1) {
				}
			} else {
				//另一种是JsonObject类型,则调用下面
				mListener.onResponse(response);
			}
		}
	}

大伙可能实际情况不是这个样子。可是思路可能差点儿相同,仅供參考。

当然,在该项目中还集成了不少别的主要的东西,比方ImageLoader图片载入。Volley请求工具,json解析工具等,假设是作为一个新项目的话,本项目还是能够作为一个最主要的起始项目来用用。

项目地址在这里:https://git.oschina.net/sahadev/General-Assembly

项目是一个开源项目,迫切的想要很多其它的人能够增加进来。将自己工作中能够提高工作效率的知识和成果分享出来,出一份力。假设想增加 请联系我:[email protected],希望能够一起发展壮大,拥有非常多为大家减轻负担的成果。

时间: 2024-10-26 14:06:08

怎样使用ListView实现一个带有网络请求,解析,分页,缓存的公共的List页面来大大的提高工作效率的相关文章

(转载)Android之三种网络请求解析数据(最佳案例)

[置顶] Android之三种网络请求解析数据(最佳案例) 2016-07-25 18:02 4725人阅读 评论(0) 收藏 举报  分类: Gson.Gson解析(1)  版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 小武:相信大家都用过网络请求解析数据,只是方法不一样而已,但是,逻辑都是差不多的: 一:AsyncTask解析数据 AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用. AsyncTask是个抽象类,使用时需要继承这个

[转] 一个优秀的程序员应该如何提高工作效率?

转自:http://www.cnblogs.com/cherry0327/p/5395957.html 当我们评价一个程序员是否优秀时,要从多个方面进行评估. 专业技能.专业知识.创造力.经验,这些都是重要的因素.当然,还有他们的工作态度.工作效率和工作质量.如果程序员的工作是没有效率的,那就意味着他们所投入的时间和精力是没有效果的.在livecoding.tv最近发布的一篇博文中,他们总结了10个影响程序员工作效率的坏习惯.看完这篇文章后你会发现,里面所列举的坏习惯,总有一两个可以在自己的身上

自己动手写一个iOS 网络请求库的三部曲[转]

代码示例:https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary 开源项目:Pitaya,适合大文件上传的 HTTP 请求库:https://github.com/johnlui/Pitaya 本系列文章中,我们将尝试使用 NSURLSession 技术构建一个自己的网络请求库. NSURLSession 简介 NSURLSession 是 iOS7 引入的新网络请求接口,在 WWDC2013

从零开始写一个Tomcat(叁)--请求解析

挖坑挖了这么长时间也该继续填坑了,上文书讲到从零开始写一个Tomcat(贰)--建立动态服务器,讲了如何让服务器解析请求,分离servlet请求和静态资源请求,读取静态资源文件输出或是通过URLClassLoader找到我们请求的servlet,反射生成对应的实例,调用其service方法,传递初级解析的request和response,完成请求. 这很tomcat,but too simple 阅读本文,你将了解 连接器(connector),处理器(processor)逻辑分离 如何高效的解

关于listview中图片切圆,网络请求数据,并Gson解析后得到list

切圆的方法: public Bitmap toRoundBitmap(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); float roundPx; float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom; if (width <= height) { roundPx = width / 2; l

模拟web请求——简单的小程序提高工作效率

研究了一段时间爬虫,了解到了用代码发起http请求的方法,想练练手.突然想起工作上遇到的一个难题,感觉非常适合.问题是这样的:工作上维护的一个系统,有时候代码脚本有变更,需要通过一个前台页面进行刷新.但是因为服务器高可用的要求,总共部署了27个server,一次更新每个server都需要刷一遍,非常麻烦.刷新页面的url是这样的:http://ip:port/OpenEbus_srv/refreshScript.jsp刷新完成后:相当简单的页面,不同的server区别就在于ip和端口.之前研究了

[每天记录一个Bug]Cell中由于block加载网络请求产生的复用

Bug 出现场景: cell中使用加载图片的网络请求出现复用,截图如下: 复用原因: Cell Model中只有一个用户的uid,所有用户相关信息:例如头像\名称\信息等是通过 block请求,通过uid 回调中取到的字段,但由于是在cell中通过系统的block回调中下载得到的头像,所以会有延时问题. 当使用 self.collection reloadData ,这个语句的时候,由于4个cell的头像还没加载成功,瞬间又重新复用4个cell出来,就会产生复用的情况. 也就是说  4个cell

Retrofit2.0+RxJava+RxAndroid——强强联合的网络请求框架

最近看了很多关于Retrofit和Rxjava的文档介绍.终于在弄清Rxjava后顺利的弄懂了Retrofit. 网上有很多人都介绍了它们的联合使用,但是我看过之后理解不是太好.可能我太笨. 不过,今天写这篇博客的目的就是想来说说它们之间如何使用以及使用的时候遇到的坑. 这两者的关系并不大,但是联合在一起使用是非常简便的.Rxjava的响应式编程加上Retrofit的注解式请求用起来是非常爽的. 并且Retrofit内置的是Okhttp,所以这更加的让Retrofit变得强大. 如果在看这篇博客

基于AFNetWorking搭建APP的网络请求框架[iOS]

自从AFNetWorking(下文简称AFN)更新2.0版本之后,AFN的许多的问题得到的有效的解决,写法也得到了完善.前期主流的第三方网络类库 ASI作者宣布不再维护,国内大多数的主流APP都逐步接受并开始采用AFN.出于各自公司项目的不同需要,大家都会在AFN的基础上加一层不尽相同的封 装.很多新APP在选择方式时也会非常纠结.如何封装才可以让AFN更有效率更方便的应用于项目呢,对于这个问题,各人有各人的看法.基于做过以及读过的 几个项目,也来谈一下如何搭建一个APP的网络请求框架.由于本人