androidapp二维码扫码下载,途牛网站的效果,怎么实现?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了androidapp二维码扫码下载,途牛网站的效果,怎么实现?相关的知识,希望对你有一定的参考价值。
android 二维码扫描是很常用的工具,是不是很Cool,到底如何实现的呢,下面我们就来探讨一下Zxing的实现方法(底部附上下载链接):
首先
工程结构:
如何引用:(内容来自雪炭网SnowCoal.com)
一般来说,你就可以改改就用了,但如果你只想把它当成一个小小的子集加入项目,你需要将三个包Copy至你的项目中:(camera、decoding、view),然后引入相对应的资源进去,不要访记还有一个Jar包哦!(Zxing.jar)
关于布局:
com.example.qr_codescan包里面有一个MipcaActivityCapture,也是直接引入,这个Activity主要处理扫描界面的类,比如,扫描成功有声音和振动等等,主要关注里面的handleDecode(Result
result, Bitmap barcode)方法,扫描完成之后将扫描到的结果和二维码的bitmap当初参数传递到handleDecode(Result
result, Bitmap barcode)里面,我们只需要在里面写出相对应的处理代码即可,其他的地方都不用改得,这里处理扫描结果和扫描拍的照片.
* 处理扫描结果
* @param result
* @param barcode
*/
public void handleDecode(Result result, Bitmap barcode)
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
String resultString = result.getText();
if (resultString.equals( "" ))
Toast.makeText(MipcaActivityCapture. this , "Scan failed!" ,
Toast.LENGTH_SHORT).show();
else
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString( "result" , resultString);
bundle.putParcelable( "bitmap" , barcode);
resultIntent.putExtras(bundle);
this .setResult(RESULT_OK, resultIntent);
MipcaActivityCapture. this .finish();
对MipcaActivityCapture界面的布局做了自己的改动,先看下效果图,主要是用到FrameLayout,里面嵌套RelativeLayout。
//xml
android:layout_width= "fill_parent"
android:layout_height= "fill_parent" >
android:layout_width= "fill_parent"
android:layout_height= "fill_parent" >
android:id= "@+id/preview_view"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_gravity= "center" />
android:id= "@+id/viewfinder_view"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content" />
< include
android:id= "@+id/include1"
android:layout_width= "fill_parent"
android:layout_height= "wrap_content"
android:layout_alignParentTop= "true"
layout= "@layout/activity_title" />
实现:
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity
private final static int SCANNIN_GREQUEST_CODE = 1 ;
/**
* 显示扫描结果
*/
private TextView mTextView ;
/**
* 显示扫描拍的图片
*/
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState)
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.result);
mImageView = (ImageView) findViewById(R.id.qrcode_bitmap);
//点击按钮跳转到二维码扫描界面,这里用的是startActivityForResult跳转
//扫描完了之后调到该界面
Button mButton = (Button) findViewById(R.id.button1);
mButton.setOnClickListener( new OnClickListener()
@Override
public void onClick(View v)
Intent intent = new Intent();
intent.setClass(MainActivity. this , MipcaActivityCapture. class );
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
);
@Override
protected void onActivityResult( int requestCode, int resultCode, Intent
data)
super .onActivityResult(requestCode, resultCode, data);
switch (requestCode)
case SCANNIN_GREQUEST_CODE:
if (resultCode == RESULT_OK)
Bundle bundle = data.getExtras();
//显示扫描到的内容
mTextView.setText(bundle.getString( "result" ));
//显示
mImageView.setImageBitmap((Bitmap) data.getParcelableExtra( "bitmap"
));
break ;
参考技术A
对于二维码扫描我们使用的是google的开源框架Zxing,在你下载完后。你直接将com.mining.app.zxing.camera,com.mining.app.zxing.decoding,com.mining.app.zxing.view这三个包拷贝到你的项目中,然后引入相对应的资源进去,当然还需要引用Zxing.jar。
界面设计,和二维码的概念,这里就不做解说了。核心代码如下:
/*
* Copyright (C) 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mining.app.zxing.view;
import java.util.Collection;
import java.util.HashSet;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import com.example.qr_codescan.R;
import com.google.zxing.ResultPoint;
import com.mining.app.zxing.camera.CameraManager;
/**
* This view is overlaid on top of the camera preview. It adds the viewfinder
* rectangle and partial transparency outside it, as well as the laser scanner
* animation and result points.
*
*/
public final class ViewfinderView extends View
private static final String TAG = "log";
/**
* 刷新界面的时间
*/
private static final long ANIMATION_DELAY = 10L;
private static final int OPAQUE = 0xFF;
/**
* 四个绿色边角对应的长度
*/
private int ScreenRate;
/**
* 四个绿色边角对应的宽度
*/
private static final int CORNER_WIDTH = 10;
/**
* 扫描框中的中间线的宽度
*/
private static final int MIDDLE_LINE_WIDTH = 6;
/**
* 扫描框中的中间线的与扫描框左右的间隙
*/
private static final int MIDDLE_LINE_PADDING = 5;
/**
* 中间那条线每次刷新移动的距离
*/
private static final int SPEEN_DISTANCE = 5;
/**
* 手机的屏幕密度
*/
private static float density;
/**
* 字体大小
*/
private static final int TEXT_SIZE = 16;
/**
* 字体距离扫描框下面的距离
*/
private static final int TEXT_PADDING_TOP = 30;
/**
* 画笔对象的引用
*/
private Paint paint;
/**
* 中间滑动线的最顶端位置
*/
private int slideTop;
/**
* 中间滑动线的最底端位置
*/
private int slideBottom;
private Bitmap resultBitmap;
private final int maskColor;
private final int resultColor;
private final int resultPointColor;
private Collection<ResultPoint> possibleResultPoints;
private Collection<ResultPoint> lastPossibleResultPoints;
boolean isFirst;
public ViewfinderView(Context context, AttributeSet attrs)
super(context, attrs);
density = context.getResources().getDisplayMetrics().density;
//将像素转换成dp
ScreenRate = (int)(20 * density);
paint = new Paint();
Resources resources = getResources();
maskColor = resources.getColor(R.color.viewfinder_mask);
resultColor = resources.getColor(R.color.result_view);
resultPointColor = resources.getColor(R.color.possible_result_points);
possibleResultPoints = new HashSet<ResultPoint>(5);
@Override
public void onDraw(Canvas canvas)
//中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改
Rect frame = CameraManager.get().getFramingRect();
if (frame == null)
return;
//初始化中间线滑动的最上边和最下边
if(!isFirst)
isFirst = true;
slideTop = frame.top;
slideBottom = frame.bottom;
//获取屏幕的宽和高
int width = canvas.getWidth();
int height = canvas.getHeight();
paint.setColor(resultBitmap != null ? resultColor : maskColor);
//画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面
//扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边
canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
paint);
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
if (resultBitmap != null)
// Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(OPAQUE);
canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
else
//画扫描框边上的角,总共8个部分
paint.setColor(Color.GREEN);
canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
frame.top + CORNER_WIDTH, paint);
canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH, frame.top
+ ScreenRate, paint);
canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
frame.top + CORNER_WIDTH, paint);
canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right, frame.top
+ ScreenRate, paint);
canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
+ ScreenRate, frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - ScreenRate,
frame.left + CORNER_WIDTH, frame.bottom, paint);
canvas.drawRect(frame.right - ScreenRate, frame.bottom - CORNER_WIDTH,
frame.right, frame.bottom, paint);
canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom - ScreenRate,
frame.right, frame.bottom, paint);
//绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
slideTop += SPEEN_DISTANCE;
if(slideTop >= frame.bottom)
slideTop = frame.top;
canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH/2, frame.right - MIDDLE_LINE_PADDING,slideTop + MIDDLE_LINE_WIDTH/2, paint);
//画扫描框下面的字
paint.setColor(Color.WHITE);
paint.setTextSize(TEXT_SIZE * density);
paint.setAlpha(0x40);
paint.setTypeface(Typeface.create("System", Typeface.BOLD));
canvas.drawText(getResources().getString(R.string.scan_text), frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density), paint);
Collection<ResultPoint> currentPossible = possibleResultPoints;
Collection<ResultPoint> currentLast = lastPossibleResultPoints;
if (currentPossible.isEmpty())
lastPossibleResultPoints = null;
else
possibleResultPoints = new HashSet<ResultPoint>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(OPAQUE);
paint.setColor(resultPointColor);
for (ResultPoint point : currentPossible)
canvas.drawCircle(frame.left + point.getX(), frame.top
+ point.getY(), 6.0f, paint);
if (currentLast != null)
paint.setAlpha(OPAQUE / 2);
paint.setColor(resultPointColor);
for (ResultPoint point : currentLast)
canvas.drawCircle(frame.left + point.getX(), frame.top
+ point.getY(), 3.0f, paint);
//只刷新扫描框的内容,其他地方不刷新
postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
frame.right, frame.bottom);
public void drawViewfinder()
resultBitmap = null;
invalidate();
/**
* Draw a bitmap with the result points highlighted instead of the live
* scanning display.
*
* @param barcode
* An image of the decoded barcode.
*/
public void drawResultBitmap(Bitmap barcode)
resultBitmap = barcode;
invalidate();
public void addPossibleResultPoint(ResultPoint point)
possibleResultPoints.add(point);
参考技术B 二维码解析出来的是一个网址,这个网址是自己服务器地址,在网页中判断浏览器与系统来决定跳转到哪个市场。
也有第三方网站支持上传的应用后,直接生成二维码把跳转的事放第三方网站帮做了。 参考技术C 二维码解析出来的是一个网址,这个网址是自己服务器地址,在网页中判断浏览器与系统来决定跳转到哪个市场。
也有第三方网站支持上传的应用后,直接生成二维码把跳转的事放第三方网站帮做了。 参考技术D 生成一个二维码
途牛网站无线架构变迁实践
从一开始的单机系统,发展到现在已拥有数百个分布式部署的系统。本文主要将途牛网站无线系统在从小到大的过程中,遇到的问题以及解决方法与大家分享,希望为大家带来一定借鉴。文章将从服务化推进、南北京机房之痛、性能提升实践、App客户端技术演进四个方面进行介绍。
服务化推进
途牛的服务化始于2011年,当时我们主要进行了会员的服务化,2012年进行了搜索2.0的服务化,2013年是服务化大举前进的时刻,主要进行了搜索3.0、价格中心、订单中心、产品基础数据等系统的服务化,2014年将TSP(途牛服务治理平台)、业务公共系统、资源搜索系统等进行服务化,2015年对产类目、开放API进行服务化。
从上面的过程可以看出,我们的服务化不是一蹴而就的,而是经历了一个漫长的过程,每一次拆分都相当于为高速行驶的汽车更换轮胎的过程。可以注意到,在2012年我们拆分了一个搜索2.0,之后很快又在2013年推出了搜索3.0。
这两个版本的区别是:做搜索2.0一开始没有什么经验,虽然采用了Solr这样非常成熟的开源搜索引擎来搭建搜索平台,但是没有明确界定搜索平台和业务系统之间的关系,导致搜索平台的逻辑非常重,被当成一个数据聚合的平台来使用,网站列表页数据和详情页数据都从搜索中出来,导致搜索获取数据源部分的逻辑非常复杂,搜索开发人员将70%的时间都放在和业务系统对接逻辑的处理上,索引效率也比较低,从而导致性能不稳定,逐渐退役。吸取教训后,我们搭建了搜索3.0的平台,仅仅提供列表搜索,统一列表字段,将数据推送逻辑移到搜索外部,由各个产品系统来进行数据推送,搜索本身专注于性能的提升与稳定性,并逐步加入智能排序、人工干预搜索结果功能。迄今为止,搜索3.0是我们公司最为稳定的系统。
接下来是服务化过程中,技术层面做得比较好的两个服务:价格计算服务和服务治理平台。
价格计算服务
从技术上,价格计算服务有两个难点:一个是团期价格依赖的因素较多,并且依赖路径较深;另一个是这些因素价格变动的频率较高,尤其在旺季。因此从设计上,价格计算服务必须要有较大的容量要求,同时具有实时性。
价格计算服务从13年开始构建,架构上也经历了四个阶段:同步架构、异步架构、并发架构和分布式架构,如图1所示。
图1 服务化的推荐 - 价格计算服务
同步架构:系统间主要通过接口进行交互,其他系统通过调用接口通知价格中心发起运算,价格中心通过接口获取其他系统价格依赖的所有资源。整个计算流程采用串行模型行,效率低仅能满足小规模的计算需求。
异步架构:系统间通过MQ进行交互,价格中心通过依赖数据库获取其他系统的数据,加快了数据读取的效率,并将计算价格变成两段:先针对一个资源多个供应商的情况,将资源的最低成本价计算好,然后再算产品最低价。这种架构比同步架构数据读取的效率更高,并能通过预先生成数据,加快计算的速度,提升3倍整体性能。
并发架构:首先将价库自身的数据(资源的成本价,产品团期起价)进行了分库分表,提升了系统的数据容量,然后再根据产品的访问频度区分冷热数据的计算频率,冷数据降低计算频率,热数据增加计算频率——并通过在内存中建立团期、行程、资源这三个维度的数据结构,提升计算过程中数据的读写效率。整体上性能比异步架构提升了3.5倍,每次每个团期的价格计算时间控制在200ms以下。
分布式架构:通过解析依赖数据库的Binlog,将依赖数据库的数据转换成适合使用的内存数据库结构,进一步提升数据读取效率,从而解决计算过度依赖数据库的问题,通过使用Sharding MQ,实现本地访问、本地计算;通过使用Unix域通信的机制,实现本地通信,将每个计算实例所依赖的资源和通信尽量限制在本地服务器上,最大化提升I/O能力,降低I/O损耗。整体性能比并发架构提升2倍,每次每个团期的价格计算时间控制在100ms以下。
通过上面几个阶段的优化,价格计算服务的整体架构如图2所示。
图2 服务化的推荐 - 价格计算服务整的体架构
其中分发节点中的计算成本节点就是一些预处理节点,主要计算资源成本价,物理机中的计算节点是实际执行价格计算的单元节点。调度节点通过一定路由规则,将价格计算分片到不同机器上,Binlog同步的时候也会按照类似规则,将数据同步到不同存储节点物理机,从而整体上实现本地存储、本地计算。
截止到2015年5月,价格计算服务每天的计算量在9亿次左右,每个团期平均每天被计算2次以上。价格计算服务始终在I/O能力和计算效率上不断迭代改进,期待未来能够有更好的架构出现。
服务治理平台
随着服务化推进越来越深入,每个系统提供的接口也越来越多,整个系统逐渐产生了这样一些问题:网状接口调用;接口中存在循环依赖,可能引起雪崩效应;服务调用缺乏监控;使用硬件实现负载均衡,可维护性较差。针对这些问题,我们急需一套服务治理平台来将所有的服务管理起来。
基于开源的服务治理平台,我们进行了部分定制,很快将适合于途牛的服务治理平台搭建起来,架构如图3所示。
图3 途牛的服务治理平台
其中注册中心采用主从模式进行集群部署,“主”进行服务地址的变更及心跳的保持,“从”提供查询服务。主从之间建立长连接,保持心跳。“主”宕机后,“从”接替,变更自己的身份标识。注册中心各个部署的实例只有获得“主”身份才能接受客户端长连接请求。各个服务提供者、服务消费者感知“主”宕机后,尝试连接“从”,并与之建立长连接,使用SQLLite数据库持久化服务列表,使用高可用的内存缓存保存可用服务地址列表,与服务提供者、服务消费者之间建立长连接,维持心跳。
服务提供者启动之后,通过通用组件将提供的服务告之注册中心,注册中心更新可用服务地址列表。如果该服务没有审核记录,则作为新服务待审核。新服务提交到注册中心后,注册中心不会更新到可用服务列表,需要人工在管理页面进行审核通过后才能进入使用,被服务消费者感知。
服务提供者发生宕机,心跳中断的情况,注册中心将更新可用服务地址列表,删除提供者的所有服务,并发出变更通知。心跳具有重连保持机制。一定时间内无心跳才断开连接。服务提供者使用连接池,控制长连接的数目,设置最高连接数。如果连接数得到最高限制,则拒绝新的连接接入,保证当前系统的可用性。
管理页面上可以查询服务、查看服务详细情况及可用服务地址列表,查看服务消费者列表,新上线服务的审核,待下线服务的禁止,实时调整某个服务的负载均衡策略,对某个服务提供者进行降权、倍权、禁用、允许操作。
南北京机房之痛
本节主要介绍途牛的机房部署策略。在2014年以前,我们基本上都维持了南北京机房的结构,在当时的情况下,这种策略基本上还是比较合理的,但是随着应用体量越来越大,逐步出现了问题,我们在2015年变成了南京单机房的策略,未来我们将向两地三中心这种更加稳定、高可用的架构演变。
南北京单机房的策略,在设计之初,很好的满足了业务需求。在2010年以前,途牛70%以上的订单均为电话订单,加上旅游订单的预订流程又比较复杂,需要客服人工参与的环节较多,我们需要将订单系统部署在南京机房,以便为我们的客服提供好的用户体验。同时为了给互联网用户提供更好的机房条件,我们需要将网站部署在北京。在这种机房架构下,我们进行了大量系统优化工作,主要是为了解决异地机房之间的数据同步问题。
首先针对网站数据“读多写少”的特征,我们对每一个子系统,均采用如下的典型系统设计,如图4所示。
图4 网站数据读多写少特征下的每一个子系统的典型设计
南北京之间通过数据库的主从同步机制进行数据同步,北京机房的应用读取北京的数据库,通过专线写入南京的数据库,从而确保两边数据的一致性。
该设计方案在系统容量小的时候,可以很好地运行,但是在专线不稳定的情况下,会产生较多问题,最常见的是数据同步延迟,比如用户在网站注册后,无法立刻登录。针对这个问题,我们采用了熔断的设计方案,使用特定进程监控数据库同步延迟,如果延迟达到上限,将尝试使用公网VPN进行同步,当专线情况好转时,再切换回去。
另外为了控制数据同步的数据量,所有数据同步均采用了压缩机制,最大限度减少同步的数据量。同时我们也不断扩大专线的容量。
随着业务不断增长,同步的数据量越来越多,这种部署架构遇到的挑战越来越大,最终在2015年初,我们将两地机房进行合并。中途最大挑战是南京机房的网络条件问题,当时南京地区尚无接入条件较好的多线BPG机房,为了给全国用户提供较好的网络服务,我们最终采用了动态CDN方案,南京机房出口仅提供电信出口的IP。对联通、移动的用户通过动态域名解析,解析到本地较近中转服务器,再由中转服务器优化路由访问南京的电信线路。该方案能为全国用户提供良好的网络服务。
在整个服务器部署成本上,我们至少降低了30%,一是避免了同一套系统在南北京部署两份,二是节省了大量的专线费用。
目前的单机房策略,是一个过渡方案,为了保证系统进一步的高可用和数据的安全性,我们后期将向标准的两地三中心机房部署策略迈进。
性能优化
性能优化主要介绍我们在优化过程中总结出来几个工具,我们的思路是:首先,不断推进架构演变,系统划分整理,提前对资源进行扩展,保证总体的承载能力。然后,不断推进监控完善,性能指标具体化,发现问题、解决问题,保证总体的稳定能力。主要由这样三个工具实现:CODIS、BWT、OSS。
Codis是豌豆荚使用Go和C语言开发,以代理方式实现的一个Redis分布式集群解决方案,且完全兼容Twemproxy。Codis底层会处理请求的转发、不停机的数据迁移等工作。所有底层的处理, 对于客户端来说都是透明的。总之,可以简单地认为后台连接的是一个内存无限大的Redis服务。我们从无缓存,到文件缓存,到Memcache缓存,到今天的Codis缓存,缓存是大型架构的必然。
使用Codis后,应用端不需要再关心缓存具体存放在哪里,不需要关心缓存扩容和数据迁移的工作,不需要关心缓存数据一致性的问题,极大提升了应用开发和维护的效率。
BWT是我们自主开发的一个主动缓存更新服务,为了进一步提升页面的生成效率,应用系统发生数据变更时,将要更新的数据请求发到BWT中,BWT根据设置好的更新策略,对缓存进行更新。应用系统推送过来的数据,一般会延时3分钟,进行更新。同时BWT也会通过日志分析出来的热点数据,会根据设置的时间自动更新。更新的过程中,若目标机器负载高,会自动停止更新。
OSS也是我们自主研发的一个网站运营监控系统,该系统初期目标是对网站的性能、可用性和安全的进行监控和管理。后期将独立成一个单独的运营监控系统,为所有系统提供监控服务。图5是OSS的系统结构。
图5 网站运营监控系统OSS的系统结构
主要特点是使用UPD的方式将日志从应用系统发送出来,尽可能降低发送日志对应用系统的性能消耗。通过NSQ队列接收日志,使用Go语言编写的消费进程将日志汇总处理后,存入DB,并最终通过页面将各种统计报表呈现出来。
网站的各种故障,可以通过错误与性能图表,很快找到问题。主要有依赖接口监控,慢查SQL监控,Memcache监控,Redis监控以及单页面性能监控。
App客户端技术演进
这里主要介绍途牛App在开发过程中的实践心得,侧重于线热补丁和前端资源静态化两个方面。
在线热补丁
由于App采用客户端发布的方案,一旦发布出去的包有Bug,修复是一个非常头疼的问题,传统的修复方法主要有:服务器端屏蔽技术,也就是将有问题的功能暂时屏蔽掉;跳转H5页面,将产生问题的页面直接跳转到对应的H5页面;紧急发布新版本。这几种办法均有一定的局限性,对于服务端屏蔽技术,会增加服务端代码复杂度并隐藏局部功能;对于跳转到H5,会降低用户体验;对于紧急发布新版本,会增加运营成本并降低用户体验。
为此我们引入了阿里的在线热补丁技术,以便在问题发生时,能够快速发布补丁包将问题解决。
前端资源静态化
由于H5开发周期短,容易部署的特性,在途牛App中存在大量的H5页面,但对于H5页面,用户体验的损失也是显而易见。为了让页面中的元素更快渲染,呈现给用户,我们采用了前端资源静态化的方案,主要思路是将H5页面中的静态资源提前加载,实现要点如下:
-
静态资源异步加载,用户打开App的时候异步下载或者更新静态文件。
-
优化渲染,减少不必要的开销。通过优化DOM布局,将加载的静态资源分组,可以打包的,优先渲染,需要从服务器取的,后渲染,从而加快第一次进入速度;减少第一屏DOM渲染数,使用懒加载,分步加载;优化渲染结构,由于App中的Webview性能低于手机浏览器,所以减少不必要的渲染开销,比如减少消耗非常高的一些滚动图;优化交互,有交互操作,造成DOM重排重绘的,尽量使用最小的DOM重排,将需要新加入的一些层,与原来的DOM结构分开;使用一些3D CSS,使用GPU帮助页面重绘。
以上就是我们在架构变迁过程中的一些实践要点,虽然看起来有些散,但还是主要从架构的以下三个方面进行介绍。
逻辑架构:服务化,如何将业务中通用的功能抽象出来,以服务的方式提供给其他各个系统。
物理架构:南北京机房的设计初衷,遇到的问题,解决方案等。
系统架构:非功能性的架构,比如性能优化,App客户端性能改进实践。
以上是关于androidapp二维码扫码下载,途牛网站的效果,怎么实现?的主要内容,如果未能解决你的问题,请参考以下文章