如何从可穿戴应用程序中检索崩溃报告?
How to retrieve crash reports from a wearable app?
由于 Crashlytics 不适用于开箱即用的可穿戴应用程序,我正在寻找一种最佳方法来拦截和报告运行时抛出的任何潜在异常。我想知道为什么他们没有自动报告给 Google Play Developer Console?
Google 已经宣布未来的 Android Wear 更新将内置 Wi-Fi 支持,但即便如此,并非每台设备都会配备足够的硬件。
在那种情况下,我最初的想法是创建一个Application
的子类并实现Thread.UncaughtExceptionHandler
。然后,每个异常都必须使用 MessageApi
编组并发送到手机。手机上 WearableListenerService
的分机将收到一条消息,解组异常并将其传递给例如 Crashlytics。
然而,这又提出了几个问题。可穿戴设备和手机之间的蓝牙连接存在中断的风险,因此所有错误都应排队并存储在可穿戴设备的文件系统中。
对于一个简单的崩溃报告来说,这似乎有点矫枉过正。有更简单的方法吗?
不要为此目的使用 MessageApi
,而是 DataApi
。这样你就不用担心蓝牙连接丢失了。
它的工作方式:
当发生崩溃时,在可穿戴设备上设置一个DataItem
与崩溃;
最终会传送到移动设备。
从手机发送崩溃信息并删除DataItem
.
这里有更多信息:http://developer.android.com/training/wearables/data-layer/index.html
我通过以下方式解决了这个问题:
将 this lib 集成到您的项目中。此库会将所有异常从 wear 应用程序传输到移动应用程序。
如果您不使用 proguard - 您可以简单地使用 ExceptionWear 库并将移动应用端的异常记录到 crashlytics 中。
否则
当您在移动应用程序端收到 throwable - 您可以将其登录到 crashlytics,但有一个问题:
如果我们使用 android plugin feature 构建 mobile+wear 应用程序,我们将得到如下内容:
dependencies {
compile 'com.google.android.gms:play-services:5.0.+@aar'
...lots of cool libs...
wearApp project(':wear')
}
并在两个应用程序(移动和磨损)上应用 crashlytics 插件,然后在构建磨损应用程序期间,您可以看到在 proguard 任务和 dex 任务(gradle 任务)之后,crashlytics 插件不会存储和上传 Deobs 和结果 - stacktraces 未在 crashlytics 仪表板上重新映射(回溯):
:wear:crashlyticsCleanupResourcesRelease//预期
:磨损:crashlyticsUploadStoredDeobsRelease //预期
:wear:crashlyticsGenerateResourcesRelease//预期
:wear:generateReleaseResValues 最新
:wear:generateReleaseResources
:wear:mergeReleaseResources
:wear:processReleaseResources
:wear:generateReleaseSources
:wear:compileReleaseJava
:wear:proguardRelease
:wear:dexRelease//NO crashlytics存储和上传Deobs任务
:wear:processReleaseJavaRes 最新
:wear:shrinkReleaseResources
但是当构建 Wear 应用程序时(Wear ap 就像对移动应用程序的依赖)然后移动应用程序构建开始并且在移动构建过程中 crashlytics 插件运行良好:
:mobile:crashlyticsCleanupResourcesRelease//预期
:移动:crashlyticsUploadStoredDeobsRelease //预期
:mobile:crashlyticsGenerateResourcesRelease//预计
:mobile:generateReleaseResValues 最新
:mobile:generateReleaseResources
:mobile:mergeReleaseResourcesknown
:mobile:processReleaseResources
:mobile:generateReleaseSources
:mobile:compileReleaseJava
:mobile:proguardRelease
:mobile:dexRelease
:mobile:crashlyticsStoreDeobsRelease//预期
:移动:crashlyticsUploadDeobsRelease //预期
:mobile:crashlyticsCleanupResourcesAfterUploadRelease//预计
:移动:lintVitalRelease
:mobile:compileReleaseNdk 最新
:mobile:processReleaseJavaRes 最新
:mobile:shrinkReleaseResources
因此,在标准构建过程中 wear 模块 deobs 未上传,但有解决方法:
if start build wear app separately and then manualy package wear apk in mobile module resources,
然后穿上成功上传的deobs,你可以在仪表盘上观察回溯的崩溃。
但我个人不喜欢 manual way 构建 apk,所以我尝试执行以下操作:
首先只构建 wear 应用程序。 deobs 将上传到 crashlytics。然后 运行 使用 'wearApp project(':wear')' 功能进行完整构建,看起来它有效。
无论如何,我正在等待 android-开箱即用的 crashlytics 支持。
这是我的解决方案草稿。作为 ,我正在使用 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:
// ...
}
}
}
}
可以在构建过程中上传移动和磨损的deobs。
概念:
1. 确保 mobile 和 wear 具有唯一映射
2. 上传前将磨损映射合并到移动映射
1.配置proquard(通常是proguard-rules.pro)
磨损添加:
-useuniqueclassmembernames
对于移动添加:
-useuniqueclassmembernames
-applymapping ../wear/build/outputs/mapping/release/mapping.txt
此更改通过将可穿戴设备版本的映射应用到移动设备版本,确保您在移动设备和可穿戴设备上拥有唯一的名称。
2.配置构建合并mapping.txt
添加到手机build.gradle:
// 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 之前将磨损映射附加到移动映射。
现有解决方案要求 phone 当前在范围内。随着 Wear 2.0 提供手表自主性,我们需要能够存储崩溃并在我们连接后发送它们。 WearCrashReporter 正是这样做的。
我们在手表虚拟机上安装了一个崩溃处理程序。当崩溃被捕获时,其跟踪和类型被序列化为 json,保存到文件系统,然后在 phone 可用时作为 MessageApi 消息与服务一起发送。在 phone 应用程序中的 WearableListenerService 接收后,它被反序列化并传递给安装的 Phone 虚拟机的崩溃报告器。
由于 Crashlytics 不适用于开箱即用的可穿戴应用程序,我正在寻找一种最佳方法来拦截和报告运行时抛出的任何潜在异常。我想知道为什么他们没有自动报告给 Google Play Developer Console?
Google 已经宣布未来的 Android Wear 更新将内置 Wi-Fi 支持,但即便如此,并非每台设备都会配备足够的硬件。
在那种情况下,我最初的想法是创建一个Application
的子类并实现Thread.UncaughtExceptionHandler
。然后,每个异常都必须使用 MessageApi
编组并发送到手机。手机上 WearableListenerService
的分机将收到一条消息,解组异常并将其传递给例如 Crashlytics。
然而,这又提出了几个问题。可穿戴设备和手机之间的蓝牙连接存在中断的风险,因此所有错误都应排队并存储在可穿戴设备的文件系统中。
对于一个简单的崩溃报告来说,这似乎有点矫枉过正。有更简单的方法吗?
不要为此目的使用 MessageApi
,而是 DataApi
。这样你就不用担心蓝牙连接丢失了。
它的工作方式:
当发生崩溃时,在可穿戴设备上设置一个
DataItem
与崩溃;最终会传送到移动设备。
从手机发送崩溃信息并删除
DataItem
.
这里有更多信息:http://developer.android.com/training/wearables/data-layer/index.html
我通过以下方式解决了这个问题:
将 this lib 集成到您的项目中。此库会将所有异常从 wear 应用程序传输到移动应用程序。
如果您不使用 proguard - 您可以简单地使用 ExceptionWear 库并将移动应用端的异常记录到 crashlytics 中。
否则
当您在移动应用程序端收到 throwable - 您可以将其登录到 crashlytics,但有一个问题:
如果我们使用 android plugin feature 构建 mobile+wear 应用程序,我们将得到如下内容:
dependencies {
compile 'com.google.android.gms:play-services:5.0.+@aar'
...lots of cool libs...
wearApp project(':wear')
}
并在两个应用程序(移动和磨损)上应用 crashlytics 插件,然后在构建磨损应用程序期间,您可以看到在 proguard 任务和 dex 任务(gradle 任务)之后,crashlytics 插件不会存储和上传 Deobs 和结果 - stacktraces 未在 crashlytics 仪表板上重新映射(回溯):
:wear:crashlyticsCleanupResourcesRelease//预期
:磨损:crashlyticsUploadStoredDeobsRelease //预期
:wear:crashlyticsGenerateResourcesRelease//预期
:wear:generateReleaseResValues 最新
:wear:generateReleaseResources
:wear:mergeReleaseResources
:wear:processReleaseResources
:wear:generateReleaseSources
:wear:compileReleaseJava
:wear:proguardRelease
:wear:dexRelease//NO crashlytics存储和上传Deobs任务
:wear:processReleaseJavaRes 最新
:wear:shrinkReleaseResources
但是当构建 Wear 应用程序时(Wear ap 就像对移动应用程序的依赖)然后移动应用程序构建开始并且在移动构建过程中 crashlytics 插件运行良好:
:mobile:crashlyticsCleanupResourcesRelease//预期
:移动:crashlyticsUploadStoredDeobsRelease //预期
:mobile:crashlyticsGenerateResourcesRelease//预计
:mobile:generateReleaseResValues 最新
:mobile:generateReleaseResources
:mobile:mergeReleaseResourcesknown
:mobile:processReleaseResources
:mobile:generateReleaseSources
:mobile:compileReleaseJava
:mobile:proguardRelease
:mobile:dexRelease
:mobile:crashlyticsStoreDeobsRelease//预期
:移动:crashlyticsUploadDeobsRelease //预期
:mobile:crashlyticsCleanupResourcesAfterUploadRelease//预计
:移动:lintVitalRelease
:mobile:compileReleaseNdk 最新
:mobile:processReleaseJavaRes 最新
:mobile:shrinkReleaseResources
因此,在标准构建过程中 wear 模块 deobs 未上传,但有解决方法:
if start build wear app separately and then manualy package wear apk in mobile module resources,
然后穿上成功上传的deobs,你可以在仪表盘上观察回溯的崩溃。
但我个人不喜欢 manual way 构建 apk,所以我尝试执行以下操作:
首先只构建 wear 应用程序。 deobs 将上传到 crashlytics。然后 运行 使用 'wearApp project(':wear')' 功能进行完整构建,看起来它有效。
无论如何,我正在等待 android-开箱即用的 crashlytics 支持。
这是我的解决方案草稿。作为 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:
// ...
}
}
}
}
可以在构建过程中上传移动和磨损的deobs。
概念:
1. 确保 mobile 和 wear 具有唯一映射
2. 上传前将磨损映射合并到移动映射
1.配置proquard(通常是proguard-rules.pro)
磨损添加:
-useuniqueclassmembernames
对于移动添加:
-useuniqueclassmembernames
-applymapping ../wear/build/outputs/mapping/release/mapping.txt
此更改通过将可穿戴设备版本的映射应用到移动设备版本,确保您在移动设备和可穿戴设备上拥有唯一的名称。
2.配置构建合并mapping.txt
添加到手机build.gradle:
// 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 之前将磨损映射附加到移动映射。
现有解决方案要求 phone 当前在范围内。随着 Wear 2.0 提供手表自主性,我们需要能够存储崩溃并在我们连接后发送它们。 WearCrashReporter 正是这样做的。
我们在手表虚拟机上安装了一个崩溃处理程序。当崩溃被捕获时,其跟踪和类型被序列化为 json,保存到文件系统,然后在 phone 可用时作为 MessageApi 消息与服务一起发送。在 phone 应用程序中的 WearableListenerService 接收后,它被反序列化并传递给安装的 Phone 虚拟机的崩溃报告器。