问题:在Android平台的一个系统版本中调用了该版本中不存在或不合适的系统API,必然会出现调用失败或运行结果不符合预期,进而导致软件运行发生Crash或产生错误。
解决方案:在这些API使用时,加上Android系统版本判断,避免不兼容API的调用。
private void setUpActionBar() { ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); } |
public abstract void setDisplayShowHomeEnabled (boolean showHome)Added in API level 11 Set whether to include the application home affordance in the action bar. Home is presented as either an activity icon or logo. To set several display options at once, see the setDisplayOptions methods. Parameters
See Also |
private void setUpActionBar() { // Make sure we're running on Honeycomb or higher to use ActionBar APIs if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // 包含新API的代码块 ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); } else { // 包含旧的API的代码块 } } |
问题:当图片过大或图片数量较多时使用BitmapFactory解码图片会出java.lang.OutOfMemoryError: bitmap size exceeds VM budget,导致app出现Crash。
解决方案:对过大的图片进行压缩或者通过先设置缩放选项,再读取缩放的图片数据到内存,规避了内存引起的OOM。
LayoutInflater inflater=getLayoutInflater(); list= new ArrayList<View>(); list.add(inflater.inflate(R.layout.item1, null));//就在这! |
BitmapFactory.Options opts= new BitmapFactory.Options(); opts. inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); …… |
问题:在格式化数字时,无法将其转化为合适的数字,导致抛出NumberFormatException异常。
解决方案:在任何用到格式化数字时,捕捉异常,对异常情况进行处理。
public static String formatMileage(String mAverageSpeed) { float mileage; DecimalFormat fnum = null; mileage = Float.parseFloat(mAverageSpeed); fnum = new DecimalFormat("###,###,###.##"); |
float mileage; DecimalFormat fnum = null; try { mileage = Float.parseFloat(mAverageSpeed); fnum = new DecimalFormat("###,###,###.##"); } catch (Exception e) { e.printStackTrace(); return MILEAGE_EMPTY; } |
问题:在Java代码实现时,定义一个ArrayList对象,并初始化为null,通过调用方法返回的ArrayList对象赋值给开始定义的对象,直接使用时可能出现java.lang.NullPointerException异常,app出现Crash。
解决方案:在使用ArrayList对象前,判断该对象是否为null,并进行异常处理。
if(curCount>=(position+1)){ // getDataWithSeq返回null,空指针异常 return getDataWithSeq(i).get(position-(curCount-counts[i])); }
public ArrayList<MylocResultListPoisPoiType> getDataWithSeq(int seq){ ArrayList<MylocResultListPoisPoiType> result = null; if (seq > 0) { result = mCacheStatus; } return result; } |
if(curCount>=(position+1)){ // getDataWithSeq返回null,空指针异常 if (getDataWithSeq(i) != null) { return getDataWithSeq(i).get(position-(curCount-counts[i])); } }
public ArrayList<MylocResultListPoisPoiType> getDataWithSeq(int seq){ ArrayList<MylocResultListPoisPoiType> result = null; if (seq > 0) { result = mCacheStatus; } return result; } |
问题:在使用String.split得到的结果数组前,未对数组进行长度检查,取字段的时候可能发生越界而导致 Crash。
解决方案:在使用 split 结果数组前,添加对数组长度的判断。
String source = "<br/>"; String[] infos = source.split("<br/>"); String poiName = infos[0]; |
String source = "<br/>"; String[] infos = source.split("<br/>"); if(0 < infos.length){ String poiName = infos[0]; } |
问题:当使用FragmentTransaction调用add函数添加某个fragment时,如果该fragment已经被添加到activity,再进行添加操作会抛出Fragment already added异常。
解决方案:添加fragment前,先调用fragment.isAdded函数判断该fragment是否已经添加。
在下面代码中,用户如果快速点击或者函数被重复执行时,如果myFragment已经被添加再进行添加操作会抛出Fragment already added异常。
if (!ret) { } |
在进行执行add函数前,先调用isAdded判断myFragment是否已经被添加,修改如下:
if (!ret && !myFragment.isAdded()) { } |
问题:使用除法或者求余运算时,如果分母是通过调用函数返回的int,未对返回值进行判断,当返回值为0时,会出现java.lang.ArithmeticException: / by zero异常。
解决方案:调用函数前,对函数的返回值的长度进行判断。
在下面代码中,程使用了求余运算,但调用mBoardTextList.size()前没有判断mBoardTextList的长度,如果mBoardTextList为0时就抛出了java.lang.ArithmeticException: / by zero异常而导致app crash。
text = mBoardTextList.get(mBoardIndex % mBoardTextList.size());
mBoardIndex++; |
在进行求余运算前,先判断mBoardTextList是否为0,再进行求余运算,修改如下:
int boardListSize =mBoardTextList.size();
if (boardListSize > 0) { text = mBoardTextList.get(mBoardIndex % boardListSize); mBoardIndex++; }
|
问题:在使用String.substring时,未对字符串的长度进行检查,截取字符串时可能会发生越界而导致Crash。
解决方案:在使用 substring 前,对字符串的长度进行判断。
在下面代码中,虽然sb不为空但是长度为0,而直接进行字符串截取导致越界crash。
String sb = new String(); String value = sb.substring(0,sb.length()-1);
|
在使用sb之前,先对sb的长度作检查,修改如下:
String sb = new String(); if( sb.length() > 0){ String value = sb.substring(0,sb.length()-1); } |
问题:在Java代码实现时,通过Hashmap的get方法获取对象,并且不是使用Hashmap的Iterator获取,如果key不存在时获取的对象为null,在使用此对象可能出现java.lang.NullPointerException的异常,app出现Crash。
解决方案:在使用Hashmap获取的对象前,判断该对象是否为null,并进行异常处理。
private HashMap<Long, OfflineCacheStatus> mCacheStatus; …… synchronized (mLatestDatas) { for (TestMp3MusicFile bmmf : mLatestDatas) { if(bmmf == null) continue;//add this line,question? status = mCacheStatus.get(bmmf.mId_1); // status为null,使用status.path报空指针异常 if (!StringUtils.isEmpty(status.path)) { bmmf.mPath = status.path; } } } |
private HashMap<Long, OfflineCacheStatus> mCacheStatus; …… synchronized (mLatestDatas) { for (TestMp3MusicFile bmmf : mLatestDatas) { if(bmmf == null) continue;//add this line,question? status = mCacheStatus.get(bmmf.mId_1); // status为null,使用status.path报空指针异常 if (status != null) { if (!StringUtils.isEmpty(status.path)) { bmmf.mPath = status.path; } } } } |
问题:ArrayList通过get方法使用下标获取元素,如果使用的下标不在ArrayList大小范围内,将产生java.lang.IndexOutOfBoundsException的异常,导致app出现Crash。
解决方案:在使用ArrayList的get方法获取元素时,需要判断使用的下标的有效性。
private ArrayList<Reply> mRepliesData; public boolean onContextItemSelected(int selectedPosition) { mRepliesAdapter = new NoteReplyAdapter(this, mRepliesData); Reply reply = mRepliesData.get(selectedPosition); …… return super.onContextItemSelected(item); } |
private ArrayList<Reply> mRepliesData; public boolean onContextItemSelected(int selectedPosition) { mRepliesAdapter = new NoteReplyAdapter(this, mRepliesData); if (selectedPosition < mRepliesData.size()) { Reply reply = mRepliesData.get(selectedPosition); } …… return super.onContextItemSelected(item); } |
问题:通过inflater.inflate(R.layout.frag_name_search,null)方法动态载入界面,然后通过findViewById获取界面元素,如果这个元素不存在此界面,再使用此元素对象的方法时将产生java.lang.NullPointerException的异常,导致app出现Crash。
解决方案:在使用元素对象时先判断元素是否为null。
View view = inflater.inflate(R.layout.frag_name_search, null); mHistoryLV = (ListView) view.findViewById(R.id.navi_logo); mHistoryLV.addHeaderView(headView); |
View view = inflater.inflate(R.layout.frag_name_search, null); mHistoryLV = (ListView) view.findViewById(R.id.navi_logo); if(mHistoryLV != null){ mHistoryLV.addHeaderView(headView); } |
问题:在使用IO流进行相关操作后没关闭导致一直占用IO资源,大量流操作后没有关闭可能导致系统内存不足从而使系统抛出OOM异常。
解决方案:在使用相关IO流操作后将其关闭,如果在try-catch作用域内应在finally中关闭。
在下面代码中,使用IO流相关操作后没有关闭导致资源占用,从而导致系统内存不足而引发OOM异常。
InputStream inputStream = httpResponse.getEntity().getContent(); inputStream.read(); ……
|
对IO流进行操作完毕后关闭相关流,修改如下:
InputStream inputStream = null; try { inputStream = httpResponse.getEntity().getContent(); …… } finally { try {
} catch (IOException e) {
} |
问题:在Java程序中,调用数据库操作时,可能存在数据库连接已关闭情况(由于多线程实现不好,导致状态被改变,尽管开始可能已经进行了状态判断),导致抛出java.lang.IllegalStateException异常,此时,必须要在某个位置进行异常处理,否则该异常会层层上传,直到有代码捕获和处理该异常,可能会导致app出现crash。
解决方案:对于可能抛出异常的代码,增加try{}catch{},捕获和处理能处理的异常,对于不能处理的异常重新封装并抛给上层。(若仅仅捕获所有异常而不处理,则crash率会下降,但会掩盖程序运行错误问题)。
public static boolean isTableExists(String tableName) { Cursor cursor = database.query("sqlite_master", new String[]{"[sql]"}, "[type] = 'table' and name = ?", new String[]{tableName}, null, null, null); return cursor.getCount() == 1; } |
public static boolean isTableExists(String tableName) { try{ Cursor cursor = database.query("sqlite_master", new String[]{"[sql]"}, "[type] = 'table' and name = ?", new String[]{tableName}, null, null, null); }catch(Exception e){ // 处理能处理的异常,不能完整处理,重新封装异常抛给上层 } return cursor.getCount() == 1; } |
问题:在Java程序中,使用throw new Exception主动抛出异常而不对异常进行捕获,会导致app出现Crash。
解决方案:对于抛出异常的代码,增加try{}catch{},捕获和处理能处理的异常,对于不能处理的异常重新封装并抛给上层。(若仅仅捕获所有异常而不处理,则crash率会下降,但会掩盖程序运行错误问题)
public static int compare(String version1, String version2) { if (version1 == 0||version2 == 0) throw new IllegalArgumentException("Invalid parameter!"); } |
public static int compare(String version1, String version2) { if (version1 == 0||version2 == 0){ try{ throw new IllegalArgumentException("Invalid parameter!"); }catch(Exception e){ // 处理能处理的异常,不能完整处理,重新封装异常抛给上层 } } } |
问题:在调用系统的dismiss()方法前,没有对状态进行判断,导致抛出NullPointerException异常。
解决方案:在调用系统的dismiss()方法时,需要对状态先进行判断。
if (mContext != null &&!((Activity)mContext).isFinishing()) { popupWindow.dismiss(); setFocusable(false); } |
if (mContext != null &&!((Activity)mContext).isFinishing() && isShowing()) { popupWindow.dismiss(); setFocusable(false); } |
问题:方法中存在return null,在特定条件下触发返回return null,而对该方法的返回对象直接进方法调用将产生java.lang.NullPointerException的异常,导致app出现Crash。
解决方案:对方法中含有return null的返回对象进行直接方法调用时需要先进行判断。
public ArrayList<RouteListItem> getRouteNodeData() { if (mRouteList.size() == 0) { return null; } else { return mRouteList; } } public void clearItem(){ RouteResultManager mgr = RouteResultManager.instance(mContext); mFocusItemIndex = mgr.getRouteNodeData().get(0); } |
public ArrayList<RouteListItem> getRouteNodeData() { if (mRouteList.size() == 0) { return null; } else { return mRouteList; } } public void clearItem(){ RouteResultManager mgr = RouteResultManager.instance(mContext); if(mgr.getRouteNodeData() != null ){ mFocusItemIndex = mgr.getRouteNodeData().get(0); } } |
问题:在使用Cursor对数据库进行操作后没有关闭Cursor,在常时间大量操作情况下出现OOM的问题,异常导致app crash。
解决方案:在使用Cursor对数据库进行操作完毕后关闭Cursor。
Cursor cursor = getContentResolver().query(uri); if (cursor.moveToNext()) { …… } |
Cursor cursor = null; try { cursor = getContentResolver().query(uri); …… } finally { if (cursor != null) { try { cursor.close(); } catch (Exception e) { } } } |
问题:调用Android.app.Dialog.cancel()方法前,如果这个dialog不处于showing状态时,会抛出java.lang.IllegalArgumentException的异常,导致app出现Crash。
解决方案:调用Android.app.dialog.cancel()方法前,先判断Android.app.dialog.isShowing(),为true时才进行cancel()操作。
public void hideProgressDialog(){ if(mProgressDialog != null){ mProgressDialog.cancel(); mProgressDialog = null; } } |
public void hideProgressDialog(){ if(mProgressDialog != null && mProgressDialog.isShowing()){ mProgressDialog.cancel(); mProgressDialog = null; } } |
问题:继承自Activity或Fragment的类当复写相关的生命周期函数时,需要调用父类的super函数,该点在Android开发者官网有明确说明。
解决方案:复写Activity或Fragment的生命周期函数时,需调用其父类的super函数。
在下面代码中,复写Fragment的生命周期函数没有调用其父类的super函数,导致SuperNotCalledException。
@Override public void onDestroyView() { // TODO Auto-generated method stub myLinsenter.close(); ... }
|
对生命周期函数添加super函数,修改如下:
@Override public void onDestroyView() { // TODO Auto-generated method stub super.onDestroyView(); myLinsenter.close(); ... } |
问题:采用下标的方式获取数组元素时,如果下标越界,将产生java.lang.ArrayIndexOutOfBoundsException的异常,导致app出现Crash。
解决方案:在使用下标的方式获取数组元素时,需判断下标的有效性。
在下面代码中,程序调用getIndex()方法获取索引值,存在idx超出strArr的有效范围导致app crash的隐患。
public void func(){ String[] strArr = {"e1","e2"}; int idx = getIndex(); System.out.println(strArr[idx]);
|
在调用System.out.println(strArr[idx])方法前,先判断idx 是否在有效范围内,再进行方法调用,修改如下:
public void func(){ String[] strArr = {"e1","e2"}; int idx = getIndex(); if(idx >=0 && idx < strArr.length) { System.out.println(strArr[idx]); }
|
问题:在Intent中通过诸如“getBundleExtra、getStringExtra……”获取数据时,得到的对象有可能为null,若直接使用则有可能导致app出现Crash。
解决方案:在使用此类对象前,应做判空处理。
在下面代码中,程序调用getStringExtra("info")方法获取对象,若获取到的对象为null,则存在导致app crash的隐患。
public void func(){ String value = str.substring(str.indexOf(',')); } |
在使用该对象前,先做判空处理,修改如下:
public void func(){ String str = intent.getStringExtra("info"); if(str != null && str.contains(",")){ String value = str.substring(str.indexOf(','));
textview.setText(value); } |
问题:使用未在AndroidManifest.xml中注册的Activity会导致app出现Crash。
解决方案:所有用到的Activity都在AndroidManifest.xml中进行注册。
在下面代码中,程序使用了SecondActivity,但其未在AndroidManifest.xml中注册,程序执行到此逻辑时会报android.content.ActivityNotFoundException, 导致app crash。
public void func(){ } |
在AndroidManifest.xml中注册SecondActivity,如下:
<application android:allowBackup="true" |
问题:在使用Bundle对象时,因其可能为null,若直接使用诸如“get(String key)、getString(String key)、getByte(String key)...”函数获取Bundle对象所含数据时,有可能导致app出现Crash; 使用从Bundle获取到的数据,因其可能为null,若直接使用,可能会导致app出现crash。
解决方案:使用前,应做判空检查。
在下面代码中,程序直接调用getString("myKey")方法获取对象,若myBundle对象为null,则存在导致app crash的隐患;使用获取的value调用split函数,若value为null, 则导致app 出现crash。
Bundle myBundle = intent.getBundleExtra("myBundle"); value = myBundle.getString("myKey"); String str[] = value.split(","); |
在使用该对象前,先做判空处理和检查获取到的数据是否为空,修改如下:
Bundle myBundle = intent.getBundleExtra("myBundle"); if(myBundle != null ){ value = myBundle.getString("myKey"); }else{ //... }
if(value != null){ String str[] = value.split(","); } |