Android異常捕獲篇(下)---retrofit實現文件的上傳

前言

上篇說到我們將捕獲的crash日誌緩存到了本地,那本篇我們將開始實現日誌的上傳。

需求

根據公司的需求,需要另起一個apk(公司產品比較特殊,不是移動設備,不適用於手機app,不過上傳方法一樣),該應用不需要啓動只實現每次啓動公司應用或者切換網絡時進行掃描上傳日誌,即通過接收廣播開啓service,實現方法前篇已講解過,鏈接:Android應用在安裝後未啓動的情況下無法收到開機等各類廣播

引導

假如本地共存了十份日誌,每次觸發上傳的時候會優先遍歷出最新的日誌,上傳成功即立即刪除進行下一個文件上傳,上傳的時候千萬要注意一點,不能使用for循環直接遍歷上傳,這樣不僅可能會造成重複上傳(日誌還沒有刪除又被掃描出來重複上傳),還會給服務器造成很大的壓力,本地也容易死循環,如圖(畫的有點醜,見諒了~)
這裏寫圖片描述

實現步驟

1、在AndroidManifest.xml文件中註冊廣播和Service,添加讀寫、網絡權限

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<receiver android:name=".receiver.LauNetBroadcastReceiver" android:exported="true">
            <intent-filter>
                <action android:name="intent.action.START_UPLOAD_CRASH" />

                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
            </intent-filter>
        </receiver>

        <service android:name=".server.BootService"/>

2、創建廣播接收器來啓動service

public class LauNetBroadcastReceiver extends BroadcastReceiver {

    private static boolean isActived = false;//用於過濾連續收到兩次網絡連接上的通知

    private static final String NET_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";

    private static final String LAUNCHER_ACTION = "intent.action.START_UPLOAD_CRASH";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(NET_ACTION)) {
            //檢查網絡狀態的類型
            int netWrokState = NetUtil.getNetWorkState(context);

            switch (netWrokState) {
            case 0:
                //Log.i("TAG", "移動網絡");
            case 1:
                //Log.i("TAG", "wifi網絡");

                if (!isActived) {

                    isActived = true;

                    Intent service = new Intent(context, BootService.class);

                    context.startService(service);

                }
                break;
            case -1:
                //Log.i("TAG", "沒有網絡");

                isActived = false;
                break;
            }

        } else if (intent.getAction().equals(LAUNCHER_ACTION)) {

            //Log.i("TAG", "接收到Launcher啓動廣播");

            Intent service = new Intent(context, BootService.class);

            context.startService(service);

        }

    }
}

3、創建Service實現文件的上傳,因爲裏面涉及到耗時操作,這裏採用IntentServie(只貼關鍵代碼)

/**
     * 遍歷文件獲取最新文件上傳
     * <p>
     * 上傳成功即刪除,失敗保留
     *
     * @param
     */
    public synchronized void traverCrashLog() {

        try {

            final File file = new File(CONTENT_INFO_PATH);

            if (file != null && file.isDirectory()) {
                //獲取最新的日誌文件
                File fold = getNewFile(file);

                if (fold != null) {
                    //讀取文件內容
                    String content = readCrashContent(file.getAbsolutePath() + File.separator + fold.getName());
                    //網絡請求上傳文件
                    createRemoteImpl(content, fold);

                }

            }

        } catch (Exception e) {
            Log.e(TAG, "limitAppLogCount - " + e.getMessage());
        }
    }

先獲取到最新產生的日誌文件

/**
     * 每次遍歷一遍獲取最新的文件
     */
    public File getNewFile(File file) {

        File[] files = file.listFiles(new CrashLogFliter());

        File fold = null;

        if (files != null && files.length > 0) {

            Arrays.sort(files, comparator);

            fold = files[files.length - 1];

            return fold;

        }

        return null;

    }

過濾與排序

 /**
     * 日誌文件按修改時間排序
     */
    private Comparator<File> comparator = new Comparator<File>() {
        @Override
        public int compare(File l, File r) {

            if (l.lastModified() > r.lastModified())
                return 1;
            if (l.lastModified() < r.lastModified())
                return -1;

            return 0;
        }
    };

    /**
     * 過濾.log與crash開頭的文件,防止掃描出正在寫入的文件
     */
    public class CrashLogFliter implements FileFilter {

        @Override
        public boolean accept(File file) {

            if (file.getName().endsWith(".log") && file.getName().startsWith("crash"))
                return true;

            return false;
        }
    }

讀取crash日誌文件內容

public synchronized String readCrashContent(String fileName) {

        String content = "";

        try {

            File file = new File(fileName);

            if (!file.exists()) {

                return null;

            }

            FileInputStream fls = new FileInputStream(fileName);

            byte[] data = new byte[fls.available()];

            fls.read(data);

            content = Base64.encodeToString(data, Base64.NO_WRAP);

            fls.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }

retrofit實現網絡請求,上傳成功即刪除文件,進行下一次的遍歷上傳

public void createRemoteImpl(String content, final File fold) {

        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(getDbManageUrl(getApplicationContext()))
                .build();

        final IUploadInterface repo = retrofit.create(IUploadInterface.class);
//        Log.e("解碼上傳內容", new String(Base64.decode(content, Base64.NO_WRAP)));
        Call<DataBean> call = repo.uploadData("crash", deviceId, content);

        call.enqueue(new Callback<DataBean>() {

            @Override
            public void onResponse(Call<DataBean> call, Response<DataBean> response) {
//                Log.i(TAG, "retrofit請求成功:" + new Gson().toJson(response.body()));

                DataBean dataBean = response.body();

                if (Integer.valueOf(dataBean.getMeta().getCode()) == 200) {

                    if (fold.exists()) {

                        fold.delete();

                        traverCrashLog();

                    }
                }

            }

            @Override
            public void onFailure(Call<DataBean> call, Throwable t) {
                Log.e(TAG, "retrofit請求失敗:" + t.toString());
            }
        });
    }

由於內容比較多,涉及到文件遍歷和retrofit的使用,都貼出來會有點亂,如有需要可以留言發郵箱(包括存儲和上傳的完整demo),或者之後可能會直接上傳一份資源供大家參考~

發佈了41 篇原創文章 · 獲贊 42 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章