標籤:android開發 寫日誌 並發日誌 非同步寫入 不影響使用者體驗
公司在做一個項目 要求記錄使用者行為,寫行為記錄檔到SD卡。實現思想 不影響介面使用者體驗,要時時記錄日誌 不能漏掉。
1.並發處理日誌 寫一個類負責管理各個線程傳過來的日誌資料,日誌資料放在隊列中等待寫線程去處理。這裡每次添加一條日誌資料都會檢查寫日誌線程是否在工作,同時為了並發處理傳過來的資料採用synchronized 同步:
ConcurrentLinkedQueue 是基於連結節點的、安全執行緒的隊列。並發訪問不需要同步。因為它在隊列的尾部添加元素並從頭部刪除它們,所以只要不需要知道隊列的大小, ConcurrentLinkedQueue 對公用集合的共用訪問就可以工作得很好。收集關於隊列大小的資訊會很慢,需要遍曆隊列 建議使用 if(!queue.isEmpty()) 判斷是否空
package com.xx.log;import java.io.BufferedWriter;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Calendar;import java.util.Date;import java.util.concurrent.ConcurrentLinkedQueue;import org.apache.log4j.Logger;import org.apache.log4j.spi.LoggerFactory;import android.util.Log;/** * 行為日誌記錄 * @author Administrator * */public class ActionLog { protected final static Logger logger = Logger.getLogger(ActionLog.class);public static String filePath="";public static ConcurrentLinkedQueue tempQueue=new ConcurrentLinkedQueue<Object>();/** * 記錄基本資料 頭 * @param bi */ public static synchronized void recordBaseInfoLog(BaseInfo bi){ tempQueue.add(bi); if(!WriteThread.isWriteThreadLive){//監察寫線程是否工作中,沒有 則建立 new WriteThread().start(); } } /** * 記錄行為資訊 * @param ai */ public static synchronized void recordActionInfoLog(ActionInfo ai){ tempQueue.add(ai); if(!WriteThread.isWriteThreadLive){ new WriteThread().start(); } }/** * 開啟記錄檔並寫入日誌 * * @return * **/public static void recordStringLog(String text) {// 建立或開啟記錄檔 File file = new File(filePath);if (!file.exists()) { file.getParentFile().mkdirs();try {file.createNewFile();} catch (IOException e) {logger.error("行為日誌:在"+filePath+"建立檔案失敗!");e.printStackTrace();}} try {FileWriter filerWriter = new FileWriter(file, true);//後面這個參數代表是不是要接上檔案中原來的資料,不進行覆蓋BufferedWriter bufWriter = new BufferedWriter(filerWriter);bufWriter.write(text);bufWriter.newLine();bufWriter.close();filerWriter.close();Log.d("行為日誌寫入成功",text);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}} /** * 判斷記錄檔是否存在 * @return */ public static boolean isExitLogFile(){ File file = new File(filePath);if (file.exists()&&file.length()>3){return true;}else{return false;} } /** * 刪除記錄檔 */ public static void deleteLogFile(){ File file = new File(filePath); if (file.exists()){ file.delete(); } } }
2.非同步寫資料到檔案 不影響使用者體驗,不能因為寫日誌而有任何延遲.(實驗中如果一次寫入檔案中的資料比較小,即使同步寫入也不會太慢)為了達到這一目的首先考慮多線程另起一個線程專門負責來寫日誌到檔案中代碼如下:
package com.xx.log;import com.google.gson.Gson;public class WriteThread extends Thread{public static boolean isWriteThreadLive=false;//寫日誌線程是否已經在運行了 public WriteThread() {isWriteThreadLive=true;}@Overridepublic void run() {isWriteThreadLive=true;Gson gson=new Gson(); while(!ActionLog.tempQueue.isEmpty()){//對列不空時try {//寫日誌到SD卡ActionLog.recordStringLog(gson.toJson(ActionLog.tempQueue.poll())); } catch (Exception e) { e.printStackTrace(); }} isWriteThreadLive=false;//隊列中的日誌都寫完了,關閉線程(也可以常開 要測試下)} }
其實我覺得 寫線程還是要一直保持運行可能更好,頻繁的建立線程對象應該也很耗費效能
好了以上代碼經過測試 可以達到多個線程大量並發寫日誌,可以保證按順序寫入到檔案中,而不影響使用者體驗 反應時間使用者感覺上可以忽略不計
由於時間問題 我還要寫上傳日誌的處理,寫日誌的功能暫時先這樣,以後有時間了研究下保持寫線程一直live 直到整個程式結束。還有進過測試 能提升多少效能是否有這個必要。