被微信拍照时页面会被刷新的BUG给折磨疯了

查了好久,也没有结果,只能默默的忍受着,痛苦着;在调用微信的

wx.chooseImage
wx.getLocalImgData
wx.uploadImage

 总会莫名的出现页面刷新,导致上传的图片丢失;一直存在这个明显的BUG:在微信公众号拍照后,会出现概率重定向到当前页面(当前页面被刷新,或者叫做重新加载)。

 后来发现是说Android设备中底层bug导致,无法在上层处理,所有调用微信的jssdk也会出现此问题;

现象

  • 在上传组件中(哪怕是最简单的<input type="file">),当选择好本地图片或者调取系统摄像头拍好照片后,网页被刷新。这一问题无法100%重现,但概率不低,在如红米等低端安卓机上发生的概率会比较高。
  • 与用系统自带的浏览器打开的情况相比,在微信中打开的情况下该问题出现的更为频繁。
  • 与选择本地图片上传相比,调取系统摄像头拍照上传的情况下该问题出现的更为频繁。

随便找了两家知名公司的网站(新浪微博、赶集网),在用Android设备访问网页并试图进行图片上传时,都发生了这一问题。

在经过一番搜索和调研后,发现这个问题的原因远比自己想象的要复杂:这是一个由Android操作系统底层设计缺陷所导致的问题,对于Web开发者来说,没有什么办法能真正解决这一bug。

这个问题并不是最近才开始出现的,早在2013年该问题就已报给Android技术团队了(Android issue)。但可惜的是,直至现在Android团队依旧没有正视该问题。导致问题的步骤是这样的(为方便描述,App内嵌的webview也以“浏览器”一词进行表示):

问题原因

  1. 当在Android的浏览器上调用文件上传功能时,Android系统将把当前进程从“浏览器”切换到“文件选择器”(根据不同的用户选择,这一新的进程可能是图片选择器,也可能是系统摄像头,或者其它别的),此时浏览器进程将变为后台进程。
  2. 由于Android操作系统的设计缺陷,此时浏览器进程的留存优先级(不被系统kill掉的优先级)与所有其它的后台进程是一样的,因此如果操作系统认为内存不足需要进行清理,此时浏览器进程将不会得到任何保护 -- 很不幸,因为浏览器占用内存一般都比较大,所以这次kill操作很容易kill掉浏览器进程。
  3. 当用户选择好图片返回浏览器时,浏览器进程已经不复存在了,按照Android的机制,浏览器进程将进行恢复,试图恢复到kill之前的状态 -- 很不幸,恢复到什么状态是由浏览器来决定的,而浏览器不可能100%恢复到选择文件之前的那个状态。因此最终用户所看到的现象就是:选择文件完成后,浏览器刷新了一下,而刷新到什么状态由浏览器决定。

对于这一问题,Android技术团队的态度是回避(将bug标记为obsolete) -- 可能是因为此bug涉及了OS底层进程切换的机制,修复起来风险太高;而随着手机设备内存容量的增大,这一问题所发生的概率会越来越小。

理解了问题的原因,对问题的一些表征也就有了合理的解释:低端安卓机的内存更小,因此发生问题的频率更高;而与选择本地图片相比,调取系统摄像头所耗的内存更大(当拍摄有大量文字的图片时尤其如此),因此调取摄像头时问题发生的更加频繁;当用户已经开了很多别的App时,问题发生的概率更大。

问题的优化

对于这一问题,真正的解决方法需要对Android的底层细节进行修改,比如定义与当前进程相关联的后台进程,并对这些有关联的后台进程设置高优先级。在Android团队将问题彻底解决之前,这一bug是无解的,但存在一些workaround:

  1. 在上传操作之前,往浏览器里写一个标志性的cookie(或者localStorage等本地存储方案),上传成功后清除该标志位。而在初始加载页面时,判断该标志位是否存在 -- 如果存在,那么就意味着当前的这一次页面加载是由于浏览器进程被kill后重启所导致的。此时可以alert出一些消息,提醒用户内存不足,需要关闭一些别的App。这一方案只能说提升了一些用户体验,无法避免问题的发生;同时,由于部分Android版本的内存优化缺陷,导致在系统内存还有很多的情况下也会启动内存清理操作,使得alert出来的消息对用户造成困扰。
  2. 如果主要场景是在微信中打开的,那么可以尝试使用微信JSSDK中的图片上传接口来完成图片的上传操作。我没有做过严格的测试对比,因此只能做一些猜测:对于JSSDK中的接口,相信微信团队是做了不少测试和修复工作的(比如针对此文所提到的问题,避免切换到外部进程,或者让webview的恢复更加正确),所以采用这一方案该问题的发生概率也许会比较小。但不利的是,调用微信JSSDK所上传的图片会在微信的服务器上暂存3天,因此这一方案不适用于需要上传敏感信息的情况。

除了上述的两种workaround,另外还存在两个替代方案:

  1. 开发Native App。当自己的App进程由于内存清理而被kill掉后,对于恢复阶段要恢复成什么样子,开发人员可以做的会更多。
  2. 限定发布的web服务所支持的手机范围为iPhone和内存较多的中高端Android机型

很遗憾,这一问题没有很好的解决方案,既有的workaround要么隐私性上有问题(也不确定到底会有多大的改善),要么基本于事无补。Web项目开发能做的,也就只能是从设计上尽量减少图片上传操作了。

下面是一段EXT的图片上传的方法,截取补全,仅供记录和参考。

 onTap: function(e) {
    	var me = this;
    	//单张图片上传
    	if(me.config.type == 'single'){
    		if(me.config.src.indexOf ('common/xiangji.png') > -1
    				|| me.config.src.indexOf ('common/shenfenzheng.png') > -1
    				|| me.config.src.indexOf ('common/yinhangka.png') > -1
    				|| me.config.src.indexOf ('/fileupload/display/') > -1){
    			//选择图片
    			var mesrc = me.config.src;
    			wx.chooseImage({
    			    count: 1, // 默认9
    			    sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有,当前指压缩的
    			    sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
    			    success: function (res) {
    			    	var localIds = res.localIds;
    			    	var id = localIds[0].toString();
    					if(window.__wxjs_is_wkwebview){
    				    	wx.getLocalImgData({
    				    	    localId: id, // 图片的localID
    				    	    success: function (res) {
    				    	        var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
    				    	        localData = localData.replace('jgp', 'jpeg');
    				    			me.setSrc(localData);
    		    	    			me.config.src = localData;
    				    	    }
    				    	});
    					}else{
    						me.config.src = id;
    						me.setSrc(id);
    				    	if (id.indexOf("wxlocalresource") != -1) {
    				    		id = id.replace("wxlocalresource", "wxLocalResource");
    		                }
    					}
    					//图片上传
    					setTimeout(function(){
    						wx.uploadImage({
        					    localId: id,
        					    isShowProgressTips: 1, // 默认为1,显示进度提示
        					    success: function (res) {
        					    	var fmMediaId = res.serverId; // 返回身份证图片的微信服务器端ID
        					    	me.parent.query('input[type="hidden"]')[0].setValue(fmMediaId);
        					    	me.config.mediaId = fmMediaId
        					    	
        					    	if( mesrc.indexOf ('common/shenfenzheng.png') > -1
        				    				|| mesrc.indexOf ('common/yinhangka.png') > -1){
        					    		var parentWidth = me.parent.el.dom.offsetWidth;
        					    		var meWidth = me.el.dom.offsetWidth;
        					    		var left = (parentWidth-meWidth)/2;
        					    		var button = Ext.create('Ext.Button', {
            								height:28,
            				    			width:meWidth,
            				    			left:left,
            				    			style:'background: #000;border-radius: 0;opacity: 0.7;color: #fff;font-size: 1.4rem;letter-spacing: 6px;text-indent: 6px;',
            								text:'删除',
            								bottom:0,
            								listeners:{
            						        	tap: function(){
            							    		this.parent.remove(this);
            							    		me.setSrc(mesrc);
            							    		me.config.src = mesrc;
            							    		me.parent.query('input[type="hidden"]')[0].setValue('');
            						        	}
            						        }
        	    			    	    });
        					    		me.parent.add(button);
        					    	} else {
        					    		var button = Ext.create('Ext.Button', {
            								height:28,
            				    			width:105,
            				    			style:'background: #000;border-radius: 0;opacity: 0.7;color: #fff;font-size: 1.4rem;letter-spacing: 6px;text-indent: 6px;',
            								text:'删除',
            								bottom:0,
            								listeners:{
            						        	tap: function(){
            							    		this.parent.remove(this);
            							    		me.setSrc('./resources/common/xiangji.png');
            							    		me.config.src = './resources/common/xiangji.png';
            							    		me.parent.query('input[type="hidden"]')[0].setValue('');
            						        	}
            						        }
        	    			    	    });
        					    		me.parent.add(button);
        					    	}
    	    			    		
        					    }
        					});
    		    		},100);
    			    }
    			});
    		}else{
        		var srcList = [];
        		srcList.push(me.config.src);
    			//图片预览
    			wx.previewImage({
    			    current: me.config.src, // 当前显示图片的http链接
    			    urls: srcList// 需要预览的图片http链接列表
    			});
    		}
    	}
    	//多张图片上传
    	else if(this.config.type == 'multiple'){
    		//默认最多上传3张图片
            if(EU.isEmpty(me.config.max)) {
            	me.config.max = 3;
    		}
            
    		var objHidden = me.parent.parent.query('input[type="hidden"]')[0];
	    	if(me.config.src.indexOf ('common/xiangji.png') > -1){
	    		//图片选择
	    		wx.chooseImage({
				    count: 1, // 默认9
				    sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有,当前指压缩的
				    sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
				    success: function (res) {
				    	var localIds = res.localIds;
				    	var id = localIds[0].toString();
		    			me.config.src = id;
						if(window.__wxjs_is_wkwebview){
					    	wx.getLocalImgData({
					    	    localId: id, // 图片的localID
					    	    success: function (res) {
					    	        var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
					    	        localData = localData.replace('jgp', 'jpeg');
					    			me.setSrc(localData);
					    	    }
					    	});
						}else{
							me.setSrc(id);
					    	if (id.indexOf("wxlocalresource") != -1) {
					    		id = id.replace("wxlocalresource", "wxLocalResource");
			                }
						}
						//图片上传
						setTimeout(function(){
							wx.uploadImage({
							    localId: id,
							    isShowProgressTips: 1, // 默认为1,显示进度提示
							    success: function (res) {
							    	var fmMediaId = res.serverId; // 返回身份证图片的微信服务器端ID
						    		me.config.mediaId = fmMediaId;
						    		var mediaId = objHidden.getValue();
						    		if(EU.isEmpty(mediaId)){
						    			objHidden.setValue(fmMediaId);
						    		}else{
						    			objHidden.setValue(mediaId + ',' + fmMediaId);
						    		}
						    		var button = Ext.create('Ext.Button', {
										height:28,
										width:'100%',
						    			style:'background: #000;border-radius: 0;opacity: 0.7;color: #fff;font-size: 1.4rem;letter-spacing: 6px;text-indent: 6px;',
										text:'删除',
										bottom:0,
										listeners:{
								        	tap: function(){
									    		var mediaId = objHidden.getValue();
									    		if(mediaId.substring(mediaId.length-1,mediaId.length) !== ','){
									    			mediaId = mediaId + ',';
									    		}
									    		var ouccerMediaId = me.config.mediaId;
									    		mediaId = mediaId.replace(ouccerMediaId + ',','');
									    		objHidden.setValue(mediaId);
									    		me.config.src = './resources/common/xiangji.png';
									    		this.parent.destroy();
								        	}
								        }
								    });
						    		me.parent.add(button);
						    		if(me.config.max>objHidden.getValue().split(",").length){
						    			var container = Ext.create('Ext.Container', {
											width:'30%',
											margin:'0 10px 10px 0',
											style:'float:left',
											items:[]
							    	    });
							    		var image = Ext.create('PlusApp.ux.ImgPhoto', {
							    			height:105,
											width:'100%',
											style:'float:left;border-radius: 2px;background-size: cover;',
											type:'multiple',
								        	cls:'imgGroup',
											src: './resources/common/xiangji.png'
							    	    });
							    		container.add(image);
							    		me.parent.parent.add(container);
						    		}
							    }
							});
    		    		},100);
				    	
				    }
				});
	    	}else{
	    		var srcList = [];
	    		var imgGroup = Ext.query('.imgGroup');
				for (var i = 0; i < imgGroup.length; i++) {
					var imgSrc = imgGroup[i].style.backgroundImage;
					if(imgSrc.indexOf('weixin') > -1){
						imgSrc = imgSrc.substring(imgSrc.indexOf('weixin'),imgSrc.indexOf('")'));
						srcList.push(imgSrc);
					}
				}
    			//图片预览
    			wx.previewImage({
    			    current: me.config.src, // 当前显示图片的http链接
    			    urls: srcList// 需要预览的图片http链接列表
    			});
    		}
	    }
    }

 

参考学习:https://blog.csdn.net/dobuy/article/details/87949273

参考学习:http://blog.shaochuancs.com/android-upload-page-refresh/

Uniapp是一种跨平台开发框架,可以帮助开发者将应用程序快速部署到不同的平台上。虽然Uniapp在开发过程中能够实现页面缓存,但有候在微信中使用Uniapp应用程序可能遇到页面导致空白的问题。 造成这个问题的原因可能是由于微信缓存机制的影响。微信为了提高用户体验,将一些页面进行缓存,以加快页面的加载速度。然而,当使用Uniapp开发的应用程序在微信中被缓存,可能出现一些意外的问题,导致页面无法正常显示。 解决这个问题的方法有几种。一种是通过在app.vue中的mounted生命周期钩子函数中加入防止缓存的代码,例如在进入页面,通过调用uni.reLaunch方法重新载入页面,这样可以避免页面被缓存,确保每次都是重新加载。 另一种方法是通过修改manifest.json文件来禁用缓存。在manifest.json文件中的pages项中添加一个属性,例如"disableSwipeBack": true,这样可以禁用页面的滑动返回功能,同也可以避免页面被缓存。 此外,还可以尝试升级Uniapp框架的版本,以获得更好的兼容性和稳定性。Uniapp开发团队不断优化框架,修复bug,并提供新的功能和特性,以提供更好的开发体验和性能。 总之,当Uniapp应用程序在微信中出现空白页面的问题,可以尝试以上提到的方法进行解决。通过避免缓存或禁用页面的滑动返回功能,可以确保每次加载页面都能够正常显示,提供更好的用户体验。同,及关注Uniapp框架的更新和优化也是解决问题的一个重要步骤。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值