';
数据存储(二)–SAX引擎XML存储(附Demo)
最后更新于:2022-04-01 20:09:24
Android SDK支持采用SAX技术读取XML,SAX采用顺序读取的方式来处理XML文档。这就要求在每读取XML文档的某个节点时会触发相应的事件来处理这个节点。下面基于一个实例讲述SAX的使用:
~~~
public class Book
{
private String name;
private String id;
private String price;
private String publisher;
private int count;
.... get,set方法省略
}
~~~
XML文件如下:
~~~
12
10
21
~~~
XMLTool.java
1.构建一个工厂SAXParserFactory
2.构建并实例化SAXPraser对象
~~~
public class XMLTool {
private static SAXParser getSAXParser() throws ParserConfigurationException, SAXException
{
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
return parserFactory.newSAXParser();
}
public static DefaultHandler parse(InputStream inStream,DefaultHandler handler){
if(inStream!=null){
try {
SAXParser parser = getSAXParser();
parser.parse(inStream, handler);
return handler;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(inStream!=null){
try {
inStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
return null;
}
}
~~~
BookXMLParser.java
~~~
public class BookXMLParser extends DefaultHandler
{
private ArrayList dataList;
private Book book;
private StringBuffer stringBuffer = new StringBuffer();
//private StringBuffer buffer=new StringBuffer();
public ArrayList getData()
{
return dataList;
}
public void startDocument() throws SAXException
{
// TODO Auto-generated method stub
dataList = new ArrayList();
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException
{
// TODO Auto-generated method stub
if(qName.equals("book"))
{
book = new Book();
book.setName(attributes.getValue("book:name"));
book.setId(attributes.getValue("book:id"));
book.setPrice(attributes.getValue("book:price"));
book.setPublisher(attributes.getValue("book:publisher"));
}
super.startElement(uri, localName, qName, attributes);
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
stringBuffer.append(ch,start,length);
super.characters(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if(qName.equals("book"))
{
if(stringBuffer.toString()!=null && !stringBuffer.toString().equals(""))
{
book.setCount(Integer.parseInt(stringBuffer.toString().trim()));
stringBuffer.setLength(0);//必须清空缓冲区
}
dataList.add(book);
}
super.endElement(uri, localName, qName);
}
}
~~~
SAX引擎需要处理5个分析点,也可以称为分析事件。
1.开始分析XML文件。该分析点表示SAX引擎刚刚开始处理XML文件,但是还没有读取XML文件中的内容,该分析点对应:
~~~
public void startDocument() throws SAXException
{
// TODO Auto-generated method stub
dataList = new ArrayList();
}
~~~
在此方法里面可以做一些初始化的工作。
2.开始处理每一个XML元素。也就是遇到这样的起始标记的时候都会触发这个分析节点,所对应的事件方法是startElement。在这个节点可以获得元素的名称、属性的相关信息。
~~~
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException
{
// TODO Auto-generated method stub
if(qName.equals("book"))
{
book = new Book();
book.setName(attributes.getValue("book:name"));
book.setId(attributes.getValue("book:id"));
book.setPrice(attributes.getValue("book:price"));
book.setPublisher(attributes.getValue("book:publisher"));
}
super.startElement(uri, localName, qName, attributes);
}
~~~
3.处理完每一个XML元素。也就是遇到这样的结束标记的时候会触发endElement方法,在该事件中可以获得当前处理完元素的全部信息。
~~~
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if(qName.equals("book"))
{
if(stringBuffer.toString()!=null && !stringBuffer.toString().equals(""))
{
book.setCount(Integer.parseInt(stringBuffer.toString().trim()));
stringBuffer.setLength(0);//必须清空缓冲区
}
dataList.add(book);
}
super.endElement(uri, localName, qName);
}
~~~
4.处理完XML文件。如果SAX引擎将整个XML文件全部扫描完就会出发endDocument方法。这个方法可能不是必须的,但在这个方法中可以完成一些收尾工作,比如说释放资源等。在该例中我没有使用。
5.读取字符分析点。这是一个很重要的分析点。如果没有这个分析点, 前面的工作相当于白做,虽然扫描了XML文件,但是没有保存.....而这个分析点所对应的characters事件方法的主要作用就是保存SAX读取的XML文件内容。具体的说就是12中的“12”
~~~
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
stringBuffer.append(ch,start,length);
super.characters(ch, start, length);
}
~~~
使用SAX解析XML:
~~~
public class MainActivity extends Activity
{
private List books;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InputStream inStream = getResources().openRawResource(R.raw.books);
BookXMLParser parser = new BookXMLParser();
books = ((BookXMLParser)XMLTool.parse(inStream, parser)).getData();
if(books!=null && books.size()>0)
{
for(int i = 0;i books, OutputStream out) throws Exception
{
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(out, "UTF-8");
serializer.startDocument("UTF-8", true);
serializer.startTag(null, "books");
for (Book book : books)
{
serializer.startTag(null, "book");
serializer.attribute(null, "book:name", book.getName());
serializer.attribute(null, "book:id",book.getId());
serializer.attribute(null, "book:price", book.getPrice());
serializer.attribute(null, "book:publisher",book.getPublisher());
serializer.text(String.valueOf(book.getCount()));
serializer.endTag(null, "book");
}
serializer.endTag(null, "books");
serializer.endDocument();
out.flush();
out.close();
}
~~~
Demo:[http://download.csdn.net/detail/tangnengwu/7664719](http://download.csdn.net/detail/tangnengwu/7664719)
';
数据存储(一)–SharedPreferences之你不知道的事
最后更新于:2022-04-01 20:09:22
一、SharedPreferences将数据文件保存在指定路径上
SharedPreferences原则上是只能保存在当前应用程序私有的shared_prefs目录中,不过也不是绝对的,我们可以用一些非常规的方法改变存储目录,反射技术是很好的选择。
先上实现代码:
~~~
private SharedPreferences share;
private SharedPreferences.Editor editor;
~~~
修改路径关键代码:
~~~
private void initSharedPreferences(String path,String name,int mode)
{
try {
Field field =ContextWrapper.class.getDeclaredField("mBase");
field.setAccessible(true);
Object obj = field.get(this);
field = obj.getClass().getDeclaredField("mPreferencesDir");
field.setAccessible(true);
File file = new File(path);
field.set(obj, file);
share = getSharedPreferences(name, mode);
editor = share.edit();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
~~~
~~~
Field field =ContextWrapper.class.getDeclaredField("mBase");
~~~
获取ContextWrapper对象中的mBase变量,该变量保存了ContextImpl对象,ContextImpl对象中的mPreferencesDir保存了数据文件的保存路径。
~~~
share = getSharedPreferences(name, mode);
~~~
执行这句后会在指定目录下创建文件用来保存数据。
PS:使用反射技术,要求仔细研究源码,这样才会知道要去修改哪个地方,哪个变量。
使用:
~~~
initSharedPreferences("/data/fly","config",Activity.MODE_PRIVATE);
editor.putString("AA", "AAaa");
editor.commit();
Toast.makeText(this, share.getString("AA", ""), 1000).show();
~~~
二、SharedPreferences保存图片
SharedPreferences原则上只能将字符串以key-value的形式保存,但是我们可以采用编码的方式将任何二进制数据转化为字符串,从而将可以将二进制数据保存在SharedPreferences文件中,最常用的编码格式是Base64.
~~~
private void saveDrawable(int id)
{
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, 50, baos);
String imageBase64 = new String(Base64.encodeToString(baos.toByteArray(),Base64.DEFAULT));
editor.putString("P",imageBase64 );
editor.commit();
}
private Drawable loadDrawable()
{
String temp = share.getString("P", "");
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(temp.getBytes(), Base64.DEFAULT));
return Drawable.createFromStream(bais, "");
}
~~~
三、SharedPreferences保存对象
由于二进制数据经过编码后可以用SharedPreferences以字符串的形式存储,所以保存对象也称为可能。
~~~
private void saveProduct(Product product)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(product);
String temp = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
Log.i("AAA", temp);
editor.putString("product", temp);
editor.commit();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private Product getProduct()
{
String temp = share.getString("product", "");
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(temp.getBytes(), Base64.DEFAULT));
Product product = null;
try {
ObjectInputStream ois = new ObjectInputStream(bais);
product = (Product) ois.readObject();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.i("AAA", e.toString());
}catch(ClassNotFoundException e1)
{
Log.i("AAA", e1.toString());
}
return product;
}
~~~
对象可以被SharedPreferences存储的前提是该对象被序列化了,也就是说要实现Serializable接口,实际上Serializable接口是个空接口,只是为了标记该对象是被序列化的。
~~~
public static class Product implements Serializable
{
String name;
String id;
int count;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String toString()
{
return "name:"+name+" id:"+id+" count:"+count;
}
}
~~~
如果该类是内部类的话要写成static 不然会出现java.io.NotSerializableException异常:[解决办法点这里
](http://blog.csdn.net/tangnengwu/article/details/37901059)
';
android中反射技术使用实例
最后更新于:2022-04-01 20:09:20
在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义.反射 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。
**1.通过反射技术可以访问到其他包名下数据方法等,这些为一些APK换皮肤提供了方便**
首先初始化skinContext
~~~
try {
skinContext = this.createPackageContext("com.skin",
CONTEXT_IGNORE_SECURITY|CONTEXT_INCLUDE_CODE);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
skinContext=null;
e.printStackTrace();
}
~~~
可以通过下面的方法访问到指定包名下的资源ID
~~~
/**
* 取得对应包的所有资源的ID
* 存在MAP中
* @param packageName
* @return
*/
private Map> getSkinResourcesId(String packageName)
{
Map temp = null;
Map> resMap =new HashMap>();
try {
//取得皮肤包中的R文件
Class> rClass = skinContext.getClassLoader().loadClass(packageName+".R");
//取得记录各种资源的ID的类
Class>[] resClass =rClass.getClasses();
String className,resourceName;
int resourceId=0;
for(int i=0;i();
temp.put(resourceName, resourceId);
Log.i("DDDDD", "className:"+className+" resourceName:"+resourceName+" " +
"resourceId:"+Integer.toHexString(resourceId));
}
}
//由于内部类的关系className应该是com.skin.R$layout的形式
//截掉前面的包名和.R$以方便使用
className = className.substring(packageName.length()+3);
resMap.put(className, temp);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return resMap;
}
~~~
最后通过资源ID和skinContext可以访问到指定包下的所有资源,例如要访问layout
~~~
/**
* 获取皮肤包中的layout
* 并转化为VIEW
* @param layoutName
* @return
*/
private View getLayoutFromSkin(String layoutName)
{
View view;
if(resMap == null)
return null;
Map temp = resMap.get("layout");
int viewId = (Integer) temp.get(layoutName);
if(viewId != 0)
{
//引用皮肤包资源转化View
LayoutInflater inflater =LayoutInflater.from(skinContext);
view = inflater.inflate(skinContext.getResources().getLayout(viewId), null);
}
else
{
view = null;
}
return view;
}
~~~
注:换皮肤思路详见:[http://blog.csdn.net/tangnengwu/article/details/22801107](http://blog.csdn.net/tangnengwu/article/details/22801107)
**2. 访问android 隐藏的API**
Toast信息框的关闭是由系统管理的,因为hide方法是隐藏的开发者没有办法直接调用,这种情况下可以用发射机制获取这个方法,创建一个显示和隐藏都由开发者控制的Toast信息框。
~~~
package com.example.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MyToast
{
Context context=null;
Object obj =null;
public MyToast(Context context,String text)
{
this.context =context;
Toast toast =Toast.makeText(context, text, 1);
try {
Field field = toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
obj =field.get(toast);
} catch (Exception e) {
// TODO: handle exception
Log.d("AAA", "MyToast Exception--->"+e.toString());
}
}
public void show()
{
try {
//android4.0以上就要以下处理
// Field mNextViewField = obj.getClass().getDeclaredField("mNextView");
// mNextViewField.setAccessible(true);
// LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// View v = inflate.inflate(R.layout.ui_toast, null);
// mNextViewField.set(obj, v);
Method method =obj.getClass().getDeclaredMethod("show", null);
method.invoke(obj, null);
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("AAA", "show Exception--->"+e.toString());
e.printStackTrace();
}
}
public void hide()
{
try {
Method method =obj.getClass().getDeclaredMethod("hide", null);
method.invoke(obj, null);
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("AAA", "hide Exception--->"+e.toString());
e.printStackTrace();
}
}
}
~~~
显示toast:
~~~
MyToast toast = new MyToast(this, "反射机制!");
toast.show();
~~~
隐藏toast:
toast.hide();
**注意在4.0以上的版本中,还需要对Toast 中的View进行处理,如代码中所示**
**3. 修改某些“不可改” 的系统资源**
ListView组件没有提供修改快速滑块图像的API,因此不能直接修改,但可通过反射实现
~~~
package com.example.reflection;
import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.ListView;
public class MListView extends ListView
{
public MListView(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
setNewDrawable(context);
}
private void setNewDrawable(Context context)
{
try {
Field field = AbsListView.class.getDeclaredField("mFastScroller");
field.setAccessible(true);
Object obj = field.get(this);
field =field.getType().getDeclaredField("mThumbDrawable");
field.setAccessible(true);
Drawable drawable = (Drawable)field.get(obj);
drawable = context.getResources().getDrawable(R.drawable.ic_launcher);
field.set(obj, drawable);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
~~~
~~~
Field field = AbsListView.class.getDeclaredField("mFastScroller");
~~~
FastScroller.mThunbDrawable变量保存了快速滑块图像,但首先要获取AbsListView.mFastScroller变量
~~~
~~~
~~~
android:fastScrollEnabled="true"
~~~
使用快速滑块
效果图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bbdd97e.jpg)
总结:
Java中的反射机制,被称为Reflection,它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性或方法。Reflection机制允许程序在正在执行的过程中,利用Reflection APIs取得任何已知名称的类的内部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,并可以在执行的过程中,动态生成Instances、变更fields内容或唤起methods。再次基础上我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。
有关反射技术的API:
Class类:
Class类代表着某个类的字节码,要使用反射,就需要取得对应的Class对象,然后就通过这个对象,就可解剖出类的成员变量,成员方法等等。
获取Class类对象
//通过Class的forName()方法,此方法最为常用
Class class1 = Class.forName(className);
//通过 .class
Class class2 = XXX.class;
//通过对象获得
Class class3 = new XXX().getClass();
Class类的常用方法:
getConstructor() 获取构造函数
getMethod() 获取成员方法
getField() 获取成员变量
getDeclaredConstructor() 获取私有的构造函数
getDeclaredMethod() 获取私有的成员方法
getDeclaredField() 获取私有的成员变量
取得method对象之后
调用
~~~
method.invoke(obj, null)
~~~
使用该方法
';
android SQLite 使用实例
最后更新于:2022-04-01 20:09:17
Android作为目前主流的移动操作系统,完全符合SQLite占用资源少的优势,故在Android平台上,集成了一个嵌入式关系型数据库—SQLite。如果想要开发 Android 应用程序,需要在 Android 上存储数据,使用SQLite 数据库是一种非常好的选择。在一般程序中使用数据库的过程都可以框架化,套路化,实例如下:
表说明:
1.班级 classes:
class_id 主键 class_name
2.学生 students:
student_id 主键 student_name score class_id 外键
创建表:
CREATE TABLE classes(class_id varchar(10) primary key , class_name varchar(20))
CREATE TABLE students(student_id varchar(10) primary key ,
student_name varchar(20) ,
score varchar(4) ,
class_id varchar(10),
foreign key (class_id) references classes(class_id) **on delete cascade on update cascade**)
**1. 继承扩展 SQLiteOpenHelper 创建数据库和对应表**
~~~
package com.tang.databasedemo;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBHelper extends SQLiteOpenHelper
{
public DBHelper(Context context) {
super(context, "info.db", null, 1);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db)
{
// TODO Auto-generated method stub
String classesSQL = "CREATE TABLE classes(class_id varchar(10) primary key , " +
"class_name varchar(20))";
String studentsSQL = "CREATE TABLE students(student_id varchar(10) primary key , " +
"student_name varchar(20) ,score varchar(4) ,class_id varchar(10), " +
"foreign key (class_id) references classes(class_id) " +
"on delete cascade on update cascade )";
db.execSQL(classesSQL);
Log.d("my", "create table classes:"+classesSQL);
db.execSQL(studentsSQL);
Log.d("my", "create table students:"+studentsSQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
// TODO Auto-generated method stub
}
}
~~~
**2. 创建学生(Class)学生(Student)实体**
~~~
package com.tang.databasedemo;
import android.util.Log;
public class Class
{
private String classId;
private String className;
public String getClassId() {
return classId;
}
public void setClassId(String classId) {
this.classId = classId;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String toString() {
return "Class--->"+"classId:"+classId+" className:"+className;
}
}
~~~
~~~
package com.tang.databasedemo;
public class Student
{
private String studentId;
private String studentName;
private String score;
private String classId;
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getScore() {
return score;
}
public void setScore(String score) {
this.score = score;
}
public String getClassId() {
return classId;
}
public void setClassId(String classId) {
this.classId = classId;
}
public String toString()
{
return "Student--->"+"studentId:"+studentId+" studentName:"+studentName+" score:"+score+" classId:"+classId;
}
}
~~~
**3. 创建DBServer类,在该类中定义增删改查等方法来操作数据库**
~~~
package com.tang.databasedemo;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class DBServer
{
private DBHelper dbhelper;
public DBServer(Context context)
{
this.dbhelper = new DBHelper(context);
}
/**
* 添加班级
* @param entity
*/
public void addClass(Class entity)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Object[] arrayOfObject = new Object[2];
arrayOfObject[0] = entity.getClassId();
arrayOfObject[1] = entity.getClassName();
localSQLiteDatabase.execSQL("insert into classes(class_id,class_name) values(?,?)", arrayOfObject);
localSQLiteDatabase.close();
}
/**
* 添加学生
* @param entity
*/
public void addStudent(Student entity)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Object[] arrayOfObject = new Object[4];
arrayOfObject[0] = entity.getStudentId();
arrayOfObject[1] = entity.getStudentName();
arrayOfObject[2] = entity.getScore();
arrayOfObject[3] = entity.getClassId();
localSQLiteDatabase.execSQL("insert into students(student_id,student_name,score,class_id) values(?,?,?,?)", arrayOfObject);
localSQLiteDatabase.close();
}
/**
* 删除一个班级
* 同时会删除students中该班级的学生
* @param class_id
*/
public void deleteClass(String class_id)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
//设置了级联删除和级联更新
//在执行有级联关系的语句的时候必须先设置“PRAGMA foreign_keys=ON”
//否则级联关系默认失效
localSQLiteDatabase.execSQL("PRAGMA foreign_keys=ON");
Object[] arrayOfObject = new Object[1];
arrayOfObject[0] =class_id;
localSQLiteDatabase.execSQL("delete from classes where class_id=?", arrayOfObject);
localSQLiteDatabase.close();
}
/**
* 删除一个学生
* @param student_id
*/
public void deleteStudent(String student_id)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Object[] arrayOfObject = new Object[1];
arrayOfObject[0] =student_id;
localSQLiteDatabase.execSQL("delete from students where student_id=?", arrayOfObject);
localSQLiteDatabase.close();
}
/**
* 修改学生信息
* @param entity
*/
public void updateStudentInfo(Student entity)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Object[] arrayOfObject = new Object[4];
arrayOfObject[0] = entity.getStudentName();
arrayOfObject[1] = entity.getScore();
arrayOfObject[2] = entity.getClassId();
arrayOfObject[3] = entity.getStudentId();
localSQLiteDatabase.execSQL("update students set student_name=?,score=?,class_id=? where student_id=?", arrayOfObject);
localSQLiteDatabase.close();
}
/**
* 使用班级编号查找该班级所有学生
* @param classId
* @return
*/
public List
findStudentsByClassId(String classId)
{
List localArrayList=new ArrayList();
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select student_id, student_name ,score from students " +
"where class_id=? order by score desc", new String[]{classId});
while (localCursor.moveToNext())
{
Student temp=new Student();
temp.setStudentId(localCursor.getString(localCursor.getColumnIndex("student_id")));
temp.setStudentName(localCursor.getString(localCursor.getColumnIndex("student_name")));
temp.setScore(localCursor.getString(localCursor.getColumnIndex("score")));
temp.setClassId(classId);
localArrayList.add(temp);
}
localSQLiteDatabase.close();
return localArrayList;
}
/**
* 使用班级名查找该班级所有学生
* @param className
* @return
*/
public List findStudentsByClassName(String className)
{
List localArrayList=new ArrayList();
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select student_id, student_name,score,classes.class_id from students,classes" +
" where students.class_id=classes.class_id and classes.class_name =? order by score asc" , new String[]{className});
while (localCursor.moveToNext())
{
Student temp=new Student();
temp.setStudentId(localCursor.getString(localCursor.getColumnIndex("student_id")));
temp.setStudentName(localCursor.getString(localCursor.getColumnIndex("student_name")));
temp.setScore(localCursor.getString(localCursor.getColumnIndex("score")));
temp.setClassId(localCursor.getString(3));
localArrayList.add(temp);
}
localSQLiteDatabase.close();
return localArrayList;
}
/**
* 查找所有学生
* @param className
* @return
*/
public List findAllStudents()
{
List localArrayList=new ArrayList();
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select * from students " +
"where 1=1 order by score desc ", null);
while (localCursor.moveToNext())
{
Student temp=new Student();
temp.setStudentId(localCursor.getString(localCursor.getColumnIndex("student_id")));
temp.setStudentName(localCursor.getString(localCursor.getColumnIndex("student_name")));
temp.setScore(localCursor.getString(localCursor.getColumnIndex("score")));
temp.setClassId(localCursor.getString(localCursor.getColumnIndex("class_id")));
localArrayList.add(temp);
}
localSQLiteDatabase.close();
return localArrayList;
}
/**
* 取得所有班级
* @return
*/
public List findAllClasses()
{
List localArrayList=new ArrayList();
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select * from classes " +
"where 1=1", null);
while (localCursor.moveToNext())
{
Class temp=new Class();
temp.setClassId(localCursor.getString(localCursor.getColumnIndex("class_id")));
temp.setClassName(localCursor.getString(localCursor.getColumnIndex("class_name")));
localArrayList.add(temp);
}
localSQLiteDatabase.close();
return localArrayList;
}
/**
* 成绩最好
* @return
*/
public Student findMaxScoreStudent()
{
Student temp =new Student();
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select student_id,student_name,class_id,max(score) from students " +
"where 1=1",null );
localCursor.moveToFirst();
temp.setStudentId(localCursor.getString(0));
temp.setStudentName(localCursor.getString(1));
temp.setClassId(localCursor.getString(2));
temp.setScore(localCursor.getString(3));
return temp;
}
/**
* 查找是否有该学生
* @param studentId
* @return
*/
public boolean isStudentsExists(String studentId)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select count(*) from students " +
"where student_id=?", new String[]{studentId});
localCursor.moveToFirst();
if(localCursor.getLong(0)>0)
return true;
else
return false;
}
/**
* 确认该班级是否存在
* @param classId
* @return
*/
public boolean isClassExists(String s)
{
SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase();
Cursor localCursor = localSQLiteDatabase.rawQuery("select count(*) from classes " +
"where class_id=? or class_name=?", new String[]{s,s});
localCursor.moveToFirst();
if(localCursor.getLong(0)>0)
return true;
else
return false;
}
}
~~~
**4.调用DBServer里的方法,操作数据**
~~~
package com.tang.databasedemo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.text.AlteredCharSequence;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
private List classData =new ArrayList();
private List studentsData =new ArrayList();
private static final String className ="A/B/C/D/E";
private static final String studentName ="彭大/黄二/张三/李四/王五/郑六/田七/周八/叶九/孔十/萧十一";
private DBServer db;
private SharedPreferences share;
private SharedPreferences.Editor editor;
private String info ="";
private EditText editText;
private Button b,b1,b2,b3,b4,b5,b6;
private EditText sId,sName,score,cId,cName;
private Handler hander =new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
if(msg.what==0)
{
sId.setText("");
sName.setText("");
score.setText("");
cName.setText("");
cId.setText("");
}
else if(msg.what==1)
{
db.deleteClass((String)msg.obj);
info += "删除一个班级及班级里面的学生:班级Id:"+(String)msg.obj;
editText.setText(info);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
share = getSharedPreferences("DatabaseDamo", 0);
editor =share.edit();
db=new DBServer(this);
if(share.getInt("times", 0)==0)
{
initDatabase();
editor.putInt("times", 1);
editor.commit();
}
}
private void initView()
{
editText = (EditText) findViewById(R.id.info);
sId = (EditText) findViewById(R.id.studentId);
sName = (EditText) findViewById(R.id.studentName);
score = (EditText) findViewById(R.id.score);
cId = (EditText) findViewById(R.id.classId);
cName = (EditText) findViewById(R.id.className);
b =(Button) findViewById(R.id.button);
b1 =(Button) findViewById(R.id.button1);
b2 =(Button) findViewById(R.id.button2);
b3 =(Button) findViewById(R.id.button3);
b4 =(Button) findViewById(R.id.button4);
b5 =(Button) findViewById(R.id.button5);
b6 =(Button) findViewById(R.id.button6);
b.setOnClickListener(this);
b1.setOnClickListener(this);
b2.setOnClickListener(this);
b3.setOnClickListener(this);
b4.setOnClickListener(this);
b5.setOnClickListener(this);
b6.setOnClickListener(this);
}
private void initDatabase()
{
info="";
editText.setText("");
String []classTemp = className.split("/");
Class c;
for(int i=0;iOnce opened successfully, the database is cached, so you can
* call this method every time you need to write to the database.
* (Make sure to call {@link #close} when you no longer need the database.)
* Errors such as bad permissions or a full disk may cause this method
* to fail, but future attempts may succeed if the problem is fixed.
*
* Database upgrade may take a long time, you
* should not call this method from the application main thread, including
* from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
*
* @throws SQLiteException if the database cannot be opened for writing
* @return a read/write database object valid until {@link #close} is called
*/
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
}
/**
* Create and/or open a database. This will be the same object returned by
* {@link #getWritableDatabase} unless some problem, such as a full disk,
* requires the database to be opened read-only. In that case, a read-only
* database object will be returned. If the problem is fixed, a future call
* to {@link #getWritableDatabase} may succeed, in which case the read-only
* database object will be closed and the read/write object will be returned
* in the future.
*
*
Like {@link #getWritableDatabase}, this method may
* take a long time to return, so you should not call it from the
* application main thread, including from
* {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
*
* @throws SQLiteException if the database cannot be opened
* @return a database object valid until {@link #getWritableDatabase}
* or {@link #close} is called.
*/
public SQLiteDatabase getReadableDatabase() {
synchronized (this) {
return getDatabaseLocked(false);
}
}
private SQLiteDatabase getDatabaseLocked(boolean writable) {
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {
// The database is already open for business.
return mDatabase;
}
}
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}
SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} else if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
try {
if (DEBUG_STRICT_READONLY && !writable) {
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
}
}
onConfigure(db);
final int version = db.getVersion();
if (version != mNewVersion) {
if (db.isReadOnly()) {
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
}
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
onOpen(db);
if (db.isReadOnly()) {
Log.w(TAG, "Opened " + mName + " in read-only mode");
}
mDatabase = db;
return db;
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
db.close();
}
}
}
~~~
对于getReadableDatabase()的注释,大致意思也就是:
getWritableDatabase()和getReadableDatabase()会返回相同的对象,除非出现了一些如空间已满的问题,这时就会返回一个只读的对象。当问题解决了之后,只读对象将会被关闭,这时就会返回一个可读写的对象。
SQL实例可执行代码:
[http://download.csdn.net/detail/tangnengwu/7369503](http://download.csdn.net/detail/tangnengwu/7369503)
';
launchMode 总结
最后更新于:2022-04-01 20:09:15
转载:[http://blog.csdn.net/shift_wwx/article/details/9225951](http://blog.csdn.net/shift_wwx/article/details/9225951)
launchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象,一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。
Activity一共有以下四种launchMode:
1.standard
2.singleTop
3.singleTask
4.singleInstance
我们可以在AndroidManifest.xml配置
的android:launchMode属性为以上四种之一即可。
下面我们结合实例一一介绍这四种lanchMode:
**1.standard**
standard模式是默认的启动模式,不用为配置android:launchMode属性即可,当然也可以指定值为standard。
我们将会一个Activity,命名为FirstActivity,来演示一下标准的启动模式。FirstActivity代码如下:
~~~
1. package com.shift.launchermode;
1.
1. import android.os.Bundle;
1. import android.view.View;
1. import android.view.View.OnClickListener;
1. import android.widget.Button;
1. import android.widget.TextView;
1. import android.app.Activity;
1. import android.content.Intent;
1.
1. public class FirstActivity extends Activity{
1.
1. @Override
1. protected void onCreate(Bundle savedInstanceState) {
1. super.onCreate(savedInstanceState);
1. setContentView(R.layout.first);
1. TextView show = (TextView)findViewById(R.id.first_show);
1. show.setText(this.toString());
1.
1. Button go = (Button)findViewById(R.id.first_go);
1. go.setOnClickListener(new OnClickListener() {
1.
1. @Override
1. public void onClick(View v) {
1. gotoSelf();
1. }
1. });
1. }
1.
1. private void gotoSelf(){
1. Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
1. startActivity(intent);
1. }
~~~
我们FirstActivity界面中的TextView用于显示当前Activity实例的序列号,Button用于跳转到下一个FirstActivity界面。
然后我们连续点击几次按钮,将会出现下面的现象:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d746d497.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d748ec5c.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d74ae922.jpg)
我们注意到都是FirstActivity的实例,但序列号不同,并且我们需要连续按后退键两次,才能回到第一个FristActivity。standard模式的原理如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d74caa7c.jpg)
如图所示,每次跳转系统都会在task中生成一个新的FirstActivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的FirstActivity实例。
当然,你也可以在两个activity之间切换,你会发现序列号也是不同的,如果first中start second,在second中start first,例如按照1 - 2 - 1 - 2,然后你会发现序列号是不可能重复的。
这就是standard启动模式,不管有没有已存在的实例,都生成新的实例。
**2.singleTop**
我们在上面的基础上为指定属性android:launchMode="singleTop",系统就会按照singleTop启动模式处理跳转行为。我们重复上面几个动作,将会出现下面的现象:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7548a0d.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7548a0d.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7548a0d.jpg)
我们看到这个结果跟standard有所不同,三个序列号是相同的,也就是说使用的都是同一个FirstActivity实例;如果按一下后退键,程序立即退出,说明当前栈结构中只有一个Activity实例。singleTop模式的原理如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7576b82.jpg)
正如上图所示,跳转时系统会先在栈结构中寻找是否有一个FirstActivity实例正位于栈顶,如果有则不再生成新的,而是直接使用。也许朋友们会有疑问,我只看到栈内只有一个Activity,如果是多个Activity怎么办,如果不是在栈顶会如何?我们接下来再通过一个示例来证实一下大家的疑问。
我们再新建一个Activity命名为SecondActivity,如下:
~~~
1. package com.shift.launchermode;
1.
1. import android.os.Bundle;
1. import android.view.View;
1. import android.view.View.OnClickListener;
1. import android.widget.Button;
1. import android.widget.TextView;
1. import android.app.Activity;
1. import android.content.Intent;
1.
1. public class SecondActivity extends Activity {
1.
1. @Override
1. protected void onCreate(Bundle savedInstanceState) {
1. super.onCreate(savedInstanceState);
1. setContentView(R.layout.second);
1. TextView show = (TextView)findViewById(R.id.second_show);
1. show.setText(this.toString());
1.
1. Button go = (Button)findViewById(R.id.second_go);
1. go.setOnClickListener(new OnClickListener() {
1.
1. @Override
1. public void onClick(View v) {
1. Intent intent = new Intent(SecondActivity.this, FirstActivity.class);
1. startActivity(intent);
1. }
1. });
1. }
1. }
~~~
然后将之前的FirstActivity跳转代码改为:
~~~
1. Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
1. startActivity(intent);
~~~
是的,FirstActivity会跳转到SecondActivity,SecondActivity又会跳转到FirstActivity。演示结果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550b911222.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550b92e5d9.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550b94a739.jpg)
我们看到,两个FirstActivity的序列号是不同的,证明从SecondActivity跳转到FirstActivity时生成了新的FirstActivity实例。原理图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550ba671d4.jpg)
我们看到,当从SecondActivity跳转到FirstActivity时,系统发现存在有FirstActivity实例,但不是位于栈顶,于是重新生成一个实例。
这就是singleTop启动模式,如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例。
**3.singleTask**
在上面的基础上我们修改FirstActivity的属性android:launchMode="singleTask"。演示的结果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550ba83a9e.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550baa351a.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550ba83a9e.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550badc7b4.jpg)
我们注意到,在上面的过程中,FirstActivity的序列号是不变的,SecondActivity的序列号却不是唯一的,说明从SecondActivity跳转到FirstActivity时,没有生成新的实例,但是从FirstActivity跳转到SecondActivity时生成了新的实例。singleTask模式的原理图如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb07abf.jpg)
在图中的下半部分是SecondActivity跳转到FirstActivity后的栈结构变化的结果,我们注意到,SecondActivity消失了,没错,在这个跳转过程中系统发现有存在的FirstActivity实例,于是不再生成新的实例,而是将FirstActivity之上的Activity实例统统出栈,将FirstActivity变为栈顶对象,显示到幕前。也许朋友们有疑问,如果将SecondActivity也设置为singleTask模式,那么SecondActivity实例是不是可以唯一呢?在我们这个示例中是不可能的,因为每次从SecondActivity跳转到FirstActivity时,SecondActivity实例都被迫出栈,下次等FirstActivity跳转到SecondActivity时,找不到存在的SecondActivity实例,于是必须生成新的实例。但是如果我们有ThirdActivity,让SecondActivity和ThirdActivity互相跳转,那么SecondActivity实例就可以保证唯一。
这就是singleTask模式,如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前。
**4.singleInstance**
这种启动模式比较特殊,因为它会启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。
我们修改FirstActivity的launchMode="standard",SecondActivity的launchMode="singleInstance",由于涉及到了多个栈结构,我们需要在每个Activity中显示当前栈结构的id,所以我们为每个Activity添加如下代码:
**[java]**[view plain](http://blog.csdn.net/shift_wwx/article/details/9225951# "view plain")[copy](http://blog.csdn.net/shift_wwx/article/details/9225951# "copy")
1. TextView taskIdView = (TextView) findViewById(R.id.taskIdView);
1. taskIdView.setText("current task id: " + this.getTaskId());
然后我们再演示一下这个流程:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb24b3f.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb3d64a.jpg)
我们发现这两个Activity实例分别被放置在不同的栈结构中,关于singleInstance的原理图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb626ed.jpg)
我们看到从FirstActivity跳转到SecondActivity时,重新启用了一个新的栈结构,来放置SecondActivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在SecondActivity中再次跳转到FirstActivity,这个时候系统会在原始栈结构中生成一个FirstActivity实例,然后回退两次,注意,并没有退出,而是回到了SecondActivity,为什么呢?是因为从SecondActivity跳转到FirstActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。
如果我们修改FirstActivity的launchMode值为singleTop、singleTask、singleInstance中的任意一个,流程将会如图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb7b114.jpg)
singleInstance启动模式可能是最复杂的一种模式,为了帮助大家理解,我举一个例子,假如我们有一个share应用,其中的ShareActivity是入口Activity,也是可供其他应用调用的Activity,我们把这个Activity的启动模式设置为singleInstance,然后在其他应用中调用。我们编辑ShareActivity的配置:
~~~
1.
1.
1.
1.
1.
1.
1.
1.
1.
1.
~~~
然后我们在其他应用中这样启动该Activity:
~~~
1. Intent intent = new Intent("android.intent.action.SINGLE_INSTANCE_SHARE");
1. startActivity(intent);
~~~
当我们打开ShareActivity后再按后退键回到原来界面时,ShareActivity做为一个独立的个体存在,如果这时我们打开share应用,无需创建新的ShareActivity实例即可看到结果,因为系统会自动查找,存在则直接利用。大家可以在ShareActivity中打印一下taskId,看看效果。关于这个过程,原理图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb97805.jpg)
';
android 延时执行任务
最后更新于:2022-04-01 20:09:13
~~~
package com.example.delaydemo;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
//非UI线程不能更新UI需要借助Handler
Handler handler =new Handler()
{
public void handleMessage(android.os.Message msg)
{
setContentView(R.layout.main);
};
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
//第一种方法就是启动一个子线程做加载数据之类的耗时操作
//做完之后使用handler通知主线程更新UI
// new Thread(new Runnable() {
// @Override
// public void run() {
// // TODO Auto-generated method stub
// try {
// Thread.sleep(4000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// handler.sendEmptyMessage(0);
// }
// }).start();
//第二种方法
//直接使用handler,这里可以更新UI,原因是new Handler()相当于new Handler(getMainLooper())
//下面的Log打印的是main 说明还是运行在主线程中
// new Handler().postDelayed(new Runnable()
// {
// @Override
// public void run() {
// // TODO Auto-generated method stub
// setContentView(R.layout.main);
// Log.i("MainActivity", Thread.currentThread().getName());
// }
// }, 4000);
Timer timer = new Timer();
timer.schedule(task, 4000);
//schedule(TimerTask task, long delay)
//在延时delay毫秒后执行task。并没有重复执行
// schedule(TimerTask task, long delay, long period)
//在延时delay毫秒后重复的执行task,周期是period毫秒。
//也就是说在delay毫秒后每隔period毫秒执行一次task
//schedule(TimerTask task, Date time)
//在指定的时间执行一次
}
//第三种方法
//使用TimerTask和handler
TimerTask task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
handler.sendEmptyMessage(0);
//这里打印的是 Timer-0 说明不是运行在主线程中
//若在这里直接使用setContentView(R.layout.main);
//会报CalledFromWrongThreadException: Only the original thread that created a
//view hierarchy can touch its views.错误
Log.i("MainActivity", Thread.currentThread().getName());
}
};
}
~~~
除了上面3种方法其实还可以使用AlarmManager来实现延时执行或重复执行,只是这种方法有点繁琐,是用广播机制实现的,对于循环执行的任务,如果需要停止执行的话,必须执行closeAlarm()方法,不然即使是退出了程序,这个任务还是会一直执行。
~~~
package com.example.delaydemo;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class Alarm
{
/**
* delay秒后执行一次
* @param context
* @param delay
*/
public Alarm(Context context,int delay)
{
Intent intent =new Intent(context, AlarmReceiver.class);
intent.setAction("com.alarm.justonce");
PendingIntent sender=
PendingIntent.getBroadcast(context, 0, intent, 0);
Calendar calendar=Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, delay);
AlarmManager alarm=(AlarmManager)context.getSystemService(context.ALARM_SERVICE);
alarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
Toast.makeText(context, delay+"后alarm开启", 2000).show();
}
/**
* 到firstTime 后 以period周期执行
* @param context
* @param firstTime
* @param period
*/
public Alarm(Context context,long firstTime,long period)
{
Intent intent =new Intent(context, AlarmReceiver.class);
intent.setAction("com.alarm.repeating");
PendingIntent sender=PendingIntent
.getBroadcast(context, 0, intent, 0);
//开始时间
long firstime=firstTime;
AlarmManager am=(AlarmManager)context.getSystemService(context.ALARM_SERVICE);
//period秒一个周期,不停的发送广播
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP
, firstime, period*1000, sender);
Toast.makeText(context, firstTime+"后alarm开启", 2000).show();
}
/**
* 取消周期执行任务
* @param context
*/
public void closeAlarm(Context context)
{
Intent intent =new Intent(context, AlarmReceiver.class);
intent.setAction("repeating");
PendingIntent sender=PendingIntent
.getBroadcast(context, 0, intent, 0);
AlarmManager alarm=(AlarmManager)context.getSystemService(context.ALARM_SERVICE);
alarm.cancel(sender);
}
}
~~~
广播接受器,别忘了在Manifest中注册
~~~
package com.example.delaydemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals("com.alarm.justonce"))
{
Toast.makeText(context, "justonce alarm", 2000).show();
}
else
{
Toast.makeText(context, "repeating alarm",
2000).show();
}
}
}
~~~
最后附上程序:[http://download.csdn.net/detail/tangnengwu/7217595](http://download.csdn.net/detail/tangnengwu/7217595)
';
android数据读写
最后更新于:2022-04-01 20:09:11
~~~
package com.example.filerw;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.net.URL;
import org.apache.http.util.EncodingUtils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
public class FileUtil
{
private Context context;
public FileUtil(Context context)
{
this.context=context;
}
/**
* 读取data/data/
/files 目录下的文件
* @param fileName
* @return
*/
public String readFromData(String fileName)
{
try
{
FileInputStream fis =context.openFileInput(fileName);
//图片如此操作
//InputStream in = new BufferedInputStream(fis);
//Bitmap bitmap= BitmapFactory.decodeStream(in);
byte[]buff =new byte[1024];
int hasRead=0;
StringBuilder sb =new StringBuilder("");
while((hasRead=fis.read(buff))>0)
{
sb.append(new String(buff,0,hasRead));
}
fis.close();
return sb.toString();
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* 将数据写入data/data//files 目录下的文件
* @param content
* @param fileName
* @param mode
*/
public void writeToData(String content,String fileName,int mode)
{
try {
FileOutputStream fos =context.openFileOutput(fileName, mode);
PrintStream ps =new PrintStream(fos);
ps.println(content);
ps.close();
} catch (Exception e) {
// TODO: handle exception
}
}
/**
* 读取SD卡 目录下的文件
* @param fileName
* @return
*/
public String readFromSDCard(String fileName)
{
try {
//检测SD卡是否存在
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
File sdCardDir = Environment.getExternalStorageDirectory();
FileInputStream fis =new FileInputStream(sdCardDir.getCanonicalPath()+"/"+fileName);
BufferedReader br =new BufferedReader(new InputStreamReader(fis));
StringBuilder sb =new StringBuilder("");
String line=null;
while((line =br.readLine())!=null)
{
sb.append(line);
}
br.close();
return sb.toString();
}
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
/**
* 接着写入SD卡目录下的文件 追加内容
* @param content
* @param FileName
* @param mode
*/
public void writeToSDCard(String content,String FileName,String mode)
{
try {
//检测SD卡是否存在
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
File sdCardDir = Environment.getExternalStorageDirectory();
File targetFile =new File(sdCardDir.getCanonicalPath()+"/"+FileName);
RandomAccessFile raf =new RandomAccessFile(targetFile,mode);
raf.seek(targetFile.length());
raf.write(content.getBytes());
raf.close();
}
} catch (Exception e) {
// TODO: handle exception
}
}
/**
* fileName可以是路径加文件名
* 必须先检测SD卡是否存在
* @param fileName
* @return
* @throws IOException
*/
public String readSDFile(String fileName) throws IOException
{
if(isSDCardExists())
{
File file = new File(fileName);
FileInputStream fis = new FileInputStream(file);
//如果是图片可以这样得到BitMap
// InputStream is = new BufferedInputStream(fis);
// Bitmap bitmap = BitmapFactory.decodeStream(is);
int length = fis.available();
byte [] buffer = new byte[length];
fis.read(buffer);
String res = EncodingUtils.getString(buffer, "UTF-8");
fis.close();
return res;
}
else
{
return null;
}
}
/**
* 必须先检测SD卡是否存在
* fileName可以是路径加文件名
* @param fileName
* @param write_str
* @throws IOException
*/
public void writeSDFile(String fileName, String write_str) throws IOException
{
if(isSDCardExists())
{
File file = new File(fileName);
FileOutputStream fos = new FileOutputStream(file);
byte [] bytes = ((String) write_str).getBytes();
fos.write(bytes);
fos.close();
}
}
/**
* 获取网络资源
* 图片 文件 。。。
* @throws IOException
*/
public void writeToSDFromURL(String s,String fileName) throws IOException
{
if(isSDCardExists())
{
URL url =new URL(s);
InputStream is =url.openStream();
//转化为Bitmap
//Bitmap bitmap =BitmapFactory.decodeStream(is);
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
File file =new File(path+"/"+fileName);
FileOutputStream fop= new FileOutputStream(file);
OutputStream os = new BufferedOutputStream(fop);
//若写入到DATA中就如下做
//OutputStream os=context.openFileOutput(fileName, context.MODE_WORLD_READABLE);
byte [] buff=new byte[1024];
int hasRead=0;
while((hasRead=is.read(buff))>0)
{
os.write(buff,0,hasRead);
}
is.close();
os.close();
}
}
public boolean isSDCardExists()
{
try {
String s= Environment.getExternalStorageDirectory().getCanonicalPath();
if(new File(s).exists())
return true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
return false;
}
//有关SD卡读写都要加上读写权限
//
//
// String Name = File.getName(); //获得文件或文件夹的名称:
// String parentPath = File.getParent(); //获得文件或文件夹的父目录
// String path = File.getAbsoultePath();//绝对路经
// String path = File.getPath();//相对路经
// File.createNewFile();//建立文件
// File.mkDir(); //建立文件夹
// File.isDirectory(); //判断是文件或文件夹
// File[] files = File.listFiles(); //列出文件夹下的所有文件和文件夹名
// File.renameTo(dest); //修改文件夹和文件名
// File.delete(); //删除文件夹或文件
}
~~~
文件读写总结:
SD卡中的文件使用FileInputStream和FileOutputStream进行文件的操作。
存放在数据区(/data/data/..)的文件只能使用openFileOutput和openFileInput进行操作,不能使用FileInputStream和FileOutputStream进行文件的操作。
文件操作的一些方法:
String Name = File.getName(); //获得文件或文件夹的名称:
String parentPath = File.getParent(); //获得文件或文件夹的父目录
String path = File.getAbsoultePath();//绝对路经
String path = File.getPath();//相对路经
File.createNewFile();//建立文件
File.mkDir(); //建立文件夹
File.isDirectory(); //判断是文件或文件夹
File[] files = File.listFiles(); //列出文件夹下的所有文件和文件夹名
File.renameTo(dest); //修改文件夹和文件名
File.delete(); //删除文件夹或文件
';
前言
最后更新于:2022-04-01 20:09:08
> 原文出处:[Android基础技术总结](http://blog.csdn.net/column/details/androidbasetang.html)
作者:[tangnengwu](http://blog.csdn.net/tangnengwu)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# Android基础技术总结
> 基础知识总结,为进阶奠基。
';