2010.10.28———Android 02
内容一
*************************************
单元测试
************************************
开发android时 错误原因不太好找 可以使用android只带的单元测试
1、引入单元测试库,在AndroidManifest.xml里面加入
<
要放到application里面 activity外面
2、通过那个类来启动单元测试 这个是和uses-sdk同目录 在xml根目录下面
3、编写单元测试代码 和activity同目录 在上面android:targetPackage包下面
[b]AndroidManifest.xml[/b]
[b]TestServiceTest.java[/b]
在单元测试里面可以输出不同级别的控制信息
可以在LogCat里面配置不同的过滤器 来显示特定的信息
除了用log以外 我们还可以用
System.out.println()
System.err.println();
不过 一般建议用log来输出日志信息
内容二
******************************
存储方式——文件
******************************
********************************
把内容保存到手机自带的存储空间内
********************************
需要文件输出流
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,
如果文件不存在,Android 会自动创建它。
创建的文件保存在/data/data/<package name>/files目录,
如: /data/data/lp.file/files/itcast.txt ,
通过点击Eclipse菜单“Window”-“Show View”-“Other”,
在对话窗口中展开android文件夹,选择下面的File Explorer视图,
然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。
要想看文件的内容 必须把文件从android系统导入到我们的window系统中来
第二个参数 文件操作模式:
Context.MODE_PRIVATE 私有模式
1、此文件仅能被本应用访问
2、采用覆盖方式存储 也就是说以前有数据 后来存储的将覆盖原来的
Context.MODE_APPEND 追加模式
1、仅能被本应用访问
2、采用追加方式存储 文件不存在 就创建 存在 就追加内容
MODE_WORLD_READABLE
当前文件可以被其他应用所读取
MODE_WORLD_WRITEABLE
当前文件可以被其他应用写 但不能读
如果希望文件被其他应用读和写,可以传入:
相当于
因为:
读取文件
要读取/data/data/<package name>/files目录下的文件 可以
但是 如果要访问其他应用的文件时 即必须写绝对路径了
另外:
******************************
SDCard存数据
******************************
1、获得sdcard的权限
如果要往sdcard写入数据 就必须获得sdcard的权限 仅仅读的话 不需要
2、 写读取方法
2.2的sdcard的路径是/mnt/sdcard
各个版本的sdcard路径 不是统一的 所以 最好不要再应用中使用绝对路径
Environment.getExternalStorageDirectory()
Android为我们提供的方法 获得sdcard的路径
3、开始存储文件
因为是SDcard 所以 我们不知道用户手机是否有SDcard 所以 开始之前 必须先判断一下
代码:
[b]工具类
FileUtil.java[/b]
[b]清单文件
AndroidManifest.xml[/b]
[b]MainActivity.java[/b]
内容三
***********************
解析xml
**********************
欲解析的文件
[b]person.xml[/b]
[color=red]解析xml有三种 SAX DOM pull[/color]
1、SAX解析:速度快 内存下 事件驱动
2、DOM解析:内存大 所有内容以文档树方式存放在内存 操作简单
3、pull解析器解析 开源的java项目 android已经集成了 和SAX类似 也是事件驱动
但是 需要手动调用parser.next()进入下一个元素 而不是自动的
而且 Pull解析器产生的事件是一个数字
另外 还有就是介绍一下用pull来生成xml 这种方式很简单 就跟我们手写xml文件一样
内容四
******************************
存储方式——SharedPreferences
******************************
作用是向用户提供软件的参数设置功能
使用SharedPreferences保存数据,其背后是用xml文件存放数据,
文件存放在/data/data/<package name>/shared_prefs目录下
1、存参数
2、取参数
现在 我们已经设置了参数了 我们需要在下一次启动应用时
这些参数应该回显在界面上
[b]主界面
MainActivity.java[/b]
另外 还有一个方法 getPrefences(文件操作模式);
它可以在Activity里面直接调用
SharedPreferences sharedPreferences = this.getPrefences(Context.MODE_PRIVATE);
这个方法没有文件名参数 所以 默认的xml存放名字就是此Activity的名字
内容一
*************************************
单元测试
************************************
开发android时 错误原因不太好找 可以使用android只带的单元测试
1、引入单元测试库,在AndroidManifest.xml里面加入
<
uses-library android:name="android.test.runner" />
要放到application里面 activity外面
2、通过那个类来启动单元测试 这个是和uses-sdk同目录 在xml根目录下面
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="lp.test" android:label="Tests for My App" />
targetPackage指定的包名 应该和应用的package一样
android:label 是名字 可有可无
3、编写单元测试代码 和activity同目录 在上面android:targetPackage包下面
a:继承AndroidTestCase
b:方法名为test****
c:方法为public void
[b]AndroidManifest.xml[/b]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="lp.junit"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="lp.junit" android:label="Tests for My App" />
</manifest>
[b]TestServiceTest.java[/b]
package lp.junit;
import junit.framework.Assert;
import lp.service.TestService;
import android.test.AndroidTestCase;
public class TestServiceTest extends AndroidTestCase {
public void test_save(){
TestService service = new TestService();
service.save();
}
public void test_add(){
TestService service = new TestService();
int result = service.add(1,1);
//单元测试可以判断执行结果
Assert.assertEquals(2, result);
}
}
在单元测试里面可以输出不同级别的控制信息
log.v() 全部信息
log.i() info以上级别信息
log.d() debug以上级别信息
log.w() warning以上级别信息
log.e() error以上级别信息
可以在LogCat里面配置不同的过滤器 来显示特定的信息
除了用log以外 我们还可以用
System.out.println()
System.err.println();
不过 一般建议用log来输出日志信息
内容二
******************************
存储方式——文件
******************************
文件存储位置 一般有两种
1.SDCard
2.自带的存储空间
********************************
把内容保存到手机自带的存储空间内
********************************
需要文件输出流
public void saveFile(String fileName,String content) throws Exception{
FileOutputStream fos = this.context.openFileOutput(fileName, Context.MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
}
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,
如果文件不存在,Android 会自动创建它。
创建的文件保存在/data/data/<package name>/files目录,
如: /data/data/lp.file/files/itcast.txt ,
通过点击Eclipse菜单“Window”-“Show View”-“Other”,
在对话窗口中展开android文件夹,选择下面的File Explorer视图,
然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。
要想看文件的内容 必须把文件从android系统导入到我们的window系统中来
第二个参数 文件操作模式:
Context.MODE_PRIVATE 私有模式
1、此文件仅能被本应用访问
2、采用覆盖方式存储 也就是说以前有数据 后来存储的将覆盖原来的
Context.MODE_APPEND 追加模式
1、仅能被本应用访问
2、采用追加方式存储 文件不存在 就创建 存在 就追加内容
MODE_WORLD_READABLE
当前文件可以被其他应用所读取
MODE_WORLD_WRITEABLE
当前文件可以被其他应用写 但不能读
如果希望文件被其他应用读和写,可以传入:
openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
相当于
openFileOutput("itcast.txt", 3);
因为:
Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
读取文件
要读取/data/data/<package name>/files目录下的文件 可以
public String readFile(String fileName) throws Exception{
StringBuffer sb = new StringBuffer();
FileInputStream fis = this.context.openFileInput(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;
while((line=br.readLine())!=null){
sb.append(line);
}
return sb.toString();
}
但是 如果要访问其他应用的文件时 即必须写绝对路径了
String path = "/data/data/应用名/files/文件名;
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
whiel(len=fis.read(buffer))!=-1){
baos.wrtite(buffer,0.len);
}
String result = new String(baos.toByteArray());
fis.close();
baos.close();
另外:
Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取当前应用的cache文件夹,/data/data/<package name>/cache目录
getFilesDir()方法用于获取当前应用的files文件夹,/data/data/<package name>/files目录
******************************
SDCard存数据
******************************
1、获得sdcard的权限
如果要往sdcard写入数据 就必须获得sdcard的权限 仅仅读的话 不需要
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2、 写读取方法
2.2的sdcard的路径是/mnt/sdcard
各个版本的sdcard路径 不是统一的 所以 最好不要再应用中使用绝对路径
Environment.getExternalStorageDirectory()
Android为我们提供的方法 获得sdcard的路径
public void saveFileToSDCard(String fileName,String content) throws Exception{
//String path = "/mnt/sdcard";
File file = new File(Environment.getExternalStorageDirectory(),fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(content.getBytes());
fos.close();
}
3、开始存储文件
因为是SDcard 所以 我们不知道用户手机是否有SDcard 所以 开始之前 必须先判断一下
if(Environment.getExternalStorageState.equals(Environment.MEDIA_MOUNTED)){
}else{
Toast.makeText(Activity.this,"SDCard没有插入或者写保护",1).show();
}
代码:
[b]工具类
FileUtil.java[/b]
package lp.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import android.content.Context;
import android.os.Environment;
public class FileUtil {
private Context context;
public FileUtil(Context context){
this.context = context;
}
/**
* 在SDCard上保存文件
* @param fileName
* @param content
* @throws Exception
*/
public void saveFileToSDCard(String fileName,String content) throws Exception{
//String path = "/mnt/sdcard";
File file = new File(Environment.getExternalStorageDirectory(),fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(content.getBytes());
fos.close();
}
/**
* 保存指定文本到指定文件
* @param fileName
* @param content
* @throws Exception
*/
public void saveFile(String fileName,String content) throws Exception{
FileOutputStream fos = this.context.openFileOutput(fileName, Context.MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
}
/**
* 读取指定文件
* @param fileName
* @return
* @throws Exception
*/
public String readFile(String fileName) throws Exception{
StringBuffer sb = new StringBuffer();
FileInputStream fis = this.context.openFileInput(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;
while((line=br.readLine())!=null){
sb.append(line);
}
return sb.toString();
}
}
[b]清单文件
AndroidManifest.xml[/b]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="lp.file"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="lp.file" android:label="Tests for My App" />
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
[b]MainActivity.java[/b]
this.save.setOnClickListener(new OnClickListener() {
/*
* 在手机上存储文件
@Override
public void onClick(View v) {
String file = MainActivity.this.fileName.getText().toString();
String text = MainActivity.this.content.getText().toString();
//FileOutputStream fos = null;
try {
//如果文件不存在,Android 会自动创建它。
//创建的文件保存在/data/data/<package name>/files目录
//如: /data/data/cn.itcast.action/files/itcast.txt
//业务方法 应该抽取出去
// fos = MainActivity.this.openFileOutput(file, MODE_PRIVATE);
// fos.write(text.getBytes());
util.saveFile(file, text);
Toast.makeText(MainActivity.this, R.string.success, 1).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this, R.string.error, 1).show();
Log.e(TAG, e.toString());
}
// }finally{
// try {
// fos.close();
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
}
*/
//在SDCard上存储文件
@Override
public void onClick(View v) {
String file = MainActivity.this.fileName.getText().toString();
String text = MainActivity.this.content.getText().toString();
try {
//往SDCard上存储数据 首先应该判断存储卡是否存在
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
util.saveFileToSDCard(file, text);
Toast.makeText(MainActivity.this, R.string.success, 1).show();
}else{
Toast.makeText(MainActivity.this, "没有插入SD卡或者写保护", 1).show();
}
}catch(Exception e){
e.printStackTrace();
Toast.makeText(MainActivity.this, R.string.error, 1).show();
Log.e(TAG, e.toString());
}
}
});
内容三
***********************
解析xml
**********************
欲解析的文件
[b]person.xml[/b]
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>liming</name>
<age>30</age>
</person>
<person id="20">
<name>zhangxiaoxiao</name>
<age>25</age>
</person>
</persons>
[color=red]解析xml有三种 SAX DOM pull[/color]
1、SAX解析:速度快 内存下 事件驱动
package lp.util;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import lp.pojo.Person;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* SAX解析XML文件
* @param is
* @return
* @throws Exception
*/
public class SAXParserUtil {
public List<Person> getPersons(InputStream is) throws Exception{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
PersonHandler handler = new PersonHandler();
//将会按照PersonHandler里面的事件来解析xml
parser.parse(is, handler);
return handler.getList();
}
private class PersonHandler extends DefaultHandler{
private List<Person> list;
private String parentTag;
private Person bean = null;
public List<Person> getList(){
return list;
}
@Override
public void startDocument() throws SAXException {
list = new ArrayList<Person>();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if("person".equals(localName)){
bean = new Person();
bean.setId(Integer.parseInt(attributes.getValue(0)));
}
parentTag = localName;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if(parentTag!=null){
String data = new String(ch,start,length);
if(parentTag.equals("name")){
bean.setName(data);
}else if(parentTag.equals("age")){
bean.setAge(Integer.parseInt(data));
}
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if("person".equals(localName)){
list.add(bean);
bean = null;
}
parentTag = null;
}
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
}
}
}
2、DOM解析:内存大 所有内容以文档树方式存放在内存 操作简单
package lp.util;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import lp.pojo.Person;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* DOM解析xml
* @author Administrator
*
*/
public class DomParserUtil {
public List<Person> getPersons(InputStream is) throws Exception{
List<Person> list = new ArrayList<Person>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
Element root = document.getDocumentElement();
NodeList personNodes = root.getElementsByTagName("person");
for(int i=0;i<personNodes.getLength();i++){
Person bean = new Person();
Element person = (Element)personNodes.item(i);
bean.setId(Integer.parseInt(person.getAttribute("id")));
NodeList children = person.getChildNodes();
for(int k=0;k<children.getLength();k++){
Node node = children.item(k);
if(node.getNodeType()==Node.ELEMENT_NODE){
if(node.getNodeName().equals("name")){
bean.setName(node.getFirstChild().getNodeValue());
}else if(node.getNodeName().equals("age")){
bean.setAge(new Integer(node.getFirstChild().getNodeValue()));
}
}
}
list.add(bean);
}
return list;
}
}
3、pull解析器解析 开源的java项目 android已经集成了 和SAX类似 也是事件驱动
但是 需要手动调用parser.next()进入下一个元素 而不是自动的
而且 Pull解析器产生的事件是一个数字
package lp.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import lp.pojo.Person;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import android.util.Xml;
/**
* pull解析xml文件
* @author Administrator
*
*/
public class PullParserUtil {
/**
* 解析
* @param is
* @return
* @throws Exception
*/
public List<Person> getPersons(InputStream is) throws Exception{
List<Person> list = null;
Person bean = null;
XmlPullParser pull = Xml.newPullParser();
pull.setInput(is, "UTF-8");
//得到数字事件
int event = pull.getEventType();
//事件不等于文档结束事件 就一直执行
while(event!=XmlPullParser.END_DOCUMENT){
switch(event){
case XmlPullParser.START_DOCUMENT :
list = new ArrayList<Person>();
break;
case XmlPullParser.START_TAG :
if(pull.getName().equals("person")){
bean = new Person();
bean.setId(new Integer(pull.getAttributeValue(0)));
}
if(bean!=null){
if(pull.getName().equals("name")){
bean.setName(pull.nextText());
}else if(pull.getName().equals("age")){
bean.setAge(new Integer(pull.nextText()));
}
}
break;
case XmlPullParser.END_TAG :
if(pull.getName().equals("person")){
list.add(bean);
bean = null;
}
break;
default : break;
}
event = pull.next();
}
return list;
}
}
另外 还有就是介绍一下用pull来生成xml 这种方式很简单 就跟我们手写xml文件一样
/**
* pull解析器生成xml文件
* @param list
* @param os
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
public void save(List<Person> list,OutputStream os) throws Exception{
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(os, "UTF-8");
//生成xml开始部分
//<?xml version="1.0" encoding="UTF-8"?>
serializer.startDocument("UTF-8", true);
//生成<persons>
//第一个参数是前缀
serializer.startTag(null, "persons");
for(Person person : list){
//生成<person id="23">
serializer.startTag(null, "person");
serializer.attribute(null, "id", person.getId()+"");
//生成<name>
serializer.startTag(null, "name");
//生成lipeng
serializer.text(person.getName());
//生成</name>
serializer.endTag(null, "name");
//生成<age>
serializer.startTag(null, "age");
//生成22
serializer.text(person.getAge()+"");
//生成</age>
serializer.endTag(null, "age");
//生成</person>
serializer.endTag(null, "person");
}
//生成</persons>
serializer.endTag(null, "persons");
serializer.endDocument();
os.close();
}
内容四
******************************
存储方式——SharedPreferences
******************************
作用是向用户提供软件的参数设置功能
使用SharedPreferences保存数据,其背后是用xml文件存放数据,
文件存放在/data/data/<package name>/shared_prefs目录下
1、存参数
2、取参数
/*
* 存参数
*/
public void save(String name,int age){
//第一个参数 xml文件的名字 第二个参数 文件操作模式
SharedPreferences preferences = this.context.getSharedPreferences("person", Context.MODE_PRIVATE);
//通过编辑器来存入数据
Editor editor = preferences.edit();
//editor提供的一系列方法
editor.putString("name", name);
editor.putInt("age", age);
//把内存中数据保存到文件中
editor.commit();
}
现在 我们已经设置了参数了 我们需要在下一次启动应用时
这些参数应该回显在界面上
/**
* 取参数
* @return
*/
public Map<String,String> get(){
Map<String,String> map = new HashMap<String,String>();
//取的参数xml 要和你存的时候的文件名一样
SharedPreferences preferences = this.context.getSharedPreferences("person", Context.MODE_PRIVATE);
//第二个参数 是一个默认值 当xml中name不存在时
String name = preferences.getString("name", "");
int age = preferences.getInt("age", 18);
map.put("name",name);
map.put("age",age+"");
return map;
}
[b]主界面
MainActivity.java[/b]
package lp.SharedPreferences;
import java.util.Map;
import lp.service.PreferenceService;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
private EditText name ;
private EditText age;
private Button set;
private PreferenceService service;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.name = (EditText) this.findViewById(R.id.name);
this.age = (EditText) this.findViewById(R.id.age);
this.set = (Button) this.findViewById(R.id.set);
service = new PreferenceService(this);
Map<String,String> map = service.get();
this.name.setText(map.get("name"));
this.age.setText(map.get("age"));
this.set.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String name_str = MainActivity.this.name.getText().toString();
String age_str = age.getText().toString();
service.save(name_str, new Integer(age_str));
Toast.makeText(MainActivity.this, R.string.success, 1).show();
}
});
}
}
另外 还有一个方法 getPrefences(文件操作模式);
它可以在Activity里面直接调用
SharedPreferences sharedPreferences = this.getPrefences(Context.MODE_PRIVATE);
这个方法没有文件名参数 所以 默认的xml存放名字就是此Activity的名字