Android异常捕获篇(下)---retrofit实现文件的上传

本文介绍了一种在Android应用中实现Crash日志自动上传的方法。通过接收特定广播启动Service,每次仅上传最新的Crash日志,并在上传成功后删除,避免重复上传和服务器压力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上篇说到我们将捕获的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),或者之后可能会直接上传一份资源供大家参考~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值