Android Crashlytics - 限制网络访问

19

我已经围绕着在特定情况下限制Crashlytics网络使用能力的问题工作了一段时间。例如 - 漫游、计量网络等等。

根据SDK文档,我只找到了两个选项来解决这个问题:

  • 在运行时通过“Opt Out”简单地不初始化Crashlytics

  • 发送崩溃报告之前内置用户同意对话框

这些API非常有限,因为:

  • 不仅会防止网络访问,而且还会防止任何机会使Crashlytics在本地保存崩溃报告,以便最终事件将被发送。更不用说没有好的方法在运行时退出,除非残酷地覆盖Thread.setUncaughtExceptionHandler

  • 如果在后台发生崩溃,同意对话框对用户没有任何意义。

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

我的动机来自于需要防止我的应用程序在某些条件下使用可能会花费用户资金的网络带宽,尽管启用了“蜂窝网络”或“漫游数据”设备设置。


1
不行,我不能这样做。因为我的应用程序是预装的系统应用程序,是OEM服务的一部分。在某些情况下,它会在用户看到任何UI之前进行后台工作。 - Tal Kanel
@TalKanel 所以没有询问用户是否想要使用漫游的可能性吗?他没有选择吗?我理解正确吗? - Gucci
我的当前“解决方法”是,当用户处于漫游或计量网络时,只要我没有得到明确的同意,就不初始化Crashlytics。 - Tal Kanel
你的应用程序是否依赖于互联网功能? - Gastón Saillén
Crashlytics使用了那么多的数据吗? - Dracarys
显示剩余4条评论
6个回答

6
没有办法限制应用程序中Crashlytics的网络使用。但我会采取的解决方法是,要么向用户提供Crashlytics正在使用漫游的信息,要么只是将崩溃报告本地保存,并在用户连接到wifi网络后发送它们。此外,您还可以让用户选择是将崩溃报告本地保存还是立即通过漫游发送它们。
以下是解决方案:
  1. 将错误日志本地保存在设备上
  2. 一旦与wifi连接,上传错误日志
您应该能够使用ConnectivityManager获取Wi-Fi适配器的状态。然后,您可以检查它是否已连接,甚至是否可用
ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

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

6
我们的应用采用两步骤流程,这不涉及移动网络,也与漫游无关。
  1. Saving crash logs to file in app data partition i.e. on device:

    Refer to this link

  2. Upload crash data to server when WiFi network is connected:

    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
        }
      }
    }
    

5
我是Crashlytics SDK在iOS/macOS上的前维护者。对于SDK在Android上的版本相对陌生,对于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的人发电子邮件要求此功能。我认为这是一个好主意。

5
我在阅读Fabric的文档时发现了一些有趣的内容:
Crashlytics会在专用后台线程上处理异常,因此对应用程序的性能影响很小。为了减少用户的网络流量,Crashlytics会将记录的异常批处理在一起,并在下次应用程序启动时发送。
因此,考虑到没有网络的崩溃在应用程序初始化时被发送,您可以在启动时提示用户是否要连接到互联网以发送崩溃报告以解决应用程序中当前的问题。(因此,您需要得到用户同意使用其网络数据)
问题在于我们不知道如何停止Crashlytics发送这些报告,如果设备离线,它们将在设备上存储,并在设备重新连接后发送回来,正如这里所述。
另一个出路可能是只记录重要的致命问题,并使用他们提供的自定义登录信息发送它们,您可以在这里找到更多相关信息。
为了确保发送崩溃报告对用户设备的影响最小,Crashlytics日志的最大大小为64 KB。当日志超过64 KB时,为了保持此阈值,最早记录的值将被丢弃。
总之,在阅读文档后,我们发现没有办法完全禁用Crashlytics不断发送报告,您只能在想要发送或不发送报告时管理用户的网络连接。目前,这就像连接是Crashlytics的开关一样。
还有另一种方法,即创建一个启动Crashlytics的标志,然后在条件内使用Crashlytics.start()。当您想要禁用它时,只需执行以下操作。
CrashlyticsCore core = new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build();
Fabric.with(this, new Crashlytics.Builder().core(core).build());

我认为目前唯一减少Crashlytics网络使用的方法就是对这两个方面进行调整。


3
你可以通过设置静态字段来限制Crashlytics的网络使用。
定义一个静态全局变量,根据其值编写Crashlytics的逻辑。
private static boolean INROAMING = false;

现在,您可以使用以下逻辑来实现您的目的。例如,不提供协作功能。
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";
    }
}

1
我不明白。Crashlytics崩溃报告机制不会在漫游下发送崩溃的部分在哪里?你的回答没有任何意义。 - Tal Kanel
当您了解是否存在数据漫游时,可以在那里初始化您的Crashlytics。如果没有漫游数据,则可以为Crashlytics初始化,否则不要初始化Crashlytics。 - Ranjan
好的,这并没有回答问题。我知道如何监听连接事件。那不是技术问题。 - Tal Kanel
但您可以将其用于您的目的。 - Ranjan

3

无法限制应用程序中的Crashlytics的互联网使用。您可以为用户提供选择权限,如果用户更喜欢将崩溃报告保存在本地或立即通过漫游发送。

在设备上本地保存ErrorLog

在与wifi连接建立后上传ErrorLog。

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

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

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

以上代码可以添加到broadcastreceiver中,以通知连接状态。

例如:

 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
  }
 }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接