AsyncTask引发的问题

       最近写一个android小应用,用到了AsyncTask发现一些问题,写篇日志避免日后再犯相同的错误,也给遇到与我

相同问题和疑惑的小伙伴们点思路。

AsyncTask的基本使用方法这里不再赘述,直接切入主题。

我用的SDK4.0,代码(片段)如下:

package com.lic.centerctrl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.lic.activity.ActivityInterFace;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;

public class MainService extends Service{

	private static Task task;//当前执行任务
	private static Map<String, Activity> allActivitys = new HashMap<String, Activity>();//缓存activity集合
	private static ExecutorService exec = Executors.newSingleThreadExecutor();
	
	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		MainAsyncTask asyncTask = new MainAsyncTask();
		//asyncTask.execute(task);
		//asyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, task);
		//asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, task);
		//asyncTask.executeOnExecutor(Executors.newFixedThreadPool(2), task);
		asyncTask.executeOnExecutor(exec, task);
	}

	private final class MainAsyncTask extends AsyncTask<Object, Integer, Object>{
		private Task task;
		@Override
		protected Object doInBackground(Object... params) {
			Object result = null;
			task = (Task)params[0];
			switch (task.getTaskID()) {
			case Task.TASK_USER_LOGIN:
				try {
					Thread.sleep(3000);
					System.out.println("任务"+task.getTaskID()+" Thread id: "+Thread.currentThread().getId());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				break;
			case 2:
				System.out.println("任务"+task.getTaskID()+" Thread id: "+Thread.currentThread().getId());
				break;
			}
			return result;
		}

		@Override
		protected void onPostExecute(Object result) {
			super.onPostExecute(result);
			ActivityInterFace aif;
			switch (task.getTaskID()) {
			case Task.TASK_USER_LOGIN:
				aif = (ActivityInterFace)allActivitys.get("LoginActivity");
				aif.refresh(1, result);
				break;
			case 2:
				aif = (ActivityInterFace)allActivitys.get("LoginActivity");
				aif.refresh(2, result);
				break;
			default:
				break;
			}
		}
		
	}
	
	/**
	 * 添加新任务
	 * @param task
	 */
	public static void addTask(Context context, Task task) {
		MainService.task = task;
		context.startService(new Intent("mainService"));
	}

	/**
	 * 缓存activity
	 * @param activity
	 */
	public static void addActivity(Activity activity) {
		String path = activity.getClass().getName();
		String name = path.substring(path.lastIndexOf(".")+1);
		allActivitys.put(name, activity);
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

}

    当我执行两次调用asyncTask.execute(task);时发现只有当第一次的任务完成后才执行下一下任务!!怎么回事?

AyncTask不是号称异步线程池吗?既然是线程池那么多任务执行时应该可以并发执行啊,至少两个任务可以并发执

行,以前看过一个视频,人家的就可以啊!纠结了一下午,通过查阅资料和自己的动手实验终于把问题搞明白了。

    原来在SDK3.0以前的版本执行asyncTask.execute(task);时的确是多线程并发执行的,线程池大小为5,最大可大

128个,google在3.0以后的版本中做了修改,将asyncTask.execute(task);修改为了顺序执行,即只有当一个的实

的任务完成后在执行下一个实例的任务。

    那么怎么才能并发执行呢,很简单,3.0后新增了一个方法executeOnExecutor(Executor exec, Object... params),

该方法接受2个参数,第一个是Executor,第二个是任务参数。第一个是线程池实例,google为我们预定义了两种:

一种是AsyncTask.SERIAL_EXECUTOR,第二种是AsyncTask.THREAD_POOL_EXECUTOR,顾名思义,第一

其实就像3.0以后的execute方法,是顺序执行的。第二种就是3.0以前的execute方法,是可以并发执行的。我们直

接用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, task);就可以多任务并发执行了。

    既然executeOnExecutor第一个参数是Executor,那么我们可以自定义Executor吗?当然可以,Executor主要由四

类型newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

(具体使用解析可以看我上一篇文章点击打开链接),可是当我这样使用

asyncTask.executeOnExecutor(Executors.newFixedThreadPool(1), task);或者

asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor, task);并没有像我想象的与

asyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, task);那样是单线程顺序执行,而是像

asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, task);是多线程并发执行的,我不是

已经规定newFixedThreadPool的线程池数量是1或者是newSingleThreadExecutor单线程了么!怎么回事呢?原来程

序在每次调用asyncTask.executeOnExecutor(Executors.newFixedThreadPool(2), task)时会获取一个新的Executor对

象,这个对象内的线程只执行对应的task,所以无论哪种情况每个task都有一个新的线程来执行,即并发执行。

知道原因就好办了,我们定义个一全局静态变量

private static ExecutorService exec = Executors.newSingleThreadExecutor();程序在每次调用

asyncTask.executeOnExecutor(exec, task);时是使用的同一个Executor,执行效果如下:

当Executor类型为:private static ExecutorService exec = Executors.newFixedThreadPool(2);只有两个线程在执行

任务

当Executor类型为:private static ExecutorService exec = Executors.newSingleThreadExecutor();只有一个线程在执行任务

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 `onReceive()` 方法中开启线程是可能存在问题的,主要有以下几个方面的考虑: 1. 主线程阻塞:`onReceive()` 方法是在主线程中执行的,如果在该方法中开启一个耗时操作的线程,可能会导致主线程被阻塞,影响应用程序的响应性能。如果耗时操作持续时间较长,甚至可能引发 ANR(Application Not Responding)错误。 2. 生命周期管理:`onReceive()` 方法的执行时间是有限制的,如果在该方法中开启一个新的线程,而且该线程的执行时间超过了 `onReceive()` 方法的时间限制,那么系统可能会终止该线程的执行,从而导致未完成的操作。 3. 内存泄漏:如果在 `onReceive()` 方法中开启一个线程,并且该线程持有外部对象的引用,如果没有正确地管理线程生命周期,可能会导致内存泄漏问题。例如,如果线程在 `onReceive()` 方法执行完后仍然在运行,并且持有对 `Context` 或其他对象的引用,这些对象无法被及时释放,从而造成内存泄漏。 为了避免以上问题,可以考虑以下几种解决方案: 1. 使用 IntentService:可以将耗时操作放在 IntentService 中执行,IntentService 会自动创建一个工作线程来处理任务,在任务执行完毕后自动停止。 2. 使用 AsyncTask:可以在 `onReceive()` 方法中使用 AsyncTask 执行耗时操作,AsyncTask 可以方便地在后台线程执行任务,并在任务完成后回到主线程更新 UI。 3. 使用 JobScheduler 或 WorkManager:适用于需要执行较长时间的后台任务,可以使用 JobScheduler 或 WorkManager 来调度任务的执行,以避免在 `onReceive()` 方法中开启线程。 需要根据具体的场景和需求来选择适合的解决方案,确保在 `onReceive()` 方法中不会产生性能问题和内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值