| 关键词: 程序 方法 debug public logInfo 一个 Android 问题 static String |
本文介绍Android开发过程中的一些基本常识,大多是一些流程、专业术语和解决问题的方法等。 软件开发流程一个完整的软件开发流程离不开策划、交互、视觉、软件、测试、维护和运营这七个环节,这七个环节并不是孤立的,它们是开发一款成功产品的前提,但每一项也都可以形成一个学科,是一个独立的岗位,随着敏捷开发的流行,以及来到了体验为王的时代,现代软件开发更多的是注重效率和敏捷,而不是循规蹈矩的遵循这些开发流程,比如软件开发的岗位不再仅仅是个技术岗位,它需要去参与前期的设计和评审、可以在视觉和交互方面提出自己的见解,在开发的过程中需要自测程序尽快解决现存问题,运营和维护的过程中也需要软件的帮助。可见现代软件开发对开发者的综合素质(这并不是facebook所讲的全栈工程师)越来越高,自称为码农或者程序猿显然是不合理的,因为这个过程是脑力劳动和体力脑动并存,称呼自己为工程师显得更为合理。
注:
提问的智慧大多数工作都是以结果为导向的,特别是软件开发这个职业,绩效考核、KPI这些都是在考核你工作的成果,所以工作更多地是需要你解决问题的能力,至于学习这个事情,还是在工作之外的时间去做吧。对于提高解决问题能力我有两个建议:
解决bug的方法为了写这一项我专门在知乎上提过一个问题: 在知道如何快速解决bug之前,你需要知道什么是bug。没有完成策划、交互、视觉要求的功能,这不叫bug,这叫功能缺陷;一个功能完成后不能正常使用也不叫bug,因为它根本还没达到可测试的标准。我认为当你的程序达到可测试标准之后发现的问题才叫bug。综合我自己解决bug的经验和知乎上的回答,总结常见的解决bug的方法有(你想要高效解决bug的前提是你能够快速定位到缺陷所在的位置,所以以下方法多数讲的是如何快速定位问题,至于真正解决bug,需要你自己修改程序才行):
以Eclipse为例: 1、打断点: (1)打断点: (2)清除断点: 2、启动调试模式的两种方式: (1)通过debug as启动调试程序:右键工程名–>Debug AS –>Android Application –>模拟器或者真机会弹出……watching for the debugger……的提示框,不要点击等待其自动消失 –> 此时已经进入调试模式,操作程序到达打断点的地方。 (2)在程序运行过程中,在DDMS视图下选中要调试的程序,启动调试模式: 3、调试:请自行尝试F5、F6、F7、F8这几个调试的快捷键; 4、watch成员变量:在调试的过程中,比如在执行for、while、do while循环、递归、系统回调等程序时可以通过watch来观察成员变量或者方法返回值的变化情况,watch的方法: 注:更多关于在Eclipse IDE中调试Android程序的知识请参见:Android eclipse中程序调试 打印: 打印调试的方法对于循环、异步加载、递归、JNI等代码段非常有用,特别是在循环中,在循环次数非常大时,通过打断点调试显然是一件费力的事情,这时候打印就显得更“智能”了,我通常会通过下面封装的打印调试类来输出打印信息,这个类可以打印print、log、行号、文件名、StrictMode等信息,当不需要打印信息时,只需要将DEBUG_MODE改为false就可以了: import android.content.Context;
import android.os.StrictMode;
import android.util.Log;
import android.widget.Toast;
/**
* 调试打印类
*
* */
public class DebugUtils{
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;
}
public static final String TAG = "Debug";
}目视法: 这适合于code review,但是不太靠谱,因为人的精力毕竟有限,有时候你多敲一个分号,缩进不对都有可能导致程序出现问题,但在代码量较少时是一个高效率的方法。 自动化测试: Android的自动化测试(分白盒测试和黑盒测试)工具有:monkey、Robotium、Appium、云端测试(比如testin),具体用法可参见: android实用测试方法之Monkey与MonkeyRunner 排除法: 调试、打印、目视这三种方法适合于可以复现的问题,对于随机问题(实际上不存在随机问题,只是问题不那么容易复现而已),比如在线程、音频播放、AnsynTask、Timer切换或者结束时刚好做了相应地人为操作导致出现灵异现象。这时候可以通过排除法来排查问题,具体的方法是首先大概定位到出现问题的位置,然后将代码一段一段地注释,观察程序现象,逐步缩小出现问题的范围。 版本管理介绍在较大的软件开发过程中,可能有多个软件工程师同时开发一个项目的情况,比如有负责读取数据、获取网络数据等API封装的,有负责程序架构的,有负责上层界面实现的,为了能够最终编译一个完成的程序出来,需要将代码整合,这个时候最方便的方法就是使用版本管理工具,固定时间上传(比如每天、没改动一个功能等等),这样能够实时保证服务器上的代码是最完整、最新的,也可以避免由于自然灾害、电脑异常导致本地电脑挂掉损失掉代码的问题。 常见的版本管理工具有SVN和Git,我也使用过CVS,关于版本管理工具的介绍参见: 注:对于windows用户来说,建议使用乌龟壳系列的版本控制客户端,使用github的朋友可以使用github for windows客户端: 编译通常我们用Eclipse或者Android Studio开发android程序时,只需要运行程序就可以在模拟器或者机器上运行程序了,但为了保证代码的完整性、能够在服务器上编译,需要通过编译工具将代码编译成apk,常见的编译工具有:ant、gradle,但这两种编译工具都是需要通过手动敲命令来完成编译功能(当然你也可以自己写脚本来实现编译自动化),jenkins是一个持续集成的工具,通过它可以代码克隆、编译以及程序加密自动化,其实它也是通过批处理来实现的,ant、gradle和jenkins的具体用法自行谷歌,使用起来很简单,目前android studio和github上很多功能都是通过gradle来编译的。 专业术语介绍以下解释完全是本人的理解,详细解释可自行谷歌。
Android开发资源参见我的另一篇文章:Android开发者网址导航 建议
工具推荐
|