(四) 结合AsyncTask探讨static在Android中的作用域

前言:大家新年快乐哈,今天在老家,只有笔记本,各种装备都没带上,就把之前写的博客的细节拿出来特意总结下。主要是就 (三)AsyncTask中所述的静态SerialExecutor和静态的默认线程池讨论一下static变量在Android中的作用域。

1.AsyncTask静态变量作用

首先回顾一下之前博客中说的以下这两个变量。源码如下:
  1. private static class SerialExecutor implements Executor {  
  2.         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
  3.         Runnable mActive;  
  4.   
  5.         public synchronized void execute(final Runnable r) {  
  6.             mTasks.offer(new Runnable() {  
  7.                 public void run() {  
  8.                     try {  
  9.                         r.run();  
  10.                     } finally {  
  11.                         scheduleNext();  
  12.                     }  
  13.                 }  
  14.             });  
  15.             if (mActive == null) {  
  16.                 scheduleNext();  
  17.             }  
  18.         }  
  19.   
  20.         protected synchronized void scheduleNext() {  
  21.             if ((mActive = mTasks.poll()) != null) {  
  22.                 THREAD_POOL_EXECUTOR.execute(mActive);  
  23.             }  
  24.         }  
  25.     }  
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变量的作用域

我之前我做了三次这样的探讨,分别如下,主要代码和之前一样。

  1. 一个activity中
  2. 同一Application中的两个activity中
  3. 两个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中

为了这个猜想,我以最初的activity为模板复制了一份,让这第一个activity启动的时候,将它的模板也带起来,log对应于AsyncTest2
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中共享。

本文主要还是从实际测试角度解释这个问题,没有真正从理论层面解释,待后续有更深理解后补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值