引子 web?native?这是个争论了很久的问题。 自从微信开放了更多的JS接口之后,移动web开发重新火了起来,前端程序猿也水涨船高。 毫无疑问,web页面有诸多优点: 跨平台:一次开发,可以同时在Android,iOS和Other Phone上运行(美好的愿望) 快速迭代,内容
引子
web?native?这是个争论了很久的问题。
自从微信开放了更多的JS接口之后,移动web开发重新火了起来,前端程序猿也水涨船高。
毫无疑问,web页面有诸多优点:
跨平台:一次开发,可以同时在Android,iOS和Other Phone上运行(美好的愿望)
快速迭代,内容统一:内容修改后,不同版本都能展示最新内容。可以避免频繁的客户端升级,也无需经过App Store的审核
语言优势:庞大JavaScript开发人员,能够带来移动端内容的繁荣
本人多年开发研究web与native混合APP,因为这里面有很多的坑,很有必要把经验归纳一番。
准备
区分APP和浏览器
我们的web页面是通过哪个应用打开的呢?这是要解决的第一个问题,这样才能做到APP与系统浏览器的内容差异化。
通过域名
将不同用途的页面归类到不同服务器或Web项目下,这是最简单也最笨的方法,如果同一个页面要在三端上都展示,那么就要复制3份了通过元数据
通过UA标识
这是web页面统计访问终端的品牌和分辨率的常用方法
我会在WebView的默认UA后面,加上自定义的标识,包括APP的标识 (AndroidApp、iPhoneApp)、应用包名和APP的版本号
判断是否微信浏览器就可以使用这个方法,同时最好检查是否加载了微信自定义的weixin.js文件
配置WebView基础参数
final WebSettings settings = getSettings();// 允许JS弹出提示框settings.setJavaScriptCanOpenWindowsAutomatically(true);// 允许web执行JSsettings.setJavaScriptEnabled(true);// 设置浏览器标识settings.setUserAgentString(buildAppUserAgent(getContext(), settings.getUserAgentString()));// 是否支持缩放,默认不支持(看起来更native)settings.setSupportZoom(false); settings.setBuiltInZoomControls(false);// 打开H5的离线缓存settings.setAppCacheEnabled(true);final String cachedir = getContext().getDir("cache", Context.MODE_PRIVATE).getPath(); settings.setAppCachePath(cachedir);// 打开H5的Dom Storage(localStorage,sessionStorage)settings.setDomStorageEnabled(true);// 如果要使用离线缓存和DomStorage必须要设置web databasefinal String dbdir = getContext().getDir("database", Context.MODE_PRIVATE).getPath(); settings.setDatabaseEnabled(true); settings.setDatabasePath(dbdir);
WebViewClient对象
监听页面加载情况(开始加载、资源加载、完成加载、页面错误)
通常情况下,我们都会有一个loading界面覆盖在webview上面,当页面加载完成隐藏loading。
这里存在2个小问题:
webview只有当所有内容都加载完成后,才会回调onPageFinished。
也就是说,当DOM元素加载完成,并且所有图片也加载完成才会触发,这对于追求体验的移动APP来说,显然无法接受。尤其是当网络不好或图片太大时尤为明显,用户看到的是明明页面已经基本出来了,却仍然在loading,无法操作。
其实JS端可以监听到DOMContentLoaded事件,此时是关闭loading的最佳时机出现加载错误时,不仅会调用onReceivedError,仍然会调用onPageFinished
有些人喜欢把loading界面和error界面写在同一个layout里,出现错误时显示error,完成加载时隐藏整个layout。
这对于普通页面来说没有问题,但是对于webview就会出现error无法被显示的情况。
所以最后将loading和error分开,在onPageFinished时,只需要隐藏loading部分。
拦截页面链接
重写shouldOverrideUrlLoading方法,当返回值为true时,需要自己处理url请求,webview将不会插手。
此方法只在涉及页面跳转时被触发。
这里存在2个问题:
低版本的某些请求不会触发此方法,直接在本webview打开了新页面。
如果想拦截,则需要在onPageStarted方法里判断url是否已经改变,更改了的话就表明打开了新的链接。页面重定向无法识别
当页面存在重定向时,API并没有提供方法作区分,这时需要自己处理。
替换加载内容
重写shouldInterceptRequest方法,可以替换JS、CSS、img等内容。
创建WebResourceResponse对象,并传入文件输入流,即可用其他资源替换本来要加载的内容。
通过此方法,我们可以预先下载页面所需的JS和CSS文件,保存到本地;然后当打开页面时,直接使用本地已下载的文件。这样可以大幅提高页面加载速度。
注:此方法只能API Level 11以后使用,低版本可通过ContentProvider实现类似功能。
WebChromeClient对象
WebViewClient类主要设计链接和资源加载的功能实现
WebChromeClient类则会涉及更底层的内容,如控制台调试、JS弹出框、显示自定义view等。
替换Alert对话框
web页面一般会通过alert方法,显示一些提示信息,但是对话框的样式却因不同的品牌差异很大,为了使我们的APP保持统一风格,有必要替换成我们自己设计的对话框。
重写onJsAlert方法,将message显示到Dialog。
该方法是模态的,必须返回内容才能关闭对话框,调用JsResult.cancel或者JsResult.confirm。如果只调用了Dialog.dismiss而没有调用JsResult的方法,会出现,虽然对话框消失,但是线程一直处于阻塞状态,造成假死现象,无法进行任何操作。
全屏播放视频
webview默认不能全屏播放,需要client端提供全屏的window。
代码如下:
@Override public void onShowCustomView (View view, WebChromeClient.CustomViewCallback callback) { mCustomViewCallback = callback;android.view.Window window = WebActivity.this.getWindow();window.setFlags( android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN, android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);setRequestedOrientation(android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);mTitleBar.setVisibility(View.INVISIBLE);mWebView.setVisibility(View.INVISIBLE);mExitFullscreenBtn.setVisibility(View.VISIBLE);mVideoViewContainer.setVisibility(View.VISIBLE);mVideoViewContainer.addView(view);} @Override public void onHideCustomView () { mCustomViewCallback.onCustomViewHidden();android.view.Window window = WebActivity.this.getWindow();window.setFlags(0, android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);setRequestedOrientation(android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);mTitleBar.setVisibility(View.VISIBLE);mWebView.setVisibility(View.VISIBLE);mExitFullscreenBtn.setVisibility(View.INVISIBLE);mVideoViewContainer.setVisibility(View.INVISIBLE);mVideoViewContainer.removeAllViews();}
Cookie
如果web端需要用户登录的操作,那么就涉及到native和web端同步登录状态,这就需要用到cookie。
// 打开Cookie支持CookieSyncManager.createInstance(this); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true);
// 设置cookie sBuff.append(key).append("=").append(value);sBuff.append("; path=/");sBuff.append("; domain=").append(domain);cookieManager.setCookie(url, sBuff.toString());...... CookieSyncManager.getInstance().sync();
// 删除cookie// 清除过期的cookiecookieManager.removeExpiredCookie();// 清除所有cookiecookieManager.removeAllCookie();
因为并不存在单独删除cookie的某个字段的方法,所有要清除某个字段,要先将其设为已过期,再调用removeExpiredCookie
如何同步登录状态
简单的情况,只需要有userid即可认为已经登录,分如下两种情况:
- 先从native登录
native端调用登录接口,拿到useid后,当需要打开web时,在loadUrl之前,将userid保存到cookie中,服务端就会从cookie中读出userid。
- 先从web端登录
web登录后,webview会自动保存cookie。web端需要与native端定义接口,将username和userid通知给native端,native保存起来。
Web端与Native端互相通讯
WebView调用web端JS方法
mWebView.loadUrl("javascript:JSMethod()");
API 19以后,提供了更加便捷的方法,可以直接获取JS的返回值。(iOS本身已经提供类似API)
WebView.evaluateJavascript (String script, ValueCallback<String> resultCallback)
web端调用native代码
早期API提供的方法:
WebView.addJavascriptInterface(Object object, String name) WebView会将object注入到web端的window对象中,name是object对象定义的方法,web端通过object.name即可调用native端的功能。
but,这个方法存在安全漏洞 漏洞详细说明
如果你的targetSdkVersion>=17,那么必须将java方法加上注解@JavascriptInterface,否则web端是无法调用的。
自定义scheme
通过自定义scheme的方式,在shouldOverrideUrlLoading拦截,并定向到相应的native逻辑。
iOS端需通过此方法实现。
利用WebChromeClient.onJsPrompt
因为JS端很少使用prompt(一般使用alert)方法,所以我们可以利用这个方法,通过自定义协议格式,来实现web与native端的通信。
因为此方法需要返回JsResult对象,所以利用此机制,可以实现方法的同步调用(web端可以获取到接口方法的返回值,有利于代码和逻辑的简化)
大名鼎鼎PhoneGap(现在叫Cordova)就是利用此方法实现的web与native通讯。
我会专门写一篇,我是如何实现web与native通信的。
其他技巧
屏蔽长按事件
重写performLongClick
或者setOnLongClickListener实现空的listener
隐藏选择框
可以使用全局的一个CSS
*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}
缩放按钮引起的崩溃
在某些机型上,当显示webview的缩放按钮时,退出Activity,就会报如下错误:
android.view.WindowLeaked: Activity com.secoo.activity.web.WebActivity has leaked window android.widget.ZoomButtonsController$Container{438e8248 V.E..... ........ 0,0-1536,194} that was originally added here at android.view.ViewRootImpl.(ViewRootImpl.java:382) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:261) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:76) at android.widget.ZoomButtonsController.setVisible(ZoomButtonsController.java:371) ......
这是因为按钮的隐藏是延迟触发,在activity退出之后,造成了window的泄露。
解决方法:
// API 11之后,不显示缩放按钮 WebView.getSettings().setDisplayZoomControls(false);// API 11之前,在finish时,从view层级中删除webview ViewGroup viewgroup = (ViewGroup)(mWebView.getParent());viewgroup.removeView(mWebView);
技术交流请留言…
To Be Continue…

mysql'sblobissuitable forstoringbinarydatawithinarationaldatabase、whileenosqloptionslikemongodb、redis、andcassandraofferferulesions forunstructureddata.blobissimplerbutcanslowdowdowd withwithdata

toaddauserinmysql、使用:createuser'username '@' host'identifidedby'password '; here'showtodoitsely:1)chosehostcarefilytoconを選択しますTrolaccess.2)setResourcelimitslikemax_queries_per_hour.3)usestrong、uniquasswords.4)endforcessl/tlsconnectionswith

toavoidcommonMonmistakeswithStringDatatypesinmysql、undultingStringTypenuste、choosetherightType、andManageEncodingandCollationsEttingtingive.1)Usecharforfixed-LengthStrings、Varcharforaible Length、AndText/Blobforlardata.2)setCurrectCherts

mysqloffersechar、varchar、Text、anddenumforstringdata.usecharforfixed-lengthstrings、varcharerforvariable-length、text forlarger text、andenumforenforcingdataantegritywithaetofvalues。

MySQLBlob要求の最適化は、次の戦略を通じて実行できます。1。ブロブクエリの頻度を減らす、独立した要求の使用、または読み込みの遅延。 2。適切なブロブタイプ(TinyBlobなど)を選択します。 3。ブロブデータを別々のテーブルに分離します。 4.アプリケーションレイヤーでBLOBデータを圧縮します。 5.ブロブメタデータをインデックスします。これらの方法は、実際のアプリケーションでの監視、キャッシュ、データシェルディングを組み合わせることにより、パフォーマンスを効果的に改善できます。

MySQLユーザーを追加する方法を習得することは、データベース管理者と開発者にとって重要です。これは、データベースのセキュリティとアクセス制御を保証するためです。 1)CreateUserコマンドを使用して新しいユーザーを作成し、2)付与コマンドを介してアクセス許可を割り当て、3)FlushPrivilegesを使用してアクセス許可を有効にすることを確認します。

choosecharforfixed-lengthdata、varcharforvariable-lengthdata、andtextforlargetextfields.1)chariseffienceforconsistent-lengthdatalikecodes.2)varcharsuitsvariaible-lengthdatalikenames、balancingflexibilityandperformance.3)Textisidealforforforforforforforforforforforidex

MySQLの文字列データ型とインデックスを処理するためのベストプラクティスには、次のものが含まれます。1)固定長のchar、可変長さのvarchar、大規模なテキストのテキストなどの適切な文字列タイプを選択します。 2)インデックス作成に慎重になり、インデックスを避け、一般的なクエリのインデックスを作成します。 3)プレフィックスインデックスとフルテキストインデックスを使用して、長い文字列検索を最適化します。 4)インデックスを定期的に監視および最適化して、インデックスを小さく効率的に保つ。これらの方法により、読み取りと書き込みのパフォーマンスをバランスさせ、データベースの効率を改善できます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

WebStorm Mac版
便利なJavaScript開発ツール

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境
