公司在做一个项目 要求记录用户行为,写行为日志文件到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 block e.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; } @Override public 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 直到整个程序结束。还有进过测试 能提升多少性能是否有这个必要。