1. 程式人生 > >Android產品研發(八)-->App資料統計

Android產品研發(八)-->App資料統計

上一篇文章中我們介紹了Android社群中比較火的熱修復功能,並介紹了目前的幾個比較流行的熱修復框架,以及各自的優缺點,同時也介紹了一下自身專案中對熱修復功能的實踐。目前主流的熱修復原理上其實分為兩種,一種是通過利用dex的載入順序實現熱修復功能,一種是通過native層實現指標替換實現熱修復功能,兩種各有利弊可以根據自身產品的需要選擇不同的方案,具體可參考: Android產品研發(七)–>Apk熱修復

而文字將要介紹一下Android產品中另一項基礎功能-資料統計。App資料統計的意義在於通過統計使用者的行為方式有針對性的更新展示演算法,根據使用者的行為習慣更新產品功能等等,具體而言當我們開發好App後就會把它發到應用市場上,但是目前有很多的應用市場(如,豌豆莢,應用寶,安卓市場等),那麼問題來了,假如我們想統計我們開發的應用的下載次數,就必須使用統計吧,而且它不僅可以統計我們的應用的下載量,啟動次數,還可以統計頁面訪問量、檢視程式的bug等等,所以相對於專案而言產品由於存在著持續的迭代與使用者體驗,所以做好資料統計工作是一項必不可少的工作。

本文中資料統計主要介紹兩種:第三方統計服務和自己實現資料統計功能。

相對而言這兩種方式各有利弊,第三方統計服務簡單、方便、統計範圍廣,但是資料儲存在第三方,對於一些資料比較敏感的App可能不太喜歡這種方式,而自己實現資料統計功能主要優點就是安全可定製化,當然了缺點也是顯而易見的,就是繁瑣,複雜。

下面我分別就這兩種資料統計方式做一下簡單的介紹。

  • 第三方統計服務

一般情況下App中都是使用第三方資料統計服務的,友盟、百度統計等等,這裡簡單介紹一下友盟統計,其餘的都是大同小異。

友盟統計官網
友盟統計中不但有統計相關功能,還提供了錯誤分析,社會化分享,推送,即時通訊等等,當然這裡我們重點分析的是其資料統計功能。

在友盟官網中我們可以看到其對統計服務的介紹,這裡我們大概的看一下:
這裡寫圖片描述
可以發現我們提供了使用者統計,渠道統計,頁面訪問路徑統計,點選事件統計等等。
那麼我們如何才能繼承友盟的統計功能的,其在SDK文件介紹中已經做了詳細介紹,大概的整合流程是下載友盟SDK jar包,然後引用,並在相應的Activity/Fragment的生命週期方法中呼叫相關的API。

  • 自己實現資料統計功能

對於一些資料敏感的App來說可能自己實現部分資料統計功能是一個比較不錯的選擇,當初筆者也做過類似的功能,其原理就是參考友盟的資料統計在App開啟的時候執行資料上報功能。

這裡簡單介紹一下自己實現的資料統計功能的流程:
(1)資料統計上報主要分為兩中方式,網路請求上報和檔案資訊上報;
(2)網路請求上報就是直接呼叫網路請求若請求失敗,則將上報資訊儲存至本地檔案中;
(3)本地上報檔案設定闕值和間隔時間,若距離上次上報時間大於闕值或者是檔案大小大於闕值則執行上報操作,若上報成功則刪除本地資料檔案;
(4)本地資料檔案在上報之前有加密和壓縮操作;
(5)在App開啟的時候執行一次資料上報操作,在特定的操作下可以執行上報操作;
(6)上報操作是在Service服務中執行,避免上報操作被由於程序被殺死而中斷;

最後貼上為了實現資料上報而自定義的資料統計服務:

/**
 * Created by aaron on 2016/4/8.
 * desc:UU打點service
 */
public class UUPointService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        /**
         * desc:該service為APP打點service
         * 主要實現兩個後臺服務:
         * (1)記憶體資料上報;
         * (2)檔案資料上報;
         */
        if (intent != null && intent.getStringExtra(UUPoint.LOADTYPE) != null && intent.getStringExtra(UUPoint.LOADTYPE).trim().equals(UUPoint.LOADDATA)) {
            if (UUPoint.writeMap.size() > 0) {
                //(1)轉換資料
                final List<String> strList = new ArrayList<String>();
                Iterator<Map.Entry<String, Integer>> iterator = UUPoint.writeMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    StringBuffer sb = new StringBuffer();
                    Map.Entry<String, Integer> entry = iterator.next();
                    String key = entry.getKey();
                    Integer count = entry.getValue();
                    sb.append(key).append(",").append(count);
                    strList.add(sb.toString());
                }
                if (strList.size() > 0) {
                    // 上傳伺服器資料上傳伺服器資料
                    SystemCommon.ReportLogBatch.Builder builder = SystemCommon.ReportLogBatch.newBuilder();
                    builder.addAllLogLine(strList);
                    SystemInterface.ReportLogInfoRequest.Builder request = SystemInterface.ReportLogInfoRequest.newBuilder();
                    request.setLogData(ByteString.copyFrom(GZipUtils.compress(builder.build().toByteArray())));// 壓縮資料包
                    NetworkTask task = new NetworkTask(CmdCodeDef.CmdCode.ReportLogInfo_VALUE);
                    task.setBusiData(request.build().toByteArray());
                    NetworkUtils.executeNetwork(task, new HttpResponse.NetWorkResponse<UUResponseData>() {
                        @Override
                        public void onSuccessResponse(UUResponseData responseData) {
                            if (responseData.getRet() == 0) {
                                try {
                                    SystemInterface.ReportLogInfoResponse response = SystemInterface.ReportLogInfoResponse.parseFrom(responseData.getBusiData());
                                    if (response.getRet() == 0) {
                                        /*MLog.i("tab", "########################");
                                        for (String str : strList) {
                                            MLog.i("tab", str);
                                        }*/
                                        UUPoint.writeMap.clear();
                                        UUPointUtils.setLastPointTime(UUPointService.this);
                                        /*MLog.i("tab", "上報資料完成################");*/
                                    } else {
                                        UUPoint.saveWriteToIO(UUPointService.this);// 將寫map中的資料持久化到IO
                                    }
                                } catch (Exception e) {
                                    UUPoint.saveWriteToIO(UUPointService.this);// 將寫map中的資料持久化到IO
                                }
                            } else {
                                UUPoint.saveWriteToIO(UUPointService.this);// 將寫map中的資料持久化到IO
                            }
                        }

                        @Override
                        public void onError(VolleyError errorResponse) {
                            MLog.i("tab", "儲存到本地檔案");
                            UUPoint.saveWriteToIO(UUPointService.this);
                        }

                        @Override
                        public void networkFinish() {
                        }
                    });
                }
            }
        }
        // 上傳檔案
        else if (intent != null && intent.getStringExtra(UUPoint.LOADTYPE) != null && intent.getStringExtra(UUPoint.LOADTYPE).trim().equals(UUPoint.LOADFILE)) {
            File file = new File(Config.CountFile);
            List<File> fileList = Arrays.asList(file.listFiles());
            if (fileList != null && fileList.size() > 0) {
                //按檔名稱排序
                Collections.sort(fileList, new Comparator<File>() {
                    @Override
                    public int compare(File lhs, File rhs) {
                        return rhs.getName().compareTo(lhs.getName());
                    }
                });
                // 上傳檔案
                for (final File files : fileList) {
                    // 先解密,在使用票據加密
                    FileEncrypter.decrypt(files, UUPoint.secretKey);// 解密
                    if (Config.isNetworkConnected(UUPointService.this)) {
                        NetworkTask networkTask = new NetworkTask(CmdCodeDef.CmdCode.ReportLogFile_VALUE);
                        networkTask.setUploadDataFile(true);// 上傳大點資料檔案
                        networkTask.setBusiData(UUPointUtils.getBytesFromFile(files));
                        NetworkUtils.executeNetwork(networkTask, new HttpResponse.NetWorkResponse<UUResponseData>() {
                            @Override
                            public void onSuccessResponse(UUResponseData responseData) {
                                if (responseData.getRet() == 0) {
                                    try {
                                        SystemInterface.ReportLogInfoResponse response = SystemInterface.ReportLogInfoResponse.parseFrom(responseData.getBusiData());
                                        if (response.getRet() == 0) {
                                            // 上傳成功, 刪除加密檔案
                                            files.delete();
                                            MLog.i("tab", "上傳檔案完成" + files.getName() + "######################");
                                        } else {
                                            FileEncrypter.encrypt(files, UUPoint.secretKey);// 上傳失敗, 重新加密檔案
                                        }
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        FileEncrypter.encrypt(files, UUPoint.secretKey);// 上傳失敗, 重新加密檔案
                                    }
                                } else {
                                    FileEncrypter.encrypt(files, UUPoint.secretKey);// 上傳失敗, 重新加密檔案
                                }
                            }

                            @Override
                            public void onError(VolleyError errorResponse) {
                                MLog.i("tab", errorResponse.toString());
                                FileEncrypter.encrypt(files, UUPoint.secretKey);// 上傳失敗, 重新加密檔案
                            }

                            @Override
                            public void networkFinish() {
                            }
                        });
                    }
                }
            }
        }

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

總結:
一般而言直接使用第三方統計服務就已經可以滿足絕大部分的業務需求了,當然瞭如果資料比較敏感也可以自己實現資料上報統計的功能。