最近项目中协助做了个小的测试程序,需要拷贝文件。代码如下:
// fixed for raw data test. private static final String RAW_DATA_TEST = "***123***"; private static final String EXTERNAL_STORAGE = "/mnt/sdcard2"; private static final String INTERNAL_STORAGE = "/mnt/sdcard"; private static final String RAW_DATA_CFG_FILE_NAME = "config.xml";
// fixed for raw data test. static public boolean handleRawDataTest(Context context, String input) { if (RAW_DATA_TEST.equals(input)) { Log.d(TAG, "handleRawDataTest"); if (!copyRawDataCfgFile()) { Log.d(TAG, "handleRawDataTest, get Raw Data Cfg file failed!"); Toast.makeText(context, context.getString(R.string.raw_data_cfg_file_not_exist), Toast.LENGTH_SHORT).show(); return false; } Intent intent = new Intent(); intent.setComponent(new ComponentName("com.goodix.rawdata", "com.goodix.rawdata.RawDataTest")); context.startActivity(intent); return true; } return false; } public static boolean copyRawDataCfgFile() { String state = Environment.getExternalStorageState(); if (!Environment.MEDIA_MOUNTED.equals(state)) { return false; } String srcFileName = EXTERNAL_STORAGE + File.separator + RAW_DATA_CFG_FILE_NAME; String destFileName = INTERNAL_STORAGE + File.separator + RAW_DATA_CFG_FILE_NAME; Log.d(TAG, "copyRawDataCfgFile, srcFileName = " + srcFileName + ", destFileName = " + destFileName); try { if (copyFile(srcFileName, destFileName, true)) { Log.d(TAG, "copyRawDataCfgFile, successfull!"); return true; } } catch (Exception e) { e.printStackTrace(); } Log.d(TAG, "copyRawDataCfgFile, fail!"); return false; } public static boolean copyFile(String srcFileName, String destFileName, boolean reWrite) throws IOException { Log.d(TAG, "copyFile, begin"); File srcFile = new File(srcFileName); File destFile = new File(destFileName); if(!srcFile.exists()) { Log.d(TAG, "copyFile, source file not exist."); return false; } if(!srcFile.isFile()) { Log.d(TAG, "copyFile, source file not a file."); return false; } if(!srcFile.canRead()) { Log.d(TAG, "copyFile, source file can't read."); return false; } if(destFile.exists() && reWrite){ Log.d(TAG, "copyFile, before copy File, delete first."); destFile.delete(); } try { InputStream inStream = new FileInputStream(srcFile); FileOutputStream outStream = new FileOutputStream(destFile); byte[] buf = new byte[1024]; int byteRead = 0; while ((byteRead = inStream.read(buf)) != -1) { outStream.write(buf, 0, byteRead); } outStream.flush(); outStream.close(); inStream.close(); } catch (IOException e) { throw e; } catch (Exception e) { e.printStackTrace(); } Log.d(TAG, "copyFile, success"); return true; }代码比较简单,但是中间报了个很奇怪的异常,纠结了很长时间,幸好在临下班前找到问题根结所在。记下这次事件,以此勉之。
异常如下:
01-01 00:14:31.070: E/StrictMode(3297): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks. 01-01 00:14:31.070: E/StrictMode(3297): java.lang.Throwable: Explicit termination method 'close' not called 01-01 00:14:31.070: E/StrictMode(3297): at dalvik.system.CloseGuard.open(CloseGuard.java:184) 01-01 00:14:31.070: E/StrictMode(3297): at java.io.FileInputStream.<init>(FileInputStream.java:80) 01-01 00:14:31.070: E/StrictMode(3297): at com.android.dialer.SpecialCharSequenceMgr.copyFile(SpecialCharSequenceMgrProxy.java:768) 01-01 00:14:31.070: E/StrictMode(3297): at com.android.dialer.SpecialCharSequenceMgr.copyRawDataCfgFile(SpecialCharSequenceMgrProxy.java:702) 01-01 00:14:31.070: E/StrictMode(3297): at com.android.dialer.SpecialCharSequenceMgr.handleRawDataTest(SpecialCharSequenceMgrProxy.java:674) 01-01 00:14:31.070: E/StrictMode(3297): at com.android.dialer.SpecialCharSequenceMgr.handleCharsForTest(SpecialCharSequenceMgrProxy.java:618) 01-01 00:14:31.070: E/StrictMode(3297): at com.android.dialer.SpecialCharSequenceMgr.handleChars(SpecialCharSequenceMgrProxy.java:88) 01-01 00:14:31.070: E/StrictMode(3297): at com.android.dialer.dialpad.DialpadFragment.afterTextChanged(DialpadFragment.java:451) 01-01 00:14:31.070: E/StrictMode(3297): at android.widget.TextView.sendAfterTextChanged(TextView.java:7626) 01-01 00:14:31.070: E/StrictMode(3297): at android.widget.TextView$ChangeWatcher.afterTextChanged(TextView.java:9424)在网上也搜过“java.io.Closeable for information on avoiding resource leaks”和“java.lang.Throwable: Explicit termination method ‘close‘ not called”,都没找到和我这个问题类似的情况,一般都是cursor导致的内存泄漏的问题。最终,在反复查看代码才发现,原来问题的根结在我拷贝文件的目标目录未指定正确。出现问题的时候,我指定了INTERNAL_STORAGE为“/mnt/sdcard0”,在调用到new FileOutputStream(destFile)时会抛出异常,原因是并没有这个目录。手机内部存储根目录是“/mnt/sdcard”,这个路径实际指向了“/storage/sdcard0”。