1. Is it stored internally or externally?
There is an attribute under the manifest tag in AndroidManifest.xml android:installLocation
to specify where the application is installed. This attribute has three optional values:
- auto: The program may be installed on external storage, such as an SD card; but by default it will be installed in the phone's internal memory. When the phone memory is empty, the program will be installed on the external memory; when the program is installed on the phone, the user can decide to put the program in the external memory or the internal memory.
- internalOnly: The default value, the program can only be installed in the memory, if the memory is empty, the program cannot be installed successfully.
- preferExternal: Install the program in the external storage, but the system does not guarantee that the program will be installed in the external storage. When the external storage cannot be installed or is empty, the program will be installed into the internal memory. When the program uses the forward-locking mechanism, it will also be installed into memory, because external storage does not support this mechanism. After the program is installed, the user can freely switch whether the program should be on external or internal storage.
Get permissions for External storage:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
二.openFileInput和openFileOutput
Context provides two methods to open file IO streams in the application's data folder:
- FileInputStream openFileInput(String name): Open the input stream corresponding to the name file in the data folder of the application.
- FileOutputStream openFileOutput(String name, int mode): Open the output stream corresponding to the name file in the data folder of the application. The second parameter specifies the mode in which to open the file, which supports the following values:
- MODE_PRIVATE: The file can only be read and written by the current program.
- MODE_APPEND: Opens the file in append mode, and applications can append to the file.
- MODE_WORLD_READABLE: The contents of this file can be read by other programs.
- MODE_WORLD_WRITEABLE: The contents of this file can be read and written by other programs.
Context also provides methods to access the application's data folder:
- getDir(String name, int mode): Get or create a subdirectory corresponding to name in the data folder of the application.
- File getFileDir(): Get the absolute path of the application's data folder.
- String[] fileList(): Returns all files in the data folder of the application.
- deleteFile(String): Delete the specified file under the data folder of the application.
The core code is as follows:
public String read() { try { FileInputStream inStream = this.openFileInput("message.txt"); byte[] buffer = new byte[1024]; int hasRead = 0; StringBuilder sb = new StringBuilder(); while ((hasRead = inStream.read(buffer)) != -1) { sb.append(new String(buffer, 0, hasRead)); } inStream.close(); return sb.toString(); } catch (Exception e) { e.printStackTrace (); } return null; } public void write(String msg){ // Step 1: Get the input value if(msg == null) return; try { // Step 2: Create a FileOutputStream object, MODE_APPEND append mode FileOutputStream fos = openFileOutput("message.txt", MODE_APPEND); // Step 3: Put the obtained value into the file fos.write(msg.getBytes()); // Step 4: Close the data stream fos.close(); } catch (Exception e) { e.printStackTrace (); } }
The first parameter of the openFileOutput() method is used to specify the file name, which cannot contain the path separator "/". If the file does not exist, Android will automatically create it. The created files are saved in the /data/data/<package name>/files directory, such as: /data/data/cn.tony.app/files/message.txt,
3. Read and write files on SD card
Steps to read and write files on SD card:
- Call the getExternalStorageState() method of Environment to determine whether the SD card is inserted into the mobile phone, and the application has the permission to read and write the SD card. Use the following code:
//如果返回true,说明已插入SD卡,且应用程序具有读写SD卡的能力 Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
- Call the getExternalStorageDirectory() method of Environment to get the external storage, which is the directory of the SD card.
- Use FileInputStream, FileOutputStream, FileReader or FileWriter to read and write files on the SD card.
In order to read and write data on the SD card, the permission to read and write the SD card must be added in AndroidManifest.xml:
<!-- 在SD卡中创建于删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 向SD卡中写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Then when using SDcard for reading and writing, several static methods below the Environment class will be used:
1: getDataDirectory() 获取到Android中的data数据目录(sd卡中的data文件夹)
2:getDownloadCacheDirectory() 获取到下载的缓存目录(sd卡中的download文件夹)
3:getExternalStorageDirectory() 获取到外部存储的目录 一般指SDcard(/storage/sdcard0)
4:getExternalStorageState() 获取外部设置的当前状态 一般指SDcard,比较常用的应该是 MEDIA_MOUNTED(SDcard存在并且可以进行读写)还有其他的一些状态,可以在文档中进行查找。
5:getRootDirectory() 获取到Android Root路径
以下是具体操作:
1,判断SD卡是否存在
/** * 判断SDCard是否存在 [当没有外挂SD卡时,内置ROM也被识别为存在sd卡] * * @return */ public static boolean isSdCardExist() { return Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED); }
2,获取SD卡根目录
/** * 获取SD卡根目录路径 * * @return */ public static String getSdCardPath() { boolean exist = isSdCardExist(); String sdpath = ""; if (exist) { sdpath = Environment.getExternalStorageDirectory() .getAbsolutePath(); } else { sdpath = "不适用"; } return sdpath; }
3,获取默认的文件存放路径
/** * 获取默认的文件路径 * * @return */ public static String getDefaultFilePath() { String filepath = ""; File file = new File(Environment.getExternalStorageDirectory(), "abc.txt"); if (file.exists()) { filepath = file.getAbsolutePath(); } else { filepath = "不适用"; } return filepath; }
4-1,使用FileInputStream读取文件
try { File file = new File(Environment.getExternalStorageDirectory(), "test.txt"); FileInputStream is = new FileInputStream(file); byte[] b = new byte[inputStream.available()]; is.read(b); String result = new String(b); System.out.println("读取成功:"+result); } catch (Exception e) { e.printStackTrace(); }
4-2,使用BufferReader读取文件
try { File file = new File(Environment.getExternalStorageDirectory(), DEFAULT_FILENAME); BufferedReader br = new BufferedReader(new FileReader(file)); String readline = ""; StringBuffer sb = new StringBuffer(); while ((readline = br.readLine()) != null) { System.out.println("readline:" + readline); sb.append(readline); } br.close(); System.out.println("读取成功:" + sb.toString()); } catch (Exception e) { e.printStackTrace(); }
案例代码:
// 文件写操作函数 private void write(String content) { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); // 定义File类对象 if (!file.getParentFile().exists()) { // 父文件夹不存在 file.getParentFile().mkdirs(); // 创建文件夹 } PrintStream out = null; // 打印流对象用于输出 try { out = new PrintStream(new FileOutputStream(file, true)); // 追加文件 out.println(content); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { out.close(); // 关闭打印流 } } } else { // SDCard不存在,使用Toast提示用户 Toast.makeText(this, "保存失败,SD卡不存在!", Toast.LENGTH_LONG).show(); } } // 文件读操作函数 private String read() { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); // 定义File类对象 if (!file.getParentFile().exists()) { // 父文件夹不存在 file.getParentFile().mkdirs(); // 创建文件夹 } Scanner scan = null; // 扫描输入 StringBuilder sb = new StringBuilder(); try { scan = new Scanner(new FileInputStream(file)); // 实例化Scanner while (scan.hasNext()) { // 循环读取 sb.append(scan.next() + "\n"); // 设置文本 } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } finally { if (scan != null) { scan.close(); // 关闭打印流 } } } else { // SDCard不存在,使用Toast提示用户 Toast.makeText(this, "读取失败,SD卡不存在!", Toast.LENGTH_LONG).show(); } return null; }
四.操作assets、raw、res目录下文件
1.assets
资源文件夹,在main下与res同级,与res不同的是,该目录下的资源文件在打包apk时,会按原格式一并被打包。
有三种使用方法:
- 在assets下放一个test.html文件,加载该文件:
webView.loadUrl("file:///android_asset/test.html");//假设已经创建了一个WebView实例
- 同样是读取test.html文件:
//这里的open只能打开文件,不能打开文件夹 InputStream inputStream = getResource().getAssets().open("test.html");
-
读取列表、读取图片、读音乐,assets目录下包含一个images目录和一个mp3文件xuwei.mp3,images目录中包含一张图片dog.jpg:
String[] fileNames = getAssets().list("images/");//读列表 InputStream inputStream = getAssets().open("images/dog.jpg");//读图片 Bitmap bitmap = BitmapFactory.decodeStream(inputStream); imageView.setImageBitmap(bitmap); AssetFileDescriptor assetFileDescriptor = getAssets().openFd("xuwei.mp3");//得到asset文件描述符 player.reset();//假设已创建一个MediaPlayer实例 player.setDataResource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); player.prepare(); player.start();
2.raw
资源文件夹,在res目录下,系统会为res目录下的所有资源生成相应的资源ID,raw中的文件也不例外,所以可以通过ID去访问res/raw目录中的任何文件,而assets目录中的文件就需要借助AssetManager去访问了。
assets目录允许下面有多级子目录,而res/raw下不允许存在目录结构。
读raw下的xuwei.mp3文件:
InputStream is = getResources().openRawResource(R.raw.xuwei);
3.res
res目录下的文件都可用getResources()方法读取。
五.SD卡文件浏览器
利用Java的File类开发一个SD卡文件浏览器,通过Environment.getExternalStorageDirectory()访问系统的SD卡目录,然后通过File的listFiles()方法获取指定目录下的全部文件和文件夹。
布局文件如下:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.trampcr.sdfileexplorer.MainActivity">
<TextView
android:id="@+id/path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center_horizontal" />
<ListView
android:id="@+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/path" />
<Button
android:id="@+id/parent"
android:layout_width="38dp"
android:layout_height="34dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/home" />
</RelativeLayout>
布局文件包含一个TextView用于显示当前路径,ListView显示当前目录下文件和文件夹,Button用于返回上一级目录。
ListView中的子布局,包含一个ImageView和一个TextView:
line.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/folder"/>
<TextView
android:id="@+id/file_name"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="abc"/>
</LinearLayout>
主程序代码如下:
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private TextView mTextView;
//记录当前的父文件夹
private File mCurrentParent;
//记录当前路径下的所有文件的文件数组
File[] mCurrentFiles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list);
mTextView = (TextView) findViewById(R.id.path);
//获取系统的SD卡的目录
File root = new File(String.valueOf(Environment.getExternalStorageDirectory()));
//如果SD卡存在
if (root.exists()){
mCurrentParent = root;
mCurrentFiles = root.listFiles();
//使用当前目录下的全部文件、文件夹来填充ListView
inflateListView(mCurrentFiles);
}
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mCurrentFiles[position].isFile()){
return;
}
File[] tmp = mCurrentFiles[position].listFiles();
if (tmp == null || tmp.length == 0){
Toast.makeText(MainActivity.this, "当前路径不可访问或该路径下没有文件", Toast.LENGTH_SHORT).show();
}else {
mCurrentParent = mCurrentFiles[position];
mCurrentFiles = tmp;
inflateListView(mCurrentFiles);
}
}
});
//获取上一级目录的按钮
Button parent = (Button) findViewById(R.id.parent);
parent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (!mCurrentParent.getCanonicalFile().equals("/mnt/shell/emulated/0")){
mCurrentParent = mCurrentParent.getParentFile();
mCurrentFiles = mCurrentParent.listFiles();
inflateListView(mCurrentFiles);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
private void inflateListView(File[] files) {
//创建一个List集合,List集合的元素是Map
List<Map<String, Object>> listItems = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
Map<String, Object> listItem = new HashMap<>();
if (files[i].isDirectory()){
listItem.put("icon", R.drawable.folder);
}else {
listItem.put("icon", R.drawable.file);
}
listItem.put("fileName", files[i].getName());
listItems.add(listItem);
}
//创建一个SimpleAdapter
SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems, R.layout.line, new String[]{"icon", "fileName"}, new int[]{R.id.icon, R.id.file_name});
mListView.setAdapter(simpleAdapter);
try {
mTextView.setText("当前路径为:" + mCurrentParent.getCanonicalPath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用File[]数组填充ListView,填充是程序会根据File[]数组里的数据元素代表的是文件还是文件夹来选择使用文件图标或文件夹图标。
运行上面程序,可以看到:
![](http://upload-images.jianshu.io/upload_images/2463568-0db3ff32580e7fd7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)