代码逻辑相关 遍历一个List集合 bad:
1 2 3 4 5 6 List<User> userList = new ArrayList <>(); for (int i = 0 ; i < userList.size(); i++) { User user = new User (); userList.add(user); }
better:
1 2 3 4 5 6 7 List<User> userList = new ArrayList <>(); User user = null ;for (int i = 0 , size = userList.size(); i < size; i++) { user = new User (); userList.add(user); }
①、第二种方式把 userList.size() 用变量 size 存值避免了每次迭代都去调用该方法(不要在 for 循环的第二个表达式的判 断调用对象的方法或字段);②、把 user 变量的声明放到循环外边,避免每次使用都去声明一下 User 对象。 ③、getCount() 方法的处理也类似,如用 int count = xxx.getCount() 缓存起来。
遍历HashMap的最佳方法 1 2 3 4 5 6 7 8 Map<String, User> userMap = new HashMap <>(); Iterator it = userMap.entrySet().iterator();while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); System.out.println(entry.getKey() + " = " + entry.getValue()); it.remove(); }
字符串 判断字符串str是否为null或空串 bad:
1 2 if (null == str || "" .equals(str)) {}
better:
1 2 if (TextUtils.isEmpty(str)) {}
TextUtils 是一个非常好用的工具类,把 List 转成字符串,逗号分隔;逗号分隔的 String 字符串,切割成 List ,分别可以 用 TextUtils 的 join 和 split 方法。
容易报空指针的情况 判断一个字符串的内容是否为某值 bad:
1 2 if (str.equals("hello" )) {}
better:
1 2 3 if ("hello" .equals(str)) {}
判断集合某个元素对象的某个字段是否为空 bad:
1 2 3 if (null != userList.get(i).name) {}
better:
1 2 3 if (null != userList.get(i) && null != userList.get(i).name) {}
常用资源释放 Cursor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 try { cursor = context.getContentResolver().query(Uri.parse(PROVIDER_SETTINGFILE), null , null , null , null ); } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null ){ try { cursor.close(); } catch (Exception e) { e.printStackTrace(); } } }
流文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Test { private static final int BUFFER_SIZE = 1024 ; public static void main (String[] args) { FileReader fr = null ; FileWriter fw = null ; try { fr = new FileReader ("origin.txt" ); fw = new FileWriter ("destination.txt" ); char [] buf = new char [BUFFER_SIZE]; int len = 0 ; while ((len = fr.read(buf)) != -1 ) { fw.write(buf, 0 , len); } } catch (Exception e) { throw new RuntimeException ("读写失败" ); } finally { if (fw != null ) try { fw.close(); } catch (IOException e) { e.printStackTrace(); } if (fr != null ) try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
WebView
首先就是使用自己封装的 WebView , 不在 xml 里面声明,而是直接代码 new 个对象,传入 application context 防止 activity 引用滥用。
1 2 webView = new BridgeWebView (getContext().getApplicationContext()); webFrameLayout.addView(webView, 0 );
在使用了这个方式后,基本上 90% 的 webview 内存泄漏的问题便得以解决。
而在 Android 4.4 版本以下,会出现 Android Webview 无法自动释放,如在 Fragment 中,使用 onDetach 来释放 Webview 是比较好的时机。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void onDetach () { releaseWebViews(); super .onDetach(); } private void releaseWebViews () { if (webView != null ) { try { if (webView.getParent() != null ) { ((ViewGroup) webView.getParent()).removeView(webView); } webView.destroy(); }catch (IllegalArgumentException e) { } RefWatcher refWatcher = FApplication.getRefWatcher(); refWatcher.watch(webView); webView = null ; } }
参考文章:
Android WebView: 性能优化不得不说的事
android内存优化之webview
Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class NoLeakActivity extends Activity { private NoLeakHandler mNoLeakHandler; @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); mNoLeakHandler = new NoLeakHandler (this ); Message message = Message.obtain(); mNoLeakHandler.sendMessageDelayed(message, 2000 ); } private static class NoLeakHandler extends Handler { private WeakReference<NoLeakActivity> mActivity; public NoLeakHandler (NoLeakActivity activity) { mActivity = new WeakReference <>(activity); } @Override public void handleMessage (Message msg) { super .handleMessage(msg); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class NoLeakActivity extends Activity { private Handler mHandler = new Handler (); @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); mHandler.postDelayed(new Runnable () { @Override public void run () { } }, 2000 ); } @Override protected void onDestroy () { mHandler.removeCallbacksAndMessages(null ); super .onDestroy(); } @Override public void onBackPressed () { mHandler.removeCallbacksAndMessages(null ); super .onBackPressed(); } }
其它 反面判断条件 bad:
1 2 3 4 5 6 7 public void testMethod (ArrayList<User> userList) { if (null != userList && userList.size() > 0 ) { for (int i = 0 , size = userList.size(); i < size; i++) { } } }
better:
1 2 3 4 5 6 7 8 public void testMethod (ArrayList<User> userList) { if (null == userList || userList.isEmpty()) { return ; } for (int i = 0 , size = userList.size(); i < size; i++) { } }
很多比较复杂的层级判断都可以从这些判断的反面出发,来降低程序的复杂性,从而提高可读性。
if与return搭配 bad:
1 2 3 4 5 6 7 8 9 10 11 public int testIfElse (String cmd) { if ("1" .equals(cmd)) { return 1 ; } else if ("2" .equals(cmd)) { return 2 ; } else if ("3" .equals(cmd)) { return 3 ; } else { return 4 ; } }
better:
1 2 3 4 5 6 7 8 9 10 11 12 public int testIfElse (String cmd) { if ("1" .equals(cmd)) { return 1 ; } if ("2" .equals(cmd)) { return 2 ; } if ("3" .equals(cmd)) { return 3 ; } return 4 ; }
对象序列化 Android 中的序列化官方推荐 Parceble , 其实 Parceble 最好用于内存之间数据的交换,如果要把数据写入硬盘的话,推荐实现 Serializable .
SharedPreferences SharedPreferences.Editor.commit 这个方法是同步的,一直到把数据同步到 Flash 上面之后才会返回,由于 IO 操作的不可控,尽量使用 apply 方法代替。apply 只在 API Level >= 9 才会支持,需要做兼容。不过,最新的 support v4 包已经为我们做好了处理,使用 SharedPreferencesCompat.EditorCompat.getInstance().apply(editor) 即可。
其它优化
静态变量不要直接或者间接引用 Activity、Service 等。这会使 Activity 以及它所引用的所有对象无法释放,然后,用户操作时间一长,内存就会狂升。
Handler 机制有一个特点是不会随着 Activity、Service 的生命周期结束而结束。也就是说,如果你 Post 了一个 Delay 的 Runnable , 然后在 Runnable 执行之前退出了 Activity , Runnable 到时间之后还是要执行的。如果 Runnable 里面包含更新 View 的操作,程序崩溃了。
不少人在子线程中更新 View 时喜欢使用 Context.runOnUiThread , 这个方法有个缺点,就是一但 Context 生命周期结束,比如 Activity 已经销毁时,一调用就会崩溃。
Application 的生命周期就是进程的生命周期。只有进程被干掉时,Application 才会销毁。哪怕是没有 Activity、Service 在运行,Application 也会存在。所以,为了减少内存压力,尽量不要在 Application 里面引用大对象、Context 等。
Activity 的 onDestroy 方法调用时机是不确定的(有时候离开界面很久之后才会调用 onDestroy 方法),应该避免指望通过 onDestroy 方法去释放与 Activity 相关的资源,否则会导致一些随机 bug .
如果使用 Context.startActivity 启动外部应用,最好做一下异常预防,因为寻找不到对应的应用时,会抛出异常。如果你要打开的是应用内的 Activity , 不防使用显式 Intent , 这样能提高系统搜索目标 Activity 的效率。
如果子类实现 Serializable 接口而父类未实现时,父类不会被序列化,但此时父类必须有个无参构造方法,否则会抛 InvalidClassException 异常。
transient 关键字修饰变量可以限制序列化。
View.getContext 获取控件上下文,写 RecyclerView 的 Adapter 的时候,为了使用 LayoutInflater , 不用在构造函数中传入一个外部的 context .
UI相关 Space Space 经常用于组件之间的缝隙,其 draw() 为空,减少了绘制渲染的过程。组件之间的距离使用 Space 会提高了绘制效率,特别是对于动态设置间距会很方便高效。正是因为 draw()为空,对该 view 没有做任务绘制渲染,所以不能对 Space 设置背景色。
tools 标签可以很好的帮助开发者实时预览 xml 的效果,并且运行以后 tools 标签的内容不会展示出来。例如:
1 2 3 4 <TextView android:layout_width ="match_parent" android:layout_height ="wrap_content" tools:text ="这段话只在预览时能看到,运行以后就看不到了" />
ContextCompat Android 6.0 之后 getResources().getColor() 被废弃了,可用 ContextCompat.getColor(context, R.color.color_name) 替换,ContextCompat 是 v4 包里的,请放心使用,另外还有 getDrawable() 等方法。