Android原生内嵌H5开发小结

自用笔记

外包公司,为了节约成本,采用Android内嵌H5模式开发,便于在安卓移动端上直接复用页面, 从而提高开发效率。同时改动时候只需改动H5页面,无需改动客户端。

实现的原理:

本质是:Java代码和Javascript调用

H5页面,只是Html的扩展,Javascript用来处理页面的逻辑

Android和H5互调案例:

一.java调用js

1、Java 调用 js 里面的函数、效率并不是很高、估计要200ms左右吧、做交互性很强的事情、这种速度很难让人接受、而js去调Java的方法:速度很快、50ms左右、所以尽量用js调用Java方法
2、Java 调用 js 的函数、没有返回值、调用了就控制不到了
3、Js 调用 Java 的方法、返回值如果是字符串、你会发现这个字符串是 native奶踢吾 的、转成 locale老K乐 的才能正常使用、使用 toLocale
String() 函数就可以了、不过这个函数的速度并不快、转化的字符串如果很多、将会很耗费时间
4、网页中尽量不要使用jQuery、执行起来需要5-6秒、最好使用原生的js写业务脚本、以提升加载速度、改善用户体验
交互的话,安卓这边只需要配置webview的jsBridge就可以了
一般接口什么的都是h5那边提供,这边这需要根据提供的接口进行实现就可以了
数据交互也是这样的,还有一种是写cookie
     对于安卓开发有一段时间的人来说,知道安卓4.4以前谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户
端的重要信息,甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,增加了防御措施,如果要是js调用本地
代码,开发者必须在代码申明JavascriptInterface,   列如在4.0之前我们要使得webView加载js只需如下代码:  
mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction"); 
     4.4之后调用需要在调用方法加入加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这
样就可以避免恶意网页利用js对安卓客户端的窃取和攻击。
        但是即使这样,我们很多时候需要在js记载本地代码的时候,要做一些判断和限制,或者有可能也会做些过滤和对用户友好
提示,因此JavascriptInterface也就无法满足我们的需求了,特此有大神就写出了WebViewJavascriptBridge框架。
    通过实例化webView,用法和安卓原生的view没多大区别,设置WebChromClient, 设置加载的html(同样支持网络和本地文件)
,接着我们需要给web注册和html端约定好的js方法名。代码列举的submitFromweb和js的执行的方法名一致,玩过NDK的JNI调
用的朋友也知道必须和c代码之间有个约定,其实js桥和jni有点类似,
通过注册handler来实现回调,Java代码中通过js返回的数据,进行处理后在调用function.onCallback返回给js.这里不做过多解释
//必须和js同名函数,注册具体执行函数,类似java实现类

拦截URL
Android中处理网页时我们必然用到WebView,这里我们有这样一个需求,我们想让WebView在处理网络请求的时候将某
些请求拦截替换成某些特殊的资源。具体一点儿说,在WebView加载 http://m.sogou.com 时,会加载一个logo图片,
我们的需求就是将这个logo图片换成另一张图片。

shouldInterceptRequest
好在Android中的WebView比较强大,从API 11(Android 3.0)开始, shouldInterceptRequest被引入就是为了解决这一类的问题。
shouldInterceptRequest这个回调可以通知主程序WebView处理的资源(css,js,image等)请求,并允许主程序进行处理后返回数据。如果主程序返回的数据为null,WebView会自行请求网络加载资源,否则使用主程序提供的数据。注意这个回调发生在非UI线程中,所以进行UI系统相关的操作是不可以的。shouldInterceptRequest有两种重载。

下面是:

WebView的基本设置:

private void initWebView() {

        webView = new WebView(this);

        WebSettings webSettings = webView.getSettings();

        //设置支持javaScript脚步语言

        webSettings.setJavaScriptEnabled(true);

        //支持双击-前提是页面要支持才显示

           //webSettings.setUseWideViewPort(true);

        //支持缩放按钮-前提是页面要支持才显示

        webSettings.setBuiltInZoomControls(true);

        //设置客户端-不跳转到默认浏览器中

        webView.setWebViewClient(new WebViewClient());

        //设置支持js调用java

        webView.addJavascriptInterface(new  AndroidAndJSInterface(),"Android");

 //加载本地资源       

 webView.loadUrl("file:///android_asset/JavaAndJavaScriptCall.html");

        //显示页面

//        setContentView(webView);

}

java调用js原理就是Java代码调用了Js里面的函数。

核心Java代码段:

//登录功能里,java代码调用了js里面的JavaCallJs函数实现将name传到JS中,这样JS页面可以显示该用户名了。

   private void login(String name) {

        webView.loadUrl("javascript:javaCallJs(" + "'" + name + "'" + ")");

        setContentView(webView);

}

核心js代码段:

//上面Java核心代码执行将调用下面JS代码

 <script type="text/javascript">

    function javaCallJs(arg){

         document.getElementById("content").innerHTML =

             ("欢迎:"+arg );

    }

    </script>

 不过要实现H5在APP上进行操作,我们就需要重写WebChromeClient中的openFileChooser方法才行,顺便百度了下WebView中 WebViewClient与WebChromeClient的区别

WebViewClient主要帮助WebView处理各种通知、请求事件的,比如:

onLoadResource
onPageStart
onPageFinish
onReceiveError
onReceivedHttpAuthRequest

 WebChromeClient主要辅助WebView处理JavaScript的对话框、网站图标、网站title、加载进度等比如

onCloseWindow(关闭WebView)
onCreateWindow()
onJsAlert (WebView上alert无效,需要定制WebChromeClient处理弹出)
onJsPrompt
onJsConfirm
onProgressChanged
onReceivedIcon
onReceivedTitle

看上去他们有很多不同,实际使用的话,如果你的WebView只是用来处理一些html的页面内容,只用WebViewClient就行了,如果需要更丰富的处理效果,比如JS、进度条等,就要用到WebChromeClient,所以我们用到了WebChromeClient。

举个上传的例子 得益于这两篇文章的帮助 :android使用webview上传文件(支持相册和拍照)_千里之外-CSDN博客_webview上传文件Android 如何選取圖片或是檔案? | MagicLen,后来通过一些处理 ,最终我们的代码如下:

mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String mTitle) {
super.onReceivedTitle(view, mTitle);

                                   //这里可以用来处理我们的页面标题

}
@Override
public void onProgressChanged(WebView view, int newProgress) {
//处理网页面加载进度
}

 
// For Android < 3.0

public void openFileChooser(ValueCallback<Uri> filePathCallback) {
mFilePathCallback4 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_SINGLE);
}
 

                  // For Android  > 4.1.1

public void openFileChooser(ValueCallback filePathCallback, String acceptType) {
mFilePathCallback4 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_MULTI);
}
 

                     // For Android 3.0+

public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
mFilePathCallback4 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_MULTI);
}

public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
mFilePathCallback5 = filePathCallback;
IntentUtils.jumpToSelectPics(aty, false, 1, MultiImageSelectorActivity.MODE_MULTI);
return true;
}
});
我们这里选择好图片后,还要进行处理:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
  

     if (requestCode == IntentUtils.REQUEST_IMAGE) {
     if (mFilePathCallback4 != null) {
     if (null != intent) {
     ArrayList<String> resultList = intent.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT);
     if (resultList.size() > 0) {
           String path = MediaUtility.getPath(BaseWebViewAct.this,
           Uri.parse("file://" + resultList.get(0)));//
           Uri uri = Uri.fromFile(new File(path));
           mFilePathCallback4.onReceiveValue(uri);
          }
    }else {
mFilePathCallback4.onReceiveValue(null);
          }
    }
     if (mFilePathCallback5 != null) {
          if (null != intent) {
             ArrayList<String> resultList =intent.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT);
             if (resultList.size() > 0) {
                 String path = MediaUtility.getPath(BaseWebViewAct.this,
                 Uri.parse("file://" + resultList.get(0)));//
                 Uri uri = Uri.fromFile(new File(path));
                 mFilePathCallback5.onReceiveValue(new Uri[]{uri});
           }
     }else {
       mFilePathCallback5.onReceiveValue(null);
           }
   }
       mFilePathCallback4 = null;
       mFilePathCallback5 = null;
   }
}

二.JavaScript调java

1.在初始化webview代码中配置Javascript接口:

        //设置支持js调用java,调用时候将执行第一个参数的接口类

        webView.addJavascriptInterface(new AndroidAndJSInterface(),"Android");

//第二个参数为执行接口类方法的标示,与js相呼应

1

2

3

2.在该Activity中实现Javascript接口类:

  /**

     * js可以调用该类的方法

     */

    class AndroidAndJSInterface{

        @JavascriptInterface

        public void showToast(){

            Toast.makeText(JavaAndJSActivity.this, "我被js调用了", Toast.LENGTH_SHORT).show();

        }

    }

3_JavaScript中调用java代码的核心代码段:

<input type="button" value="点击Android被调用" οnclick="window.Android.showToast()" />

1

2

执行流程:

点击js页面的button,将执行js的onclick方法(οnclick=”window.Android.showToast()”),根据该Android标示与webview配置接口方法的第二个参数相匹配,然后执行js接口实现类的showToast()方法。从而实现js代码调用java代码。

H5页面调用Android播放视频

基本思路和上面的例子(执行流程)一样,业务逻辑层面来说播放视频主要是从H5页面点击播放跳转(把视频播放的url传递给Java层)并触发Java实现播放视频的代码即可。

1. webview中的核心配置:

   //设置支持js调用java

        webView.addJavascriptInterface(new AndroidAndJSInterface(),"android");

2.在该Activity中实现Javascript接口类:

 class AndroidAndJSInterface {

        /**

         * 该方法将被js调用

         * @param id

         * @param videoUrl

         * @param tile

         */

        @JavascriptInterface

        public void playVideo(int id,String videoUrl,String tile){

            //调起系统所有播放器

            Intent intent = new Intent();

            intent.setDataAndType(Uri.parse(videoUrl),"video/*");

            startActivity(intent);

        }

}

3.JavaScript中调用java代码的核心代码段:

  javascript:android.playVideo(itemid, videourl, itemtitle);

1

H5页面调用Android拨打电话

思路都是一样的,点击H5页面把电话号码传到java层并调用拨号核心代码即可。

在该Activity中实现Javascript接口类:

 @JavascriptInterface

        public void call(String phone) {

            Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phone));

            if (ActivityCompat.checkSelfPermission(JsCallJavaCallPhoneActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {

                return;

            }

            startActivity(intent);

        }

重点内容

1.如果调用如下方法时候报错,那么可以在Javascript接口类的具体实现方法上面加注解:@JavascriptInterface,或者把targetSdkVersion 改为 16(一般不适用)

 //设置支持js调用java

        webView.addJavascriptInterface(new AndroidAndJSInterface(), "Android");

1

2

2.该申请的权限不要忘了在AndroidManifest.xml中加上。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Android原生开发是指使用Android SDK提供的工具和API,使用Java或Kotlin编写应用程序的开发方式。与使用跨平台框架如React Native或Flutter进行开发不同,原生开发可以充分利用Android设备的特性和功能,提供更好的性能和用户体验。 Android原生开发的优势在于可以直接调用系统提供的各种功能与服务,如摄像头、传感器、定位、通知等,使得应用程序能够更好地与硬件进行交互。此外,Android原生开发可以充分利用Android SDK中提供的广泛的API,包括UI组件、多媒体、网络、数据库等,以及许多第三方库和插件,使得开发过程更加灵活和高效。 Android原生开发的另一个重要特点是可以适应不同的设备和屏幕尺寸,通过适配不同的布局、样式和分辨率,使得应用程序在各种设备上都能够良好地运行和展示。此外,原生开发还可以充分利用设备的性能和资源,提供更好的响应速度和流畅度。 然而,Android原生开发也存在一些挑战。首先,与使用跨平台框架相比,原生开发需要更多的学习和了解Android系统和API的知识,对开发者的技术要求较高。其次,对于需要在多个平台上开发的应用程序来说,原生开发需要分别开发和维护各个平台的代码,增加了开发的复杂性和工作量。最后,由于Android系统的不断更新和改进,原生开发需要及时跟进和适应新的API和特性,对开发者来说可能需要持续的学习和更新。 总之,尽管Android原生开发需要具备一定的技术功底和对Android系统的了解,但它可以充分利用Android设备的特性和功能,提供更好的性能和用户体验,适应不同的设备和屏幕尺寸。对于注重性能和用户体验的应用程序来说,原生开发是一种值得选择的开发方式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

易寻资料

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

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

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

打赏作者

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

抵扣说明:

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

余额充值