AndroidのWebViewの学習します

WebViewの

何のAndroidのWebViewを検討する機会を取ります

簡単に言えばAndroidは4.4後クロームカーネルを使用し、説明し、私たちは文法など、CSS3スタイルは、ES6の使用を保証することができたときに、Webページを開発

私はいくつかのトップのAndroidのWebViewを導入するには、以下のモジュールに分割されます

WebViewのいくつかの方法、独自の

  //方式1. 加载一个网页:
  webView.loadUrl("http://www.google.com/");

  //方式2:加载apk包中的html页面
  webView.loadUrl("file:///android_asset/test.html");

  //方式3:加载手机本地的html页面
   webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");

通常の状況、WebViewのインターフェイスの下では、ユーザが直接ページを終了するには[戻る]ボタンをクリックすると、コースは我々が望むものではありません、私たちが望むの前後に自分のWebページであるので、以下では前後にいくつかのAPIのページです

//判断是否可以后退
Webview.canGoBack() 
//后退网页
Webview.goBack()

//判断是否可以前进                     
Webview.canGoForward()
//前进网页
Webview.goForward()

// 参数传负的话表示后退,传正值的话表示的是前进
Webview.goBackOrForward(int steps) 
对返回键的监听,来实现网页的后退
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { 
        mWebView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

メモリリークのWebViewのを防ぐ方法

メモリリークを防ぐ原理は、短い寿命の長いライフサイクルは再生されません。メモリリークが発生することはありませんWebViewのを防止するために、

  • 内部のXMLのWebViewに定義されていますが、活動中のApplicationContextコンテキストを構築し、使用するためのコードを使用して選択しないでください
  • 破壊時の活動は、WebViewのは空をロードした後、再rootViewは、WebViewのを破壊し、最後の空白をWebViewのを削除し、してみましょう
override fun onDestroy() {
        if (webView != null) {
            webView!!.loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
            webView!!.clearHistory()
            (webView!!.parent as ViewGroup).removeView(webView)
            webView!!.destroy()
            webView = null
        }
        super.onDestroy()

    }

WebSetting和WebViewClient、WebChromeClient

  • WebSetting

役割:WebViewのを構成および管理するには

WebSettings webSettings = webView.getSettings();
// 设置可以与js交互,为了防止资源浪费,我们可以在Activity
// 的onResume中设置为true,在onStop中设置为false
webSettings.setJavaScriptEnabled(true); 

//设置自适应屏幕,两者合用
//将图片调整到适合webview的大小 
webSettings.setUseWideViewPort(true); 
 // 缩放至屏幕的大小
webSettings.setLoadWithOverviewMode(true);

//设置编码格式
webSettings.setDefaultTextEncodingName("utf-8");

// 设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

//设置缓存的模式
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);

キャッシュに設定します。
HTMLページをロードするときに、WebViewのは、/データ/データ/ディレクトリデータベースとパッケージ名に2つのキャッシュフォルダを生成WebViewCache.dbに保たれたレコードにURLリクエスト、およびコンテンツのURLは、WebViewCacheに格納されていますフォルダ

缓存模式如下:
 //LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
 //LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取据。
 //LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
 //LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或no-cache,都使用缓存中的数据。

オフラインロード

if (NetStatusUtil.isConnected(getApplicationContext())) {
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}

webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
webSettings.setDatabaseEnabled(true);   //开启 database storage API 功能
webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能

String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录
  • 通知を処理するために、要求イベント、説明の最後には、ページのロードを開始、があり、ロードエラー(例えば、404)、プロセスHTTPS要求、次のコードの特定の使用を参照してください、明確なノート:WebViewClientの役割
webView!!.webViewClient = object : WebViewClient() {
    // 启用WebView,而不是系统自带的浏览器
            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
                view.loadUrl(url)
                return true
            }

// 页面开始加载,我们可以在这里设置loading
            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                super.onPageStarted(view, url, favicon)
                tv_start.text = "开始加载了..."
            }
// 页面加载结束,关闭loading
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                tv_end.text = "加载结束了..."
            }

            // 只要加载html,js,css的资源,每次都会回调到这里
            override fun onLoadResource(view: WebView?, url: String?) {
                loge("onLoadResource invoked")
            }

// 在这里我们可以加载我们自己的404页面
            override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
                loge("加载错误:${error.toString()}")
            }

// webview默认设计是不开启https的,下面的设置是允许使用https
            override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
                handler?.proceed()
            }

            // js调用Android的方法,在这里可以,该方法不存在通过注解的方式的内存泄漏,但是想拿到Android的返回值的话很难,
            // 可以通过Android调用js的代码的形式来传递返回值,例如下面的方式
            // Android:MainActivity.java
            //  mWebView.loadUrl("javascript:returnResult(" + result + ")");
            // JS:javascript.html
            //  function returnResult(result){
            //    alert("result is" + result);
            //   }

            override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
                val uri = Uri.parse(request?.url.toString())
                // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
                //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
                if (uri.scheme == "js") {
                    if (uri.authority == "webview") {
                        toast_custom("js调用了Android的方法")
                        val queryParameterNames = uri.queryParameterNames
                        queryParameterNames.forEach {
                            loge(it + ":" + uri.getQueryParameter(it))
                        }
                    }
                    return true
                }
                return super.shouldOverrideUrlLoading(view, request)
            }

            // 拦截资源 通常用于h5的首页页面,将常用的一些资源,放到本地
            override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
                if(request?.url.toString().contains("logo.gif")){
                    var inputStream: InputStream? = null
                    inputStream = applicationContext.assets.open("images/test.png")
                    return WebResourceResponse("image/png","utf-8", inputStream)
                }
                return super.shouldInterceptRequest(view, request)
            }
        }

注意:上記のデフォルト5.1は、HTTPとHTTPSのミックスを禁止し、以下の設定がオンになっています

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
  • WebChromeClientの役割約コールバックメソッドの補助のWebViewが進行ページの読み込みを提供することができ、ページのタイトル、ページのアイコンは、いくつかの爆弾ボックス、コードで直接見て、明確なノートをJS:
webView!!.webChromeClient = object : WebChromeClient() {

            // 网页加载的进度
            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                tv_progress.text = "$newProgress%"
            }

// 获得网页的标题
            override fun onReceivedTitle(view: WebView?, title: String?) {
                tv_title.text = title
            }

//js Alert
            override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {

                AlertDialog.Builder(this@WebActivity)
                    .setTitle("JsAlert")
                    .setMessage(message)
                    .setPositiveButton("OK") { _, _ -> result?.confirm() }
                    .setCancelable(false)
                    .show()
                return true
            }

// js Confirm
            override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
                return super.onJsConfirm(view, url, message, result)
            }

//js Prompt
            override fun onJsPrompt(
                view: WebView?,
                url: String?,
                message: String?,
                defaultValue: String?,
                result: JsPromptResult?
            ): Boolean {
                return super.onJsPrompt(view, url, message, defaultValue, result)
            }


        }

Androidとの相互作用のjs

Androidのは、JS呼び出す
loadUrlのWebViewの1.を

注:WebViewのがロードされた後に、このモードを呼び出すことができ、それはonPageFinished()コールバックメソッドのwebviewClientであり、実行の方法は、リフレッシュインターフェース、あまり効率的であろう

js代码:
function callJs(){
    alert("Android 调用了 js代码)
}
kotlin代码:
webView?.loadUrl("javascript:callJs()")

2. evaluateJavaScriptのWebViewの

第一の方法に比べて、より効率的な、しかし4.4の後に使用することができます

js代码:
function callJs(){
   //  alert("Android 调用了 js代码)
    return {name:'wfq',age:25}
}
kotlin代码:
webView?.evaluateJavascript("javascript:callJs()") {
    // 这里直接拿到的是js代码的返回值
            toast(it) // {name:'wfq',age:25}
        }

jsがAndroidの呼び出し
のaddJavaScriptInterfaceのWebViewの1.オブジェクトマッピングを

、使いやすい短所:我々は、すべてのメソッドは、内部活性の、例えば、利点は、直接以下に定義する、相互作用は全てもちろん、内部直接アクティビティを記述することができ、このクラスに書き込むことができる必要が単一のクラスを定義することができる抜け穴が存在する(4.2前に)、以下「いくつかの抜け穴とどのようにWebViewのを防ぐために」を考えます

kotlin中定义被js调用的方法
 @JavascriptInterface
fun hello(name: String) {
    toast("你好,我是来自js的消息:$msg")
}
js代码
function callAndroid(){
    android.hello("我是js的,我来调用你了")
}
kotlin中们在webview里面设置Android与js的代码的映射
webView?.addJavascriptInterface(this, "android")

URLを傍受する2. shouldOverrideUrlLoadingコールバックwebviewClient

具体的な使用:監視対象は、適切なメソッドを呼び出して、事前に合意された契約である場合、URLは、プロトコルを解析します。比較的安全な、しかし、使用が面倒、Androidのjsはパラメータのみを通じてバック渡すことによって、上記の戻り値を実行する()jsのコードloadurlを通じて、多くの問題の戻り値を取得します

首先在js中约定号协议
function callAndroid(){
        // 约定的url协议为:js://webview?name=wfq&age=24
        document.location = "js://webview?name=wfq&age=24"
     }
在kotlin里面,当loadurl的时候就会回调到shouldOverrideUrlLoading()里面

override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
            val uri = Uri.parse(request?.url.toString())
            // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
            //假定传入进来的 js://webview?name=wfq&age=24
            if (uri.scheme == "js") {
                if (uri.authority == "webview") {
                    toast_custom("js调用了Android的方法")
                    val queryParameterNames = uri.queryParameterNames
                    queryParameterNames.forEach {
                        loge(it + ":" + uri.getQueryParameter(it))
                    }
                }
                return true
            }
            return super.shouldOverrideUrlLoading(view, request)
        }

onJsConfirmのonJsAlert webChromeClient、ダイアログを傍受するonJsPromptコールバック3

それは任意の値を返すことができるので、jsのダイアログボックスを傍受することによって、彼らのニュースを取得し、セキュリティのために、我々は上記のコンテンツURLプロトコルを使用することをお勧めします、あなたが解析できる、言葉が一般的に、傍受プロンプトを傍受するために使用され、警告なし戻り値は、のみを確認していませんあなたは二つのタイプ、OKを返すと、取り消すことができます

js代码
function clickprompt(){
    var result=prompt("wfq://demo?arg1=111&arg2=222");
    alert("demo " + result);
}
kotlin代码
override fun onJsPrompt(
            view: WebView?,
            url: String?,
            message: String?,
            defaultValue: String?,
            result: JsPromptResult?
        ): Boolean {
            val uri = Uri.parse(message)
            if (uri.scheme == "wfq") {
                if (uri.authority == "demo") {
                    toast_custom("js调用了Android的方法")
                    val queryParameterNames = uri.queryParameterNames
                    queryParameterNames.forEach {
                        loge(it + ":" + uri.getQueryParameter(it))
                    }
                    // 将需要返回的值通过该方式返回
                    result?.confirm("js调用了Android的方法成功啦啦啦啦啦")
                }
                return true
            }
            return super.onJsPrompt(view, url, message, defaultValue, result)
        }

由于拦截了弹框,所以js代码的alert需要处理 这里的message便是上面代码的返回值通过alert显示出来的信息
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
            AlertDialog.Builder(this@WebActivity)
                .setTitle("JsAlert")
                .setMessage(message)
                .setPositiveButton("OK") { _, _ -> result?.confirm() }
                .setCancelable(false)
                .show()
            return true
        }

上記の3つの方法の違い:
addJavascriptInterface()簡単で便利な、4.0、@JavascriptInterfaceコメントを通じて4.0やバグフィックスの抜け穴がある
WebViewClient.shouldOverrideUrlLoading()コールバックは、脆弱性は、プロトコルを定義された制約を必要とし、複合体の使用は存在しませんが、戻り値の戻り値を必要とせず、トラブルのいくつかは、このような方法で使用することができます
何の脆弱性が存在しないonJsAlerta WebChromeClient、onJsConfirm、onJsPrompt、複合体の使用により、契約の結合の必要性は、あなたはほとんどの場合、お互いを満たすために値を返すことができますチューン通信

いくつかの脆弱性とどのようにWebViewのを防ぐために

パスワードはプレーンテキストの脆弱性に保存されています

保存機能WebViewのパスワードは、ユーザが入力した後、パスワードプロンプトボックスは、パスワードを保存するパスワードを保存するかどうかをユーザーに尋ねるポップアップ表示されます、デフォルトで有効になっていますがプレーンテキストで保存されている
以下の/data/data/com.package.name/databases/webview.db、電話ルートを表示することができ、その後、どのように解決するには?

WebSettings.setSavePassword(false) // 关闭密码保存提醒功能

WebViewの任意のコードが実行される脆弱性

すべての最初のaddJavascriptInterface抜け穴は、jsのAndroidのコードを呼び出す、それを理解するために、私たちはしばしば使用addJavascriptInterfaceがあり、
JSは、Androidの一つの方法は、今、このオブジェクトを取得し、それAndroid4.2前addJavascriptInterfaceオブジェクトマッピングインターフェイスを介してである呼び出しますこのオブジェクトのすべてのメソッドは、後に呼び出されるJS関数は、脆弱性を回避するために注釈を追加し、4.2の後に呼び出し可能です@JavascriptInterface

それでは、どのように解決します

对于Android 4.2以前,需要采用拦截prompt()的方式进行漏洞修复
对于Android 4.2以后,则只需要对被调用的函数以 @JavascriptInterface进行注解

コントロールは、厳密には、ドメインの脆弱性ではありません

  • 分析
    我々は内部Applilcation、アンドロイド:アプリはBアプリケーション導出活動ロードurlでプライベート文書Bアプリケーション内で取得することができます悪意のあるファイルプロトコルを、Bアプリケーションをすることができたときに、=「true」を輸出していますデータ漏洩の脅威をもたらします

)(安全WebViewの上、以下の方法のWebViewクラスgetSettings setAllowFileAccess影響が見

// 设置是否允许 WebView 使用 File 协议
// 默认设置为true,即允许在 File 域下执行任意 JavaScript 代码
webView.getSettings().setAllowFileAccess(true);     
如果设置为false的话,便不会存在威胁,但是,webview也无法使用本地的html文件

setAllowUniversalAccessFromFileURLs()

// 设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
// 在Android 4.1前默认允许(setAllowFileAccessFromFileURLs()不起作用)
// 在Android 4.1后默认禁止
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);

WebViewの事前ロードされ、あらかじめロードされたリソース

なぜ私がプリロードする必要があります

ゆっくりH5ページが読み込まれ、ゆっくりられる理由:スローページのレンダリングを、リソースの読み込みが遅いです

どのように最適化するには?

H5キャッシング、プリロードリソース、リソース傍受

  • AndroidのWebViewのブラウザのキャッシュバッファ1が付属していますH5キャッシュ
根据 HTTP 协议头里的 Cache-Control(或 Expires)和 Last-Modified(或 Etag)等字段来控制文件缓存的机制
浏览器自己实现,我需我们处理

アプリのキャッシュ

方便构建Web App的缓存,存储静态文件(如JS、CSS、字体文件)
 WebSettings settings = getSettings();
 String cacheDirPath = context.getFilesDir().getAbsolutePath()+"cache/";
 settings.setAppCachePath(cacheDirPath);
 settings.setAppCacheMaxSize(20*1024*1024);
 settings.setAppCacheEnabled(true);

ドムストレージ

    WebSettings settings = getSettings();
    settings.setDomStorageEnabled(true);

インデックス付きデータベース

 // 只需设置支持JS就自动打开IndexedDB存储机制
 // Android 在4.4开始加入对 IndexedDB 的支持,只需打开允许 JS 执行的开关就好了。
WebSettings settings = getSettings();
settings.setJavaScriptEnabled(true);
  • リソースプリロード
    初期化後、WebViewのがリリースされているが、一部の共有オブジェクトのWebViewがまだ存在していても、我々はアプリケーション内で事前にWebViewのを開始することができます。プリロードのWebViewオブジェクトが最初のWebViewが第二の理由より遅い多くの時間となります初期化されていますオブジェクトが、直接loadurlリソースをロードすることができます
  • リソースが検出された場合、取る直接ローカルリソースを交換することができ、ローカル、H5インターセプトリソース要求とネットワークのテストに新しい低周波といくつかの静的リソースファイルを傍受することができます
// 拦截资源 通常用于h5的首页页面,将常用的一些资源,放到本地
            override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {

                if(request?.url.toString().contains("logo.jpg")){
                    var inputStream: InputStream? = null
                    inputStream = applicationContext.assets.open("images/test.jpg")
                    return WebResourceResponse("image/png","utf-8", inputStream)
                }

                return super.shouldInterceptRequest(view, request)
            }

一般的な使用上の注意:

Android9.0、使用のWebViewのhttpを禁止している、どのように解決するには?
アンドロイド:マニフェスト以下のアプリケーションタグusesCleartextTraffic =「true」の
混乱の後にオープン、AndroidはH5と対話することはできませんか?

#保留annotation, 例如 @JavascriptInterface 等 annotation
-keepattributes *Annotation*

#保留跟 javascript相关的属性
-keepattributes JavascriptInterface

#保留JavascriptInterface中的方法
-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}
#这个类是用来与js交互,所以这个类中的 字段 ,方法, 不能被混淆、全路径名称.类名
-keepclassmembers public class com.youpackgename.xxx.H5CallBackAndroid{
   <fields>;
   <methods>;
   public *;
   private *;
}

どのようにデバッグするには?
1. WebViewActivity内側に、デバッグをオンに

// 开启调试
WebView.setWebContentsDebuggingEnabled(true)

2.chromeブラウザのアドレスバー、クロムを入力します。検査//

3.携帯電話のUSBデバッグ、オープンWebViewのページには、彼らはWeb開発に入ることができるように、クロムページを検査下のをクリックして、コンソール、ネットワーク要求などを参照してください

おすすめ

転載: blog.csdn.net/Android_SE/article/details/91554686
おすすめ