一、Android编码规范
1.java代码中不出现中文,最多注释中可以出现中文;
2.局部变量命名、静态成员变量命名:只能包含字母,单词首字母出第一个都为大写,其他字母都为小写;
3.常量命名:只能包含字母和_,字母全部大写,单词之间用_隔开;
4.layout中的id命名:命名模式为:view缩写_模块名称_view的逻辑名称
view的缩写详情如下
LayoutView:lv
RelativeView:rv
TextView:tv
ImageView:iv
ImageButton:im
Button:btn
5.activity中的view变量命名
命名模式为:逻辑名称+view缩写
建议:如果layout文件很复杂,建议将layout分成多个模块,每个模块定义一个moduleViewHolder,其成员变量包含所属view
6.strings.xml中的id命名:
二、Android性能优化
1.http用gzip压缩,设置连接超时时间和响应超时时间
http请求按照业务需求,分为是否可以缓存和不可缓存,那么在无网络的环境中,仍然通过缓存的httpresponse浏览部分数据,实现离线阅读。
2.listview 性能优化
1).复用convertView
在getItemView中,判断convertView是否为空,如果不为空,可复用。如果couvertview中的view需要添加listerner,代码一定要在if(convertView==null){}之外。
2).异步加载图片
item中如果包含有webimage,那么最好异步加载
3).快速滑动时不显示图片
当快速滑动列表时(SCROLL_STATE_FLING),item中的图片或获取需要消耗资源的view,可以不显示出来;而处于其他两种状态(SCROLL_STATE_IDLE 和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来
3.使用线程池,分为核心线程池和普通线程池,下载图片等耗时任务放置在普通线程池,避免耗时任务阻塞线程池后,导致所有异步任务都必须等待
4.异步任务,分为核心任务和普通任务,只有核心任务中出现的系统级错误才会报错,异步任务的ui操作需要判断原activity是否处于激活状态
5.尽量避免static成员变量引用资源耗费过多的实例,比如Context
6.使用WeakReference代替强引用,弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。
7.超级大胖子Bitmap
及时的销毁(Activity的onDestroy时,将bitmap回收)
设置一定的采样率
巧妙的运用软引用
drawable对应resid的资源,bitmap对应其他资源8.保证Cursor 占用的内存被及时的释放掉,而不是等待GC来处理。并且 Android明显是倾向于编程者手动的将Cursor close掉
9.线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控
10.如果ImageView的图片是来自网络,进行异步加载
11.应用开发中自定义View的时候,交互部分,千万不要写成线程不断刷新界面显示,而是根据TouchListener事件主动触发界面的更新
三、AndroidUI优化
1.layout组件化,尽量使用merge及include复用
2.使用styles,复用样式定义
3.软键盘的弹出控制,不要让其覆盖输入框
4.数字、字母和汉字混排占位问题:将数字和字母全角化。由于现在大多数情况下我们的输入都是半角,所以 字母和数字的占位无法确定,但是一旦全角化之后,数字、字母的占位就和一个汉字的占位相同了,这样就可以避免由于占位导致的排版问题。
5.英文文档排版:textview自动换行时要保持单词的完整性,解决方案是计算字符串长度,然后手动设定每一行显示多少个字母并加上‘n‘
6.复杂布局使用RelativeLayout
7.自适应屏幕,使用dp替代pix
8.使用android:layout_weight或者TableLayout制作等分布局
9.使用animation-list制作动画效果
说说应用层的开发:
1,写代码前先在本子上画好大概的模块图,类图,能和同事讨论下最好。
2,使用开源代码务必搞懂主要实现方式,以便填坑。
3,应用内的资源要统一管理,如线程,缓存,网络,数据库,配置,设备事件监听等等,以便性能分析和维护。
4,合理使用设计模式,做好UI之间,和业务以及数据层的解耦非常必要,以便应付产品的各种变化。
5,管理好各种对象的生命周期,谁说Java不泄漏内存?来我泄漏给你看看。
6,不要总是TODO,因为翔留下就留下了,还得写新的翔,等后来的童鞋来收拾,被人骂骂也不好。
7,没事多看下Github,Android程序基本可以用一个个开源库搭积木搞出来,当然业务除外。也可以看看其他平台的库和各种框架,不要把自己局限在Android上。
8,关于注释,尽量写未来自己还看得懂的注释,曾经阅读同事的代码,懂了之后留下一句注释,半年后再次阅读,感叹道,辛亏老子留了条注释,不然又看不懂了。
9,代码风格团队要统一
10,提高基本素质,做程序员,而不是Android程序员。
Google下,有Github上的Android常用库和框架这样的文章。
不请自来说点废话。肥肥做了四年 Android 开发,其中三年时间是在做各种中间件。
我觉得首先是命名规范。命名规范这种东西每个人都有自己的风格,Google 也有自己的一套规范(多看看 Android 系统源码就明白了)。好的规范可以有效地提高代码的可读性,对于将来接手代码的小伙伴也是一件幸事。题主可以自行 Google 一下 Java (Android)命名规范,会由不少的博客介绍。
其次是注释。严格来说这个应该属于命名规范的范畴。注释一方面是帮助自己记忆 ,另一方面是团队协作中的一个规范,特别是对于开发 API 的小伙伴来说,总不能天天被人跟在屁股后面问你这个接口是什么作用,你这参数是什么意思?好的注释配合好的命名规范,可以省去很多沟通上的成本。 注释至少要有如下几方面的内容:
public abstract class BaseActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
public void init(){
setContentView();
findViews();
getData();
showContent();
}
public abstract void setContentView();
public abstract void findViews();
public abstract void getData();
public abstract void showContent();
}
public static List<String> getFolderFiles( String folderPath ){
List<String> fileList = new ArrayList<String>( );
if( TextUtils.isEmpty( folderPath ) ){
return fileList;
}
File file = new File( folderPath );
if( file.isDirectory( ) ){
File[] files = file.listFiles( );
if( null != files ){
fileList = new ArrayList<String>( );
for( File subFile : files ){
fileList.add( subFile.getPath( ) );
}
}
}
return fileList;
}
public static List<String> getAssertsFiles( Context context ){
List<String> assertsFileList = new ArrayList<String>( );
if( null == context ){
return assertsFileList;
}
AssetManager assetManager = context.getAssets();
String[] files = null;
try {
files = assetManager.list("");
assertsFileList = Arrays.asList( files );
} catch (IOException e) {
e.printStackTrace( );
}
return assertsFileList;
}
public class DebugUtils{
public static final String TAG = "Debug";
private DebugUtils( ){
}
public static void println( String printInfo ){
if( Debug.DEBUG_MODE && null != printInfo ){
System.out.println( printInfo );
}
}
public static void print( String printInfo ){
if( Debug.DEBUG_MODE && null != printInfo ){
System.out.print( printInfo );
}
}
public static void printLogI( String logInfo ){
printLogI( TAG, logInfo );
}
public static void printLogI( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.i( tag, logInfo );
}
}
public static void printLogE( String logInfo ){
printLogE( TAG, logInfo );
}
public static void printLogE( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.e( tag, logInfo );
}
}
public static void printLogW( String logInfo ){
printLogW( TAG, logInfo );
}
public static void printLogW( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.w( tag, logInfo );
}
}
public static void printLogD( String logInfo ){
printLogD( TAG, logInfo );
}
public static void printLogD( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.d( tag, logInfo );
}
}
public static void printLogV( String logInfo ){
printLogV( TAG, logInfo );
}
public static void printLogV( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag || null != logInfo ){
Log.v( tag, logInfo );
}
}
public static void printLogWtf( String logInfo ){
printLogWtf( TAG, logInfo );
}
public static void printLogWtf( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.wtf( tag, logInfo );
}
}
public static void showToast( Context context, String toastInfo ){
if( null != context && null != toastInfo ){
Toast.makeText( context, toastInfo, Toast.LENGTH_LONG ).show( );
}
}
public static void showToast( Context context, String toastInfo, int timeLen ){
if( null != context && null != toastInfo && ( timeLen > 0 ) ){
Toast.makeText( context, toastInfo, timeLen ).show( );
}
}
public static void printBaseInfo( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; class:" ).append( stackTrace[ 1 ].getClassName( ) )
.append( "; method:" ).append( stackTrace[ 1 ].getMethodName( ) )
.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) )
.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) );
println( strBuffer.toString( ) );
}
}
public static void printFileNameAndLinerNumber( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) )
.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) );
println( strBuffer.toString( ) );
}
}
public static int printLineNumber( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) );
println( strBuffer.toString( ) );
return stackTrace[ 1 ].getLineNumber( );
}else{
return 0;
}
}
public static void printMethod( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; number:" ).append( stackTrace[ 1 ].getMethodName( ) );
println( strBuffer.toString( ) );
}
}
public static void printFileNameAndLinerNumber( String printInfo ){
if( null == printInfo || !Debug.DEBUG_MODE ){
return;
}
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) )
.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) ).append( "\n" )
.append( ( null != printInfo ) ? printInfo : "" );
println( strBuffer.toString( ) );
}
public static void showStrictMode( ) {
if (DebugUtils.Debug.DEBUG_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());
}
}
public static void d(String tag, String msg){
if(DebugUtils.Debug.DEBUG_MODE){
Log.d(tag, msg);
}
}
public class Debug{
public static final boolean DEBUG_MODE = true;
}
}
android:hardwareAccelerated="false"
<item name="android:windowEnableSplitTouch">false</item>
<item name="android:splitMotionEvents">false</item>
public class BtnClickUtils {
private static long mLastClickTime = 0;
private BtnClickUtils( ){
}
public static boolean isFastDoubleClick() {
long time = System.currentTimeMillis();
long timeD = time - mLastClickTime;
if ( 0 < timeD && timeD < 500) {
return true;
}
mLastClickTime = time;
return false;
}
}
1、高效地利用线程
我们知道App运行过程中所有的操作都默认在主线程(UI线程)中进行的,这样App的响应速度就会受到影响。会导致程序陷入卡顿、死掉甚至会发生系统错误。为了加快响应速度,需要把费时的操作(比如网络请求、数据库操作或者复杂的计算)从主线程移动到一个单独的线程中。最高效的方式就是在类这一级完成这项操作,可以使用AsyncTask或者IntentService来创建后台操作。如果选择使用IntentService,它会在需要的时候启动起来,然后通过一个工作线程来处理请求(Intent)。
使用IntentService时需要注意以下几点限制:
这个类不要给UI传递信息,如果要向用户展示处理结果信息请用Activity;
每次只能处理一个请求;
每一个处理请求过程都不能中断;
2、保持响应不发生ANR
从UI线程中移除费时操作这个方式还可以防止用户操作出现系统不响应(ANR)对话框。需要做的就是继承AsyncTask来创建一个后台工作线程,并实现doInBackground()方法。
还有一种方式就是自己创建一个Thread类或者HandlerThread类。需要注意这样也会使App变慢,因为默认的线程优先级和主线程的优先级是一样的,除非你明确设定线程的优先级。
3、在线程中初始化查询操作
当查询操作正在后台处理时,展示数据也不是即时的,但是你可以使用CursorLoader对象来加快速度,这个操作可以使Activity和用户之间的互动不受影响。使用这个对象后,你的App会为ContentProvider初始化一个独立的后台线程进行查询,当查询结束后就会给调用查询的Activity返回结果。
4、需要注意的方面
使用StrictMode来检查UI线程中可能潜在的费时操作;
使用一些特殊的工具如Systrace或者Traceview来寻找在你的应用中的瓶颈;
用进度条向用户展示操作进度;
如果初始化操作很费时,请展示一个欢迎界面。
优化设备的电池寿命。如果应用很费电,请不要责怪用户卸载了你的应用。对于电池使用来说,主要费电情况如下:
更新数据时经常唤醒程序;
用EDGE或者3G来传递数据;
文本数据转换,进行非JIT正则表达式操作。
5、优化网络
如果没有网络连接,请让你的应用跳过网络操作;只在有网络连接并且无漫游的情况下更新数据;
选择兼容的数据格式,把含有文本数据和二进制数据的请求全部转化成二进制数据格式请求;
使用高效的转换工具,多考虑使用流式转换工具,少用树形的转换工具;
为了更快的用户体验,请减少重复访问服务器的操作;
如果可以的话,请使用framework的GZIP库来压缩文本数据以高效使用CPU资源。
6、优化应用在前端的工作
如果考虑使用wakelocks,尽量设置为最小的级别;
为了防止潜在的bug导致的电量消耗,请明确指定超时时间;
启用 android:keepScreenOn属性;
除了系统的GC操作,多考虑手动回收Java对象,比如XmlPullParserFactory和BitmapFactory。还有正则表达式的Matcher.reset(newString)操作、StringBuilder.setLength(0)操作;
要注意同步的问题,尽管在主线程中是安全的;
在Listview中要多采用重复利用策略;
如果允许的话多使用粗略的网络定位而不用GPS,对比一下GPS需要1mAh(25s * 140 mA),而一般网络只用0.1mAh(2s * 180mA);
确保注销GPS的位置更新操作,因为这个更新操作在onPause()中也是会继续的。当所有的应用都注销了这个操作,用户可以在系统设置中重新启用GPS而不浪费电量;
请考虑在大量数理运算中使用低精度变量并在用DisplayMetrics进行DPI任务时缓存变量值;
7、优化工作在前台的应用
请确保service生命周期都是短暂的,因为每个进程都需要2MB的内存,而在前台程序需要内存时也会重新启动;
保持内存的使用量不要太大;
如果要应用每30分钟更新一次,请在设备处于唤醒状态下进行;
Service在pull或者sleep状态都是不好的,这就是为什么在服务结束时要使用AlarmManager或者配置属性stopSelf()的原因。
8、电池其它注意事项
在进行整体更新之前检查电池的状态和网络状态,等待最好的状态再进行大幅度装换操作;
让用户看到用电情况,比如更新周期,后台操作的时候;
当我们为布局单独创建UI的时候,就是在创建滥用内存的App,它在UI中会出现可恶的延时。要实现一个流畅的、低内存占用的UI,第一步就是搜索你的应用找出潜在的瓶颈布局。使用Android SDK/tools/中自带的Hierarchy Viewer Tool工具。还有一个很好的工具就是Lint,它会扫描应用的源码去寻找可能存在的bug,并为控件结果进行优化。
10、解决问题
如果布局显示结果发现了问题,你可以考虑简化布局结构。可以把LinearLayout类型转化成RelativeLayout类型,降低布局的层级结构。
1、四大组件以及Fragment的生命周期
如果只要写一个能用的APP的话,知道生命周期的流程就够用了。不过我建议还是要详细阅读源码(不一定要全部懂),知道Android系统后台管理组件的什么周期的大概流程,理解Android系统组件生命周期设计的意图,了解这些关键字:ActivityManager,FragmentManager,WindowManager。
Android系统内存空间不够后,会回收相应生命周期内的组件,所以你应该保证的你Activity在onStop或者OnDestroy后,没有被其他对象引用着,不然会造成系统无法回收内存空间,这样就造成内存泄露了,结果要么是系统变卡,要么直接抛OOM Error。
Android组件的什么周期应该是系统控制的,APP不能干预,APP要做的是在Activity销毁时保存数据,创建或恢复时恢复数据。但是为了不让Activity每次重新创建的时候都重新去拉取数据浪费流量,要在onsaveinstancestate的时候把数据保存, onrestoreinstancestate的时候把数据恢复吗?不全是,用Intent传递数据时,容量是有限制的,应该尽量保存一些关键的参数用于恢复数据(例如Adapter的当前position,最好是基本的Parcelable类型),如果数据太大会抛TransactionTooLargeException,如果真的想保存大数据的话推荐用序列化对象保存。
为了保证主线程(Activity Thread)的顺畅,应该把一些耗时的工作都放到后台线程去,但是也不要轻易地创建线程,因为线程启动后我们就很能控制它了,如果线程引用着Activity的实例的话那更糟糕,会容易造成Activity无法被回收,而Activity又引用着大量的资源(所以系统内存空间不够用的时候最喜欢回收Activity),容易造成OOM,这也是一个项目越大越容易出现卡顿的问题之一。
不要轻易new Thread().start();,采用类似Looper的循环队列,需要后台工作的使用,用对应的Looper的Handler把任务提交到Looper的队列里执行,我自己控制的线程数大概有:
每次要做长时间的任务时,都是把任务扔到现有的队列中,而不是new Thread()
AsyncTask用来做异步任务确实比自己写Thread方便许多,问题是AsyncTask自己内部维护了一个线程池,线程的数目不好控制(可以自定义),而且AsyncTask同样容易引用着Activity导致资源没被回收,Timer同理。
转自 知乎 http://www.zhihu.com/question/27227425