jekeyhui99 发表于 2019-7-31 11:42:07

Android:一个实用的WebView浏览网页,可直接使用

<div><h1>1、前言</h1>
<p>无论是日常开发还是练习,相信网页显示是经常需要实现的业务场景,现在的应用一般有网页链接传入,都直接在自己的应用中显示,不会去跳转自带浏览器。今天,我们就来实现一个能满足基本网页浏览需求的页面。国际惯例<a href="https://link.jianshu.com?t=https%3A%2F%2Fgithub.com%2FLeviWGG%2FEnableHands" target="_blank" rel="nofollow">附上源码</a>。</p>
<h1>2、效果图</h1>
<div class="image-package">
<div class="image-container" style="max-width: 474px; max-height: 1168px; background-color: transparent;">
<div class="image-container-fill" style="padding-bottom: 166.88%;"><img data-original-src="//upload-images.jianshu.io/upload_images/5256137-860264953e2cf741.gif" data-original-width="474" data-original-height="791" data-original-format="image/gif" data-original-filesize="2488827" class="" src="//upload-images.jianshu.io/upload_images/5256137-860264953e2cf741.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/474/format/webp" style="background-color: transparent; cursor: zoom-in;"></div>
</div>
<div class="image-caption">WebView浏览.gif</div>
</div>
<p>从图中可以看到进入新闻网页后,除了不能输入网址(关注自己的业务),已经与普通浏览器浏览网页相同。<br>
下面上代码。</p>
<h1>3、代码</h1>
<blockquote>
<p>急用的同学下面可以直接ctrl c + ctrl v,如果想知道一些原理不妨接着往下看</p>
</blockquote>
<p><strong>1、添加访问网络权限(AndroidManifest.xml)</strong></p>
<pre class="hljs xml"><code class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.INTERNET"</span>/&gt;</span>
</code></pre>
<p><strong>2、页面布局</strong></p>
<pre class="hljs xml"><code class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RelativeLayout</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">xmlns:app</span>=<span class="hljs-string">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attr">xmlns:tools</span>=<span class="hljs-string">"http://schemas.android.com/tools"</span>
    <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
    <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"match_parent"</span>
    <span class="hljs-attr">tools:context</span>=<span class="hljs-string">"app.main.wangliwei.enablehands.view.WebDetailActivity"</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">FrameLayout</span>
      <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/web_frame"</span>
      <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
      <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"match_parent"</span>
      <span class="hljs-attr">android:focusable</span>=<span class="hljs-string">"true"</span>
      <span class="hljs-attr">android:focusableInTouchMode</span>=<span class="hljs-string">"true"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">FrameLayout</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">RelativeLayout</span>&gt;</span>
</code></pre>
<p><strong>3、WebDetailActivity.java</strong></p>
<pre class="hljs java"><code class="java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WebDetailActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppCompatActivity</span> </span>{
    <span class="hljs-keyword">private</span> Bundle bundle;
    <span class="hljs-keyword">private</span> WebView webView;
    <span class="hljs-keyword">private</span> FrameLayout frameLayout;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> </span>{
      <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
      setContentView(R.layout.activity_web_detail);

      frameLayout = findViewById(R.id.xwalk_view);
      bundle = getIntent().getExtras();
      initView();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initView</span><span class="hljs-params">()</span> </span>{
      webView = <span class="hljs-keyword">new</span> WebView(<span class="hljs-keyword">this</span>);
      WebSettings settings = webView.getSettings();
      settings.setDomStorageEnabled(<span class="hljs-keyword">true</span>);
      <span class="hljs-comment">//解决一些图片加载问题</span>
      settings.setJavaScriptEnabled(<span class="hljs-keyword">true</span>);
      settings.setBlockNetworkImage(<span class="hljs-keyword">false</span>);
      webView.setWebViewClient(<span class="hljs-keyword">new</span> WebViewClient(){
            <span class="hljs-meta">@Override</span>
            <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">shouldOverrideUrlLoading</span><span class="hljs-params">(WebView view, String url)</span> </span>{
                Log.d(<span class="hljs-string">"webview"</span>,<span class="hljs-string">"url: "</span>+url);
                view.loadUrl(url);
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            }
      });
      frameLayout.addView(webView);
      String url = bundle.getString(<span class="hljs-string">"URL"</span>);
      webView.loadUrl(url);
    }

    <span class="hljs-comment">//监听BACK按键,有可以返回的页面时返回页面</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onKeyDown</span><span class="hljs-params">(<span class="hljs-keyword">int</span> keyCode, KeyEvent event)</span> </span>{
      <span class="hljs-keyword">if</span>(keyCode == KeyEvent.KEYCODE_BACK) {
            <span class="hljs-keyword">if</span>(webView.canGoBack()) {
                webView.goBack();
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            }
      }

      <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.onKeyDown(keyCode, event);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDestroy</span><span class="hljs-params">()</span> </span>{
      <span class="hljs-keyword">if</span> (webView != <span class="hljs-keyword">null</span>) {
            webView.loadDataWithBaseURL(<span class="hljs-keyword">null</span>, <span class="hljs-string">""</span>, <span class="hljs-string">"text/html"</span>, <span class="hljs-string">"utf-8"</span>, <span class="hljs-keyword">null</span>);
            webView.setTag(<span class="hljs-keyword">null</span>);
            webView.clearHistory();

            ((ViewGroup) webView.getParent()).removeView(webView);
            webView.destroy();
            webView = <span class="hljs-keyword">null</span>;
      }
      <span class="hljs-keyword">super</span>.onDestroy();
    }
}
</code></pre>
<p>在浏览页面时主要做了两个处理,一个是对页面返回的链接(URL)做直接显示处理,由于我是实现一个新闻业务,所以在浏览过程中跳转到其他页面是必须的,重载<code>WebClientView</code>中的<code>shouldOverrideUrlLoading</code>方法。另一个是监听BACK按键,先用<code>canGoBack()</code>判断是否有可以回退的页面,如果有再调用<code>goBack()</code>返回上一个页面。</p>
<p><strong>4、WebView的几种加载方式</strong></p>
<pre class="hljs java"><code class="java"><span class="hljs-comment">//方式1. 加载一个网页:</span>
webView.loadUrl(<span class="hljs-string">"http://www.baidu.com/"</span>);

<span class="hljs-comment">//方式2:加载apk包中的html页面</span>
webView.loadUrl(<span class="hljs-string">"file:///android_asset/test.html"</span>);

<span class="hljs-comment">//方式3:加载手机本地的html页面</span>
webView.loadUrl(<span class="hljs-string">"content://com.android.levi/sdcard/test.html"</span>);

</code></pre>
<p><strong>5、Settings的常见设置</strong></p>
<pre class="hljs java"><code class="java">WebSettings settings=webView.getSettings();
      
<span class="hljs-comment">// webview启用javascript支持 用于访问页面中的javascript</span>
settings.setJavaScriptEnabled(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">//设置WebView缓存模式 默认断网情况下不缓存</span>
settings.setCacheMode(WebSettings.LOAD_DEFAULT);


<span class="hljs-comment">/**
   * LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
   * LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
   * LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
   * LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
*/</span>
<span class="hljs-comment">//断网情况下加载本地缓存</span>
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);}

<span class="hljs-comment">//让WebView支持DOM storage API</span>
settings.setDomStorageEnabled(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">//让WebView支持缩放</span>
settings.setSupportZoom(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">//启用WebView内置缩放功能</span>
settings.setBuiltInZoomControls(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">//让WebView支持可任意比例缩放</span>
settings.setUseWideViewPort(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">//让WebView支持播放插件</span>
settings.setPluginState(WebSettings.PluginState.ON);

<span class="hljs-comment">//设置WebView使用内置缩放机制时,是否展现在屏幕缩放控件上</span>
settings.setDisplayZoomControls(<span class="hljs-keyword">false</span>);

<span class="hljs-comment">//设置在WebView内部是否允许访问文件</span>
settings.setAllowFileAccess(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">//设置WebView的访问UserAgent</span>
settings.setUserAgentString(String string);

<span class="hljs-comment">//设置脚本是否允许自动打开弹窗</span>
settings.setJavaScriptCanOpenWindowsAutomatically(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">// 加快HTML网页加载完成速度 </span>
<span class="hljs-keyword">if</span> (Build.VERSION.SDK_INT &gt;= <span class="hljs-number">19</span>) {   
    settings.setLoadsImagesAutomatically(<span class="hljs-keyword">true</span>);
} <span class="hljs-keyword">else</span> {
    settings.setLoadsImagesAutomatically(<span class="hljs-keyword">false</span>);
}

<span class="hljs-comment">// 开启Application H5 Caches 功能 </span>
settings.setAppCacheEnabled(<span class="hljs-keyword">true</span>);

<span class="hljs-comment">// 设置编码格式</span>
settings.setDefaultTextEncodingName(<span class="hljs-string">"utf-8"</span>);

</code></pre>
<h1>3、跳转Activity</h1>
<p><strong>拿到链接(URL)以后可以这样开启浏览页面</strong></p>
<pre class="hljs java"><code class="java">   Bundle bundle = <span class="hljs-keyword">new</span> Bundle();
   bundle.putString(<span class="hljs-string">"URL"</span>,url);
   Intent intent = <span class="hljs-keyword">new</span> Intent(getActivity(),WebDetailActivity.class);
   intent.putExtras(bundle);
   startActivity(intent,bundle);
</code></pre>
<h1>4、关于内存泄漏</h1>
<p>每个应用在android中所占用的内存是有限制的,碰巧WebView又是内存大户,所以在关闭页面时,如果不及时释放内存,很可能导致OOM。</p>
<p>避免内存泄漏要做一些处理:<br>
不在xml中定义 Webview ,而是在需要的时候在Activity中创建,并且Context使用 <code>getApplicationgContext()</code>,因为WebView调用<code>destory</code>时,如果传入Activity的Context对象WebView会仍绑定在Activity上。在 Activity 销毁( WebView )的时候,先让 WebView 加载null内容,然后从<strong>父容器</strong>(<code>ViewGroup</code>)中移除 WebView,再销毁 WebView,最后置空。</p>
<blockquote>
<p>需要注意的是,如果你需要在WebView中打开链接或者你打开的页面带有flash,获得你的WebView想弹出一个dialog,都会导致从ApplicationContext到ActivityContext的强制类型转换错误,从而导致你应用崩溃。这是因为在加载flash的时候,系统会首先把你的WebView作为父控件,然后在该控件上绘制flash,他想找一个Activity的Context来绘制他,但是你传入的是ApplicationContext。</p>
</blockquote>
<p>解决方法:通常根治这个问题的办法是为WebView开启另外一个进程,通过AIDL与主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。因为webview引发的 资源无法释放等问题 全部可以解决。</p>
<h1>5、最后</h1>
<p>通过以上步骤还是可以实现一个满足基本浏览需求的页面的,注意内存泄漏,比较开发到后期,对内存的使用时越来也关注。如果对你有帮助,欢迎 star 支持下,<a href="https://link.jianshu.com?t=https%3A%2F%2Fgithub.com%2FLeviWGG%2FEnableHands" target="_blank" rel="nofollow">源码地址</a>,个人项目里面有一些其他东西,直接看WebDetailActivity中的内容即可。</p></div><br><br>作者:LeviWGG<br>链接:https://www.jianshu.com/p/e8eeec4e8d71<br>来源:简书<br>简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。<br>
页: [1]
查看完整版本: Android:一个实用的WebView浏览网页,可直接使用