您的位置:首页 > 移动开发 > Android开发

android多线程以及线程池使用

2017-10-19 14:22 489 查看
之前有关线程、多线程、线程池等概念经常接触,但是实际工作中用的却不是很多,刚好公司app对商品详情页有一个优化的任务,在参考了一些主流app的处理方式以后,发现他们的思路都是一样的:拆分接口,模块化加载。

在确定了思路以后,对安卓客户端的程序设计我是这么考虑的:

- 接口请求与activity视图加载并发进行

- 尽可能早地进行接口请求

- 接口请求完成以后进入一个等待的状态,等待视图加载完成再进行数据绑定

- 由于接口请求完成的先后顺序不可控,需要一个统一的请求队列进行管理,并且利用synchronized关键字处理好同步问题

- 使用HandlerThread作为分发消息的单独线程

- 使用WeakReference来防止内存泄漏

首先介绍一些使用到的工具

CountDownLatch

CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

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实现同步了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息