您的位置:首页 > 移动开发 > Android开发

记一次android Bug解决的曲折心路历程(java.net.ProtocolException: expected 0 bytes but received 2048)

2017-12-04 19:55 423 查看
问题描述:

上周四的一天,产品经理找到我,说学校里面有几个用户在使用我们的一款应用(我们应用是教学软件)的过程中拍照后上传图片一直提交失败。

第一波心理历程:

当时我听到这个问题的第一反应就是,去后台看看对应用户的操作路径,看看操作路径上的埋点以及收集上来的用户设备信息,内存信息,报错的堆栈信息等等。从而分析出哪个步骤出问题了,以便能够找到复现的步骤,定位问题的真正原因。

可是理想很丰满,现实很骨感!

这个老破项目,哪里有什么用户数据收集模块。唯一庆幸的是后来接入了Bugly, ,但是这种老的java项目,写的代码都是能把功能做通就算完活了。 那管你什么代码规范,异常处理啥的。如果有个异常能给 try,catch{}一下就是对的起你了。 说不准catch的时候,连异常打印都不打印….

所以,想查看日志啥的就别做梦了。醒醒吧! 不过好在我们是做定制的,就是应用搭配着Pad一起卖,所以用户使用的Pad是固定的设备, 所以去测试部拿了一台设备,按照用户的描述进行操作,操作了一番。复现啦!!! 开心了一会,这就好办了。能复现的话,我的水平还是可以解决的!!

注:设备为 联想TB4-X704F, Android系统版本7.1.1。

第二波心理历程:

复现的过程也挺波折的,因为是定制产品,所以我们会有定制Rom。这个问题在复现的过程中,只在某一版的Rom中有问题,而这一版Rom偏偏不能进行debug. 只能不断的打log… 。 最后发现提交的时候抛出了这个异常(java.net.ProtocolException: expected 0 bytes but received 2048)。

这里插一句“程序员一定要冷静的分析问题,打点的时候也是,要不就是做无用功!事倍功半”,当时一看到这个异常,第一反应就是这个是网络异常了呀! 再一看使用的是okhttp请求的网络,稍稍安心了一点,okhttp的使用我还是熟悉的。定位问题也好定位。 再一看,不对呀,okhttp的版本怎么是3.2.1。 我靠现在都3.9了。 这怎么还是这么老的版本呢? 原来是用了一个开源库okhttputils中引用的okhttp, 而这okhttputils中引用的okhttp的版本比较老。所以就这样了! 当时我想,如果这个是网络问题,我要分析网络问题,怎么也得跟着okhttp的源码去定位一个这个异常的位置和原因呀。 但我用这么老的版本,万一okhttp版本迭代的过程中已经修复了这个问题,那我不是白白浪费自己的时候了。 所以我决定把okhttp升级到最新版本, 但是有点尴尬的是,项目对于这个第3方库的依赖已经相当深了。如果我直接替换okhttp版本,意味着我要手动修改这个开源库基于okhttp实现的部分。。 想到这点… 真的是…。 但是我的思路还是没有我的手快, 我直接上手替换了。 … 这明显就是copy代码多了导致的条件反射!! 万幸的是okhttp是一个优秀的开源库, api的向下兼容性很好,我并没有替换什么。 还有就是gradle很方便,只一行代码就更改了项目的依赖!! 替换以后还是抛出这个异常, 这个时候我的思维回到了正常轨道。而正好这个时候Rom商也基于这个问题版本给我了一个可以调试的版本。

第三波心理历程:

我直接debug的时候发现,拍完照后,onActivityResult中使用okhttp上传这个图片文件之前。图片文件的大小竟然为零。

这尼玛… 我才知道我错了,这和网络没有半毛钱关系。 文件都没有创建成功,你让人家上传什么!ok. 既然问题回到了调用相机拍照的问题,那就好办了, 我根本就不相信这个老项目的代码,所以直接上官网去重新回顾了一下调用相机拍照的方式。

文档在此:https://developer.android.com/training/camera/photobasics.html

然后把这个代码copy下来,重新创建了一个工程,写了一个测试demo。找了一个Andriod4.4的手机试了一把,可以正常拍照,而且拍完照后对于目录下的图片也存在。然后我在找了一个android7.1.1版本的模拟器中试了一把,可以正常拍照,而且拍完照后对于目录下的图片也存在。 ok , 那就用这个代码替换老项目的代码。 我本以为这样就解决了。 我操。我哪里知道这只是坑爹的开始。 代码运行到老项目中,问题依然在!

只好debug了一下,发现和Demo项目的区别就是Demo中没有创建新的目录来保存拍照的图片,直接使用的是系统目录:

private void takePhoto() {
if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException e) {
e.printStackTrace();
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
}

private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
//直接使用的系统目录getExternalFilesDir(Environment.DIRECTORY_PICTURES)
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName,  /* prefix */
".jpg",         /* suffix */
storageDir      /* directory */
);

// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}


而在项目中使用的是项目自己的目录,并创建了文件夹!

File file = new File(Environment.getExternalStorageDirectory()+File.separator+"Uclass_V4"+File.separator+"sse");
if(!file.exists()){
file.mkdir();
Log.e("", "Directory not created");
}


其中Environment.getExternalStorageDirectory()+File.separator+”Uclass_V4”目录已经存在了,要在该目录下面创建“sse”目录用来保存拍的照片。 这个目录作为storageDir来保存创建的图片。

坑爹的事情就在这里发生了, 我在debug的时候发现在老项目中file.mkdir() 返回true. 但是在文件管理器中对应的目录文件并没有创建。 看着这个方法@return的注释,再看看debug的结果。 我不由的怀疑起人生。 这尼玛什么情况! 怀疑人生没毛用。想想这可能是什么情况,我只好把这个代码,写在demo中测试了一下。为了避免偶现的这种概率问题,我测试了 十几次。每次都是成功创建,成功显示! 没办法只好用大招了,跟源码,看看为毛老项目的file.mkdir() 返回true. 但是代码目录没有创建。 因为之前没有debug过太多的源码,所以心里有点怵。 也不知道能不能看懂。 google了一番怎么debug源码, 然后在mkdir方案中打了一个断点,可是尼玛怎么也打不上!! 此方案“卒”。

/**
* Creates the directory named by this abstract pathname.
*
* @return  <code>true</code> if and only if the directory was
*          created; <code>false</code> otherwise
*
* @throws  SecurityException
*          If a security manager exists and its <code>{@link
*          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
*          method does not permit the named directory to be created
*/
public boolean mkdir() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
if (isInvalid()) {
return false;
}
return fs.createDirectory(this);
}


注:这个mkdir()的鬼问题,只有在这台pad上有,其他pad上没有。不同版本,不同型号的我都测试了一下!

第四波心理历程:

既然,对于mkdir()我暂时是没招了,咋整,我先不创建目录了吧,就使用系统目录算了。 本以为可以了! 可是在onActivityResult发现,图片的大小还是为零。 这个时候我突然冷静了下来,想想无非就是一个兼容问题嘛! 我用Demo完全模拟一下老项目的代码,如果demo通了,老项目自然也就是通了。 所以我针对性的修改了一下demo工程的配置, 重点是targetVersion和gradle的dubug配置。 我们知道targetVersion决定了android系统的不同版本的兼容处理。所以我要保证两个项目完全相同,要不问题就复杂了。 考虑问题的时候还要多考虑android的兼容行为。 然后我在Demo工程的onActivityResult中判断了一下拍的照片的大小,发现在android 7.0以上的版本设备中,onActivityResult中获取的image file的length为零。但是低于android 7.0的版本设备就是正常的大小。 好吧,那就拿到是7.0的行为变更引起的。

google搜了几个关键字, capture, onActivityResult , MediaStore.ACTION_IMAGE_CAPTURE

然后看了一下官方的7.0行为变更:

https://developer.android.google.cn/about/versions/nougat/android-7.0-changes.html?hl=zh-cn#accessibility

开始把问题的角度定位到android 7.0关于文件共享的行为变更上了。

但是看了相关适配文章的描述:

https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en

但是我觉得不对,这个老项目的targetVersion是19,所以如果运行在25的设备上的时候,android系统会对它进行兼容问题。而文档里面也说的很清楚,只有targetVersion==24时候,才需要主动处理这些事情。(项目太老, 做基于新版本的兼容工作量太大!)

可是如果不是这个问题,那又是什么呢?

我重新整理了一些关键字, onActivityResult ,MediaStore.ACTION_IMAGE_CAPTURE ,length ,0

终于搜到一篇文章:

https://stackoverflow.com/questions/35717371/picture-file-captured-with-camera-intent-empty-for-a-while-in-onactivityresult

最后在这个问题的讨论中找到了问题的临时解决办法!

just replace "File.create TempFile()" in my sample code by "new File(storageDir, imageFileName + ".jpg"). The camera app will then create the file for you


结束了吗!!! 操, 这才是开始好吧。 问题的原因到底是什么呢??? 断断续续的花了两天的进行调试!!

为什么要整理这个历程:

按理说这么丢人的事情,不应该整理笔记吧! 又不是解决了什么大问题,最后都没搞清楚。 但是想提高的程序员就是直面自己的错误,直面自己的不足。才能进步。最重要的是,希望有机会参加技术会议的时候,向牛人请教一下,指点一下我应该怎么做!!怎么提高解决问题的效率!

总结:

我要好好想想!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android bug
相关文章推荐