android多线程以及线程池使用
2017-10-19 14:22
489 查看
之前有关线程、多线程、线程池等概念经常接触,但是实际工作中用的却不是很多,刚好公司app对商品详情页有一个优化的任务,在参考了一些主流app的处理方式以后,发现他们的思路都是一样的:拆分接口,模块化加载。
在确定了思路以后,对安卓客户端的程序设计我是这么考虑的:
- 接口请求与activity视图加载并发进行
- 尽可能早地进行接口请求
- 接口请求完成以后进入一个等待的状态,等待视图加载完成再进行数据绑定
- 由于接口请求完成的先后顺序不可控,需要一个统一的请求队列进行管理,并且利用synchronized关键字处理好同步问题
- 使用HandlerThread作为分发消息的单独线程
- 使用WeakReference来防止内存泄漏
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。
HandlerThread
HandlerThread是一个子线程,它拥有自己的消息队列,利用它可以实现子线程与主线程之间的通信。
ExecutorService
ExecutorService线程池,系统提供了多种线程池实例,这里我用到的是newFixedThreadPool,这是一种定长线程数的线程池,这里我配置的线程数是手机CPU核心数+1。
2.视图初始化完以后开始接收数据
总结:
大致的流程就是这样,具体的代码逻辑就不贴了。在视图上,我采用模块化加载的方式将页面分成了多个模块,最理想的是每个接口对应相应的模块,但是实际上模块与接口之间都是穿插的较多,这时候就要用到synchronized实现同步了。
在确定了思路以后,对安卓客户端的程序设计我是这么考虑的:
- 接口请求与activity视图加载并发进行
- 尽可能早地进行接口请求
- 接口请求完成以后进入一个等待的状态,等待视图加载完成再进行数据绑定
- 由于接口请求完成的先后顺序不可控,需要一个统一的请求队列进行管理,并且利用synchronized关键字处理好同步问题
- 使用HandlerThread作为分发消息的单独线程
- 使用WeakReference来防止内存泄漏
首先介绍一些使用到的工具
CountDownLatchCountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。
HandlerThread
HandlerThread是一个子线程,它拥有自己的消息队列,利用它可以实现子线程与主线程之间的通信。
ExecutorService
ExecutorService线程池,系统提供了多种线程池实例,这里我用到的是newFixedThreadPool,这是一种定长线程数的线程池,这里我配置的线程数是手机CPU核心数+1。
部分代码流程
1.进入DetailActivity之前就开始请求接口(我们将详情页接口拆分成了6个接口)//title和image分别为商品的标题和图片,我们可以在进入activity后先显示这2项,一般都可以在入口处获取到这2项数据,这样能给人一种快速打开页面的感觉,主流的app都是这么处理的 public static void start(Activity activity, PageExtras pageExtras) { if (pageExtras != null) { ItemDataQueue.get().loadAll(pageExtras.getItemUid(), new int[]{MSG_WHAT_BASIC, MSG_WHAT_COMMENT, MSG_WHAT_COUPON, MSG_WHAT_MARKET, MSG_WHAT_SKU, MSG_WHAT_SUGGEST}); Intent intent = new Intent(activity, DetailActivity.class); intent.putExtra("item_uid", pageExtras.getItemUid()); intent.putExtra("title", pageExtras.getTitle()); intent.putExtra("image", pageExtras.getImage()); activity.startActivity(intent); } } //请求队列 public class ItemDataQueue { private static ItemDataQueue itemDataQueue; public synchronized static ItemDataQueue get(){ if (itemDataQueue == null){ itemDataQueue = new ItemDataQueue(); } return itemDataQueue; } private ItemDataQueueLatch<MKItemBasic> countDownLatchBasic; //省略其余5个 /** * 并行加载所有请求 * @param item_uid */ public void loadAll(final String item_uid , int...targets){ cancelAll(); if (targets != null){ for (int t : targets){ switch (t){ case DetailActivity.MSG_WHAT_BASIC: countDownLatchBasic = new ItemDataQueueLatch<>(item_uid); httpBasic(item_uid); break; ... } } } } private void httpBasic(final String item_uid) { countDownLatchBasic = new ItemDataQueueLatch<>(item_uid); MKItemCenter.getItemBasic( item_uid, new BusinessListener(null) { @Override public void onSuccess(MKBaseObject baseObject) { if (countDownLatchBasic != null && TextUtils.equals(countDownLatchBasic.getItem_uid(),item_uid)){ countDownLatchBasic.setData(((MKItemBasicResponse)baseObject).getData()); countDownLatchBasic.countDown(); } } @Override public void onFail(MKBaseObject baseObject) { if (countDownLatchBasic != null && TextUtils.equals(countDownLatchBasic.getItem_uid(),item_uid)){ countDownLatchBasic.countDown(); } } @Override public void onError() { if (countDownLatchBasic != null && TextUtils.equals(countDownLatchBasic.getItem_uid(),item_uid)){ countDownLatchBasic.countDown(); } } }); } public MKItemBasic getBasic(String item_uid){ if (countDownLatchBasic == null){ return null; } try { countDownLatchBasic.await(TIME_OUT_SEC, TimeUnit.SECONDS); if (countDownLatchBasic != null && TextUtils.equals(countDownLatchBasic.getItem_uid(),item_uid)){ return countDownLatchBasic.getData(); } } catch (InterruptedException e) { e.printStackTrace(); } return null; } public void cancelAll(){ if (countDownLatchBasic != null){ countDownLatchBasic.countDown(); countDownLatchBasic = null; } ... } }
2.视图初始化完以后开始接收数据
//创建线程池 public class ItemDataThreadPool { private static ExecutorService executorService; //分发消息的队列的单独线程 private static HandlerThread handlerThread; public static ExecutorService get(){ if (executorService == null){ executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1); } return executorService; } public static HandlerThread getHandlerThread(){ if (handlerThread == null || !handlerThread.isAlive()){ handlerThread = new HandlerThread("DetailActivity"); handlerThread.start(); } return handlerThread; } } //DetailActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_detail); ... fetchData(mItemUid,targets); } public void fetchData(String item_uid, int... targets) { for (int t : targets) { switch (t) { case DetailActivity.MSG_WHAT_BASIC: mData.clearBasic(); ItemDataThreadPool.get().execute(new Runnable() { @Override public void run() { Message msg = new Message(); msg.what = MSG_WHAT_BASIC; msg.obj = ItemDataQueue.get().getBasic(mItemUid); mHandler.sendMessage(msg); } }); break; ... } } } //向主线程发送接收到的数据 public class ItemHandler extends Handler { private final WeakReference<DetailActivity> mActivity; public ItemHandler(Looper looper , DetailActivity activity){ super(looper); mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { if (mActivity.get() != null && !mActivity.get().isFinishing()){ switch (msg.what){ case MSG_WHAT_BASIC: if (msg.obj != null){ mActivity.get().setBasicData((MKItemBasic) msg.obj); } else { mActivity.get().setBasicData(null); } break; ... } } } } //最后在DetailActivity的setBasicData等方法中绑定数据 public void setBasicData(final MKItemBasic basicData) { ... }
总结:
大致的流程就是这样,具体的代码逻辑就不贴了。在视图上,我采用模块化加载的方式将页面分成了多个模块,最理想的是每个接口对应相应的模块,但是实际上模块与接口之间都是穿插的较多,这时候就要用到synchronized实现同步了。
相关文章推荐
- Android中Callable、Future、FutureTask的概念以及几种线程池的使用
- Android中的多线程处理(二)——使用线程池中的线程
- android 多线程 - 线程池 Executors.newFixedThreadPool 的使用例子
- android 线程池的使用以及Executors的优缺点
- Android线程池(一)——Executors(线程池)以及FutureTask使用示例
- 完全解析Android多线程中线程池ThreadPool的原理和使用
- Android多线程--HandlerThread的使用以及原理
- Android线程池(一)——Executors(线程池)以及FutureTask使用示例
- 使用线程池以及FutureTask管理多线程任务
- Android 多线程,线程池,并发处理以及ANR
- Android 多线程,线程池的使用
- Android进阶——多线程系列之四大线程池的使用介绍
- android 多线程 - 并行包线程池为例说说线程池的设计需求及使用
- 【Android游戏开发二十一】Android os设备谎言分辨率的解决方案!以及简单阐述游戏引擎如何使用!
- [转]Android上GTalk以及Push机制的XMPP数据选择使用protobuf格式而非XML格式
- Android中资源文件夹res/raw和assets的使用(续)——分割文件以及合并文件
- iPhone SDK中多线程的使用方法以及注意事项
- android中APK包的安装以及adb命令的使用
- Android常用控件-DatePicker以及对话框的两种使用方法
- 如何理解、使用Android LogCat以及通过Monkey进行压力测试