2016年11月

JAVA神奇又强大的反射

最近在写安卓项目的时候遇到一个很棘手的问题,AsyncTask的某一个task似乎被前一个task卡住了很久,应该是之前的task执行了一些非常耗时的操作,使得后面的task一直被卡住。查看源码后发现那个代码中用的是execute()方法而不是executeOnExecutor,所以使用的是默认的SERIAL_EXECUTOR,我理所当然地认为是某个地方使用了SERIAL_EXECUTOR然后用时太长导致后面的task都被卡住,所以把项目里用execute的地方都改成了executeOnExecutor,发现问题没解决。苦于没有解决办法,拖了几天没看。今天再去看代码的时候神奇的发现SERIAL_EXECUTOR使用的居然也是THREAD_POOL_EXECUTOR,只不过内部做了一个serial处理,我就更加疑惑了,怎么用了线程池还会被卡住呢。。。

我就在想,如果有什么办法可以在THREAD_POOL_EXECUTOR执行execute的方法的时候printStackTrace一下就好了,然后突发其想能不能利用java的反射原理把AsyncTask的THREAD_POOL_EXECUTOR替换成我自己的,这样我就可以打log了,上网搜索了一下,果然可以,真是太强大了。

打log发现是一个第三方sdk用AsyncTask来执行网络连接,然后很长时间都没能连到服务器,导致被卡住,真是。。。所幸问题找到了,去掉那个库问题果然解决了。Java反射真是强大

不过,我在打log的时候惊奇的发现,THREAD_POOL_EXECUTOR在我手机上只开了一个线程,很奇怪,按理说不应该这样的。。。留到后面再看看是咋回事吧

在Application初始化的时候

MyExecutor executor = new MyExecutor();
    try {
      Field field = AsyncTask.class.getField("THREAD_POOL_EXECUTOR");
      field.setAccessible(true);
      field.set(null, executor);
    } catch (Exception e) {
      Log.e("MyAsyncTask","exception when using reflection");
    }

自己写的Executor

public static class MyExecutor extends ThreadPoolExecutor {

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    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;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
      private final AtomicInteger mCount = new AtomicInteger(1);

      public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
      }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

    public MyExecutor() {
      super(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
          sPoolWorkQueue, sThreadFactory);
      allowCoreThreadTimeOut(true);
    }

    @Override public void execute(Runnable command) {
      if (command != null) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        new Throwable().printStackTrace(pw);
        Log.d("MyAsyncTask", sw.toString());
      }
      super.execute(command);
    }
  }