前言:
很久,都沒更新過這個系列了…因為,除了圖表以外,然后就是數據庫了,調試了一個多星期的Ormlite數據庫,在最新版本中(orm 4.3.3)發現了幾個比較嚴重的bug(例如,查找id的時候無法使用Long類型),不過,還好,ormlite社區還算活躍,bug,已經在預覽中修復了.關於Ormlite數據庫的話,園子里面已經有了寫得很不錯的教程了,我就不重復他們的勞動了.然后,數據庫搞定了,就是寫業務了,有這么一個業務,就是,要求,在某個時間點,對插入的數據進行后台更新,然后,就涉及到了使用計划任務這么一塊知識,覺得有必要做下筆記,這塊,以后應該也能用到
業務說明:
在某時某刻進行數據庫的備份.
相關知識:
1,時間操作
(1) 熟悉使用Calendar
1,作為定時任務,我們需要一個具體的定時時間,以前,我是用Date 類取毫秒數,然后進行計數的操作
1.1例如
//以下為以前的某項目算相隔天數的演示代碼 String startDate = mDateStart.getText().toString(); String endDate = year + "-" + (month + 1) + "-" + day; Log.d("soap", startDate + "---" + endDate); DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); // long day=(startC.getTime()-endC.getTime())/(24*60*60*1000); try { Date start = df.parse(startDate); Date end = df.parse(endDate); Long d = (end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000); Log.d("soap", "相隔天數" + d); if (d > 60) { return false; } else { return true; } } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); }以前這樣寫,感覺挺傻的,希望大家不要學習了,這次的業務需求,如果,是以前的話,我會一個創建具體時間字符串,然后用SimpleDateFormat,獲取毫秒數,這樣個人感覺,很不直觀,也麻煩,也不方便國際化,我們用Calendar做就非常簡單了.
1.2 定時23:00 執行備份操作
竟然是定時任務,我們就要熟悉一個android的一個用於做定時任務的類
AlarmManager
各位,先去看一下官方文檔,在接着看下去吧…
這個類是的初始化是必須要用Context.getSystemService(Context.ALARM_SERVICE).
以下為定時代碼塊
//初始化定時類 AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); //設置定時時間,1分鍾后執行 Calendar c = Calendar.getInstance(); c.add(Calendar.MINUTE, 1); //設置時間到了執行的services Intent intent = new Intent(); //創建一個servcies intent.setClass(this, UpdateStatics.class); PendingIntent pi = PendingIntent.getService(this, 0, intent, 0); //設置定時 //需要android.permission.SET_TIME 權限 //第一個參數為定時類型(一共有四種,具體參見官方文檔),第二個參數為設置的定時時間為long類型,第三個為執行的目標 am.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);以上代碼就會在,1分鍾后,系統執行UpdateStatics 類.
如果,我們要設置具體的時間,只要具體設置Calendar的對象即可,例如23:00分執行
c.set(Calendar.HOUR_OF_DAY, 23); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0);
總結:
使用Calendar類操作時間,和進行時間計算,設置,是十分方便的事情,
2,備份操作
1, 建一個接口類用於,監聽操作.
public interface CompletionListener { void onBackupComplete(); void onRestoreComplete(); void onError(int errorCode); }2,創建一個異步備份的類
public class BackupTask extends AsyncTask<String, Void, Integer> implements CompletionListener{ //定義常量 public static final int BACKUP_SUCCESS =1; public static final int RESTORE_SUCCESS = 2; public static final int BACKUP_ERROR = 3; public static final int RESTORE_NOFLEERROR = 4; public static final String COMMAND_BACKUP = "backupDatabase"; public static final String COMMAND_RESTORE = "restroeDatabase"; private Context mContext; public BackupTask(Context context){ this.mContext = context; } @Override protected Integer doInBackground(String... params) { //1,獲得數據庫路徑 File dbFile = mContext.getDatabasePath("xxx.db"); //2,創建保存的數據庫的路徑 File exportDir = new File(Environment.getExternalStorageDirectory(),"shopBackup"); if(!exportDir.exists()){ exportDir.mkdirs(); } File backup = new File(exportDir, dbFile.getName()); //3,檢查操作 String command = params[0]; if(command.equals(COMMAND_BACKUP)){ //復制文件 try { backup.createNewFile(); fileCopy(dbFile, backup); return BACKUP_SUCCESS; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return BACKUP_ERROR; } }else{ return BACKUP_ERROR; } } private void fileCopy(File source, File dest) throws IOException { FileChannel inChannel = new FileInputStream(source).getChannel(); FileChannel outChannel = new FileOutputStream(dest).getChannel(); // FileInputStream fis = new FileInputStream(dbFile); // FileOutputStream fos = new FileOutputStream(backup); // byte buffer[] = new byte[4 * 1024]; // while(fis.read(buffer) != -1){ // fos.write(buffer); // } // fos.flush(); // long size = inChannel.size(); try { inChannel.transferTo(0, inChannel.size(), outChannel); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(inChannel != null){ inChannel.close(); } if(outChannel != null){ outChannel.close(); } } } @Override protected void onPostExecute(Integer result) { // TODO Auto-generated method stub super.onPostExecute(result); switch (result) { case BACKUP_SUCCESS: onBackupComplete(); break; default: break; } } @Override public void onBackupComplete() { Log.d("backup", "ok"); } @Override public void onRestoreComplete() { // TODO Auto-generated method stub } @Override public void onError(int errorCode) { // TODO Auto-generated method stub } }
記得,設置好權限:
android.permission.WRITE_EXTERNAL_STORAGE
AsyncTask<Params,Progress,Result> 這個類的用法,官方文檔已經解釋的很詳細了,這里也不做重復解釋.
關於:fileCopy(File source, File dest) 這個方法,我這里改用了Nio 的方式進行操作.用注釋注釋的代碼是一般的方式,這個兩者有什么區別呢?
文檔
java.nio.channels
類 FileChanneltransferTo
里面有這么一句話
與從此通道讀取並將內容寫入目標通道的簡單循環語句相比,此方法可能高效得多。很多操作系統可將字節直接從文件系統緩存傳輸到目標通道,而無需實際復制各字節。
看到這句話我就使用了這個方法了,然后,為了搞清楚實現的方式,我查看了一下transferTo 的源碼只是一個抽象方法,然后,在的FileInputSteam里面找的了channel 的實現方法,不過可惜的,具體的實現代碼,我的源碼包沒有.
然后我順便比較了java 和 android java 對於獲取,channel的區別
java jdk_1.6_u30 默認的fileChannel是用sun包進行實現,然后,關於實現的部分,應該要下個sun包的源碼了吧,因為,沒找的,就貼不出來了.
public FileChannel getChannel() { synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, true, false, this); /* * Increment fd's use count. Invoking the channel's close() * method will result in decrementing the use count set for * the channel. */ fd.incrementAndGetUseCount(); } return channel; }android java getChannel的代碼部分
public FileChannel getChannel() { // BEGIN android-changed synchronized(this) { if (channel == null) { channel = FileChannelFactory.getFileChannel(this, fd.descriptor, IFileSystem.O_RDONLY); } return channel; } // END android-changed }
android java同樣,我的源碼包到實現filechannel 部分的代碼就沒了,對於獲取filechannel ,google 是重寫了sun那部分的代碼了.至於兩者的區別,我就不清楚了.有知道的朋友,望告知