1.AsyncTask静态变量作用
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
SerialExecutor和threadPoolExecutor都被声明为static类型的变量,SerialExecutor主要负责的工作将陆续来的AsyncTask调用存储起来,等上一个任务做完就将接下来的任务取出来给threadPoolExecutor来执行,这也是AsyncTask默认执行方式是串行的原因。
2.讨论一下static变量的作用域
我之前我做了三次这样的探讨,分别如下,主要代码和之前一样。
- 一个activity中
- 同一Application中的两个activity中
- 两个Application中
package com.example.asynctasktest;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
ProgressBar progressBar;
TextView txView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
txView = findViewById(R.id.textView);
new Task.execute((Object)null);
}
class Task extends AsyncTask<Object,Integer,Object>{
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
txView.setText("end");
}
@Override
protected Object doInBackground(Object[] objects) {
for(int i = 0; i <= 100; i++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
return null;
}
@Override
protected void onProgressUpdate(Integer[] values) {
super.onProgressUpdate(values);
progressBar.setProgress(values[0]);
txView.setText(values[0] + "%");
}
@Override
protected void onPreExecute() {
super.onPreExecute();
txView.setText("begin");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上面的代码很简单,就是简单的执行一下任务就更新一下进度条。
2.1 一个activity中
new Task().execute(1);
new Task().execute(2);
new Task().execute(3);
02-10 17:30:59.675 22003-22003/com.example.asynctasktest D/asyncTest: begin
02-10 17:30:59.676 22003-22003/com.example.asynctasktest D/asyncTest: begin
02-10 17:30:59.676 22003-22003/com.example.asynctasktest D/asyncTest: begin
02-10 17:30:59.678 22003-22078/com.example.asynctasktest D/asyncTest: 1
02-10 17:31:20.092 22003-22714/com.example.asynctasktest D/asyncTest: 2
02-10 17:31:20.112 22003-22003/com.example.asynctasktest D/asyncTest: end
02-10 17:31:40.404 22003-22720/com.example.asynctasktest D/asyncTest: 3
02-10 17:31:40.410 22003-22003/com.example.asynctasktest D/asyncTest: end
02-10 17:32:00.728 22003-22003/com.example.asynctasktest D/asyncTest: end
以之前的代码为模板,多次执行异步任务,从log可以看出20s执行一个任务,依次执行,将三个任务执行完。
从以上结论可以推论出:一个activity中多个AsyncTask共享同一个static变量SerialExecutor和threadPoolExecutor。
2.2 同一Application中的两个activity中
02-10 17:57:04.016 31010-31080/com.example.asynctasktest2222 D/asyncTest: begin
02-10 17:57:04.016 31010-31080/com.example.asynctasktest2222 D/asyncTest: 1
02-10 17:57:24.307 31010-31080/com.example.asynctasktest2222 D/asyncTest: end
02-10 17:57:24.310 31010-31538/com.example.asynctasktest2222 D/asyncTest: begin
02-10 17:57:24.310 31010-31538/com.example.asynctasktest2222 D/asyncTest: 2
02-10 17:57:44.573 31010-31538/com.example.asynctasktest2222 D/asyncTest: end
02-10 17:57:44.575 31010-31546/com.example.asynctasktest2222 D/asyncTest: begin
02-10 17:57:44.575 31010-31546/com.example.asynctasktest2222 D/asyncTest: 3
02-10 17:58:04.857 31010-31546/com.example.asynctasktest2222 D/asyncTest: end
02-10 17:58:04.859 31010-31664/com.example.asynctasktest2222 D/asyncTest2: begin
02-10 17:58:04.859 31010-31664/com.example.asynctasktest2222 D/asyncTest2: 1
02-10 17:58:25.168 31010-31664/com.example.asynctasktest2222 D/asyncTest2: end
02-10 17:58:25.170 31010-31676/com.example.asynctasktest2222 D/asyncTest2: begin
02-10 17:58:25.171 31010-31676/com.example.asynctasktest2222 D/asyncTest2: 2
02-10 17:58:45.487 31010-31676/com.example.asynctasktest2222 D/asyncTest2: end
02-10 17:58:45.488 31010-31682/com.example.asynctasktest2222 D/asyncTest2: begin
02-10 17:58:45.488 31010-31682/com.example.asynctasktest2222 D/asyncTest2: 3
02-10 17:59:05.799 31010-31682/com.example.asynctasktest2222 D/asyncTest2: end
从以上log可以发现第二个activity是在第一个activity的任务依次执行完后再依次执行的。
从以上结论可以推论出:一个application中不同的activity的多个AsyncTask共享同一个static变量SerialExecutor和threadPoolExecutor。
2.3 两个application中
为了这个猜想,我复制了初始的activity,改了一下包名,使之有两个逻辑一样但是两个application的存在。包名为com.example.aysnctasktest2222就是第二个包了。
02-10 17:46:52.274 27250-27269/com.example.asynctasktest D/asyncTest: begin
02-10 17:46:52.274 27250-27269/com.example.asynctasktest D/asyncTest: 1
02-10 17:46:53.735 27281-27300/com.example.asynctasktest2222 D/asyncTest: begin
02-10 17:46:53.735 27281-27300/com.example.asynctasktest2222 D/asyncTest: 1
02-10 17:47:12.555 27250-27269/com.example.asynctasktest D/asyncTest: end
02-10 17:47:12.557 27250-27320/com.example.asynctasktest D/asyncTest: begin
02-10 17:47:12.557 27250-27320/com.example.asynctasktest D/asyncTest: 2
02-10 17:47:14.026 27281-27300/com.example.asynctasktest2222 D/asyncTest: end
02-10 17:47:14.027 27281-27321/com.example.asynctasktest2222 D/asyncTest: begin
02-10 17:47:14.028 27281-27321/com.example.asynctasktest2222 D/asyncTest: 2
02-10 17:47:32.829 27250-27320/com.example.asynctasktest D/asyncTest: end
02-10 17:47:32.831 27250-27326/com.example.asynctasktest D/asyncTest: begin
02-10 17:47:32.831 27250-27326/com.example.asynctasktest D/asyncTest: 3
02-10 17:47:34.337 27281-27321/com.example.asynctasktest2222 D/asyncTest: end
02-10 17:47:34.339 27281-27327/com.example.asynctasktest2222 D/asyncTest: begin
02-10 17:47:34.339 27281-27327/com.example.asynctasktest2222 D/asyncTest: 3
02-10 17:47:53.097 27250-27326/com.example.asynctasktest D/asyncTest: end
02-10 17:47:54.658 27281-27327/com.example.asynctasktest2222 D/asyncTest: end
从上面的log可以看出这两个应用的任务执行互不影响,为并行执行。
从以上结论可以推论出:两个application中activity的多个AsyncTask并不共享同一个static变量SerialExecutor和threadPoolExecutor。
2.4 总结
static变量可以在同一个application中共享,不能在不同的application中共享。(其实还可以探讨Service、Fragment之类的共享行为,这样才更有说服力)
3.探讨结论的正确性
1)我先在网上搜了下static变量的基础,如下:
static
1. static变量
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。
两者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),
当然也可以通过对象来访问(但是这是不推荐的)。对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响
(灵活)。
2. static代码块
static代码块是类加载时,初始化自动执行的。如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。
3. static方法
static方法可以直接通过类名调用,任何的实例也都可以调用,因此static方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法
(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
static方法只能访问static的变量和方法,因为非static的变量和方法是需要创建一个对象才能访问的,而static的变量/方法不需要创建任何对象。
********
static的数据或方法,属于整个类的而不是属于某个对象的,是不会和类的任何对象实例联系到一起。所以子类和父类之间可以存在同名的static方法名,这里不涉及重载。所以不能把任何方法体内的变量声明为static,例如:
fun() {
static int i=0; //非法。
}
其实理解static是只有一个存储地方,而使用时直接使用,不需要创建对象,就能明白以上的注意事项。
另外,一般的类是没有static的,只有内部类可以加上static来表示嵌套类。
我其实对进程线程以及它们所携带的存储堆栈什么的一直没搞清楚,这里先有个猜想。每个进程有自己的存储仓库,是分别独立的,static就存储在各个进程的存储仓库里,由于进程是互相独立的(好像是数据安全考虑),所以static应该也是互相独立的。
2)下面我还找到一个相关的:
在Android开发中,我们经常会使用到static来修饰我们的成员变量,其本意是为了让多个对象共用一份空间,
节省内存,或者是使用单例模式,让该类只生产一个实例而在整个app中使用。然而在某些时候不恰当的使用或者
是编程的不规范却会造成了内存泄露现象(java上的内存泄漏指内存得不到gc的及时回收,从而造成内存占用过多的现象)
这里直接是以结论的形式给出,static变量由整个app共享,并没有说具体原因。
3)下面找到的也是证明static是由进程所共享的例子。
当窗口消毁时,Activity的静态变量是存在的,因为静态变量是属全局变量、静态变量是整个应用程序的公共变量(即使上面写的是私有),所以Activity消毁时,静态变量是不会清除的。但当什么时候才会清除呢,
重写onDestory方法,在onDestory里添加android.os.Process.killProcess(android.os.Process.myPid());把线程干掉,再次调用就正常了。
上面说过静态变量是整个应用程序的,所以只有当各个应用程序的进程消毁时,静态变量才会毁,所以调用android.os.Process.killProcess(android.os.Process.myPid());就正常了。
算了,找不到直接探讨这个问题的。结论结合实验大致是正确的,暂且先这样认为吧。。。
4. 总结
static变量可以在同一个application中共享,不能在不同的application中共享。
本文主要还是从实际测试角度解释这个问题,没有真正从理论层面解释,待后续有更深理解后补充。