Android的数据存储有五种:文件SharedPreferences、SQLite数据库、内容提供者(Content provider)、网络。

 

一、Android的单元测试

       昨天进行的只是简单的开发,但从今天起的开发内容比较重要。所以首先应该学习Android的单元测试。在Android工程中添加单元测试的方法:

       1.向androidManifest.xml加入:

元素体内。是<application>的子元素。

        <instrumentation android:name="android.test.InstrumentationTestRunner"

  android:targetPackage="cn.itcast.action" android:label="Tests for My App" />

       </application> 与<application>元素并列,是<application>元素的兄弟元素。这里的targetPackage必须是我们创建工程时指定的包名。

 

       2.单元测试类

       我们的单元测试类,必须继承自AndroidTestCase类。

 

       3.单元测试方法

       单元测试方法必须以test开头

 

       4.方法抛出异常

       方法要throws Throwable异常,Throwable是Exception的父类,单元测试框架捕获Throwable。

 

       5.调用测试

       在outline面板或方法名上右键,Run AS Android Junit Test。

 

       6.打印信息

       在android中不能使用System.out.println()打印信息,但我们可以使用Android为我们提供的Log类来打印信息。可以使用Log.i打开info信息、使用Log.e打印error信息、使用Log.d打印调试信息...。

 

       7.查看打印的信息

       因为我们安装了ADT插件,所以选择菜单windows->Show View->Other...->Android->LogCat,打开 LogCat面板。在这个面板中我们可以看到Android输出的所有信息。

 

       但我们只想查看我们自己输出的信息怎么办呢?面板的右上角有个+号,使用它可以创建一个过滤器。比如我们输入一个info信息调用Log.i(tag,”Hello Android!”),tag是信息的标签,一般使用类名。创建过滤器,将Filter Name和by Log Tag都设置为我的们的tag ,OK。它为我们创建了一个新的以tag名称的分页,在这个分页中我们可以查看过滤出来的信息。

 

 

二、Android的文件存储和访问

       Android的文件读写与JavaSE的文件读写相同,都是使用IO流。而且Android使用的正是JavaSE的IO流,下面我们通过一个练习来学习Android的文件读写。

 

 

Android建议采用MVC开发模式,所以我们在Android应用开发中最好使用MVC设计模式。MVC设计模式使三层分离,从而很好的解耦,何乐而不为。

       首先我们向工程中添加一个FileService.java:

public class FileService { Context context; public FileService(Context context) { this.context = context; } /** * 保存文件 * * @param fileName * @param fileContent * @throws Exception */ public void save(String fileName, String fileContent) throws Exception { // Activity的父类的父类就是context,context与其他框架中的context相同为我们以供了一些核心操作工具。 FileOutputStream fileOutputStream = this.context.openFileOutput( fileName, Context.MODE_PRIVATE); fileOutputStream.write(fileContent.getBytes()); } /** * 读取文件 * * @param fileName * @return * @throws Exception */ public String read(String fileName) throws Exception { FileInputStream fileInputStream = this.context.openFileInput(fileName); ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = fileInputStream.read(buffer)) > 0) { byteArray.write(buffer, 0, len); }; return byteArray.toString(); } }

文件读写的操作模式:

       Context.MODE_PRIVATE:新内容覆盖原内容

       Context.MODE_APPEND:新内容追加到原内容后

       Context.MODE_WORLD_READABLE:允许其他应用程序读取

       Context.MODE_WORLD_WRITEABLE:允许其他应用程序写入,会覆盖原数据。

       可以使用+连接这些权限。

然后再向工程中添加FileButtonOnClickEvent.java:
package com.changcheng.file.event;
 
import com.changcheng.file.R;
import com.changcheng.file.service.FileService;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
 
/**
 * 按钮事件类
 * @author Administrator
 * 
 */
public class FileButtonOnClickEvent implements OnClickListener {
      
       // 通过activity获取其他控件
       private Activity activity;
       // 通过FileService读写文件
       private FileService fileService;
       // 打印信息用的标签
       private static final String TAG = "FileButtonOnClickEvent";
 
       public FileButtonOnClickEvent(Activity activity) {
              this.fileService = new FileService(activity);
              this.activity = activity;
       }
 
       @Override
       public void onClick(View v) {
              Button button = (Button) v;
              switch (button.getId()) {
              case R.id.bt_save:
                     // 获取文件名
                     EditText etFileNameS = (EditText) this.activity
                                   .findViewById(R.id.et_file_name);
                     String fileNameS = etFileNameS.getText().toString();
                     // 获取文件内容
                     EditText etFileConS = (EditText) this.activity
                                   .findViewById(R.id.et_file_content);
                     String fileContentS = etFileConS.getText().toString();
                     // 保存
                     try {
                            this.fileService.save(fileNameS, fileContentS);
                            // 在窗口中显示一个特效信息框
                            Toast.makeText(this.activity, R.string.file_save_success,
                                          Toast.LENGTH_LONG).show();
                            Log.i(TAG, "save file success!");
                     } catch (Exception e) {
                            Toast.makeText(this.activity, R.string.file_save_failed,
                                          Toast.LENGTH_LONG).show();
                            Log.e(TAG, e.toString());
                     }
                     break;
              case R.id.bt_read:
                     // 获取文件名
                     EditText etFileNameR = (EditText) this.activity
                                   .findViewById(R.id.et_file_name);
                     String fileNameR = etFileNameR.getText().toString();
                     // 读取文件
                     try {
                            String fielContentR = this.fileService.read(fileNameR);
                            EditText etFileConR = (EditText) this.activity
                                          .findViewById(R.id.et_file_content);
                            etFileConR.setText(fielContentR);
                            Log.i(TAG, "read file success!");
                     } catch (Exception e) {
                            Toast.makeText(this.activity, R.string.file_read_failed,
                                          Toast.LENGTH_LONG).show();
                            Log.e(TAG, e.toString());
                     }
                     break;
              default:
                     break;
              }
       }
}

 

 5.运行程序

      

 

       我们也可以通过菜单Windows->Show View->Other...->Android->File Explorer,打开 File Explorer面板。通过它可以查看Android的目录结构:

       data:应用数据,我们保存的文件在/data/data/packagename/files。

sdcard:现在的手机一般都可以外插一个SD卡,这个目录就是SDCard的目录。操作此目录时需要在主配置文件中注册操作权限。

       system:Android操作系统的文件,我们不要修改。

       我们可以点击 File Explorer右上角的“软盘向左箭头”图标,导出文件。

 

       6.其他程序获取文件路径的方法

       1.绝对路径:/data/data/packagename/files/filename;

       2.context:context.getFilesDir()+”/filename”;

       缓存目录:/data/data/packagename/CachegetCacheDir();

       如果文件过大就不能存放在手机的文件目录,需要存储到SDCard上。

       SDCard目录:/sdcard/Environment.getExternalStorageDirectory()

       使用SDCard目录前,需要判断是否有sdcardEnvironment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

操作此目录时需要在主配置文件中注册操作权限。sdcard里的文件是可以给任何程序访问,与存放在手机卡里是不一样的,因为权限的问题

 

openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。

openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE    =  0

Context.MODE_APPEND    =  32768

Context.MODE_WORLD_READABLE =  1

Context.MODE_WORLD_WRITEABLE =  2

 

Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND

Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。

Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。

MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。

如果希望文件被其他应用读和写,可以传入:

openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);

 

android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。

 

如果要打开存放在/data/data/<package name>/files目录应用私有的文件,可以使用Activity提供openFileInput()方法。

FileInputStream inStream = this.getContext().openFileInput("itcast.txt");

Log.i("FileTest", readInStream(inStream));

readInStream()的方法请看本页下面备注。

 

或者直接使用文件的绝对路径:

File file = new File("/data/data/cn.itcast.action/files/itcast.txt");

FileInputStream inStream = new FileInputStream(file);

Log.i("FileTest", readInStream(inStream));

注意:上面文件路径中的“cn.itcast.action”为应用所在包,当你在编写代码时应替换为你自己应用使用的包。

对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。

如果需要读和写的方法,则要吧两者使用加号合并权限

Activity还提供了getCacheDir()和getFilesDir()方法:

getCacheDir()方法用于获取/data/data/<package name>/cache目录


getFilesDir

()

方法用于获取

/data/data/<package name>/files

目录