WebView

//https://github.com/youlookwhat/WebViewStudy

基本使用脑图

webView的基本使用

配置权限

<uses-permission android:name="android.permission.INTERNET"/>
复制代码

创建webView

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout android:layout_width="match_parent"
                    android:layout_height="match_parent">

        <RelativeLayout
            android:id="@+id/rl_head"
            android:layout_width="match_parent"
            android:layout_height="?android:actionBarSize"
            android:background="@color/colorAccent">

            <ImageView
                android:id="@+id/iv_back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:padding="10dp"
                android:src="@drawable/sel_back"/>

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:ellipsize="end"
                android:maxLength="200"
                android:maxLines="1"
                android:text="标题"/>
        </RelativeLayout>

        <ProgressBar
            android:id="@+id/progressBar"
            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/rl_head"/>

        <WebView android:id="@+id/webView"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_below="@id/progressBar"></WebView>

    </RelativeLayout>
</layout>
复制代码

配置webView

WebSettings ws = webView.getSettings();
// 网页内容的宽度是否可大于WebView控件的宽度
ws.setLoadWithOverviewMode(false);
// 保存表单数据
ws.setSaveFormData(true);
// 是否应该支持使用其屏幕缩放控件和手势缩放
ws.setSupportZoom(true);
ws.setBuiltInZoomControls(true);
ws.setDisplayZoomControls(false);
// 启动应用缓存
ws.setAppCacheEnabled(true);
// 设置缓存模式
ws.setCacheMode(WebSettings.LOAD_DEFAULT);
// setDefaultZoom  api19被弃用
// 设置此属性,可任意比例缩放。
ws.setUseWideViewPort(true);
// 不缩放
webView.setInitialScale(100);
// 告诉WebView启用JavaScript执行。默认的是false。
ws.setJavaScriptEnabled(true);
//  页面加载好以后,再放开图片
ws.setBlockNetworkImage(false);
// 使用localStorage则必须打开
ws.setDomStorageEnabled(true);
// 排版适应屏幕
ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
// WebView是否新窗口打开(加了后可能打不开网页)
//        ws.setSupportMultipleWindows(true);

// webview从5.0开始默认不允许混合模式,https中不能加载http资源,需要设置开启。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    ws.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
/** 设置字体默认缩放大小(改变网页字体大小,setTextSize  api14被弃用)*/
ws.setTextZoom(100);

//如果想显示加载进度的话,可以给webView 设置setWebChromeClient,并重写onProgressChanged()方法,至于onReceivedTitle()这个方法是拿到网页加载中的 标题
mWebChromeClient = new MyWebChromeClient(this);
webView.setWebChromeClient(mWebChromeClient);

//与js交互
webView.addJavascriptInterface(new MyJavascriptInterface(this), "injectedObject");

// 如果想在当前客户端打开浏览器的话,可以给webView 设置 setWebViewClient,并重写 shouldOverrideUrlLoading这个方法,返回true,在当前 webView 打开,否则在浏览器中打开。
webView.setWebViewClient(new MyWebViewClient(this));

webView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        return handleLongClick();
    }
});
复制代码

想大多数APP的做法一样,如微信,按下返回键,只是想后退,并不是想销毁Activity,我们可以这样做,重写 Activity的 onKeyDown()方法 ,并监听按下的键,采取 相应的 操作。

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
        mWebView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}
复制代码

既然有后退操作,当然也有前进操作

//是够能够前进
mWebView.canGoForward();
//前进
mWebView.goForward();
复制代码

调用该方法开始加载网页

mWebView.loadUrl(mUrl);
复制代码

webView 配置缓存

缓存模式主要有一下几种:

  • LOAD_CACHE_ELSE_NETWORK Use cached resources when they are available, even if they have expired.(如果本地有缓存,优先使用 本地 缓存,即使已经过期了 )

  • LOAD_CACHE_ONLY Don't use the network, load from the cache.(只使用本地 缓存)

  • LOAD_DEFAULT Default cache usage mode.(默认的缓存 模式)

  • LOAD_NORMAL This constant was deprecated in API level 17. This value is obsolete, as from API level HONEYCOMB and onwards it has the same effect as LOAD_DEFAULT.

  • LOAD_NO_CACHE Don't use the cache, load from the network.(不使用 本地缓存 )

我们可以通过以下方法设置缓存模式

WebSettings settings = mWebView.getSettings();
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
复制代码

webView请求错误时候的处理

因为系统自带的 错误页面太丑了,所以我们经常会对其 进行处理,目前本人了解到的主要有两种方法

  • 加载本地的控件,显示 错误信息
  • 加载自己 定义的 html页面
加载本地的控件
@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
        failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);
    if (errorCode == 404) {
        //用javascript隐藏系统定义的404页面信息
        //String data = "Page NO FOUND!";
       // view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
        mWebView.setVisibility(View.INVISIBLE);
        mErrorView.setVisibility(View.VISIBLE);
        
    }
}
复制代码
加载 定义的 html页面
@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
        failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);
    if (errorCode == 404) {
        //用javascript隐藏系统定义的404页面信息
        String data = "Page NO FOUND!";
        view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
    }
}
复制代码

当然实际开发中为了给用户比较还要的体验,会做非常多的处理,包括有网络情况和没有网络情况的处理,对于没有网络情况的处理,这里我们跳转到打开WiFi界面,而对于有网络情况的处理,这里我们只处理404错误,其他错误请根据项目的需求自行处理。

@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
        failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);
    //  没有网络连接
    if (false == APP.getInstance().isConnected()) {
        APP.getInstance().showWifiDlg(NewsDetailActivity.this);
    } else {
        if (errorCode == 404) {
            //用javascript隐藏系统定义的404页面信息
            String data = "Page NO FOUND!";
            view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
            mWebView.setVisibility(View.INVISIBLE);
        } else {//其他状态码错误的处理,这里就不罗列出来了

        }
    }
}
复制代码

webView cookie的同步与清除

  • 怎样获取cookie
  • 怎样将cookie与webView进行 同步
获取cookie
HttpURLConnection
URLConnection con= (HttpURLConnection) url.openConnection(); 
// 取得sessionid. 
String cookieval = con.getHeaderField("set-cookie"); 
String sessionid; 
if(cookieval != null) { 
    sessionid = cookieval.substring(0, cookieval.indexOf(";")); 
}
复制代码

发送设置cookie:

URL url = new URL(requrl);
HttpURLConnectioncon= (HttpURLConnection) url.openConnection(); 
if(sessionid != null) { 
   con.setRequestProperty("cookie", sessionid); 
}
复制代码
使用Retrofit
Call<ResponseBody> call = tnGouAPi.getTest(test);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        ResponseBody body = response.body();
        Headers headers = response.headers();
        Set<String> names = headers.names();
        for(String  key:names){
            String value = headers.get(key);
        }


        try {
            Logger.i("onResponse:   body=" + body.string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Logger.i("onResponse:   t=" + t.getMessage());
    }
});
复制代码
cookie 同步
/**
 * cookie同步 
 */
@SuppressWarnings("deprecation")
private void syncCookieToWebView(String url,List<String> cookies)
{
    CookieSyncManager.createInstance(this);
    CookieManager cm = CookieManager.getInstance();
    cm.setAcceptCookie(true);
    if(cookies!=null)
    {
        for (String cookie : cookies)
        {
            cm.setCookie(url,cookie);//注意端口号和域名,这种方式可以同步所有cookie,包括sessionid 
        }
    }
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
        CookieManager.getInstance().flush();
    } else {
        CookieSyncManager.getInstance().sync();
    }
}
复制代码
清除cookie
@SuppressWarnings("deprecation")
public void clearCookies(Context context) {
    CookieSyncManager.createInstance(context);
    CookieManager cookieManager = CookieManager.getInstance();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        cookieManager.removeAllCookies(null);
    } else {
        cookieManager.removeAllCookie();
    }
}
复制代码

同步cookie要在WebView加载url之前,否则WebView无法获得相应的cookie,也就无法通过验证。

每次登录成功后都需要调用”syncCookie”方法将cookie同步到WebView中,同时也达到了更新WebView的cookie。如果登录后没有及时将cookie同步到WebView可能导致WebView拿的是旧的session id和服务器进行通信。

webView 下载文件的两种方法

  • 自己实现实现逻辑 ,下载,保存到相应目录;
  • 调用系统的下载方法

主要是给webView设置DownloadListener监听器

mWebView.setDownloadListener(new DownloadListener() {
    @Override
    public void onDownloadStart(String url, String userAgent, String contentDisposition,
                                String mimetype, long contentLength) {

        //第一种下载方式是 自定义的http工具类
//                new HttpDownloadThread(url,contentDisposition,mimetype,contentLength).start();
        //第二种下载方式是调用系统的webView,具有默认的进度条
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse(url));
        startActivity(intent);
    }
});
复制代码

HttpDownloadThread

public class HttpDownloadThread extends Thread {


    private String mUrl;
    private String mContentDisposition;
    private String mMimetype;
    private long mContentLength;

    public HttpDownloadThread(String url, String contentDisposition, String mimetype, long contentLength) {
        this.mUrl = url;
        this.mContentDisposition=contentDisposition;
        this.mContentDisposition=mimetype;
        this.mContentDisposition=contentDisposition;

    }

    @Override
    public void run() {
        URL url;
        try {
            url = new URL(mUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setDoInput(true);
            conn.setDoOutput(true);
            InputStream in = conn.getInputStream();

            File downloadFile;
            File sdFile;
            FileOutputStream out = null;
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_UNMOUNTED)){
                downloadFile = Environment.getExternalStorageDirectory();
                sdFile = new File(downloadFile, "test.file");
                out = new FileOutputStream(sdFile);
            }

            //buffer 4k
            byte[] buffer = new byte[1024 * 4];
            int len = 0;
            while((len = in.read(buffer)) != -1){
                if(out != null)
                    out.write(buffer, 0, len);
            }

            //close resource
            if(out != null)
                out.close();

            if(in != null){
                in.close();
            }



        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
复制代码

提高Android WebView运行效率

1、提高渲染的优先级 webView.getSettings().setRenderPriority(RenderPriority.HIGH);

2、使把图片加载放在最后来加载渲染

webView.getSettings().setBlockNetworkImage(true);

3、使用硬件加速,该功能在Android 3.0 (API level 11)才加入。

webView与JS交互

1、Java调用WebView里的js代码(传递参数)

2、WebView里的js代码调用Java本地方法(传递参数)

3、外部注入js代码

4、WebView长按事件

<html>
<head>
    <title>js调用android原生代码</title>
    <meta http-equiv="Content-Type" content="text/html;charset=gb2312">
    <meta id="viewport" name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,minimal-ui">
    <script type="text/javascript">
        function javacalljs(){
             document.getElementById("content").innerHTML +=
                 "<br\>java调用了js函数,无参";
        }

        <!--这里取到的是 android端传过来的数据-->
        function javacalljswithargs(data){
             document.getElementById("content").innerHTML +=
                 ("<br\>"+data);
        }

    </script>
</head>
<body>
    <br/><br/>
    <li><a onClick="window.injectedObject.startFunction()">点击调用java代码</a></li>
    <!--可以将android端传过来的数据,处理后,放在这里再传给android端-->
    <li><a onClick="window.injectedObject.startFunction('我是网页传出来的数据')">点击调用java代码并传递参数</a></li><br/>
<div id="content">内容显示</div>
</body>
</html>
复制代码

Java调用WebView里的js代码(传递参数)

// 告诉WebView启用JavaScript执行。默认的是false。 ws.setJavaScriptEnabled(true);

1、如果点击调用就直接执行就好:

// 无参数调用
webView.loadUrl("javascript:javacalljs()");
// 传递参数调用
webView.loadUrl("javascript:javacalljswithargs('" + "android传入到网页里的数据,有参" + "')");
复制代码

2、如果是显示后就调用,注意放在html显示完成之后

MyWebViewClient.java

@Override
public void onPageFinished(WebView view, String url) {
    // 无参数调用
    webView.loadUrl("javascript:javacalljs()");
    // 传递参数调用
    webView.loadUrl("javascript:javacalljswithargs('" + "android传入到网页里的数据,有参" + "')");
    super.onPageFinished(view, url);
}
复制代码
 /**
 * 4.4以上可用 evaluateJavascript 效率高
 */
private void load(String jsCode) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        evaluateJavascript(jsCode, null);
    } else {
        loadUrl(jsCode);
    }
}
复制代码

WebView里的js代码调用Java本地方法(传递参数)

这里有一个js点击方法:

<li><a onClick="window.injectedObject.startFunction()">点击调用java代码</a></li>
<li><a onClick="window.injectedObject.startFunction('我是网页传出来的数据')">点击调用java代码并传递参数</a></li>
复制代码

实现与js交互接口:

webView.addJavascriptInterface(new MyJavascriptInterface(this), "injectedObject");
复制代码

这里的"injectedObject"对应js里的"window.injectedObject.startFunction()",其中MyJavascriptInterface.java:

public class MyJavascriptInterface {
    private Context context;

    public MyJavascriptInterface(Context context) {
        this.context = context;
    }

    /**
     * 前端代码嵌入js:
     * imageClick 名应和js函数方法名一致
     *
     * @param src 图片的链接
     */
    @JavascriptInterface
    public void imageClick(String src) {
        Log.e("imageClick", "----点击了图片");
        Log.e("src", src);
    }

    /**
     * 前端代码嵌入js
     * 遍历<li>节点
     *
     * @param type    <li>节点下type属性的值
     * @param item_pk item_pk属性的值
     */
    @JavascriptInterface
    public void textClick(String type, String item_pk) {
        if (!TextUtils.isEmpty(type) && !TextUtils.isEmpty(item_pk)) {
            Log.e("textClick", "----点击了文字");
            Log.e("type", type);
            Log.e("item_pk", item_pk);
        }
    }

    /**
     * 网页使用的js,方法无参数
     */
    @JavascriptInterface
    public void startFunction() {
        Log.e("startFunction", "----无参");
    }

    /**
     * 网页使用的js,方法有参数,且参数名为data
     *
     * @param data 网页js里的参数名
     */
    @JavascriptInterface
    public void startFunction(String data) {
        Log.e("startFunction", "----有参" + data);
    }
}
复制代码

且对标里面的两个方法:

/**
 * 网页使用的js,方法无参数
 */
@JavascriptInterface
public void startFunction() {
    Log.e("startFunction", "----无参");
}

/**
 * 网页使用的js,方法有参数,且参数名为data
 *
 * @param data 网页js里的参数名
 */
@JavascriptInterface
public void startFunction(String data) {
    Log.e("startFunction", "----有参" + data);
}
复制代码

外部注入js代码

有时候我们得到一个网页,这个网页并不是我们订制的,里面没有我们调用的js代码,这时候我们可在前端直接注入js的。 比如 网页里面有图片,我们点击图片想要查看图片和保存图片到本地,这时候网页并没有实现这个js方法供我们调用,但是我们可以注入js代码,来实现这个需求。 在html加载完成之后,我们调用这段js:

// 这段js函数的功能就是,遍历所有的img节点,并添加onclick函数,函数的功能是在图片点击的时候调用本地java接口并传递url过去
webView.loadUrl("javascript:(function(){" +
        "var objs = document.getElementsByTagName(\"img\");" +
        "for(var i=0;i<objs.length;i++)" +
        "{" +
        "objs[i].onclick=function(){window.injectedObject.imageClick(this.getAttribute(\"src\"));}" +
        "}" +
        "})()");
复制代码

这时候再看MyJavascriptInterface.java里的imageClick方法:

/**
 * 前端代码嵌入js:
 * imageClick 名应和js函数方法名一致
 *
 * @param src 图片的链接
 */
@JavascriptInterface
public void imageClick(String src) {
    Log.e("imageClick", "----点击了图片");
    Log.e("src", src);
}
复制代码

src即为图片链接,可以作为显示和下载用。

WebView长按事件

webView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        final WebView.HitTestResult hitTestResult = webView.getHitTestResult();
        // 如果是图片类型或者是带有图片链接的类型
        if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE ||
                hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
            // 弹出保存图片的对话框
            new AlertDialog.Builder(WebViewActivity.this)
                    .setItems(new String[]{"查看大图", "保存图片到相册"}, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            String picUrl = hitTestResult.getExtra();
                            //获取图片
                            Log.e("picUrl", picUrl);
                            switch (which) {
                                case 0:
                                    break;
                                case 1:
                                    break;
                                default:
                                    break;
                            }
                        }
                    })
                    .show();
            return true;
        }
        return false;
    }
});
复制代码

应用被作为第三方浏览器打开

在Manifest文件中,给想要接收跳转的Activity添加配置:

<activity
      android:name=".WebViewActivity"
      android:configChanges="orientation|screenSize"
      android:hardwareAccelerated="true"
      android:launchMode="singleTask"
      android:screenOrientation="portrait"
      android:theme="@style/WebViewTheme">

      <!--需要添加下面的intent-filter配置-->
      <intent-filter tools:ignore="AppLinkUrlError">
          <action android:name="android.intent.action.VIEW" />

          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <!--使用http,则只能打开http开头的网页-->
          <data android:scheme="https" />
      </intent-filter>

  </activity>
复制代码

然后在 WebViewActivity 中获取相关传递数据:

public class WebViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);
        getIntentData();
        initTitle();
        initWebView();
        webView.loadUrl(mUrl);
       // 处理 作为三方浏览器打开传过来的值
        getDataFromBrowser(getIntent());
    }

   /**
     * 使用singleTask启动模式的Activity在系统中只会存在一个实例。
     * 如果这个实例已经存在,intent就会通过onNewIntent传递到这个Activity。
     * 否则新的Activity实例被创建。
     */
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        getDataFromBrowser(intent);
    }

    /**
     * 作为三方浏览器打开传过来的值
     * Scheme: https
     * host: www.jianshu.com
     * path: /p/1cbaf784c29c
     * url = scheme + "://" + host + path;
     */
    private void getDataFromBrowser(Intent intent) {
        Uri data = intent.getData();
        if (data != null) {
            try {
                String scheme = data.getScheme();
                String host = data.getHost();
                String path = data.getPath();
                String text = "Scheme: " + scheme + "\n" + "host: " + host + "\n" + "path: " + path;
                Log.e("data", text);
                String url = scheme + "://" + host + path;
                webView.loadUrl(url);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

以上是核心代码,有几点需要说明的:

1、设置启动模式:launchMode="singleTask"

2、getDataFromBrowser()方法会在onCreate和onNewIntent方法中调用。

在实际使用中,有这样的场景: 我们在微信中“通过浏览器”打开自己的应用,然后将自己的应用切到后台。重复上面的操作,会一直创建应用的实例,这样肯定是不好的,为了避免这种情况我们设置启动模式为:launchMode="singleTask"。

使用singleTask启动模式的Activity在系统中只会存在一个实例。 如果这个实例不存在,新的Activity实例被创建。 如果这个实例已经存在,那么在Activity回退栈中,所有位于该Activity上面的Activity实例都将被销毁掉(销毁过程会调用Activity生命周期回调),这样使得singleTask Activity实例位于栈顶。与此同时,Intent会通过onNewIntent传递到这个SingleTask Activity实例。

也许大家用知乎进行过类似的操作,会发现有这样一个效果: 当知乎没有开启时,在其他应用打开知乎后,关闭页面会回到知乎的主页面。正常是不会有出现这样的情况的,我们需要简单设置一下。

1、在主页面设置静态变量,初始化时改变静态变量的值

public class MainActivity extends AppCompatActivity  {

    // 是否开启了主页,没有开启则会返回主页
    public static boolean isLaunch = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        isLaunch = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isLaunch = false;
    }
复制代码

2、当WebViewActivity关闭时判断是否打开主页面

 /**
     * 直接通过三方浏览器打开时,回退到首页
     */
    public void handleFinish() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            finishAfterTransition();
        } else {
            finish();
        }
        if (!MainActivity.isLaunch) {
            MainActivity.start(this);
        }
    }
复制代码

转载于:https://juejin.im/post/5cd500af6fb9a03246586d70

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值