YangMing +

基本知识

所有代码研究基于android4.2.2_R1

关于instance单例的一些问题

import java.util.ArrayList;
import java.util.List;

public class Hello {
        public List<String> mXs = new ArrayList<String>();
        public List<String> mYs = new ArrayList<String>();
        public List<String> mZs = new ArrayList<String>();

        static Hello sInstance = null;
        
        public static Hello getInstance() {
                sInstance = new Hello();
                return sInstance;
        }
        
        public String getX(int i) {
                synchronized (Hello.class) {
                        return mXs.get(i);
                }
        }
        
        public void addX(String s) {
                synchronized (Hello.class) {
                        mXs.add(s);
                }
        }
        
        public String getY(int i) {
                synchronized (Hello.class) {
                        return mYs.get(i);
                }
        }
        
        public void addY(String s) {
                synchronized (Hello.class) {
                        mYs.add(s);
                }
        }

        public void addZ(String s) {
                synchronized (this) {
                        mZs.add(s);
                }
        }

        public String getZ(int i) {
                synchronized (this) {
                        return mZs.get(i);
                }
        }
        

        
        public synchronized int removeAllFromX(String x) {
                int i = 0;
                for(String s : mXs) {
                        if(s.equals(x)) {
                                mXs.remove(s);
                                ++i;
                        }
                }
                return i;
        }
}

这段代码存在的问题:

  1. 单例非线程安全,可能会出现多个实例
  2. mXs多线程操作会出问题(Race Condition)
    • 原因: addX 使用的是受Hello类的静态锁保护的临界区 / removeAllFromX 使用的是对象实例的内置锁保护的临界区,因此多线程可以同时读写mXs,产生RaceCondition
    • 解决方法:
public String getX(int i) {
        synchronized (this) {
            return mXs.get(i);
        }
    }
    
    public void addX(String x) {
        synchronized (this) {
            mXs.add(x);
        }
    }
  1. 并发性能差
  2. 原因: mXs/mYs/mZs的读写操作存在共用同一把锁的问题, 会导致不必要的block. 比如操作mXs时,会block操作mY(取 决于怎么修改问题2)
  3. 解决方法, mXs/mYs/mZs 各用各的锁
比如:
  Object lockX = new Object(); // -> 保护mXs
  Object lockY = new Object(); // -> 保护mYs
  Object lockZ = new Object(); // -> 保护mZs
  1. removeAllFromX遍历删除有问题:
    • 解决方法: 使用Iterator或者遍历时标记, 遍历完毕后再统一删除

高效的代码样例

public class Demo {  
    
    public static void main(String[] args){
        
        //1:考察Integer的cache,避免coding的时候产生更多的对象
        //-128~127有缓存对象的
        Integer i1 = Integer.valueOf(1);
        Integer i2 = Integer.valueOf(1);
        System.out.println(i1==i2);//true
        
        Integer i3 = Integer.valueOf(1000);
        Integer i4 = Integer.valueOf(1000);
        System.out.println(i3 == i4);//false
        
        //2:考察Java中的常量池概念,避免coding的时候产生更多的对象
        String s1 = "aa";
        String s2 = new String("aa");
        String s3 = String.valueOf("aa");
        
        System.out.println(s1 == s2);//false
        System.out.println(s1 == s3);//true
        System.out.println(s2 == s3);//false
        //调用s2的什么方法,可以让s2和s1,s3相等,这个方法的作用是什么?
        //intern方法,这个方法就是返回常量池中值为:"aa"的对象
        
        //3:考察对ArrayList的源码解析程度,coding的时候提高程序的的效率,ArrayList默认的容量是10,如果超过10的话,会自动扩容。
        //已知list的大小为10,list1和list2的定义哪种方式比较好:效果一样的
        //已知list的大小为20,list1和list3的定义哪种方式比较好:list3方式好
        ArrayList<String> list1 = new ArrayList<String>();
        ArrayList<String> list2 = new ArrayList<String>(10);
        ArrayList<String> list3 = new ArrayList<String>(20);
        
    }

}

值传递与引用传递

public class Test {
        public static void main(String[] args) {
                String str = new String("good");
                char[] ch = {'a', 'b', 'c'};
                
                Test t = new Test();
                t.change(str, ch);
                System.out.print(str + " ");
                System.out.print(ch);
                
        }
        
        public void change(String str, char[] ch) {
                str = "welcome";
                ch[0] = 'g';
        }
}

打印结果为:good gbc

View的常见动画

实现继承View的控件,在加载到布局后,得到长宽时画一个扇形在2秒内从0度到360度匀速动画。长宽为100dp。

public class SectorView extends View{
        
        private float mHeight = 0;
        private float mWidth = 0;
        private Paint mPaint;
        private RectF mRectf;
        private float mSweep = 0;
        
        public SectorView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
            getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                        
                        @Override
                        public void onGlobalLayout() {
                                int height = getHeight();
                                if(height != 0){
                                        mHeight = height;
                                        mWidth = getWidth();
                                        mRectf = new RectF(0, 0, mWidth, mHeight);
                                        getViewTreeObserver().removeGlobalOnLayoutListener(this);
                                        startAnim();
                                }
                        }

                });
    }
        
        private void startAnim() {
                ValueAnimator anim = ValueAnimator.ofFloat(0,360);
                anim.setDuration(2000);
                anim.setInterpolator(new LinearInterpolator());
                anim.addUpdateListener(new AnimatorUpdateListener() {
                        
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                                float sweep = (Float)animation.getAnimatedValue();
                                mSweep = sweep;
                                invalidate();
                        }
                });
                anim.start();
    }

        private void init() {
                mPaint = new Paint();
                mPaint.setAntiAlias(true);
                mPaint.setColor(0xFF0000FF);
    }
        
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            
            if(mHeight > 0){
                canvas.save();
                canvas.rotate(-90, mWidth/2, mHeight/2);
                canvas.drawArc(mRectf, 0, mSweep, true, mPaint);
                canvas.restore();
            }
        }


}

常见内存问题

  1. 构建Adapter适配器时 convertview不重用
  2. 游标 不及时关闭
  3. I/O流不及时关闭
  4. bitmap确认不在用时不及时recycle
  5. 大的对象比如activity不被回收

SharedPreference的问题:

Editor 的apply和commit方法异同

  1. apply没有返回值而commit返回boolean表明修改是否提交成功
  2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
  3. apply方法不会提示任何失败的提示。 由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。

关于webview:

How to resize a android webview after adding data in it:

private void setupWebView() {
    webView.getSettings().setJavaScriptEnabled(true);
    webView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            webView.loadUrl("javascript:MyApp.resize(document.body.getBoundingClientRect().height)");
            super.onPageFinished(view, url);
        }
    });
    webView.addJavascriptInterface(this, "MyApp");
}
@JavascriptInterface
public void resize(final float height) {
    MyActivity.this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            webView.setLayoutParams(new LinearLayout.LayoutParams(getResources().getDisplayMetrics().widthPixels, (int) (height * getResources().getDisplayMetrics().density)));
        }
    });
}

参考

AndRoid-Memory-leaks-OR-Different-Ways-to-Leak

评论:

Blog

Opinion

Project