Android学习能力之统计日志上传设计

一款软件就像一个孩子,不断的在学习,在探索,当孩子犯下错误的时候,我们可以去包容,当孩子犯不改的时候,获取他就不再让人喜欢,甚至是去抛弃他。人之常情的问题,也是做软件的我们需要去考虑的问题。同样孩子的成长速度和懂事程度也是我们非常关注的。

本章讲述Android日志上传功能,更快的更准确的将孩子的错误通知给我们,我们帮助孩子改正它的错误。

1.日志统计和日志存储

    public static void record(Context context, String ex) {
        if (context == null) {
            return;
        }
        PrintStream printStream = null;
        ByteArrayOutputStream bos = null;

        try {
            bos = new ByteArrayOutputStream(256);
            printStream = new PrintStream(bos);
            printStream.print(ex);
            JSONObject createJson = createJson(context, new String(bos.toByteArray()));//(1)做json
            DBManager.getInstance(context).addCrashLog(createJson.toString());//(2)存储数据信息
            Prefs.setPrefBoolean(PrefsContants.IS_NEEDED_UPLOAD, true));
        } catch (Exception e) {
            Log.e("CrashUploadUtils", "writeLog", e);
        } finally {
            closeQuietly(printStream);//(3)
            closeQuietly(bos);
        }
    }

由上面代码可以看出我们将数据字符串传入统计记录后,首先做成json数据,如步骤(1);其次将数据存入数据库,如步骤(2);最后关闭我们的数据流,如步骤三。

(1)做成json数据

    private static JSONObject createJson(Context context, String log) {
        JSONObject object = null;
        try {
            object = new JSONObject();
            object.put("type", "crash");
            JSONObject data = new JSONObject();
            data.put("log", log);
            data.put("uk", AccountManager.getUK(context));
            data.put("version", IMManager.getVersion());
            data.put("trigger_id", Utility.getTriggerId(context));
            if (!TextUtils.isEmpty(android.os.Build.VERSION.RELEASE)) {
                data.put("os", android.os.Build.VERSION.RELEASE);
            }
            if (!TextUtils.isEmpty(android.os.Build.MODEL)) {
                data.put("device_model","mode" + android.os.Build.MODEL);
            }
            if (!TextUtils.isEmpty(android.os.Build.MANUFACTURER)) {
                data.put("manufacture",android.os.Build.MANUFACTURER);
            }
            object.put("data", data);
            object.put("ts", System.currentTimeMillis());

        } catch (JSONException e) {
            Log.e("CrashUploadUtils", "createJson", e);
        }

        return object;
    }

内容这里可以忽略,自己放入就好。

(2)添加数据库

    public void addCrashLog(String log) {
        synchronized (mSyncLock) {
            ContentValues values = new ContentValues();
            values.put(CrashLogColumns.COLUMN_COTENT, log);
            insert(TableDefine.DB_TABLE_CRASH_LOG, values);
        }
    }

其中insert函数,读者要自己写了。

(3)关闭数据流

    private static void closeQuietly(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                Log.e("CrashUploadUtils", "closeQuietly", e);
            }
        }
    }

上面我们完成了,日志的统计和信息的存储,其中日志的存储看上面添加数据库的过程。

2.日志上传

日志上传是该功能的关键步骤,涉及的问题有:怎么上传,什么时候上传,什么网络环境下上传?

日志上传功能

    private static void upLoad(Context context) {
        Pair<Long, JSONArray> log = getCrashLog(context);//1获取数据库中异常信息
        if (log == null || log.first < 0) {
            return;
        }
        StringBuilder builder = new StringBuilder();
        builder.append("device_id=" + Utility.getDeviceType(context));
        builder.append("&appid=" + AccountManager.getAppid(context));
        builder.append("&statistic=" + log.second.toString());
        String jsonResult = null;
        try {//2上传异常信息
            jsonResult = HttpUtility.doUploadPost(url, builder.toString().getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            Log.e("CrashUploadUtils", "upLoadCrash UnsupportedEncodingException", e);
        }
        if (jsonResult == null) {
            Log.e("CrashUploadUtils", "upload crash log failed!!");
            return;
        }

        try {
            JSONObject jsonObject = new JSONObject(jsonResult);
            int errorCode = jsonObject.optInt("err_code");
            String msg = jsonObject.optString("msg");//根据网络结果删除已经上传过的信息,并更新上传记录时间
            if (errorCode == OK) {//3删除上传过的信息
                int result = DBManager.getInstance(context).deleteLogBeforeId(log.first);
                if (result > 0) {//4更新上传时间
                    updateUploadTime(context, System.currentTimeMillis());
                }
            }
        } catch (JSONException e) {
            Log.e("CrashUploadUtils", "upLoadCrash JSONException", e);
        }
    }

(1)获取数据库中异常信息

    public static Pair<Long, JSONArray> getCrashLog(Context context) {
        return DBManager.getInstance(context).getLog();
    }

    public Pair<Long, JSONArray> getLog() {
        synchronized (mSyncLock) {
            CrashLogParse parse = new CrashLogParse();
            query(TableDefine.DB_TABLE_CRASH_LOG, null, null, null, null, null, CrashLogColumns._ID + " asc ", " 10 ",
                    parse);
            return parse.getResult();
        }
    }

    class CrashLogParse implements CursorParse {
        Pair<Long, JSONArray> result = null;

        @Override
        public void parseCursor(Cursor cursor) {
            if (cursor != null) {
                long maxid = -1;
                long id = -1;
                String log = null;
                JSONArray array = null;
                try {
                    array = new JSONArray();
                    while (cursor.moveToNext()) {
                        id = cursor.getLong(cursor.getColumnIndex(CrashLogColumns._ID));
                        log = cursor.getString(cursor.getColumnIndex(CrashLogColumns.COLUMN_COTENT));
                        if (id > maxid) {
                            maxid = id;
                        }
                        array.put(new JSONObject(log));
                    }
                } catch (JSONException e) {
                    array = null;
                    e.printStackTrace();
                }
                if (array != null) {
                    result = new Pair<Long, JSONArray>(maxid, array);
                }
            }
        }

        @Override
        public Pair<Long, JSONArray> getResult() {
            return result;
        }

    }

获取到相应的数据库信息,获取的是其中的所有记录和最大id值。

(2)上传异常信息

    public static String doUploadPost(String httpUrl, byte[] byteToUpload) {
        if (byteToUpload == null || byteToUpload.length < 0) {
            return null;
        }
        URL url;
        HttpURLConnection httpUrlConnection = null;
        try {
            url = new URL(httpUrl);
            httpUrlConnection = (HttpURLConnection) url.openConnection();
            httpUrlConnection.setRequestMethod("POST");
            httpUrlConnection.setDoInput(true);
            // Post mode
            httpUrlConnection.setDoOutput(true);
            httpUrlConnection.setConnectTimeout(30 * 1000);
            httpUrlConnection.setReadTimeout(30 * 1000);
            httpUrlConnection.setUseCaches(false);
            OutputStream outputStream = httpUrlConnection.getOutputStream();
            outputStream.write(byteToUpload);
            outputStream.flush();
            outputStream.close();
            // while (offset < byteLength) {
            // bufferOutStream.write(byteToUpload);
            // offset += length;
            // }
            int response = httpUrlConnection.getResponseCode();
            if (Constants.isDebugMode()) {
                Log.e("HttpUtility", "upload response:" + response);
            }
            if (response != HttpURLConnection.HTTP_OK) {
                return null;
            }
            return dealResponseResult(httpUrlConnection.getInputStream());
        } catch (MalformedURLException e) {
            Log.e("HttpUtility", "MalformedURLException doUploadPost", e);
        } catch (IOException e) {
            Log.e("HttpUtility", "IOException doUploadPost", e);
        } catch (Exception e) {
            Log.e("HttpUtility", "Exception doUploadPost", e);
        } finally {
            if (httpUrlConnection != null) {
                httpUrlConnection.disconnect();
            }
        }

        return null;
    }

    /**
     * @param inputStream
     * @return
     */
    public static String dealResponseResult(InputStream inputStream) {
        String resultData = null; // 存储处理结果
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] data = new byte[1024];
        int len = 0;
        int offset = 0;
        try {
            while ((len = inputStream.read(data)) != -1) {
                byteArrayOutputStream.write(data, offset, len);
                offset += len;
            }
            resultData = new String(byteArrayOutputStream.toByteArray());
            if (Constants.isDebugMode()) {
                Log.d("HttpUtility", "resultData:" + resultData);
            }
        } catch (IOException e) {
            Log.e("HttpUtility", "IOException dealResponseResult", e);
        }
        return resultData;
    }

(3)删除上传过的信息

    public int deleteLogBeforeId(long id) {
        synchronized (mSyncLock) {
            return delete(TableDefine.DB_TABLE_CRASH_LOG, CrashLogColumns._ID + " <=?",
                    new String[] { String.valueOf(id) });
        }
    }

(4)更新上传时间

其中定义了上传时间间隔,不能上传过于频繁,造成服务压力,基本几个在10分钟左右,这里时间自己可以设定

    private static boolean updateUploadTime(Context context, long time) {
        if (time > lastUpdateTime) {
            lastUpdateTime = time;
            return Utility.writeLongData(context, key, lastUpdateTime);
        }
        return false;

    }

    private static long getUpdateTime(Context context) {
        if (lastUpdateTime == -1) {
            lastUpdateTime = Utility.readLongData(context, key, -1);
        }
        return lastUpdateTime;
    }

    public static boolean isNeedToUpload(Context context, long time) {
        return System.currentTimeMillis() - getUpdateTime(context) > time;
    }

(5)开始上传

    public static void statUpload(final Context context) {
        if(!Prefs.getPrefBoolean(PrefsContants.IS_NEEDED_UPLOAD, false))){
            return;
        }
        if(!isNeedToUpload){
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                upLoadCrash(context);
            }
        }).start();
    }

以后就可以通过在服务端查看日志去快速修改app所犯下的错误。

时间: 2024-10-02 08:22:59

Android学习能力之统计日志上传设计的相关文章

关于Android Studio打包混淆以及上传mapping文件

关于android Studio打包混淆以及上传mapping文件 转载请注明出处: http://blog.csdn.net/u014163726?viewmode=contents 本文出自Wrh的博客 打包 android studio的打包很简单 然后如果已经有keystore的一路next下去,如果没有的可能需要先创建keystore,最后我们就会得到一个apk文件 混淆 现在网上关于反编译的博客很多了,我在此就不多做介绍了,放上个传送门传送门 那么我们已经知道了反编译是如此的简单,我

android下大文件分割上传

由于android自身的原因,对大文件(如影视频文件)的操作很容易造成OOM,即:Dalvik堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题. 文件分割后分多次请求服务. 1 //文件分割上传 2 public void cutFileUpload(String fileType,String filePath) 3 { 4 try 5 { 6 FileAccessI fileAccessI = new FileAccessI(filePath, 0); 7 Long nStartPo

开源中国源码学习(三)——Log日志上传

在AppStart中开启了一个服务LogUploadService用来上传应用程序的日志. 采用的是start的方式开启服务,代码如下: Intent uploadLog = new Intent(this, LogUploadService.class); startService(uploadLog); 一.功能介绍: 在服务LogUploadService被开启后,根据情况进行如下几种操作: 读取osc本地文件夹下的日志信息 如果日志信息为空,服务停止-- LogUploadService

Android使用HttpClient实现文件上传到PHP服务器,并监控进度条

上传 服务器端PHP 代码如下 : <?php $target_path = "./tmp/";//接收文件目录 $target_path = $target_path.($_FILES['file']['name']); $target_path = iconv("UTF-8","gb2312", $target_path); if(move_uploaded_file($_FILES['file']['tmp_name'], $targ

flume实例二、监听目录日志上传到HDFS文件系统

一.概述 接实例一,实例一中server-aget是把日志上传保存到服务器上面,随着日志越来越大,公司启动了hadoop项目,需要把日志直接上传hdfs中保存,配置文件target_hdfs.conf如下: a2.sources = r2 a2.channels = c2 a2.sinks = k2 #source a2.sources.r2.type = avro a2.sources.r2.channels = c2 a2.sources.r2.compression-type = defl

日志易数据接入之 Syslog 日志上传

在企业运维环境中,运维人员管理的主要对象分别是服务器.存储.网络设备.安全设备等.而在对整个企业网站进行监控时,这些设备的日志数据都需纳入到监控的范围之内.日志分析与监控,这事说大不大,说小也不小.传统的ES开源解决方案虽然节约了应用成本,但对应的人力维护成本较高.这个中的辛酸故事毋需多言.在此介绍一种商业日志分析解决方案--日志易.好不好用暂不评论,是否拿来对比试用也取决于各位心情.第一步自然是日志数据接入.数据源如何接入到日志易服务之中呢? 日志易的日志接入方式主要包括以下三种: 日志易Ag

shell案例 - 统计用户上传文件的时间

问题与需求: 一个web服务跑论坛,每天都有很多用户会上传文件到服务器里,假设这些文件会保存在/data/www/attachment目录下,我想知道用户大概会在什么时候上传文件? 从而了解用户的行为习惯 解决思路: 1. 判断新文件是否被上传到了服务器里?以5分钟为一个间隔,进行检测. 2. 确定这些文件是什么时候被上传的? 3. 将新文件的列表输出到一个按年.月.日.时.分为名字的日志里,以方便我们进行查看分析 4. 写一个shell脚本来帮我们自动实现这个检测和记录的过程,当然只检测一次是

Android项目能运行,上传svn后再下载却不能运行

今天遇到一个比较奇怪的问题,android项目上传到svn之前,可以运行,但是上传到svn后再check, 就出错. 搜索了一下,发现真的解决了问题. svn 不知道是出于什么原因,不能上传.so文件,所以,如果有朋友在上传的项目中引入了.so文件(这个文件一般是在libs文件夹下面).那么就会出现如标题一样的情况.所以, 一定要记住呀.

Android实现模拟表单上传

很久以前,写过一篇关于下载的文章:基于HTTP协议的下载功能实现,今天对于Android上的文件上传,也简单的提两笔.在Android上,一般使用Http 模拟表单或者FTP来进行文件上传,使用FTP协议,可以直接使用Appache的FTPClient,使用方法很简单,不再赘述.这里主要说明一下Http模拟表单上传的实现. 模拟表单上传,其实也很简单,主要需要在Http post 的数据体中构建表单信息(multipart/form),表单数据格式的规范,可以参考REC标准.下面是一个格式示例: