短信发送
短信发送:(通过获得默认的短信管理器来实现)
android:minLines=“3”文本输入框显示行数
通知的三种方式:状态栏,对话框,吐司
吐司显示的2个重载方法,一个是传入字符串,一个是传入ID(ID有利于国际化)
Toast.makeText(MainActivity.this,R.string.success,Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(),"success",1).show();
代码中引入资源:R.string.success;XML中引入资源android:text="@string/success";
用模拟器接收中文短信会出现乱码,但手机上不会。如果文本内容过多,则会自动分条发送。
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button=(Button) this.findViewById(R.id.button);
button.setOnClickListener(new ButtonClickListener());
}
private class ButtonClickListener implements View.OnClickListener{
@Override
public void onClick(View v) {
EditText mobileText=(EditText) findViewById(R.id.mobile);
EditText contentText=(EditText) findViewById(R.id.content);
String mobile=mobileText.getText().toString();
String content=contentText.getText().toString();
SmsManager smsManager=SmsManager.getDefault();//获得系统短信管理器
ArrayList<String> texts=smsManager.divideMessage(content);//自动分割文本内容
for(String text:texts){
smsManager.sendTextMessage(mobile, null, text, null, null);//参数:接收号码地址,短信中心地址,文本,发送状态,接受状态
}
Toast.makeText(getApplicationContext(), R.string.success, 1).show();//传入Activity可用MainActivity.this或getApplicationContext();1和0分别代表吐司显示时间的长短,吐司内容可以传入字符串或ID(推荐使用ID,有利于国际化)
}
}
}
单元测试
android:stretchColumns="1"用于表格布局,值为0和1,1表示单元格可以拉升
单元测试:
1.搭建测试环境,清单中引入测试库(application之下),最后配置启动装置和要测试应用所在的包。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.itcast.action“ android:versionCode="1“ android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
....
</application>
<uses-sdk android:minSdkVersion="6" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="cn.itcast.action" android:label="Tests for My App" />//targetPackage配置被测试应用所在包
2.编写业务类,业务方法
3.编写业务测试用例,测试用例要继承AndroidTestCase,测试用例中,测试方法testSave 等命名,并在方法后抛出异常
public void testSave()throws Throwable{
实例化业务类,并调用方法进行测试
Assert.assertEquals(期望结果,实际结果)可以对比实际结果是否是正确的
}
4.在OUTLINE中运行相应的方法查看测试结果。
编写中错误:
没有实例化业务类,没有将结果传入assertEquals方法。
日志输出
日志:时间,进程ID, 标志,信息
W ,E 代表查看大于警告,错误的所有日志。
日志过滤器,通过TAG,PID,TYPE查找日志信息。过滤器名任意。
日志输出通过类Log实现:Log.i,Log.e,Log.w输出不同级别的信息
Log.i(tag,msg);tag是当前类名或代表当前类名常量,msg要输出的日志信息
输出日志的另一种方法: System.out.println("xxxxxxxxxxx"); 输出info级别,TAG为System.out
System.err.println("xxxxxxx"); 输出ERROR 级别的信息,TAG为System.err
推荐使用Log输出。日志视图输出中文信息会显示乱码。
数据存储与访问1
对处理后的数据进行存储,以供再次访问。
android为数据存储提供了5种方式:文件,SharedPreferendes参数,SQLite数据库,ContentProvider 内容提供
者,网络
文件存储方式:
文件可存储在手机自带存储空间(1G,200M)或外存储设备(SDCard,36G),要考虑文件的大小来选择。
案例NOTICE:
文件存储主要用IO实现。存储方法写在业务类中。
public class FileService {
private Context context;
public FileService(Context context) {
this.context = context;
}
//存储数据
public void save(String name, String content) throws Exception {
FileOutputStream out=context.openFileOutput(name, Context.MODE_PRIVATE);
out.write(content.getBytes());
out.close();
}
//读取数据
public String read(String name)throws Exception{
ByteArrayOutputStream out=new ByteArrayOutputStream();
FileInputStream in=context.openFileInput(name);
int len=0;
byte[] buf=new byte[1024];
while((len=in.read(buf))!=-1){
out.write(buf,0,len);
}
byte[] data=out.toByteArray();
return new String(data);
}
}
获得输出流:context.openFileOutput(filename,mode);//参数为文件名和操作模式,注意要给context 赋值(构造函数或set)
文件操作模式:
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);
context.openFileOutput默认将文件保存在data\data\应用包\files文件夹下。context.openFileInput默认从该文件夹下读取文件
android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。
SDcard文件写入
SD卡的目录:/mnt/sdcard,推荐使用Environment类来得到SD卡路径。(Environment方法试验时失败,还需验证)
往SD卡写数据,要在清单文件加创建权限和写入权限
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
判断SD的状态,Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED),存在则创建写入数据。
SD卡写数据方法:
public void send(String name,String content)throws Exception{
File file=new File(new File("/mnt/sdcard"), name);
FileOutputStream outStream=new FileOutputStream(file);//区别于手机自带存储的openFileInput
outStream.write(content.getBytes());
outStream.close();
}
保存到SD卡的路径可以供其他应用访问。
XML的pull解析
在Android平台上可以使用Simple API for XML(SAX) 、 Document Object Model(DOM)和Android附带的pull解析器解析XML文件。
Pull解析器是一个开源的java项目,既可以用于android,也可以用于JavaEE。
原理:将XML内容读取到一个字符数组中,switch,不同的元素触发不同的事件。
解析XML:
为了方便获取,XML文件直接放在SRC目录下
写一个java bean用来存储数据,在业务类中编写解析方法。
调用业务方法进行解析
测试时获得InputStream 类型的XML:this.getClass().getClassLoader().getResourceAsStream("person.xml");
parser.next()可以进入下一个元素并触发相应事件
空白节点属于文本节点,不处理
parser.nextText()可获得下一个文本节点的文本值
public static List<Person> getPersons(InputStream xml)throws Exception{
XmlPullParser parser=Xml.newPullParser();
List<Person> persons=null;
Person person=null;
parser.setInput(xml, "UTF-8");
int event=parser.getEventType();
while(event!=XmlPullParser.END_DOCUMENT){
switch(event){
case XmlPullParser.START_DOCUMENT:
persons=new ArrayList<Person>();
break;
case XmlPullParser.START_TAG:
if("person".equals(parser.getName())){
person=new Person();
Integer id=new Integer(parser.getAttributeValue(0));
person.setId(id);
}else if("name".equals(parser.getName())){
person.setName(parser.nextText());
}else if("age".equals(parser.getName())){
person.setAge(new Integer(parser.nextText()));
}
break;
case XmlPullParser.END_TAG:
if("person".equals(parser.getName())){//这一句,试验时忘了加
persons.add(person);
person=null;
}
break;
}
event=parser.next();
}
return persons;
}
}
案例错误:
业务方法中case XmlPullParsr.END_TAG: 忘了判断"person".equals(parser.getName());
测试时没有在清单文件中添加测试环境。
用pull解析器将数据生成XML文件
生成方式和编写XML的顺序一样,用到类XmlSerializer
测试时getContext().getFilesDir(),用于获得手机自带存储的files文件夹目录
public class XmlService {
public void save(List<Person> persons, OutputStream out) throws Exception {
XmlSerializer ser = Xml.newSerializer();
ser.setOutput(out, "UTF-8");
ser.startDocument("UTF-8", true);// 指定文档开始编码和文档是否能够独立存在
ser.startTag(null, "persons");
for (Person person : persons) {//编写时写错了for 循环的位置
ser.startTag(null, "person");// 指定开始元素的域名空间,和元素名
ser.attribute(null, "id", person.getId().toString());//写javaBean时,数据类型推荐使用包装类型Integer,Short
ser.startTag(null, "name");
ser.text(person.getName());
ser.endTag(null, "name");
ser.startTag(null, "age");
ser.text(person.getAge().toString());
ser.endTag(null, "age");
ser.endTag(null, "person");
ser.startTag(null, "person");
ser.startTag(null, "name");
ser.text(person.getName());
ser.endTag(null, "name");
ser.startTag(null, "age");
ser.text(person.getAge().toString());
ser.endTag(null, "age");
ser.endTag(null, "person");
}
ser.endTag(null, "persons");
ser.endDocument();
out.flush();
out.close();
}
SharedPreferences参数存储
使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下
另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。
public class PreferenceService {
private Context context;
public PreferenceService(Context context) {
this.context = context;
}
public void save(String name, int age) {
SharedPreferences sp=context.getSharedPreferences("person", Context.MODE_PRIVATE);//获得参数类型对象并设置存储到 xml,XML的文件名,不用加后缀
Editor editor=sp.edit();// 获得编辑器
editor.putString("name", name);
editor.putInt("age", age);
editor.commit();//将内存的数据写入XML文件
}
}
2.从存储的XML中读取参数回显
在onClick()方法中为EditText设置回显值即可。nametext.setText(sp.getString("name"));
public Map<String ,String> read(){
Map<String,String> map=new HashMap<String,String>();
SharedPreferences sp=context.getSharedPreferences("person", Context.MODE_PRIVATE);
map.put("name", sp.getString("name", ""));
map.put("age", String.valueOf(sp.getInt("age", 0)));//操作时,类型转换出现过错误
return map;
}
SQLite数据库存储数据
SQLite 是Android 系统内置的一种嵌入式关系数据库。SQLite可以解析大部分标准SQL语句
SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。 SQLite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。 另外,在编写CREATE TABLE 语句时,你可以省略跟在字段名称后面的数据类型信息。
创建数据库:
1写一个业务类继承SQLiteOpenHelper,一般在业务类的onCreate()方法中编写创建表代码,onUpdate()方法中编写数据库更新操作。这两个方法都由系统调用。
2.在业务类实例调用getWritableDatabase() 或getReadableDatabase()时完成数据库创建。
public class DBOpen extends SQLiteOpenHelper {
public DBOpen(Context context) {
super(context, "hd.db", null, 1);//显示调用父类构造方法,参数:上下文,数据库名,游标工厂,版本号
}
@Override
public void onCreate(SQLiteDatabase db) {//当数据库创建时系统调用
db.execSQL("CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))");//创建表
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//当数据库版本发生变化时系统调用
db.execSQL(" ALTER TABLE person ADD phone VARCHAR(12) NULL ");//在原表中额外添加新字段
}
}
SQLite数据CURD操作2种方式
获取数据库操作对象的2个方法的区别:getWritableDatabase()和getReadabelDatabase();
getReadabelDatabase();的源代码是先调用getWritableDatabase()获取可写数据库对象,如果获取不到,则再获取只读的数据库。
第一种方式:数据库操作对象SQLiteDatabase调用execSQL()或rawQuery()实现,编码简单(推荐使用)
第二种凡是:数据库操作对象调用insert,update,delete,query实现。
public class CRUDService {
private DBCreate service;
public CRUDService(Context context) {//将对象传入
service=new DBCreate(context);
}
public void save(Person person){
SQLiteDatabase db=service.getWritableDatabase();
db.execSQL("insert into person(name,phone) values (?,?)", new Object[]{person.getName(),person.getPhone()});
}
public void update(Person person){
SQLiteDatabase db=service.getWritableDatabase();
db.execSQL("update person set name=?,phone=? where id=?", new String[]{person.getName(),person.getPhone(),person.getId().toString()});
}
public Person find(Integer personid){
SQLiteDatabase db=service.getReadableDatabase();
Cursor cursor=db.rawQuery("select * from person where id=?", new String[]{personid.toString()});
if(cursor.moveToFirst()){
int id=cursor.getInt(cursor.getColumnIndex("id"));
String name=cursor.getString(cursor.getColumnIndex("name"));
String phone=cursor.getString(cursor.getColumnIndex("phone"));
cursor.close();
return new Person(id,name,phone);
}
return null;
}
public void delete(Integer id){
SQLiteDatabase db=service.getWritableDatabase();
db.execSQL("delete from person where id=?",new Object[]{id.toString()});
}
public List<Person> getScroll(int offset,int maxsize){//记录分页代码
List<Person> persons=new ArrayList<Person>();
SQLiteDatabase db=service.getReadableDatabase();
Cursor cursor=db.rawQuery("select * from person limit ?,?", new String[]{String.valueOf(offset),String.valueOf(maxsize)});
while(cursor.moveToNext()){
int id=cursor.getInt(cursor.getColumnIndex("id"));
String name=cursor.getString(cursor.getColumnIndex("name"));
String phone=cursor.getString(cursor.getColumnIndex("phone"));
persons.add(new Person(id,name,phone));
}
cursor.close();
return persons;
}
public long getCount(){
SQLiteDatabase db=service.getReadableDatabase();
Cursor cursor=db.rawQuery("select count(*) from person", null);
cursor.moveToFirst();
return cursor.getLong(0);
}
}
第二种是拼接SQL语句的方式
pubic void save(Person person){
SQLiteDatabase db=DBCreate.getWritableDatabase();
ContentValues values=new ContentValues();//内容集
values.put("name",person.getName());
values.put("phone",person.getPhone());
db.insert("new",null,values);//参数:表明,空字段名(只有values为null时设置值),内容集
}
db.update("new",values,"personid=?",new String[]{person.getId|().toString()});//参数:表名,内容集,条件,条件值
db.query("new",new String[]{"name","phone"},"personid=?",new String[]{id.toString},null,null,null)//第二个参数,要查询的字段,null表示全部
db.delete("new","personid=?",new String[]{id.toString()});
Cursor cursor =db.query("new",null,null,null,null,null,"personid asc",offset+","+maxsize);分页
db.query("new",new String("conut(*)"),null,null,null,null,null);相当于 count *
SQLite 数据库事物操作
public void pay(){
SQLiteDatabase db=dbservice.getWritableDatabase();//有缓存
db.beginTransaction();//开事物。
try{
db.execSQL("update person set amount=amount+100 where id=1");
db.execSQL("update person set amount=amount-100 where id=2");
db.setTransactionSuccessfully();//设置事物标志为True,相当于提交
}finally{
db.endTransaction();//根据事物标志判断是否提交或回滚,默认为false(回滚),并结束事物
}
}