Android数据储存之File文件储存数据

 

一.存储在内部还是外部?


AndroidManifest.xml中manifest标签下有一个属性android:installLocation,用于指定应用程序安装在什么地方,该属性有三个可选值:

  • auto:程序可能被安装在外部存储器上,例如SD卡;但是默认会被安装到手机内存中。当手机内存为空时,程序将被安装到外部存储器上;当程序安装到手机上后,用户可以决定把程序放在外部存储器还是内存中。
  • internalOnly:默认值,程序只能被安装在内存中,如果内存为空,程序则不能成功被安装。
  • preferExternal:将程序安装在外部存储器,但是系统不保证程序一定会被安装到外部存储器上。当外部存储器不可以安装或为空时,程序将被安装到内存中。当程序使用了forward-locking机制时也将被安装到内存中,因为外部存储不支持此机制。程序安装后,用户可以自由切换程序应该在外部还是内部存储器上。

获取External存储的权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

二.openFileInput和openFileOutput


Context提供了两个方法打开应用程序的数据文件夹里的文件IO流:

  • FileInputStream openFileInput(String name):打开应用程序的数据文件夹下的name文件对应的输入流。
  • FileOutputStream openFileOutput(String name, int mode):打开应用程序的数据文件夹下的name文件对应的输出流。第二个参数指定打开文件的模式,该模式支持如下值:
    • MODE_PRIVATE:该文件只能被当前程序读写。
    • MODE_APPEND:以追加方式打开该文件,应用程序可以向该文件中追加内容。
    • MODE_WORLD_READABLE:该文件的内容可以被其他程序读取。
    • MODE_WORLD_WRITEABLE:该文件的内容可以由其他程序读写。

Context还提供了访问应用程序的数据文件夹的方法:

 

  • getDir(String name, int mode):在应用程序的数据文件夹下获取或创建name对应的子目录。
  • File getFileDir():获取应用程序的数据文件夹的绝对路径。
  • String[] fileList():返回应用程序的数据文件夹下的全部文件。
  • deleteFile(String):删除应用程序的数据文件夹下的指定文件。

 核心代码如下:

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){
        // 步骤1:获取输入值
        if(msg == null) return;
        try {
            // 步骤2:创建一个FileOutputStream对象,MODE_APPEND追加模式
            FileOutputStream fos = openFileOutput("message.txt",
                    MODE_APPEND);
            // 步骤3:将获取过来的值放入文件
            fos.write(msg.getBytes());
            // 步骤4:关闭数据流
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.tony.app/files/message.txt,

三.读写SD卡上的文件

读写SD卡上文件的步骤:

  1. 调用Environment的getExternalStorageState()方法判断手机上是否插入了SD卡,并且应用程序具有读写SD卡的权限。使用如下代码:
    //如果返回true,说明已插入SD卡,且应用程序具有读写SD卡的能力
    Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
  2. 调用Environment的getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录。
  3. 使用FileInputStream、FileOutputStream、FileReader或FileWriter读写SD卡里的文件。

为了读写SD卡上的数据,必须在AndroidManifest.xml中添加读写SD卡的权限:

<!-- 在SD卡中创建于删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 向SD卡中写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

接着在使用SDcard进行读写的时候 会用到Environment类下面的几个静态方法  :  

 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[]数组里的数据元素代表的是文件还是文件夹来选择使用文件图标或文件夹图标。

运行上面程序,可以看到:

猜你喜欢

转载自ch-kexin.iteye.com/blog/2355284