android中使用sqlite、复制assets下的数据库到SD卡、支持大于1M的文件

如果使用SD卡,需要在AndroidManifest.xml中设置权限



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

    1 package cn.arthur.common; 
   
 2 
   
 3  import java.io.File; 
   
 4  import java.io.FileOutputStream; 
   
 5  import java.io.IOException; 
   
 6  import java.io.InputStream; 
   
 7  import java.io.OutputStream; 
   
 8 
   
 9  import android.content.Context; 
   
 10  import android.database.sqlite.SQLiteDatabase; 
   
 11  import android.database.sqlite.SQLiteDatabase.CursorFactory; 
   
 12 import android.database.sqlite.SQLiteException; 
   
 13 import android.database.sqlite.SQLiteOpenHelper; 
   
 14 
   
 15 /** 
   
 16 * @author Joshua 
   
 17 * 用法: 
   
 18 * DBHelper dbHelper = new DBHelper(this); 
   
 19 * dbHelper.createDataBase(); 
   
 20 * SQLiteDatabase db = dbHelper.getWritableDatabase(); 
   
 21 * Cursor cursor = db.query() 
   
 22 * db.execSQL(sqlString); 
   
 23 * 注意:execSQL不支持带;的多条SQL语句,只能一条一条的执行,晕了很久才明白 
   
 24 * 见execSQL的源码注释 (Multiple statements separated by ;s are not supported.) 
   
 25 * 将把assets下的数据库文件直接复制到DB_PATH,但数据库文件大小限制在1M以下 
   
 26 * 如果有超过1M的大文件,则需要先分割为N个小文件,然后使用copyBigDatabase()替换copyDatabase() 
   
 27 */ 
   
 28 public class DBHelper extends SQLiteOpenHelper { 
   
 29 //用户数据库文件的版本 
   
 30 private static final int DB_VERSION = 1; 
   
 31 //数据库文件目标存放路径为系统默认位置,cn.arthur.examples 是你的包名 
   
 32 private static String DB_PATH = "/data/data/cn.arthur.examples/databases/"; 
   
 33 /* 
   
 34 //如果你想把数据库文件存放在SD卡的话 
   
 35 private static String DB_PATH = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() 
   
 36 + "/arthurcn/drivertest/packfiles/"; 
   
 37 */ 
   
 38 private static String DB_NAME = "hello.db"; 
   
 39 private static String ASSETS_NAME = "hello.db"; 
   
 40 
   
 41 private SQLiteDatabase myDataBase = null; 
   
 42 private final Context myContext; 
   
 43 
   
 44 /** 
   
 45 * 如果数据库文件较大,使用FileSplit分割为小于1M的小文件 
   
 46 * 此例中分割为 hello.db.101 hello.db.102 hello.db.103 
   
 47 */ 
   
 48 //第一个文件名后缀 
   
 49 private static final int ASSETS_SUFFIX_BEGIN = 101; 
   
 50 //最后一个文件名后缀 
   
 51 private static final int ASSETS_SUFFIX_END = 103; 
   
 52 
   
 53 /** 
   
 54 * 在SQLiteOpenHelper的子类当中,必须有该构造函数 
   
 55 * @param context 上下文对象 
   
 56 * @param name 数据库名称 
   
 57 * @param factory 一般都是null 
   
 58 * @param version 当前数据库的版本,值必须是整数并且是递增的状态 
   
 59 */ 
   
 60 public DBHelper(Context context, String name, CursorFactory factory, int version) { 
   
 61 //必须通过super调用父类当中的构造函数 
   
 62 super(context, name, null, version); 
   
 63 this.myContext = context; 
   
 64 } 
   
 65 
   
 66 public DBHelper(Context context, String name, int version){ 
   
 67 this(context,name,null,version); 
   
 68 } 
   
 69 
   
 70 public DBHelper(Context context, String name){ 
   
 71 this(context,name,DB_VERSION); 
   
 72 } 
   
 73 
   
 74 public DBHelper (Context context) { 
   
 75 this(context, DB_PATH + DB_NAME); 
   
 76 } 
   
 77 
   
 78 public void createDataBase() throws IOException{ 
   
 79 boolean dbExist = checkDataBase(); 
   
 80 if(dbExist){ 
   
 81 //数据库已存在,do nothing. 
   
 82 }else{ 
   
 83 //创建数据库 
   
 84 try { 
   
 85 File dir = new File(DB_PATH); 
   
 86 if(!dir.exists()){ 
   
 87 dir.mkdirs(); 
   
 88 } 
   
 89 File dbf = new File(DB_PATH + DB_NAME); 
   
 90 if(dbf.exists()){ 
   
 91 dbf.delete(); 
   
 92 } 
   
 93 SQLiteDatabase.openOrCreateDatabase(dbf, null); 
   
 94 // 复制asseets中的db文件到DB_PATH下 
   
 95 copyDataBase(); 
   
 96 } catch (IOException e) { 
   
 97 throw new Error("数据库创建失败"); 
   
 98 } 
   
 99 } 
   
100 } 
   
101 
   
102 //检查数据库是否有效 
   
103 private boolean checkDataBase(){ 
   
104 SQLiteDatabase checkDB = null; 
   
105 String myPath = DB_PATH + DB_NAME; 
   
106 try{ 
   
107 checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY); 
   
108 }catch(SQLiteException e){ 
   
109 //database does't exist yet. 
   
110 } 
   
111 if(checkDB != null){ 
   
112 checkDB.close(); 
   
113 } 
   
114 return checkDB != null ? true : false; 
   
115 } 
   
116 
   
117 /** 
   
118 * Copies your database from your local assets-folder to the just created empty database in the 
   
119 * system folder, from where it can be accessed and handled. 
   
120 * This is done by transfering bytestream. 
   
121 * */ 
   
122 private void copyDataBase() throws IOException{ 
   
123 //Open your local db as the input stream 
   
124 InputStream myInput = myContext.getAssets().open(ASSETS_NAME); 
   
125 // Path to the just created empty db 
   
126 String outFileName = DB_PATH + DB_NAME; 
   
127 //Open the empty db as the output stream 
   
128 OutputStream myOutput = new FileOutputStream(outFileName); 
   
129 //transfer bytes from the inputfile to the outputfile 
   
130 byte[] buffer = new byte[1024]; 
   
131 int length; 
   
132 while ((length = myInput.read(buffer))>0){ 
   
133 myOutput.write(buffer, 0, length); 
   
134 } 
   
135 //Close the streams 
   
136 myOutput.flush(); 
   
137 myOutput.close(); 
   
138 myInput.close(); 
   
139 } 
   
140 
   
141 //复制assets下的大数据库文件时用这个 
   
142 private void copyBigDataBase() throws IOException{ 
   
143 InputStream myInput; 
   
144 String outFileName = DB_PATH + DB_NAME; 
   
145 OutputStream myOutput = new FileOutputStream(outFileName); 
   
146 for (int i = ASSETS_SUFFIX_BEGIN; i < ASSETS_SUFFIX_END+1; i++) { 
   
147 myInput = myContext.getAssets().open(ASSETS_NAME + "." + i); 
   
148 byte[] buffer = new byte[1024]; 
   
149 int length; 
   
150 while ((length = myInput.read(buffer))>0){ 
   
151 myOutput.write(buffer, 0, length); 
   
152 } 
   
153 myOutput.flush(); 
   
154 myInput.close(); 
   
155 } 
   
156 myOutput.close(); 
   
157 } 
   
158 
   
159 @Override 
   
160 public synchronized void close() { 
   
161 if(myDataBase != null){ 
   
162 myDataBase.close(); 
   
163 } 
   
164 super.close(); 
   
165 } 
   
166 
   
167 /** 
   
168 * 该函数是在第一次创建的时候执行, 
   
169 * 实际上是第一次得到SQLiteDatabase对象的时候才会调用这个方法 
   
170 */ 
   
171 @Override 
   
172 public void onCreate(SQLiteDatabase db) { 
   
173 } 
   
174 
   
175 /** 
   
176 * 数据库表结构有变化时采用 
   
177 */ 
   
178 @Override 
   
179 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
   
180 } 
   
181 
   
182 } 
   
183


很多手机游戏,在安装APK之后都得需要下载相应的资源包,然后才能进入游戏。

有这样一个需求:就是游戏中需要的资源包打在APK内,随apk一起进行安装到手机中。

这样就不需要,在安装APK之后,去下载资源。(这样APK的大小就会大一些)

于是在网上就开始找对应的直接访问随apk一起安装到手机中的资源。比如在assets目录下放置了一个“test.zip”的资源。当apk安装之后,可能我就需要访问这个"test.zip"资源文件。(可能我会去解压到某处)

在网上找了很长时间,说什么在assets下的资源怎么怎么不能超过1M、或者要分成多个不超过1M的小文件,然后用的时候在拼成一个大文件、说什么要用到什么数据库。总之说的,感觉没有一个有用的。

于是自己就实现了一个从assets下拷贝资源到sd卡中的一个功能。这里不限制非得拷贝到sd卡中。

之所以要拷贝出来,是因为在assets内的资源随apk安装之后,不好访问。所以想拷贝到一个容易访问的地方。比如是一个assets下的是一个zip文件,为需要在安装之后进行解压。而解压的时候我又想使用 ant.jar (支持编码) 。于是我先将zip资源拷贝到一个地方,然后在进行解压。

下面代码:

private void copyBigDataToSD(String strOutFileName) throws IOException 
    {  
        InputStream myInput;  
        OutputStream myOutput = new FileOutputStream(strOutFileName);  
        myInput = this.getAssets().open("yphone.zip");  
        byte[] buffer = new byte[1024];  
        int length = myInput.read(buffer);
        while(length > 0)
        {
            myOutput.write(buffer, 0, length); 
            length = myInput.read(buffer);
        }
        
        myOutput.flush();  
        myInput.close();  
        myOutput.close();        
    }



备注:参数为要拷贝的目的地例如“/mnt/sdcard/test/Out.zip”;

此处“myInput = cGameActivity.getAssets().open("yphone.zip"); ”open 的是你放在assets下的资源的名称。本例放的是一个叫"yphone.zip"的文件;

在进行测试的时候发现在资源文件很大时,例如200M左右,可能会报错。

但可以保证的是100M一下的没问题。

最后,此函数运行需要加权限!