如何从可穿戴应用程序中检索崩溃报告?

时间:2015-03-18 10:19:32

标签: android wear-os crash-reports crashlytics

由于Crashlytics没有开箱即用的可穿戴应用程序,我正在寻找一种拦截和报告运行时抛出的任何潜在异常的最佳方法。我想知道为什么他们没有自动报告给Google Play开发者控制台?

谷歌已宣布未来的Android Wear更新将内置Wi-Fi支持,但即便如此,并非所有设备都配备足够的硬件。

在这种情况下,我最初的想法是创建Application的子类并实现Thread.UncaughtExceptionHandler。然后,必须使用MessageApi对每个例外进行编组并发送到手机。手机上WearableListenerService的扩展名会收到一条消息,解组该异常并将其传递给Crashlytics。

然而,这又提出了一些问题。存在可穿戴设备和手机之间的蓝牙连接中断的风险,因此所有错误都应排队并存储在可穿戴设备的文件系统中。

对于简单的崩溃报告来说,这似乎有些过分。有更简单的方法吗?

5 个答案:

答案 0 :(得分:4)

请勿MessageApi用于此目的,DataApi。那你就不必担心失去蓝牙连接。

它的工作方式:

  1. 发生崩溃时,在可穿戴设备上设置DataItem崩溃;

  2. 最终会将其发送到移动设备。

  3. 从移动设备发送有关崩溃的信息并删除DataItem

  4. 此处提供更多信息:http://developer.android.com/training/wearables/data-layer/index.html

答案 1 :(得分:4)

这是我的解决方案的草稿。作为@gruszczy suggested,我使用DataApi

可穿戴应用程序:

public class WApplication extends Application
        implements Thread.UncaughtExceptionHandler {

    private static final String LOG_TAG = WApplication.class.getSimpleName();

    private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;

    ...

    @Override
    public void onCreate() {
        super.onCreate();
        mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, final Throwable throwable) {
        Log.e(LOG_TAG, "Uncaught exception thrown.");

        WearableService.launchService(throwable, WApplication.this);

        mDefaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
    }

}

可穿戴服务:

public class WearableService extends Service {
    ...
    public static void launchService(Throwable throwable, Context context) {
        Intent startServiceIntent = new Intent(context, WearableService.class);
        startService.putExtra(EXTRA_KEY_EXCEPTION, throwable);
        context.startService(startServiceIntent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Throwable throwable = (Throwable) intent.getSerializableExtra(KEY_EXCEPTION);

        sendExceptionToMobile(throwable);

        return super.onStartCommand(intent, Service.START_REDELIVER_INTENT, startId);
    }

    private void sendExceptionToMobile(final Throwable throwable) {
        if (throwable == null) {
            return;
        }
        Log.d(LOG_TAG, "Sending exception to mobile...");
        PutDataMapRequest putDataMapReq = PutDataMapRequest
                .create(WearCommunicationConstants.PATH_EXCEPTION);
        DataMap dataMap = putDataMapReq.getDataMap();
        StringWriter sw = new StringWriter();
        throwable.printStackTrace(new PrintWriter(sw));
        String stackTrace = sw.toString();
        dataMap.putString(WearCommunicationConstants.KEY_STACK_TRACE, stackTrace);
        PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
        PendingResult<DataApi.DataItemResult> pendingResult =
                Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
        pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
            @Override
            public void onResult(final DataApi.DataItemResult result) {
                if (result.getStatus().isSuccess()) {
                    Log.d(LOG_TAG,
                            "DataItem synced: " + result.getDataItem().getUri());
                } else {
                    Log.e(LOG_TAG,
                            "Failed to sync DataItem: " + result.getStatus().getStatusCode() + ", "
                                    + result.getStatus().getStatusMessage());
                }
            }
        });
    }
}

移动服务:

public class MobileService extends WearableListenerService {
    ...
    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        Log.d(LOG_TAG, "Data changed, data event(s) received.");

        for (DataEvent event : dataEvents) {
            Log.d(LOG_TAG, "Data event type: " + event.getType());
            switch (event.getType()) {
                case DataEvent.TYPE_CHANGED:
                    DataItem item = event.getDataItem();
                    DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
                    switch (item.getUri().getPath()) {
                        case WearCommunicationConstants.PATH_EXCEPTION:
                            Log.e(LOG_TAG, "Received exception from a wearable device.");
                            String stackTrace = dataMap
                                    .getString(WearCommunicationConstants.KEY_STACK_TRACE);
                            Utils.logWithCrashlytics(stackTrace);

                            break;

                        // ...
                    }
                    break;
                case DataEvent.TYPE_DELETED:
                    // ...
            }
        }
    }
}

答案 2 :(得分:2)

现有解决方案要求手机目前在范围内。使用Wear 2.0提供手表自主权,我们需要能够存储崩溃并在连接后将其发送出去。 WearCrashReporter就是这样做的。

我们在监视虚拟机上安装崩溃处理程序。捕获崩溃时,其跟踪和类型将序列化为json,保存到FileSystem,然后在手机可用时将其作为MessageApi消息与服务一起发送。在手机应用程序中通过WearableListenerService接收后,它将被反序列化并传递给已安装的Phone Virtual Machine的崩溃记者。

答案 3 :(得分:0)

我通过以下方式解决了这个问题:

this lib集成到您的项目中。此lib将把所有异常从磨损应用程序传输到移动应用程序。

如果您不使用proguard - 您可以简单地使用ExceptionWear lib并将移动应用程序端的异常记录到崩溃物中。

否则

当您在移动应用程序端收到throwable时 - 您可以将其记录到crashlytics中,但是有一个问题:
如果我们使用android plugin feature构建移动+磨损应用程序,我们将会这样:

dependencies {
   compile 'com.google.android.gms:play-services:5.0.+@aar'
   ...lots of cool libs...
   wearApp project(':wear')
}

并在两个应用程序(移动和磨损)上应用crashlytics插件,然后在构建磨损应用程序期间,您可以看到在proguard任务和dex任务(gradle任务)后,crashlytics插件不会存储和上传Deobs,因此 - 在crashlytics仪表板上堆栈跟踪不会重新映射(回溯)

<强>:穿:crashlyticsCleanupResourcesRelease // EXPECTED :磨损:crashlyticsUploadStoredDeobsRelease //预期 :磨损:crashlyticsGenerateResourcesRelease //预期
:wear:generateReleaseResValues UP-TO-DATE
:磨损:generateReleaseResources
:磨损:mergeReleaseResources
:磨损:processReleaseResources
:磨损:generateReleaseSources
:磨损:compileReleaseJava
:磨损:proguardRelease
:wear:dexRelease //没有crashlytics存储和上传Deobs任务 :穿:processReleaseJavaRes UP-TO-DATE
:磨损:shrinkReleaseResources

但是当磨损应用程序被构建时(磨损ap就像对移动应用程序的依赖),移动应用程序构建开始,并且在移动构建过程中,crashlytics插件运行良好:

<强>:移动:crashlyticsCleanupResourcesRelease // EXPECTED :手机:crashlyticsUploadStoredDeobsRelease //预期 :移动:crashlyticsGenerateResourcesRelease //预期
:mobile:generateReleaseResValues UP-TO-DATE
:手机:generateReleaseResources
:手机:mergeReleaseResourcesknown
:手机:processReleaseResources
:手机:generateReleaseSources
:手机:compileReleaseJava
:手机:proguardRelease
:手机:dexRelease
:移动:crashlyticsStoreDeobsRelease // EXPECTED :手机:crashlyticsUploadDeobsRelease //预期 :移动:crashlyticsCleanupResourcesAfterUploadRelease //预期
:手机:lintVitalRelease
:mobile:compileReleaseNdk UP-TO-DATE
:mobile:processReleaseJavaRes UP-TO-DATE
:移动:shrinkReleaseResources

因此,在标准构建过程中磨损模块deobs不会上传,但有解决方法:

如果开始构建单独使用app,然后在移动模块资源中使用manualy package wear apk, 然后成功上传了deobs,你可以在仪表板上观察到被追踪的崩溃。

但我个人不喜欢建造apk的manual way,所以我尝试做以下事项:
首先只建立穿着应用程序。 deobs将上传到crashlytics。然后使用&#39; wearApp项目运行完整版本(&#39;:穿&#39;)&#39;功能,看起来很有效。

无论如何,我正在等待崩解剂开箱即用的android-wear支持。

答案 4 :(得分:0)

可以在构建过程中上传移动设备并佩戴。

概念:
 1.确保移动和磨损具有独特的映射  2.在上传之前将磨损映射合并到移动映射中
1.配置proquard(通常是proguard-rules.pro)
换穿:

-useuniqueclassmembernames

for mobile add:

-useuniqueclassmembernames
-applymapping ../wear/build/outputs/mapping/release/mapping.txt

此更改通过将磨损构建的映射应用于移动构建,确保您在移动设备上具有唯一的名称并佩戴。

2.配置构建以合并mapping.txt
添加到build.gradle of mobile:

// allows to use Crashlytics also for wear by merging the mappings of wear into the
// mappings of mobile
//noinspection GroovyAssignabilityCheck
task mergeMappings(dependsOn: "transformClassesAndResourcesWithProguardForRelease") << {
  File wearMappingFile = new File("wear/build/outputs/mapping/release/mapping.txt");
  File mobileMappingFile = new File("mobile/build/outputs/mapping/release/mapping.txt");
  if (wearMappingFile.exists() && mobileMappingFile.exists()) {
    println("merge mapping.txt")
    java.nio.file.Files.copy(wearMappingFile.toPath(),
                             new FileOutputStream(mobileMappingFile, true))
  } // else we are on the wear build and the mobile build was not yet executed
}

afterEvaluate {
  project.("crashlyticsStoreDeobsRelease").dependsOn(mergeMappings);
}

在crashlyticsStoreDeobsRelease之前将磨损映射附加到移动映射。