第十一章 WebView

文章目录

(一)WebView使用基础

1、简介

WebView是一个基于webkit引擎、展现web页面的控件。

  • 显示和渲染Web页面
  • 直接使用html文件(网络上或本地assets中)作布局
  • 可和JavaScript交互调用

Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4前使用webkit,4.4后直接使用了Chrome。

由于H5具备 开发周期短、灵活性好 的特点,所以现在 Android App大多嵌入了 Android Webview 组件进行 Hybrid 开发

移动应用开发的3种方式比较:Native App:本地应用程序(原生App),Web App:网页应用程序(移动web),Hybrid App:混生应用程序(混生App)
在这里插入图片描述

2、使用

WebView可单独使用,也可联合其工具类一起使用

2.1)WebView类常用方法

2.1.1)加载url
  • 加载一个网页
  webView.loadUrl("http://www.google.com/");
  • 加载apk包中的html页面
  webView.loadUrl("file:///android_asset/test.html");
  • 加载手机本地的html页面
   webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
  • 加载 HTML 页面的一小段内容
  WebView.loadData(String data, String mimeType, String encoding)
  • 参数说明
// 参数1:需要截取展示的内容
// 内容里不能出现 ’#’, ‘%’, ‘\’ , ‘?’ 这四个字符,若出现了需用 %23, %25, %27, %3f 对应来替代,否则会出现异常
// 参数2:展示内容的类型
// 参数3:字节码
2.1.2)WebView状态
  • webView.onResume() ;
    激活WebView为活跃状态,能正常执行网页的响应
  • webView.onPause();
    当页面被失去焦点被切换到后台不可见状态,需要执行onPause,通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
  • webView.pauseTimers();
    当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview,它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
  • webView.resumeTimers();
    恢复pauseTimers状态。
  • rootLayout.removeView(webView);
    webView.destroy();
    销毁Webview。在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview,但是注意:webview调用destory时,webview仍绑定在Activity上,这是由于自定义webview构建时传入了该Activity的context对象。因此需要先从父容器中移除webview,然后再销毁webview。
2.1.3)关于前进/后退网页
//是否可以后退
Webview.canGoBack() 
//后退网页
Webview.goBack()

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

//以当前的index为起始点前进或者后退到历史记录中指定的steps
//如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps) 
2.1.4)清除缓存数据
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);

//清除当前webview访问的历史记录
//只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();

//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();

2.2)WebView常用工具类

2.2.1)WebSetting类

对WebView进行配置和管理

(1)配置
//声明WebSettings子类
WebSettings webSettings = webView.getSettings();

//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);  
// 若加载的 html 里有JS 在执行动画等操作,会造成资源浪费(CPU、电量)
// 在 onStop 和 onResume 里分别把 setJavaScriptEnabled() 给设置成 false 和 true 即可

//支持插件
webSettings.setPluginsEnabled(true); 

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

//缩放操作
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件

//其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存 
webSettings.setAllowFileAccess(true); //设置可以访问文件 
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
(2)设置缓存

当加载 html 页面时,WebView会在/data/data/包名目录下生成 database 与 cache 两个文件夹,请求的 URL记录保存在 WebViewCache.db,而 URL的内容是保存在 WebViewCache 文件夹下。

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

    //不使用缓存: 
    WebView.getSettings().setCacheMode(WebSettings.LOAD_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 缓存目录

注:每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()

2.2.2)WebViewClient

处理各种通知&请求事件

(1)shouldOverrideUrlLoading

打开网页时不调用系统浏览器, 而是在本WebView中显示;在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。

//步骤1. 定义Webview组件
Webview webview = (WebView) findViewById(R.id.webView1);

//步骤2. 选择加载方式
  webView.loadUrl("http://www.google.com/");

//步骤3. 复写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
    webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
          view.loadUrl(url);
      return true;
      }
  });
(2)onPageStarted

开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。

(3)onPageFinished

在页面加载结束时调用。我们可以关闭loading 条,切换程序动作。

(4)onLoadResource

在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。

(5)onReceivedError

加载页面的服务器出现错误时(如404)调用。

App里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面,即webview如何加载一个本地的页面

//步骤1:写一个html文件(error_handle.html),用于出错时展示给用户看的提示页面
//步骤2:将该html文件放置到代码根目录的assets文件夹下

//步骤3:复写WebViewClient的onRecievedError方法
//该方法传回了错误码,根据错误类型可以进行不同的错误分类处理
    webView.setWebViewClient(new WebViewClient(){
      @Override
      public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
switch(errorCode)
                {
                case HttpStatus.SC_NOT_FOUND:
                    view.loadUrl("file:///android_assets/error_handle.html");
                    break;
                }
            }
        });
(6)onReceivedSslError

处理https请求。webView默认是不处理https请求的,页面显示空白,需要进行如下设置:

webView.setWebViewClient(new WebViewClient() {    
        @Override    
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {    
            handler.proceed();    //表示等待证书响应
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
        }    
    });  

// 特别注意:5.1以上默认禁止了https和http混用,以下方式是开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
2.2.3)WebChronmeClient

辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。

(1)onProgressChanged

获得网页的加载进度并显示

webview.setWebChromeClient(new WebChromeClient(){

      @Override
      public void onProgressChanged(WebView view, int newProgress) {
          if (newProgress < 100) {
              String progress = newProgress + "%";
              progress.setText(progress);
            } else {
        }
    });
(2)onReceivedTitle

获取Web页中的标题

(3)onJsAlert/onJsConfirm/onJsPrompt

支持javascript的警告框\确认框\输入框

webview.setWebChromeClient(new WebChromeClient() {
            
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result)  {
    new AlertDialog.Builder(MainActivity.this)
            .setTitle("JsAlert")
            .setMessage(message)
            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            })
            .setCancelable(false)
            .show();
    return true;
            }

2.3)注意事项:如何避免WebView内存泄露

(1)不在xml中定义 WebView ,而是在需要的时候在Activity中创建,并且Context使用 getApplicationgContext()
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mWebView = new WebView(getApplicationContext());
        mWebView.setLayoutParams(params);
        mLayout.addView(mWebView);
(2) 在 Activity 销毁( WebView )的时候,先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。
@Override
    protected void onDestroy() {
        if (mWebView != null) {
            mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            mWebView.clearHistory();

            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
            mWebView.destroy();
            mWebView = null;
        }
        super.onDestroy();
    }

2.4)总结

3、实例

目标:实现显示“www.baidu.com”、获取其标题、提示加载开始 & 结束和获取加载进度

步骤1:添加访问权限

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

步骤2:主布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.carson_ho.webview_demo.MainActivity">


   <!-- 获取网站的标题-->
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>

    <!--开始加载提示-->
    <TextView
        android:id="@+id/text_beginLoading"
        android:layout_below="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>

    <!--获取加载进度-->
    <TextView
        android:layout_below="@+id/text_beginLoading"
        android:id="@+id/text_Loading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>

    <!--结束加载提示-->
    <TextView
        android:layout_below="@+id/text_Loading"
        android:id="@+id/text_endLoading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>
    
    <!--显示网页区域-->
    <WebView
        android:id="@+id/webView1"
        android:layout_below="@+id/text_endLoading"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginTop="10dp" />
</RelativeLayout>

步骤3:根据需要实现的功能从而使用相应的子类及其方法

MainActivity.java

public class MainActivity extends AppCompatActivity {
    WebView mWebview;
    WebSettings mWebSettings;
    TextView beginLoading,endLoading,loading,mtitle;

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


        mWebview = (WebView) findViewById(R.id.webView1);
        beginLoading = (TextView) findViewById(R.id.text_beginLoading);
        endLoading = (TextView) findViewById(R.id.text_endLoading);
        loading = (TextView) findViewById(R.id.text_Loading);
        mtitle = (TextView) findViewById(R.id.title);

        mWebSettings = mWebview.getSettings();

        mWebview.loadUrl("http://www.baidu.com/");

        
        //设置不用系统浏览器打开,直接显示在当前Webview
        mWebview.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });

        //设置WebChromeClient类
        mWebview.setWebChromeClient(new WebChromeClient() {


            //获取网站标题
            @Override
            public void onReceivedTitle(WebView view, String title) {
                System.out.println("标题在这里");
                mtitle.setText(title);
            }


            //获取加载进度
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                if (newProgress < 100) {
                    String progress = newProgress + "%";
                    loading.setText(progress);
                } else if (newProgress == 100) {
                    String progress = newProgress + "%";
                    loading.setText(progress);
                }
            }
        });


        //设置WebViewClient类
        mWebview.setWebViewClient(new WebViewClient() {
            //设置加载前的函数
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                System.out.println("开始加载了");
                beginLoading.setText("开始加载了");

            }

            //设置结束加载函数
            @Override
            public void onPageFinished(WebView view, String url) {
                endLoading.setText("结束加载了");

            }
        });
    }

    //点击返回上一页面而不是退出浏览器
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && mWebview.canGoBack()) {
            mWebview.goBack();
            return true;
        }

        return super.onKeyDown(keyCode, event);
    }

    //销毁Webview
    @Override
    protected void onDestroy() {
        if (mWebview != null) {
            mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            mWebview.clearHistory();

            ((ViewGroup) mWebview.getParent()).removeView(mWebview);
            mWebview.destroy();
            mWebview = null;
        }
        super.onDestroy();
    }
}

在这里插入图片描述

(二)WebView与JS交互方式

2.1)Android通过WebView调用JS代码

方式1:通过WebView的loadUrl()

点击Android按钮,即调用WebView JS(文本名为javascript)中callJS()

步骤1:将需要调用的JS代码以.html格式放到src/main/assets文件夹里

需要加载JS代码:javascript.html

// 文本名:javascript
<!DOCTYPE html>
<html>

   <head>
      <meta charset="utf-8">
      <title>Carson_Ho</title>
      
// JS代码
     <script>
// Android需要调用的方法
   function callJS(){
      alert("Android调用了JS的callJS方法");
   }
</script>

   </head>

</html>
步骤2:在Android里通过WebView设置调用JS代码

Android代码:MainActivity.java

public class MainActivity extends AppCompatActivity {

    WebView mWebView;
    Button button;

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

        mWebView =(WebView) findViewById(R.id.webview);

        WebSettings webSettings = mWebView.getSettings();

        // 设置与Js交互的权限
        webSettings.setJavaScriptEnabled(true);
        // 设置允许JS弹窗
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

        // 先载入JS代码
        // 格式规定为:file:///android_asset/文件名.html
        mWebView.loadUrl("file:///android_asset/javascript.html");

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 通过Handler发送消息
                mWebView.post(new Runnable() {
                    @Override
                    public void run() {

                        // 注意调用的JS方法名要对应上
                        // 调用javascript的callJS()方法
                        mWebView.loadUrl("javascript:callJS()");
                    }
                });
                
            }
        });

        // 由于设置了弹窗检验调用结果,所以需要支持js对话框
        // webview只是载体,内容的渲染需要使用webviewChromClient类去实现
        // 通过设置WebChromeClient对象处理JavaScript的对话框
        //设置响应js 的Alert()函数
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
                b.setTitle("Alert");
                b.setMessage(message);
                b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        result.confirm();
                    }
                });
                b.setCancelable(false);
                b.create().show();
                return true;
            }

        });


    }
}

特别注意:JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。
onPageFinished()属于WebViewClient类的方法,主要在页面加载结束时调用

方式2:通过WebView的evaluateJavascript()

// 只需要将第一种方法的loadUrl()换成下面该方法即可
    mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为 js 返回的结果
        }
    });
}

方式对比

在这里插入图片描述

使用建议

两种方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2

// Android版本变量
final int version = Build.VERSION.SDK_INT;
// 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断
if (version < 18) {
    mWebView.loadUrl("javascript:callJS()");
} else {
    mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为 js 返回的结果
        }
    });
}

2.2)JS通过WebView调用Android代码

方式1:通过 WebView的addJavascriptInterface()进行对象映射

步骤1:定义一个与JS对象映射关系的Android类:AndroidtoJs

AndroidtoJs.java

// 继承自Object类
public class AndroidtoJs extends Object {

    // 定义JS需要调用的方法
    // 被JS调用的方法必须加入@JavascriptInterface注解
    @JavascriptInterface
    public void hello(String msg) {
        System.out.println("JS调用了Android的hello方法");
    }
}
步骤2:将需要调用的JS代码以.html格式放到src/main/assets文件夹里

需要加载JS代码:javascript.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <title>Carson</title>  
      <script>

         function callAndroid(){
        // 由于对象映射,所以调用test对象等于调用Android映射的对象
            test.hello("js调用了android中的hello方法");
         }
      </script>
   </head>
   <body>
      //点击按钮则调用callAndroid函数
      <button type="button" id="button1" onclick="callAndroid()"></button>
   </body>
</html>
步骤3:在Android里通过WebView设置Android类与JS代码的映射
public class MainActivity extends AppCompatActivity {

    WebView mWebView;

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

        mWebView = (WebView) findViewById(R.id.webview);
        WebSettings webSettings = mWebView.getSettings();

        // 设置与Js交互的权限
        webSettings.setJavaScriptEnabled(true);

        // 通过addJavascriptInterface()将Java对象映射到JS对象
        //参数1:Javascript对象名
        //参数2:Java对象名
        mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS类对象映射到js的test对象

        // 加载JS代码
        // 格式规定为:file:///android_asset/文件名.html
        mWebView.loadUrl("file:///android_asset/javascript.html");

方式2:通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url

Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url,解析该 url 的协议,如果检测到是预先约定好的协议,就调用相应方法。

步骤1:在JS约定所需要的Url协议

JS代码:javascript.html,以.html格式放到src/main/assets文件夹里

<!DOCTYPE html>
<html>

   <head>
      <meta charset="utf-8">
      <title>Carson_Ho</title>
      
     <script>
         function callAndroid(){
            /*约定的url协议为:js://webview?arg1=111&arg2=222*/
            document.location = "js://webview?arg1=111&arg2=222";
         }
      </script>
</head>

<!-- 点击按钮则调用callAndroid()方法  -->
   <body>
     <button type="button" id="button1" onclick="callAndroid()">点击调用Android代码</button>
   </body>
</html>

当该JS通过Android的mWebView.loadUrl(“file:///android_asset/javascript.html”)加载后,就会回调shouldOverrideUrlLoading ()

步骤2:在Android通过WebViewClient复写shouldOverrideUrlLoading ()

MainActivity.java

public class MainActivity extends AppCompatActivity {

    WebView mWebView;
//    Button button;

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

        mWebView = (WebView) findViewById(R.id.webview);

        WebSettings webSettings = mWebView.getSettings();

        // 设置与Js交互的权限
        webSettings.setJavaScriptEnabled(true);
        // 设置允许JS弹窗
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

        // 步骤1:加载JS代码
        // 格式规定为:file:///android_asset/文件名.html
        mWebView.loadUrl("file:///android_asset/javascript.html");


// 复写WebViewClient类的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(new WebViewClient() {
                                      @Override
                                      public boolean shouldOverrideUrlLoading(WebView view, String url) {

                                          // 步骤2:根据协议的参数,判断是否是所需要的url
                                          // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
                                          //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)

                                          Uri uri = Uri.parse(url);                                 
                                          // 如果url的协议 = 预先约定的 js 协议
                                          // 就解析往下解析参数
                                          if ( uri.getScheme().equals("js")) {

                                              // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                                              // 所以拦截url,下面JS开始调用Android需要的方法
                                              if (uri.getAuthority().equals("webview")) {

                                                 //  步骤3:
                                                  // 执行JS所需要调用的逻辑
                                                  System.out.println("js调用了Android的方法");
                                                  // 可以在协议上带有参数并传递到Android上
                                                  HashMap<String, String> params = new HashMap<>();
                                                  Set<String> collection = uri.getQueryParameterNames();

                                              }

                                              return true;
                                          }
                                          return super.shouldOverrideUrlLoading(view, url);
                                      }
                                  }
        );
   }
        }

JS获取Android方法的返回值,只能通过 WebView 的 loadUrl ()去执行 JS 方法把返回值传递回去,相关的代码如下

// Android:MainActivity.java
mWebView.loadUrl("javascript:returnResult(" + result + ")");

// JS:javascript.html
function returnResult(result){
    alert("result is" + result);
}

方式3:通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

JS的三种常用对话框方法
在这里插入图片描述
Android通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调分别拦截JS对话框
(即上述三个方法),得到他们的消息内容,然后解析即可。

步骤1:加载JS代码

javascript.html,以.html格式放到src/main/assets文件夹里

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <title>Carson_Ho</title>
      
     <script>
        
    function clickprompt(){
    // 调用prompt()
    var result=prompt("js://demo?arg1=111&arg2=222");
    alert("demo " + result);
}

      </script>
</head>

<!-- 点击按钮则调用clickprompt()  -->
   <body>
     <button type="button" id="button1" onclick="clickprompt()">点击调用Android代码</button>
   </body>
</html>

当使用mWebView.loadUrl(“file:///android_asset/javascript.html”)加载了上述JS代码后,就会触发回调onJsPrompt()

如果是拦截警告框(即alert()),则触发回调onJsAlert();
如果是拦截确认框(即confirm()),则触发回调onJsConfirm();

步骤2:在Android通过WebChromeClient复写onJsPrompt()
public class MainActivity extends AppCompatActivity {

    WebView mWebView;
//    Button button;

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

        mWebView = (WebView) findViewById(R.id.webview);

        WebSettings webSettings = mWebView.getSettings();

        // 设置与Js交互的权限
        webSettings.setJavaScriptEnabled(true);
        // 设置允许JS弹窗
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

// 先加载JS代码
        // 格式规定为:file:///android_asset/文件名.html
        mWebView.loadUrl("file:///android_asset/javascript.html");


        mWebView.setWebChromeClient(new WebChromeClient() {
                                        // 拦截输入框(原理同方式2)
                                        // 参数message:代表promt()的内容(不是url)
                                        // 参数result:代表输入框的返回值
                                        @Override
                                        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                                            // 根据协议的参数,判断是否是所需要的url(原理同方式2)
                                            // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
                                            //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)

                                            Uri uri = Uri.parse(message);
                                            // 如果url的协议 = 预先约定的 js 协议
                                            // 就解析往下解析参数
                                            if ( uri.getScheme().equals("js")) {

                                                // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                                                // 所以拦截url,下面JS开始调用Android需要的方法
                                                if (uri.getAuthority().equals("webview")) {

                                                    //
                                                    // 执行JS所需要调用的逻辑
                                                    System.out.println("js调用了Android的方法");
                                                    // 可以在协议上带有参数并传递到Android上
                                                    HashMap<String, String> params = new HashMap<>();
                                                    Set<String> collection = uri.getQueryParameterNames();

                                                    //参数result:代表消息框的返回值(输入值)
                                                    result.confirm("js调用了Android的方法成功啦");
                                                }
                                                return true;
                                            }
                                            return super.onJsPrompt(view, url, message, defaultValue, result);
                                        }

// 通过alert()和confirm()拦截的原理相同,此处不作过多讲述

                                        // 拦截JS的警告框
                                        @Override
                                        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                                            return super.onJsAlert(view, url, message, result);
                                        }

                                        // 拦截JS的确认框
                                        @Override
                                        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
                                            return super.onJsConfirm(view, url, message, result);
                                        }
                                    }
        );


            }

        }

方式对比

在这里插入图片描述

2.3)总结

在这里插入图片描述

(三)性能优化

1、性能问题

1.1)H5页面加载速度慢

H5页面体验与原生Native存在较大差距。

1.1.1)渲染速度慢
  • JS解析效率
    Js 本身的解析过程复杂、解析速度不快 & 前端页面涉及较多 JS 代码文件,所以叠加起来会导致 Js 解析效率非常低
  • 手机硬件设备
    由于Android机型碎片化,这导致手机硬件设备的性能不可控,而大多数的Android手机硬件设备无法达到很好的硬件性能
1.1.2)页面资源加载缓慢

H5页面从服务器获得,并存储在Android手机内存中:

  • H5页面一般比较多
  • 每加载一个H5页面,都会产生较多的网络请求(1、HTML主URL自身请求2、HTML外部引用的JS、CSS、字体文件、图片也是一个独立的网络请求)
    每一个请求都串行的,这么多请求串起来,这导致 H5页面资源加载缓慢

1.2)耗费流量

每次使用 H5页面时,用户都需要重新加载 Android WebView的H5 页面,每加载一个 H5页面,都会产生较多网络请求(上面提到),每一个请求都串行的,这么多请求串起来,这导致消耗的流量也会越多。

2、解决方案(性能优化)

2.1)前端H5的缓存机制

2.1.1)介绍

缓存,即离线存储。这意味着 H5网页 加载后会存储在缓存区域,在无网络连接时也可访问。WebView的本质 = 在 Android中嵌入 H5页面,所以,Android WebView自带的缓存机制其实就是 H5页面的缓存机制。Android WebView除了新的File System缓存机制还不支持,其他都支持。

2.1.2)作用

1、离线浏览:用户可在没有网络连接时进行H5页面访问
2、提高页面加载速度&减少流量消耗:直接使用已缓存的资源,不需重新加载

2.1.3)缓存机制

如何将加载过的网页数据保存到本地

(1)浏览器缓存机制

a.原理
根据 HTTP 协议头里的 Cache-Control(或 Expires)和 Last-Modified(或 Etag)等字段来控制文件缓存的机制。
一般Cache-Control& Last-Modified或者Expires&Etag一起使用,一个用于控制缓存有效时间,一个用于在缓存失效后,向服务查询是否有更新

  • Cache-Control:用于控制文件在本地缓存有效时长
    如服务器回包:Cache-Control:max-age=600,则表示文件在本地应该缓存,且有效时长是600秒(从发出请求算起)。在接下来600秒内,如果有请求这个资源,浏览器不会发出 HTTP 请求,而是直接使用本地缓存的文件。
  • Expires:与Cache-Control功能相同,即控制缓存的有效时间
    Expires是 HTTP1.0 标准中的字段,Cache-Control 是 HTTP1.1 标准中新加的字段
    当这两个字段同时出现时,Cache-Control 优先级较高
  • Last-Modified:标识文件在服务器上的最新更新时间
    下次请求时,如果文件缓存过期,浏览器通过 If-Modified-Since 字段带上这个时间,发送给服务器,由服务器比较时间戳来判断文件是否有修改。如果没有修改,服务器返回304告诉浏览器继续使用缓存;如果有修改,则返回200,同时返回最新的文件。
  • Etag:功能同Last-Modified ,即标识文件在服务器上的最新更新时间。
    不同的是,Etag 的取值是一个对文件进行标识的特征字串。
    在向服务器查询文件是否有更新时,浏览器通过If-None-Match 字段把特征字串发送给服务器,由服务器和文件最新特征字串进行匹配,来判断文件是否有更新:没有更新回包304,有更新回包200
    b.特点
    优点:支持 Http协议层
    不足:缓存文件需要首次加载后才会产生;浏览器缓存的存储空间有限,缓存有被清除的可能;缓存的文件没有校验。
    c.应用场景
    静态资源文件的存储,如JS、CSS、字体、图片等。Android Webview会将缓存的文件记录及文件内容会存在当前 app 的 data 目录中。
    d.具体实现
    属于浏览器内核机制,Android WebView内置自动实现,即不需要设置即实现。
(2)Application Cache 缓存机制

a.原理
以文件为单位进行缓存,且文件有一定更新机制(类似于浏览器缓存机制),AppCache 原理有两个关键点:manifest 属性和 manifest 文件。

<!DOCTYPE html>
<html manifest="demo_html.appcache">
// HTML 在头中通过 manifest 属性引用 manifest 文件
// manifest 文件:就是上面以 appcache 结尾的文件,是一个普通文件文件,列出了需要缓存的文件
// 浏览器在首次加载 HTML 文件时,会解析 manifest 属性,并读取 manifest 文件,获取 Section:CACHE MANIFEST 下要缓存的文件列表,再对文件缓存
<body>
...
</body>
</html>

// 原理说明如下:
// AppCache 在首次加载生成后,也有更新机制。被缓存的文件如果要更新,需要更新 manifest 文件
// 因为浏览器在下次加载时,除了会默认使用缓存外,还会在后台检查 manifest 文件有没有修改(byte by byte)
发现有修改,就会重新获取 manifest 文件,对 Section:CACHE MANIFEST 下文件列表检查更新
// manifest 文件与缓存文件的检查更新也遵守浏览器缓存机制
// 如用户手动清了 AppCache 缓存,下次加载时,浏览器会重新生成缓存,也可算是一种缓存的更新
// AppCache 的缓存文件,与浏览器的缓存文件分开存储的,因为 AppCache 在本地有 5MB(分 HOST)的空间限制

b.特点
专门为 Web App离线使用而开发的缓存机制
c.应用场景
存储静态文件(如JS、CSS、字体文件)

AppCache 是对 浏览器缓存机制 的补充,不是替代。

d.具体实现

        // 通过设置WebView的settings来实现
        WebSettings settings = getSettings();

        String cacheDirPath = context.getFilesDir().getAbsolutePath()+"cache/";
        settings.setAppCachePath(cacheDirPath);
        // 1. 设置缓存路径

         settings.setAppCacheMaxSize(20*1024*1024);
        // 2. 设置缓存大小

        settings.setAppCacheEnabled(true);
        // 3. 开启Application Cache存储机制
    
// 特别注意
// 每个 Application 只调用一次 WebSettings.setAppCachePath() 和
 WebSettings.setAppCacheMaxSize()
(3)Dom Storage 缓存机制

a.原理
通过存储字符串的 Key - Value 对来提供
DOM Storage 分为 sessionStorage & localStorage; 二者使用方法基本相同,区别在于作用范围不同:

sessionStorage:具备临时性,即存储与页面相关的数据,它在页面关闭后无法使用
localStorage:具备持久性,即保存的数据在页面关闭后也可以使用。

b.特点
存储空间大( 5MB):存储空间对于不同浏览器不同,如Cookies 才 4KB
存储安全、便捷: Dom Storage 存储的数据在本地,不需要经常和服务器进行交互,不像 Cookies每次请求一次页面,都会向服务器发送网络请求
c.应用场景
存储临时、简单的数据

可代替cookies,存储不需要让服务器知道的信息。
Dom Storage机制类似于Android的SharedPreference机制

d.具体实现

		// 通过设置 `WebView`的`Settings`类实现
        WebSettings settings = getSettings();
        // 开启DOM storage
        settings.setDomStorageEnabled(true);
(4)Web SQL Database 缓存机制(不再维护)
(5)IndexedDB 缓存机制

a.原理
属于 NoSQL (非关系型数据库)数据库,通过存储字符串的 Key - Value 对来提供,类似于 Dom Storage 存储机制 的key-value存储方式
b.特点
(1)功能强大、使用简单
通过数据库的事务(tranction)机制进行数据操作,可对对象任何属性生成索引,方便查询
(2)存储空间大
较大的存储空间,默认推荐250MB,比Dom Storagede 5MB大得多
(3)使用灵活
以key-value的方式存取对象,可以是任何类型值或对象,包括二进制。异步的API调用,避免造成等待而影响体验。
c.应用场景
存储复杂、数据量大的结构化数据
d.具体实现

// 通过设置WebView的settings实现
        WebSettings settings = getSettings();

        settings.setJavaScriptEnabled(true);
        // 只需设置支持JS就自动打开IndexedDB存储机制
        // Android 在4.4开始加入对 IndexedDB 的支持,只需打开允许 JS 执行的开关就好了。
(6)File System

H5新加入的缓存机制,所以Android WebView暂不支持

2.1.4)缓存机制总结

在这里插入图片描述
综合上述缓存机制的分析,我们可以根据 需求场景的不同(缓存不同类型的数据场景) 从而选择不同的缓存机制(组合使用)
在这里插入图片描述

2.1.5)缓存模式

加载H5网页时如何读取之前保存到本地的网页缓存
Android WebView再带缓存模式有4种

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

具体使用

WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// 设置参数即可

2.2)资源预加载

使用时直接取过来用而不用在需要时才去加载提早加载将需使用的H5页面,即 提前构建缓存

2.2.1)预加载WebView对象

1、首次使用WebView对象
原因
首次初始化WebView会比第二次初始化慢很多。初始化后,即使WebView已释放,但一些多个WebView公用的全局服务/资源对象仍未释放。故第二次初始化则不需要再生成,从而变快。
实现思路
应用启动时就初始化1个全局WebView对象,当用户需加载H5页面时,直接用全局WebView对象加载&显示
具体实现
在Android的BaseApplication里初始化一个WebView对象
2、后续使用WebView队形
原因
多次创建WebView对象会耗费很多时间&资源
实现思路
(1)自身构建WebView复用池
(2)当用户需使用WebView加载H5页面时,直接复用WebView对象加载&展示
具体实现
采用2个/多个WebView重复使用,而不需每次打开H5都新建(每次页面跳转时需清空上1个页面,设置WebView内容为空)

2.2.2)预加载H5资源

原理
在应用启动、初始化第一个WebView对象时,直接开始网络请求加载H5页面,后续需打开这些H5页面时就直接从该本地对象中获取。
a. 从而 事先加载常用的H5页面资源(加载后就有缓存了)
b. 此方法虽然不能减小WebView初始化时间,但数据请求和WebView初始化可以并行进行,总体的页面加载时间就缩短了;缩短总体的页面加载时间:
具体实现
在Android 的BaseApplication里初始化一个WebView对象(用于加载常用的H5页面资源);当需使用这些页面时再从BaseApplication里取过来直接使用

2.3)资源拦截(自身构建缓存)

2.3.1)介绍

H5页面有一些更新频率低、常用&固定的静态资源文件,如JS、CSS文件、图片等。每次重新加载会浪费很多资源(时间&流量)。可以通过拦截H5页面的资源请求网络,若资源相同,可以直接从本地读取资源而不需发送网络请求到服务器读取。

2.3.2)步骤

步骤1:事先将更新频率较低、常用 & 固定的H5静态资源 文件(如JS、CSS文件、图片等) 放到本地
步骤2:拦截H5页面的资源网络请求 并进行检测。(重写WebViewClient 的 shouldInterceptRequest 方法,当向服务器访问这些静态资源时进行拦截)
步骤3:如果检测到本地具有相同的静态资源 就 直接从本地读取进行替换 而 不发送该资源的网络请求 到 服务器获取
在这里插入图片描述

2.3.3)实现

说明:通过 替换主页面(http:// ip.cn/)中的一个图片(http:// s.ip-cdn.com/img/logo.gif) 来对静态资源拦截 进行说明。
在这里插入图片描述

步骤1:定义WebView布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="scut.carson_ho.webview_interceptrequest.MainActivity">

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
步骤2:进行资源的拦截、检测 & 替换

MainActivity.java

public class MainActivity extends AppCompatActivity {
    
    WebView mWebview;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mWebview = (WebView) findViewById(R.id.webview);
        // 创建WebView对象

        mWebview.getSettings().setJavaScriptEnabled(true);
        // 支持与JS交互

        mWebview.loadUrl("http://ip.cn/");
        // 加载需要显示的网页

        mWebview.setWebViewClient(new WebViewClient() {

             // 复写shouldInterceptRequest
             //API21以下用shouldInterceptRequest(WebView view, String url)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

                // 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名
                // 此处网页里图片的url为:http://s.ip-cdn.com/img/logo.gif
                // 图片的资源文件名为:logo.gif

                if (url.contains("logo.gif")) {

                    InputStream is = null;
                    // 步骤2:创建一个输入流


                    try {
                        is =getApplicationContext().getAssets().open("images/error.png");
                        // 步骤3:打开需要替换的资源(存放在assets文件夹里)
                        // 在app/src/main下创建一个assets文件夹
                        // assets文件夹里再创建一个images文件夹,放一个error.png的图片

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    // 步骤4:替换资源

                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    // 参数1:http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2:编码类型
                    // 参数3:替换资源的输入流

                    System.out.println("旧API");
                    return response;
                }

                return super.shouldInterceptRequest(view, url);
            }


            // API21以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

                // 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名
                // 此处图片的url为:http://s.ip-cdn.com/img/logo.gif
                // 图片的资源文件名为:logo.gif
                if (request.getUrl().toString().contains("logo.gif")) {

                    InputStream is = null;
                    // 步骤2:创建一个输入流

                    try {
                        is = getApplicationContext().getAssets().open("images/error.png");
                        // 步骤3:打开需要替换的资源(存放在assets文件夹里)
                        // 在app/src/main下创建一个assets文件夹
                        // assets文件夹里再创建一个images文件夹,放一个error.png的图片

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    //步骤4:替换资源
                   
                    WebResourceResponse response = new WebResourceResponse("image/png",
                            "utf-8", is);
                    // 参数1:http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2:编码类型
                    // 参数3:存放着替换资源的输入流(上面创建的那个)
                    
                    return response;
                }
                return super.shouldInterceptRequest(view, request);
            }

    });

}
}
步骤3:网络权限

Manifest.xml

 <uses-permission android:name="android.permission.INTERNET"/>
注:本地静态资源更新

方式1:发布新版本安装更新
方式2:增量更新:在用户处于WIFI环境时让服务器推送到本地

2.3.4)好处
  • 有效解决 H5页面静态资源 加载速度慢 & 流量消耗多的问题
  • 开发成本低
    没有改变前端H5的任何代码,不需要为 APP 做定制化的东西
    该方法只是更好地加快H5加载速度,哪怕失效,也不会对H5页面产生其他负面影响
  • 同样能获得相应的cookie
    发送的网络请求会直接带上先前用户操作所留下的 cookie 而都能够留下来,因为我们没有更改资源的 URL 地址

(四)WebView使用漏洞

4.1)任意代码执行漏洞

4.1.1)addJavascriptInterface 接口引起远程代码执行漏洞

  • a.原因
    JS调用Android的其中一个方式是通过addJavascriptInterface接口进行对象映射:
 webView.addJavascriptInterface(new JSObject(), "myObj");
// 参数1:Android的本地对象
// 参数2:JS的对象
// 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法

所以,漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。如可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露
具体获取系统类的描述:
Android中的对象有一公共的方法:getClass() ;该方法可以获取到当前类 类型Class;该类有一关键的方法: Class.forName;该方法可以加载一个类(可加载 java.lang.Runtime 类)而该类是可以执行本地命令的
攻击的JS核心代码:

function execute(cmdArgs)  
{  
    // 步骤1:遍历 window 对象
    // 目的是为了找到包含 getClass ()的对象
    // 因为Android映射的JS对象也在window中,所以肯定会遍历到
    for (var obj in window) {  
        if ("getClass" in window[obj]) {  

      // 步骤2:利用反射调用forName()得到Runtime类对象
            alert(obj);          
            return  window[obj].getClass().forName("java.lang.Runtime")  

      // 步骤3:以后,就可以调用静态方法来执行一些命令,比如访问文件的命令
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);  

// 从执行命令后返回的输入流中得到字符串,有很严重暴露隐私的危险。
// 如执行完访问文件的命令之后,就可以得到文件名的信息了。
        }  
    }  
}   
  • b.解决
    解决方案1: Android 4.2版本之后
    Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击
    解决方案2:Android 4.2版本之前
    在Android 4.2版本之前采用拦截prompt()进行漏洞修复。
    步骤1:继承 WebView ,重写 addJavascriptInterface 方法,然后在内部自己维护一个对象映射关系的 Map;将需要添加的 JS 接口放入该Map中
    步骤2:每次当 WebView 加载页面前加载一段本地的 JS 代码,原理是:
    (1)让JS调用一Javascript方法:该方法是通过调用prompt()把JS中的信息(含特定标识,方法名称等)传递到Android端;
    (2)在Android的onJsPrompt()中 ,解析传递过来的信息,再通过反射机制调用Java对象的方法,这样实现安全的JS调用Android代码。
    关于Android返回给JS的值:可通过prompt()把Java中方法的处理结果返回到Js中
    具体需加载的JS代码如下:
javascript:(function JsAddJavascriptInterface_(){  
// window.jsInterface 表示在window上声明了一个Js对象

//   jsInterface = 注册的对象名
// 它注册了两个方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2)
// 如果有返回值,就添加上return
    if (typeof(window.jsInterface)!='undefined') {      
        console.log('window.jsInterface_js_interface_name is exist!!');}   
    else {  
        window.jsInterface = {     

     // 声明方法形式:方法名: function(参数)
            onButtonClick:function(arg0) {   
// prompt()返回约定的字符串
// 该字符串可自己定义
// 包含特定的标识符MyApp和 JSON 字符串(方法名,参数,对象名等)    
                return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));  
            },  
              

            onImageClick:function(arg0,arg1,arg2) {   
         return
prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));  
            },  
        };  
    }  
}  
)()

// 当JS调用 onButtonClick() 或 onImageClick() 时,就会回调到Android中的 onJsPrompt ()
// 我们解析出方法名,参数,对象名
// 再通过反射机制调用Java对象的方法

4.1.2)searchBoxJavaBridge_接口引起远程代码执行漏洞

a.原因
在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_的Js接口给 WebView 添加一个JS映射对象:searchBoxJavaBridge_对象
该接口可能被利用,实现远程任意代码。
b.解决
删除searchBoxJavaBridge_接口

// 通过调用该方法删除接口
removeJavascriptInterface();

4.1.3)accessibility和 accessibilityTraversal接口引起远程代码执行漏洞

同上

4.2)密码明文存储漏洞

a.原因
WebView默认开启密码保存功能 :

mWebView.setSavePassword(true)`

开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码;
如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险
b.解决
关闭密码保存提醒

WebSettings.setSavePassword(false) 

4.3)域控制不严格漏洞

问题分析

public class WebViewActivity extends Activity {
    private WebView webView;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        webView = (WebView) findViewById(R.id.webView);

        //webView.getSettings().setAllowFileAccess(false);                    (1)
        //webView.getSettings().setAllowFileAccessFromFileURLs(true);         (2)
        //webView.getSettings().setAllowUniversalAccessFromFileURLs(true);    (3)
        Intent i = getIntent();
        String url = i.getData().toString(); //url = file:///data/local/tmp/attack.html 
        webView.loadUrl(url);
    }
 }

/**Mainifest.xml**/
// 将该 WebViewActivity 在Mainifest.xml设置exported属性
// 表示:当前Activity是否可以被另一个Application的组件启动
android:exported="true"

即 A 应用可以通过 B 应用导出的 Activity 让 B 应用加载一个恶意的 file 协议的 url,从而可以获取 B 应用的内部私有文件,从而带来数据泄露威胁

4.3.1)setAllowFileAccess()

问题描述:

// 设置是否允许 WebView 使用 File 协议
webView.getSettings().setAllowFileAccess(true);     
// 默认设置为true,即允许在 File 域下执行任意 JavaScript 代码

使用 file 域加载的 js代码能够使用进行同源策略跨域访问,从而导致隐私信息泄露
同源策略跨域访问:对私有目录文件进行访问——针对 IM 类产品,泄露的是聊天信息、联系人等等、针对浏览器类软件,泄露的是cookie 信息泄露。
如果不允许使用 file 协议,则不会存在上述的威胁;但同时也限制了 WebView 的功能,使其不能加载本地的 html 文件
解决:
1、对于不需要使用 file 协议的应用,禁用 file 协议;

setAllowFileAccess(false); 

2、对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。

setAllowFileAccess(true); 

// 禁止 file 协议加载 JavaScript
if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
} else {
    setJavaScriptEnabled(true);
}

4.3.2)setAllowFileAccessFromFileURLs()

问题描述:

// 设置是否允许通过 file url 加载的 Js代码读取其他的本地文件
webView.getSettings().setAllowFileAccessFromFileURLs(true);
// 在Android 4.1前默认允许
// 在Android 4.1后默认禁止

当AllowFileAccessFromFileURLs()设置为 true 时,攻击者的JS代码为:

<script>
function loadXMLDoc()
{
    var arm = "file:///etc/hosts";
    var xmlhttp;
    if (window.XMLHttpRequest)
    {
        xmlhttp=new XMLHttpRequest();
    }
    xmlhttp.onreadystatechange=function()
    {
        //alert("status is"+xmlhttp.status);
        if (xmlhttp.readyState==4)
        {
              console.log(xmlhttp.responseText);
        }
    }
    xmlhttp.open("GET",arm);
    xmlhttp.send(null);
}
loadXMLDoc();
</script>

解决:
设置setAllowFileAccessFromFileURLs(false);
当设置成为 false 时,上述JS的攻击代码执行会导致错误,表示浏览器禁止从 file url 中的 javascript 读取其它本地文件。

4.3.3)setAllowUniversalAccessFromFileURLs()

问题描述:

// 设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);

// 在Android 4.1前默认允许(setAllowFileAccessFromFileURLs()不起作用)
// 在Android 4.1后默认禁止

当AllowFileAccessFromFileURLs()被设置成true时,攻击者的JS代码是:

// 通过该代码可成功读取 http://www.so.com 的内容
<script>
function loadXMLDoc()
{
    var arm = "http://www.so.com";
    var xmlhttp;
    if (window.XMLHttpRequest)
    {
        xmlhttp=new XMLHttpRequest();
    }
    xmlhttp.onreadystatechange=function()
    {
        //alert("status is"+xmlhttp.status);
        if (xmlhttp.readyState==4)
        {
             console.log(xmlhttp.responseText);
        }
    }
    xmlhttp.open("GET",arm);
    xmlhttp.send(null);
}
loadXMLDoc();
</script>

解决:
设置setAllowUniversalAccessFromFileURLs(false);

4.3.4)setJavaScriptEnabled()

问题描述:

// 设置是否允许 WebView 使用 JavaScript(默认是不允许)
webView.getSettings().setJavaScriptEnabled(true);  

// 但很多应用(包括移动浏览器)为了让 WebView 执行 http 协议中的 JavaScript,都会主动设置为true,不区别对待是非常危险的。

即使把setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()都设置为 false,通过 file URL 加载的 javascript 仍然有方法访问其他的本地文件:符号链接跨源攻击
前提是允许 file URL 执行 javascript,即webView.getSettings().setJavaScriptEnabled(true);
这一攻击能奏效的原因是:通过 javascript 的延时执行和将当前文件替换成指向其它文件的软链接就可以读取到被符号链接所指的文件。具体攻击步骤:
1、把恶意的 js 代码输出到攻击应用的目录下,随机命名为 xx.html,修改该目录的权限;
2、修改后休眠 1s,让文件操作完成;
3、完成后通过系统的 Chrome 应用去打开该 xx.html 文件
4、等待 4s 让 Chrome 加载完成该 html,最后将该 html 删除,并且使用 ln -s 命令为 Chrome 的 Cookie 文件创建软连接
注:在该命令执行前 xx.html 是不存在的;执行完这条命令之后,就生成了这个文件,并且将 Cookie 文件链接到了 xx.html 上。
于是就可通过链接来访问 Chrome 的 Cookie
1、Google 没有进行修复,只是让Chrome 最新版本默认禁用 file 协议,所以这一漏洞在最新版的 Chrome 中并不存在
2、但是,在日常大量使用 WebView 的App和浏览器,都有可能受到此漏洞的影响。通过利用此漏洞,容易出现数据泄露的危险
如果是 file 协议,禁用 javascript 可以很大程度上减小跨源漏洞对 WebView 的威胁。
1、但并不能完全杜绝跨源文件泄露。
2、例:应用实现了下载功能,对于无法加载的页面,会自动下载到 sd 卡中;由于 sd 卡中的文件所有应用都可以访问,于是可以通过构造一个 file URL 指向被攻击应用的私有文件,然后用此 URL 启动被攻击应用的 WebActivity,这样由于该 WebActivity 无法加载该文件,就会将该文件下载到 sd 卡下面,然后就可以从 sd 卡上读取这个文件了
解决:
1、对于不需要使用 file 协议的应用,禁用 file 协议;

// 禁用 file 协议;
setAllowFileAccess(false); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

2、对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。

// 需要使用 file 协议
setAllowFileAccess(true); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

// 禁止 file 协议加载 JavaScript
if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
} else {
    setJavaScriptEnabled(true);
}

(五)WebView组件

5.1)内置的WebView组件缺点

1、内存泄露问题

Android中的WebView组件,内存泄漏的问题一直没有非常有效的解决方案,让程序猿们痛不欲生。

2、不同版本内核不同

Android中的WebView组件,在4.4以前的版本是WebKit的内核,4.4以后才换成chromium的内核,同时鉴于Google版本帝的风格,因此也导致各个版本之间的运行效率参差不齐。而且即使是chromium内核的版本,也因为要考虑兼容以前的版本,而变得不是那么美好。

5.2)Crosswalk

5.3)TBS服务

由腾讯QQ浏览器团队出品。支持“共享X5内核模式”和“独立下载X5内核模式”。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
由于源文件大小为99.3MB,所以采用分卷压缩的方式进行上传   《Google Android SDK开发范例大全(第3版)》在上一版的基础上,以Android手机应用程序开发(采用Android SDK 2.3.3)为主题,超过200多个范例全面且深度地整合了手机、网络及服务等多个开发领域,为读者提高程序设计能力提供了很大的帮助。   全书共分11章,主要以范例集的方式来讲述Android的知识点,详细介绍了开发Android的人机交互界面、Android常用的开发控件、Android手机收发短信等通信服务、开发Android手机的自动服务功能和娱乐多媒体功能以及整合Android与Google强大的网络服务等内容。随书光盘中包括了所有范例的程序代码。   《Google Android SDK开发范例大全(第3版)》继承前两版由浅入深的方式,范例总数由原先的160多个增加到了200多个,在用户交互界面、手机控件、交互式通信服务、手机自助服务、娱乐多媒体等方面均增加了相应的范例来介绍新的开发技术,特别是新增加了第11章来专门介绍HTML5技术在Android移动设备里的应用,相信当下两个热门技术的交汇会碰撞出不一样的火花。   《Google Android SDK开发范例大全(第3版)》内容由Android的基础知识到实际开发应用,结构清晰、语言简洁,非常适合Android的初学者和Android的进阶程序开发者阅读参考。 目录   第1章 了解、深入、动手做   1.1 红透半边天的Android   1.2 本书目的及范例涵盖范围   1.3 如何阅读本书   1.4 使用本书范例   1.5 参考网站   第2章 Android初体验   2.1 安装Android SDK与ADT/DDMS   2.2 创建第一个Android项目(Hello Android!)   2.3 Android应用程序架构——从此开始   2.4 可视化的界面开发工具   2.5 部署应用程序到Android手机   第3章 用户人机界面   3.1 更改与显示文字标签   3.2 更改手机窗口画面底色   3.3 更改TextView文字颜色   3.4 置换TextView文字   3.5 取得手机屏幕大小   3.6 样式化的定型对象   3.7 简易的按钮事件   3.8 手机页面的转换   3.9 调用另一个Activity   3.10 不同Activity之间的数据传递   3.11 返回数据到前一个Activity   3.12 具有交互功能的对话框   3.13 置换文字颜色的机关   3.14 控制不同的文字字体   3.15 如iPhone拖动相片特效   3.16 自制计算器   3.17 关于(About)程序信息   3.18 程序加载中,请稍候   3.19 全屏幕以按钮重写   3.20 今晚到哪儿打牙祭   3.21 Android变脸   3.22 打勾显示输入的密码   3.23 Android多语系支持   3.24 判断手机操作系统版本是否允许运行程序   3.25 两个不同的程序彼此调用   3.26 指定安装应用程序迁移至SD卡   3.27 手机动态Layout主题随手势物换迁移   第4章 史上超豪华的手机控件   4.1 EditText与TextView共舞   4.2 设计具有背景图的按钮   4.3 给圣诞老人的信息   4.4 我同意条款   4.5 消费券采购列表   4.6 向左或向右   4.7 专业相框设计   4.8 自定义下拉菜单模式   4.9 动态添加/删除的Spinner菜单   4.10 心爱小宝贝相片集   4.11 快速地搜索手机文件引擎   4.12 按钮也能随单击变换   4.13 具自动提示功能的菜单   4.14 数字及模拟小时钟设计   4.15 动态输入日期与时间   4.16 猜猜红桃A在哪儿   4.17 后台程序运行进度提示   4.18 动态文字排版   4.19 在Activity里显示列表   4.20 以动态列表配置选项   4.21 查找程序根目录下所有文件   4.22 加载手机磁盘里的图片文件   4.23 动态放大缩小ImageView里的图片   4.24 动态旋转图片   4.25 猜猜我在想什么   4.26 离开与关闭程序的弹出窗口   4.27 隐藏式抽屉   4.28 手机桌面上的小玩意   4.29 手机图片搜索管理器   4.30 实时配置桌面上的AppWidget UI Layout   4.31 识别输入装置ID与InputDevice装置   4.32 选取文字的聪明文字联想   4.33 设计与Launcher相同的三页滑动Layout   第5章 交互式通信服务与手机控制   5.1 具有正则表达式的TextView   5.2 ACTION!CALL!拨打电话   5.3 自制发送短信程序   5.4 自制发送E-mail程序   5.5 自制日历手机数据库   5.6 手机振动的节奏   5.7 图文可视化提醒   5.8 状态栏的图标与文字提醒   5.9 搜索手机通讯录自动完成   5.10 取得联系人资料   5.11 制作有图标的文件资源管理器   5.12 还原手机默认桌面   5.13 置换手机后台图   5.14 获取手机现存桌面   5.15 文件资源管理器再进化   5.16 取得目前File与Cache的路径   5.17 打开/关闭WiFi服务   5.18 取得SIM卡内的信息   5.19 调用拨号按钮   5.20 DPAD按键处理   5.21 任务管理器正在运行的程序   5.22 动态更改屏幕方向   5.23 系统设置更改事件   5.24 取得电信网络与手机的相关信息   5.25 建立自己的手写笔画图案   5.26 判断手持设备的WiFi与GPS状态   5.27 手机安全设置管理器   5.28 Shell Script运行Cmmand   5.29 模拟文件下载Notification进度变化与关闭   5.30 取得已安装应用程序列表与安装日期信息   5.31 造假的Toast画面   5.32 剪贴簿管理器   第6章 手机自动服务纪实   6.1 您有一条短信pop up提醒   6.2 手机电池计量还剩多少   6.3 群发拜年短信给联系人   6.4 开始与停止系统服务   6.5 通过短信发送E-mail通知   6.6 手机拨接状态   6.7 有来电,发送邮件通知   6.8 存储卡剩余多少容量   6.9 访问本机内存与存储卡   6.10 实现可定时响起的闹钟   6.11 黑名单来电自动静音   6.12 手机翻背面即静音振动   6.13 指定时间置换桌面背景   6.14 判断发送短信后的状态   6.15 后台服务送出广播信息   6.16 开机程序设计   6.17 双向短信常驻服务   6.18 测量电池温度   6.19 电池电量显示Widget小工具   6.20 取出通讯录数据及多组电话号码   6.21 电子Sensor精确罗盘   6.22 屏幕手写事件判断   6.23 画一颗心,打电话给HONEY   6.24 开启内置闹钟与已安装的应用程序设置   6.25 在SDK开发环境中访问JNI接口   第7章 娱乐多媒体   7.1 访问Drawable资源的宽和高   7.2 绘制几何图形   7.3 手机屏幕保护程序   7.4 用手指移动画面里的照片   7.5 加载存储卡的Gallery相簿   7.6 取得手机内置媒体中的图片文件   7.7 相片导航向导与设置背景桌面   7.8 调整音量大小   7.9 播放mp3资源文件   7.10 播放存储卡里的mp3音乐   7.11 自制录音/播放录音程序   7.12 通过收到短信开始秘密录音   7.13 内置影片播放器载入3gp电影   7.14 自制3gp影片播放器   7.15 相机预览及拍照临时文件   7.16 拍照瞬间启动相机自动对焦   7.17 制作开场动画   7.18 我的手机会说话   7.19 手势触控屏幕控制   7.20 多点触控屏幕事件捕捉   7.21 电流急急棒   7.22 自定义动画按钮事件   7.23 设置录音取样频率与显示录音时间   7.24 EXIF照片信息编辑及读取   7.25 声音音效均衡器、重低音与音场控制   7.26 在自定义的SurfaceView上绘制动画   第8章 当Android与Internet接轨   8.1 HTTP GET/POST传递参数   8.2 在程序里浏览网页   8.3 嵌入HTML标记的程序   8.4 设计前往打开网页功能   8.5 将网络图像网址放入Gallery中显示   8.6 即时访问网络图片文件展示   8.7 手机气象局,实时卫星云图   8.8 通过网络播放mp3   8.9 设置远程下载音乐为手机铃声   8.10 远程下载桌面背景图案   8.11 将手机文件上传至网站服务器   8.12 移动博客发布器   8.13 移动RSS阅读器   8.14 远程下载安装Android程序   8.15 手机下载看3gp影片   8.16 访问网站Login API   8.17 地震速报   8.18 通过Web API动态更改手机Spinner控件   8.19 网络语音搜索   8.20 网络下载过程中显示加载中   8.21 快照WebView加载的网页画面   8.22 网页的放大与缩小   8.23 WebView网页载入中、载入完成样版   8.24 通过内嵌Flash Player播放FLV电影   8.25 WebView网站Session Cookie判断与访问   8.26 文件下载管理器   8.27 手机Socket联机拨号服务器   第9章 Google服务与Android完美整合   9.1 Google账号验证Token   9.2 Google搜索   9.3 前端产生QR Code二维条形码   9.4 以经纬度查找目的地位置   9.5 GPS Google地图   9.6 移动版Google Map   9.7 规划导航路径   9.8 移动设备上的Picasa相册   9.9 随身翻译机   9.10 通过YouTube ID调用YouTube播放影片   9.11 Google Analytics分析手机用户行为   第10章 创意Android程序设计   10.1 手机手电筒   10.2 GPS轨迹记录器   10.3 女性贴身看护   10.4 手机QR Code二维条形码生成器   10.5 Android QR Code二维条形码扫描仪   10.6 上班族今天中午要吃什么   10.7 掷杯筊   10.8 电蚊香   10.9 转转轮盘——Lucky!   10.10 任务管理器   10.11 免费的云端发音字典   10.12 动起来!手机桌面上会动的动态桌布   10.13 正妹墙相簿浏览器   10.14 YouTube影片FLV文件下载器   第11章 HTML5 Mobile应用程序开发   11.1 HTML5 INPUT输入框控制手机输入键盘   11.2 HTML5利用CSS3排版的墙贴相册   11.3 HTML5网页离线数据库   11.4 可拨打电话的HTML连接   11.5 HTML5取回所在经纬度坐标并反查地址   附录A 如何发布程序到Android Market   A.1 支付Google Android Market注册费   A.2 生成签署凭证.keystore金钥档案   A.3 上传*.apk程序到Android Market上   附录B 升级项目至新版SDK 与Honeycomb平板程序部署   B.1 Android SDK版本与项目最低运行版本设置   B.2 升级程序至Honeycomb平板运行
Android应用开发详解》 作者:郭宏志 编著 内容简介    本书分为三个部分,包括基础篇、技术篇和应用篇。由浅入深地讲述了Android应用开发的方方面面。    第一篇 基础篇 第1章 Android概述 Android概述,讲述了Android的前生后世、架构和特点、Android Market、应用程序组件和Android与Java ME的区别及联系    第2章 Android开发基础 Android开发基础,讲述了Android开发环境的搭建、Android常用工具的使用和第一个Android应用程序的开发    第二篇 技术篇 第3章 Android中的资源访问 Android 中的资源访问,讲述了如何定义和访问Android中的外部资源。   第4章 Android用户界面 Android 用户界面,讲述了Android中的事件处理机制、布局管理和常用组件的使用。    第5章 Android基本程序单元Activity Android 基本程序单元Activity,讲述了Android中重要组件Activity的创建、启动和生命周期等内容    第6章 Android组件之间的信使Intent Android 组件之间的信使Intent,讲述了Intent对象及其属性、Intent的实现策略和Intent的常见应用   第7章 Android Service组件 Android Service组件,讲述了Android中的后台服务Service的概念、创建和使用,并详细讲解了远程服务的调用    第8章 Android广播事件处理 Broadcast Receiver Android 广播事件处理 Broadcast Receiver,讲述了广播事件处理机制、Notification、NotificationManager和AlarmManager的使用    第9章 Android中的数据存取 Android中的数据存取,讲述了Android的四种数据存取方法:Preference、File、SQLite和Network    第10章 Content Provider Content Provider,讲述了Android不同应用程序之间相互共享数据的机制,包括ContentProvider和ContentResolver    第11章 Android中的多媒体应用 Android中的多媒体应用,讲述了Android的图片应用、音频及视频播放、音频及视频录制和照相机的使用    第12章 Android中的图形图像 Android中的图形图像,讲述了Android中的图片、动画、图形绘制和图形特效    第13章 Android中的互联网应用 Android中的互联网应用,讲述了Android中的各种网络应用,包括Socket、URL、HTTP、Web Service和WebView组件    第14章 Android中的GPS应用 Android中的GPS应用,讲述了LocationManager、LocationProvider、跟踪、定位、Geocoder正逆向编解码和可视化位置服务    第三篇 应用篇 第15章 Android应用案例——移动警务通 Android 应用案例——移动警务通,通过一个真实的商业案例讲解了Android的各种应用,本项目包括信息采集、信息查询、照片上传、GPS定位等综合警务应用    第16章 Android应用案例——雷电游戏 Android 应用案例——雷电游戏,通过一个完整的雷电游戏,讲述了Android的游戏开发思路、框架和具体实现,并比较了Android游戏开发和Java ME游戏开发的异同,成功移植了Java ME游戏API到Android当中    第17章 Android应用案例——备忘录 Android 应用案例——备忘录,通过一个Android基础应用项目综合应用了Android中的各种组件,包括Activity、Service、Broadcast Receiver、ContentProvider、Intent和View的应用    第18章 Android应用案例——无线点餐系统 Android 应用案例——无线点餐系统,通过一个真实的商业案例讲解了Android的各种应用,本项目包括操作员登录、点餐、结算、转台、并台、查台、更新数据和退出系统等餐厅点餐的常用操作
《疯狂Android讲义(第2版)》.(李刚).源代码 疯狂Android讲义目录结构: 第2章、Android应用程序界面设计,即View 2.2、布局管理(Layout):LinearLayout、TableLayout、FrameLayout、RelativeLayout; 2.3、基本界面组件:TextView、EditText; Button、ImageButton; 9Patch; RadioButton、CheckBox; ToggleButton;AnalogClock、DigitalClock; ImageView; 2.4、高级界面组件:AutoCompleteTextView; Spinner; DatePicker、TimePicker; ProgressBar; SeekBar; RatingBar; TabHost; ScrollView; ListView、ListActivity; ExpandableListView; GridView、ImageSwitcher; Gallery; 2.5、对话框:AlertDialog; PopupWindow; DatePickerDialog、TimePickerDialog; ProgressDialog; 2.6、消息提示:Toast; Notification; 2.7、菜单:OptionMenu、SubMenu; ContextMenu; 第3章、Android事件处理,包括按键响应机制和消息传递机制 3.2、基于监听器的事件处理: 3.3、基于回调的事件的处理: 3.4、响应系统设置的事件: 3.5、Handler消息传递机制: 第4章、深入理解Activity 4.1、建立、配置和使用Activity: 4.2、Activity的回调机制: 4.3、Activity的生命周期: 第5章、使用Intent和IntentFilter进行通信 5.1、Intent对象详解: 5.2、Intent的属性及intent-filter配置:Component属性; Action、Category属性与intent-filter配置; Data、Type属性与intent-filter配置; Extra属性; 5.3、使用Intent创建Tab页面: 第6章、Android应用的资源 6.1、资源的类型及存储方式: 6.2、使用字符串、颜色、尺寸资源: 6.3、数组资源: 6.4、使用Drawable资源:图片资源; StateListDrawable资源; LayerDrawable资源; ShapeDrawable资源; ClipDrawable资源; AnimationDrawable资源; 6.5、使用原始XML资源: 6.6、使用Layout资源: 6.7、使用菜单(Menu)资源: 6.8、样式(Style)和主题(Theme)资源: 6.9、属性(Attribute)资源: 6.10、使用原始资源: 6.11、国际化和资源自适应: 第7章、图形与图像处理 7.1、使用简单图片:Drawable; Bitmap、BitmapFactory; 7.2、绘图:Canvas; Paint; Path; 7.3、图形特效处理:使用Matrix控制变换; 使用drawBitmapMesh扭曲图像; 使用Shader填充图形; 7.4、逐帧(Frame)动画:AnimationDrawable; 7.5、补间(Tween)动画:Interpolator; 位置、大小、旋转度、透明度; 7.6、使用SurfaceView实现动画: 第8章、Android的数据存储和IO 8.1、使用SharedPreferences:SharedPreferences; Editor; 8.2、File存储:openFileOutput和openFileInput; 读写SD卡文件; 8.3、SQLite数据库:SQL语句; SQLiteDatabase; SQLiteOpenHelper; sqlite3 tools; 8.4、手势(Gesture): 8.5、自动朗读(TTS): 8.6、网络存储: 第9章、使用ContentProvider实现数据共享 9.1、数据共享标准:ContentProvider; Uri; ContentResolver; 9.2、操作系统的ContentProvider:使用ContentProvider管理联系人和多媒体; 9.3、实现ContentProvider:创建ContentProvider的步骤; 9.4、监听ContentProvider的数据:ContentObserver; 第10章、Service与BroadcastReceiver 10.1、Service:Service的创建、配置、启动、停止、绑定和通信; Service的生命周期; 10.2、跨进程调用Service(AIDL服务):创建AIDL文件; 将接口暴露给客户端; 客户端访问AIDLService; 10.3、电话管理器:TelephoneManager; 10.4、短信管理器:SmsManager; 10.5、音频管理器:AudioManager; 10.6、振动器:Vibrator; 10.7、手机闹钟服务:AlarmManager; 10.8、接受广播信息:BroadcastReceiver; 10.9、接受系统广播消息: 第11章、多媒体应用开发 11.1、音频和视频的播放:MediaPlayer; SoundPool; VideoView; 11.2、使用MediaRecorder录制音频: 11.3、控制摄像头拍照:Camera; 第12章、OpenGL与3D应用开发 12.2、OpenGL ES基础: 12.3、绘制2D图形: 12.4、绘制3D图形: 第13章、Android的网络应用 13.1、基于TCP协议的网络通信(套接字Socket):Socket; ServerSocket; 13.2、使用URL访问网络资源:URL; URLConnection; 13.3、使用HTTP访问网络:HttpURLConnection; HttpClient; 13.4、使用WebView视图显示网页: 13.5、使用WebService进行网络编程: 第14章、管理Android手机桌面 14.1、管理手机桌面: 14.2、改变手机壁纸: 14.3、桌面快捷方式: 14.4、管理桌面小控件: 14.5、实时文件夹(LiveFolder): 第15章、传感器应用开发 15.2、Android的常用传感器:方向传感器Orientation; 磁场传感器Magnetic Field; 温度传感器Temperature; 光传感器Light; 压力传感器Pressure; 第16章、GPS应用开发 16.1、支持GPS的核心API: 16.2、获取LocationProvider: 16.3、获取定位信息: 16.4、临近警告: 第17章、使用Google Map服务 17.1、调用Google Map的准备: 17.2、根据GPS信息在地图上定位: 17.3、GPS导航: 17.4、根据地址定位: 第18章、疯狂连连看 第19章、电子拍卖系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李一恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值