Nginx反向代理慢解决办法

在配置nginx反向代理时upstream里面server用localhost:端口,非常慢,看log,默认将localhost解析成ipv6的地址,故出现此问题,改成127.0.0.1后问题解决。
后端被代理网络应用使用Delphi开发

Get process name in Android

public static String getProcessName() {
  try {
    File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
    BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
    String processName = mBufferedReader.readLine().trim();
    mBufferedReader.close();
    return processName;
  } catch (Exception e) {
    e.printStackTrace();
    return null;
  }
}

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);
    }
  }

LoadingCircleView 会转圈圈的圆弧的实现

这个转圈圈,不是单纯的转圈圈,而是最近在安卓上很常见的那个很难用语言描述的不知道是谁发明出来的一段圆上的弧在旋转然后忽长忽短的那个。我期初看了好久也没太看懂他的规律,根据canvas的drawArc方法,我原来是想用startAngle和endAngle来描述圆弧,后来发现太复杂,灵光一闪发现用drawArc里面的startAngle和sweepAngle就能做到,而且逻辑相对不复杂。

public class LoadingCircleView extends View {

    private int strokeColor = Color.RED; // TODO: use a default from R.color...
    private int mCircleLineStrokeWidth = 8;
    private boolean drawCircle = true;
    private int circleBgColor = Color.WHITE;

    private RectF mRectF;
    private Paint mPaint;
    private int mWidth;
    private int mHeight;
    private int startAngle = -90;
    private int sweepAngle = 0;
    private Timer timer;
    private RedrawTimerTask timerTask;
    private boolean isRotating = false;
    private MyHandler handler = new MyHandler(this);

    public LoadingCircleView(Context context) {
        super(context);
        mContext = context;
        init(null, 0);
    }

    public LoadingCircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init(attrs, 0);
    }

    public LoadingCircleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        if(attrs != null){
            final TypedArray a = getContext().obtainStyledAttributes(
                    attrs, R.styleable.LoadingCircleView, defStyle, 0);

            strokeColor = a.getColor(
                    R.styleable.LoadingCircleView_strokeColor,
                    getResources().getColor(R.color.theme_dark_blue));
            drawCircle = a.getBoolean(R.styleable.LoadingCircleView_drawCircle,true);
            circleBgColor = a.getColor(R.styleable.LoadingCircleView_circleBgColor,circleBgColor);
            a.recycle();
        }else{
            strokeColor = getResources().getColor(R.color.theme_dark_blue);
        }

        mPaint = new Paint();
        mRectF = new RectF();
        mPaint.setAntiAlias(true);

        addOnLayoutChangeListener(new OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                mWidth = getWidth()-getPaddingLeft()-getPaddingRight();
                mHeight = getHeight()-getPaddingTop()-getPaddingBottom();
                if (mWidth != mHeight) {
                    int min = Math.min(mWidth, mHeight);
                    mWidth = min;
                    mHeight = min;
                }
                mCircleLineStrokeWidth = mWidth/15;
                if(mCircleLineStrokeWidth==0){
                    mCircleLineStrokeWidth = 1;
                }

                if(drawCircle){
                    mRectF.left = getPaddingLeft() + (int)(mHeight*0.2) + mCircleLineStrokeWidth / 2; 
                    mRectF.top = getPaddingTop() + (int)(mHeight*0.2) + mCircleLineStrokeWidth / 2; 
                    mRectF.right = getPaddingLeft() + (int)(mHeight*0.8) - mCircleLineStrokeWidth / 2; 
                    mRectF.bottom = getPaddingTop() + (int)(mHeight*0.8) - mCircleLineStrokeWidth / 2;
                }else{
                    mWidth -= mCircleLineStrokeWidth*2;
                    mHeight -= mCircleLineStrokeWidth*2;
                    mRectF.left = getPaddingLeft() + mCircleLineStrokeWidth / 2; // 左上角x
                    mRectF.top = getPaddingTop() + mCircleLineStrokeWidth / 2; // 左上角y
                    mRectF.right = getPaddingLeft() + mWidth + mCircleLineStrokeWidth / 2; // 左下角x
                    mRectF.bottom = getPaddingTop() + mHeight + mCircleLineStrokeWidth / 2; // 右下角y
                }
            }
        });
    }

    private void invalidateTextPaintAndMeasurements() {
        
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(Color.TRANSPARENT);

        if(drawCircle){
            mPaint.setColor(circleBgColor);
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(getPaddingLeft()+((float)mWidth)/2,getPaddingTop()+((float)mWidth)/2,((float)mWidth)/2,mPaint);
        }

        mPaint.setStrokeWidth(mCircleLineStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE);

        /*mPaint.setColor(Color.rgb(0xe9, 0xe9, 0xe9));
        canvas.drawArc(mRectF, -90, 360, false, mPaint);*/

        mPaint.setColor(strokeColor);
        canvas.drawArc(mRectF, startAngle, sweepAngle, false, mPaint);
    }

    public void startRotation(){
        if(isRotating){
            return;
        }
        isRotating = true;
        if(timer!=null){
            timer.cancel();
        }
        startAngle = -90;
        sweepAngle = 30;
        timer = new Timer();
        timerTask = new RedrawTimerTask(this);
        timer.schedule(timerTask,0,15);
    }

    public void stopRotation(){
        if(timer!=null){
            timer.cancel();
        }
        startAngle = -90;
        sweepAngle = 0;
        isRotating = false;
    }

    public void setProgress(float progress){
        if(progress<0 || progress>1){
            return;
        }
        if(isRotating){
            stopRotation();
        }
        startAngle = -90;
        sweepAngle = (int)(progress * 360);
        invalidate();
    }

    private class RedrawTimerTask extends TimerTask{

        private WeakReference<LoadingCircleView> v;

        private int direction = 0;
        private int startAngle = -90;
        private int sweepAngle = 30;

        public RedrawTimerTask(LoadingCircleView view){
            v = new WeakReference<LoadingCircleView>(view);
        }

        @Override
        public void run(){
            LoadingCircleView view = v.get();
            if(view == null){
                cancel();
                return;
            }
            startAngle = startAngle + 3 + direction * 6;
            sweepAngle = sweepAngle + 6 - direction * 12;

            if(sweepAngle == 30 && direction == 1){
                startAngle = startAngle%360;
                direction = 0;
                sweepAngle = -30;
            }else if(sweepAngle == 360){
                startAngle = startAngle%360;
                direction = 1;
                sweepAngle = 330;
            }

            int finalSweepAngle = sweepAngle;
            if(finalSweepAngle < 30){
                finalSweepAngle = 30;
            }else if(finalSweepAngle > 330){
                finalSweepAngle = 330;
            }
            view.handler.removeMessages(0);
            view.handler.sendMessage(Message.obtain(view.handler,0,startAngle,finalSweepAngle));
        }
    }

    private static class MyHandler extends Handler {

        private WeakReference<LoadingCircleView> v;

        MyHandler(LoadingCircleView view){
            v = new WeakReference<LoadingCircleView>(view);
        }

        @Override
        public void handleMessage(Message msg){
            LoadingCircleView view = v.get();
            if(view == null){
                return;
            }
            view.startAngle = msg.arg1;
            view.sweepAngle = msg.arg2;
            view.invalidate();
        }
    }
}

需要定义styleable

<declare-styleable name="LoadingCircleView">
    <attr name="strokeColor" format="color" /><!--圆弧颜色-->
    <attr name="drawCircle" format="boolean" /><!--是否在底层画一个圆-->
    <attr name="circleBgColor" format="color" /><!--是否圆的颜色-->
</declare-styleable>

Handle uncaught exception in Android app

  1. Handle uncaughtException in your Application subclass.
  2. After catching an exception, start a new activity to ask the user to send a log.
  3. Extract the log info from logcat's files and write to your own file.
  4. Start an email app, providing your file as an attachment.
  5. Manifest: filter your activity to be recognized by your exception handler.
  6. Optionally, setup Proguard to strip out Log.d() and Log.v().
public class MyApplication extends Application
{
  public void onCreate ()
  {
    // Setup handler for uncaught exceptions.
    Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
    {
      @Override
      public void uncaughtException (Thread thread, Throwable e)
      {
        handleUncaughtException (thread, e);
      }
    });
  }

  public void handleUncaughtException (Thread thread, Throwable e)
  {
    e.printStackTrace(); // not all Android versions will print the stack trace automatically

    Intent intent = new Intent ();
    intent.setAction ("com.mydomain.SEND_LOG"); // see step 5.
    intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
    startActivity (intent);

    System.exit(1); // kill off the crashed app
  }
}
private String extractLogToFile()
{
  PackageManager manager = this.getPackageManager();
  PackageInfo info = null;
  try {
    info = manager.getPackageInfo (this.getPackageName(), 0);
  } catch (NameNotFoundException e2) {
  }
  String model = Build.MODEL;
  if (!model.startsWith(Build.MANUFACTURER))
    model = Build.MANUFACTURER + " " + model;

  // Make file name - file must be saved to external storage or it wont be readable by
  // the email app.
  String path = Environment.getExternalStorageDirectory() + "/" + "MyApp/";
  String fullName = path + <some name>;

  // Extract to file.
  File file = new File (fullName);
  InputStreamReader reader = null;
  FileWriter writer = null;
  try
  {
    // For Android 4.0 and earlier, you will get all app's log output, so filter it to
    // mostly limit it to your app's output.  In later versions, the filtering isn't needed.
    String cmd = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) ?
                  "logcat -d -v time MyApp:v dalvikvm:v System.err:v *:s" :
                  "logcat -d -v time";

    // get input stream
    Process process = Runtime.getRuntime().exec(cmd);
    reader = new InputStreamReader (process.getInputStream());

    // write output stream
    writer = new FileWriter (file);
    writer.write ("Android version: " +  Build.VERSION.SDK_INT + "\n");
    writer.write ("Device: " + model + "\n");
    writer.write ("App version: " + (info == null ? "(null)" : info.versionCode) + "\n");

    char[] buffer = new char[10000];
    do 
    {
      int n = reader.read (buffer, 0, buffer.length);
      if (n == -1)
        break;
      writer.write (buffer, 0, n);
    } while (true);

    reader.close();
    writer.close();
  }
  catch (IOException e)
  {
    if (writer != null)
      try {
        writer.close();
      } catch (IOException e1) {
      }
    if (reader != null)
      try {
        reader.close();
      } catch (IOException e1) {
      }

    // You might want to write a failure message to the log here.
    return null;
  }

  return fullName;
}
public class SendLog extends Activity implements OnClickListener
{
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    requestWindowFeature (Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
    setFinishOnTouchOutside (false); // prevent users from dismissing the dialog by tapping outside
    setContentView (R.layout.send_log);
  }
}
<activity
    android:name="com.mydomain.SendLog"
    android:theme="@android:style/Theme.Dialog"
    android:textAppearance="@android:style/TextAppearance.Large"
    android:windowSoftInputMode="stateHidden">
    <intent-filter>
      <action android:name="com.mydomain.SEND_LOG" />
      <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
-assumenosideeffects class android.util.Log {
    public static int v(...);
    public static int d(...);
}