Android Crashlytics - 限制网络访问

Android Crashlytics - restrict network access

我一直在研究无法在特定条件下限制 Crashlytics 网络使用的问题。例如 - 漫游、按流量计费的网络等。

根据 SDK 文档,我发现只有两个选项可以解决这个问题:

这个 API 非常有限,因为:

我的问题基本上是: 我错过了什么吗? 有什么方法可以限制 Crashlytics 网络访问吗?

我的动机来自于需要防止我的应用使用网络带宽的情况,在某些情况下可能会给用户带来费用,尽管 "cellular network" 或 "use data over roaming" 设备设置已启用。

无法限制应用程序中 Crashlytics 的互联网使用。但我将如何解决它是要么向用户提供 Crashlytics 正在使用漫游的信息,要么只在本地保存崩溃报告并在用户连接到 wifi 网络后发送它们。如果用户更喜欢在本地保存崩溃报告或通过漫游立即发送它们,您也可以让用户选择。

  1. Save the ErrorLog locally 在设备上
  2. 与 wifi 建立连接后上传错误日志

您应该能够使用 ConnectivityManager 获取 Wi-Fi 适配器的状态。从那里你可以 check if it is connected or even available.

ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

if (mWifi.isConnected()) {
    // post error logs
}

我正在阅读 fabric 的文档,我刚刚发现了一些有趣的东西:

Crashlytics processes exceptions on a dedicated background thread, so the performance impact to your app is minimal. To reduce your users’ network traffic, Crashlytics batches logged exceptions together and sends them the next time the app launches.

所以我在考虑一个解决方法,因为在应用程序初始化时会发送没有网络的崩溃,您可以在启动时向用户提示任何对话框,告诉他们是否要连接到互联网以将崩溃报告发送到解决应用程序中的当前问题。 (所以你在用户同意的情况下使用他们的网络数据)

这里的问题是我们不知道如何阻止 crashlytics 发送此报告,如果设备处于离线状态,他们会将其存储在设备上,并在设备再次连接后将其发回,如其所述here

另一种解决方法是使用他们提供的自定义登录记录重要的致命问题并发送,您可以找到更多相关信息 here

To make sure that sending crash reports has the smallest impact on your user’s devices, Crashlytics logs have a maximum size of 64 KB. When a log exceeds 64 KB, the earliest logged values will be dropped in order to maintain this threshold.

总而言之,阅读文档后,无法禁用 Crashlytics 不断发送报告,您只能在用户发送或不发送报告时管理用户的网络连接。就好像连接是 Crashlytics 的开关。

它只谈到“减少网络流量”,而根本没有谈到禁用 Crashlytics 网络。

我想到的另一种方法是为启动 crashlytics 制作一个标志,然后在条件内使用 Crashlytics.start()

当你想禁用它时,只需执行以下操作

CrashlyticsCore core = new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build();
Fabric.with(this, new Crashlytics.Builder().core(core).build());

玩这两个东西是我认为目前可以减少 Crashlytics 网络使用的唯一方法。

我是 iOS/macOS 的 Crashlytics SDK 的前维护者。我对 Android 版本的 SDK 比较陌生,总体上对 Android 肯定不熟悉。但是,我会试一试。

你想做的是iOS那边已经要求过几次的事情。实际上,我很想这样做,因为强迫最终用户承担这些费用似乎非常糟糕。然而,iOS SDK 的网络和启动程序都非常复杂和微妙。保证崩溃交付 并且 不一致状态的可能性为零是非常具有挑战性的。我相信Android在这里更简单,但我不能权威地说。

但是,iOS SDK 确实有一些用于附加客户端级功能的挂钩。查看其中一个 API 周围的警告:

 *  @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can
 *           impact the reliability of reporting crashes very early in application launch.

基本上,为了满足这个特定 API 的合同,必须禁用一些提高报告可靠性的技术。问题是,有时候这是值得的。许多应用程序决定做出这种权衡。许多应用程序还会延迟初始化 Crashlytics 以寻求额外的性能。这对报告可靠性有 巨大 影响,但这是应用程序开发人员必须做出的另一个权衡。

我认为您应该认真考虑在这些情况下不启用 Crashlytics,如果您可以轻松检测到它们的话。也许 Android 甚至允许最终用户在每个应用程序的基础上执行此操作?在那种情况下,你永远不会得到任何报告。我想你的用户群足够多样化,在这些情况下丢失一些报告也不会那么糟糕。或者,您可能希望将其作为面向用户的选项显示。

你甚至可以做一些非常疯狂的事情,比如自己覆盖 Thread.setUncaughtExceptionHandler,并在这种情况下将异常缓冲到磁盘。然后,在情况好转时将它们重播到 Crashlytics。把它变成一个开源库。我打赌人们会喜欢它!不过可能不是 Crashlytics 的 Android 团队 ;)(嗨!)

这也基本上与 Gastón 上面提供的建议相同,只是我在 iOS 方面看到的内容有一些额外的背景信息。还要向 Crashlytics 人员发送一封电子邮件,要求这样做。我认为这是个好主意。

我们在我们的应用程序中使用了两步过程,这没有使用 Mobile Network 也没有使用 not related to roaming

  1. 将崩溃日志保存到应用程序数据分区中的文件,即在设备上

    参考这个link

  2. WiFi网络连接时上传崩溃数据到服务器:

    public class ConnectivityStatusReceiver extends BroadcastReceiver {
    
      @Override
      public void onReceive(Context context, Intent intent) {
    
        final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    
        NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
    
        if (activeNetworkInfo != null && activeNetworkInfo.getTypeName() == "WIFI") {
          // post your crash logs to server
        }
      }
    }
    

您可以通过静态字段限制 Crashlytics 网络使用。

定义一个静态全局变量,根据它的值为你的 Crashlytics 编写逻辑。

private static boolean INROAMING = false;

现在您可以使用以下逻辑来达到您的目的。如不提供co

if(isInternetIsConnected(this).equals("MOBILE")){
        if(INROAMING){
            //write your logic for context here, when phone is in roaming
            //restrict logic for crashlytics
        }else{
            //write your logic for context herem, when phone is not in roaming
            //un-restrict logic for crashlytics
        }
    }

public boolean checkForRoaming() {
        final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        PhoneStateListener phoneStateListener = new PhoneStateListener() {
            @Override
            public void onServiceStateChanged(ServiceState serviceState) {
                super.onServiceStateChanged(serviceState);
                if (telephonyManager.isNetworkRoaming()) {
                    // In Roaming
                    INROAMING = true;
                } else {
                    // Not in Roaming
                    INROAMING = false;
                }
                // You can also check roaming state using this
                if (serviceState.getRoaming()) {
                    // In Roaming
                    INROAMING = true;
                } else {
                    // Not in Roaming
                    INROAMING = false;
                }
            }
        };
    }

    public String isInternetIsConnected(Context context) {
        try {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            assert cm != null;
            @SuppressLint("MissingPermission") NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
            if (activeNetwork != null) { // connected to the internet
                if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                    // connected to wifi
                    return "WIFI";

                } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                    // connected to the mobile provider's data plan
                    checkForRoaming();
                    return "MOBILE";
                }
            } else {
                // not connected to the internet
                return "NO CONNECTION";
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        return "NO CONNECTION";
    }
}

无法限制应用程序中 Crashlytics 的互联网使用。您可以让用户选择,如果他更喜欢在本地保存崩溃报告或立即发送它们而不是漫游。

Save the ErrorLog locally on the device

与 wifi 建立连接后上传错误日志。

您可以使用ConnectivityManager获取网络状态。您可以检查它是否已连接甚至可用。

ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

if (mWifi.isConnected()) {
    // send error logs
}

Above code you can add in broadcastreceiver which will notify connection

示例:

 public class ConnectivityStatusReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {

  ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

  if (mWifi.isConnected()) {
   // send error logs
  }
 }
}