WebView实践总结

  前段时间有点android与h5交互的需求开发,考虑到自己的需求,所以选择webView作为中间层,实现双方通讯,下面是在实践过程中关于JNIBridge WebView的一些总结

  首先我们知道android(Java)与js属于不同种类的语言,所以两种平台语言的通讯必须借助外力,例如我们最初学的桥接模式,或者适配器模式,都是通过中间层去转化而实现不同模式的适配,再例如现在很火的语言中间层,thrift,protobuf,或者是grpc的中间层,可以使不同语言之间进行交互。这里WebView是为了支持h5与android原生进行交互的Bridge,当然底层也是jni的调用,详细的就不介绍了下面直接上代码。

 [1] 在Activity中定义WebView

 private WebView webView; //定义webview

 [2] 初始化WebView,针对以下封装方法,后面会有详细的介绍。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        captureManager = new ImageCaptureManager(MainActivity.this);
        initWebView();  //初始化WebView组件
        initWebViewSetting();   //初始化WebView配置
        initWebChromeClient(); //初始化Web浏览器设置
        initWebViewClient();   //初始化客户端配置
        initListener();        //初始化事件监听器
        loadWebView();         //加载webView视图
    }

[2.1] 初始化并绑定webView组件 ,具体如下:

   /**
     * 初始化视图
     */
    public void initWebView() {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        webView = new WebView(getApplicationContext());
        this.addContentView(webView, params);
    }

 [2.2] 初始化webViewSetting,具体如下:


    @SuppressLint("SetJavaScriptEnabled")
    public void initWebViewSetting() {
        webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);  //设置android能否使用js
        //设置自适应屏幕
        webSettings.setUseWideViewPort(true); //将图片调整到适合webView的大小
        webSettings.setLoadWithOverviewMode(true); //缩放至屏幕大小
        //缩放操作
        webSettings.setSupportZoom(true); //支持缩放默认为true
        webSettings.setBuiltInZoomControls(true); //设置内置缩放控件。若为false,则该webView不可缩放
        webSettings.setDisplayZoomControls(true); //隐藏原生的缩放控件
       
        if (isNetworkAvailable(getApplicationContext())) {
            //有网络连接,设置默认缓存模式    
            webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        } ele { 
            //无网络连接,设置本地缓存模式缓存模式
            webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        }
        webSettings.setAllowFileAccess(true); //设置允许访问文件
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//支持通过js打开新的弹窗
        webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
        //设置缓存目录名
        webSettings.setAppCacheMaxSize(1024 * 1024 * 8);//存储的最大容量
        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath() + "rainbow";
        webSettings.setAppCachePath(appCachePath); //设置  Application Caches 缓存目录
        //缓存设置
        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setAppCacheEnabled(true);
        webSettings.setDefaultTextEncodingName("utf-8"); //设置文本默认编码格式
    }

 [2.2.1] 检测网络是否可用,根据网络状况,设置不同的缓存策略,具体如下:


    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager connectivity = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivity != null) {
            NetworkInfo info = connectivity.getActiveNetworkInfo();
               // 当前处于网络环境
            if (info != null && info.isConnected()) {
                  // 网络环境可用,主要防止App未授权网络
                if (info.getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
        return false;
    }

 [3] 初始化客户端浏览器,具体如下:


    public void initWebChromeClient() {
        //不直接启动浏览器去加载,而是通过webView去加载页面
        webView.setWebChromeClient(new WebChromeClient() {
            /**
             * 设置js弹窗
             * @param view 视图
             * @param url  路径
             * @param message 弹窗内容
             * @param result  结果
             * @return boolean
             */
            @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, (dialog, which) -> result.confirm());
                b.setCancelable(false);
                b.create().show();
                return super.onJsAlert(view, url, message, result);
            }

            /**
             * 设置响应js Confirm函数回调
             * @param view 视图
             * @param url  路径
             * @param message 弹窗内容
             * @param result  结果
             * @return
             */
            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
                b.setTitle("Confirm");
                b.setMessage(message);
                b.setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm());
                b.setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel());
                b.create().show();
                return true;
            }

            /**
             * 设置响应js 函数回调
             * @param view 视图
             * @param url  路径
             * @param message 弹窗内容
             * @param result  结果
             * @return
             */
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
                final View v = View.inflate(MainActivity.this, R.layout.prompt_dialog, null);
                ((TextView) v.findViewById(R.id.prompt_message_text)).setText(message);
                ((EditText) v.findViewById(R.id.prompt_input_field)).setText(defaultValue);
                AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
                b.setTitle("Prompt");
                b.setView(v);
                b.setPositiveButton(android.R.string.ok, (dialog, which) -> {
                    String value = ((EditText) v.findViewById(R.id.prompt_input_field)).getText().toString();
                    result.confirm(value);
                });
                b.setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel());
                b.create().show();
                return true;
            }

            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
                mUploadCallbackAboveL = filePathCallback;
                take();
                return true;
            }

            @SuppressWarnings("unused")
            //<3.0
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                mUploadMessage = uploadMsg;
                take();
            }

            @SuppressWarnings("unused")
            //>3.0+
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
                mUploadMessage = uploadMsg;
                take();
            }

            @SuppressWarnings("unused")
            //>4.1.1
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                take();
            }
        });
    }

 [4]  初始化WebView客户端,具体如下:


    public void initWebViewClient() {

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
      
           /**这里是进行android调用js的关键
             *tips:
             *
             * 1:如果android需要调用js组件,需要在回调函数onPageFinished中执行,否则调用无效
             *2、android调用js的两种手段
             * 2.1、通过webView.evaluateJavaScript("javascript:MyJsMethod()")
             * 2.2、通过webView.loadUrl("javascript:MyJsMethod()");
             */
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                webView.post(() -> {

                });
            }

            @Override
            public void onLoadResource(WebView view, String url) {
                super.onLoadResource(view, url);
            }

            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                switch (error.getErrorCode()) {
                    case 404:
                        view.loadUrl("错误界面");
                }
            }
        });
    }

 [5]  监听手机回退事件及解决手机back键直接退出的问题,具体如下:

  public void initListener() {
        webView.setOnKeyListener((v, keyCode, event) -> {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
                    webView.goBack();
                    return true;    //已处理
                }
            }
            return false;
        });
    }

  /**
     * 手机回退事件处理
     *
     * @param keyCode 回退事件码
     * @param event   事件
     * @return boolean
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KEYCODE_BACK && webView.canGoBack()) {
            webView.goBack();
        }
        return super.onKeyDown(keyCode, event);
    }

 [6] 通过webView加载html资源,具体如下:

/**
     * 加载WebView
     */
    public void loadWebView() {

        webView.clearCache(true);   //清除页面缓存
        webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); //取消右侧滑动栏
        String url="http://localhost:8080/xx/xx"; //通过网络加载
        String url="/asset/abc.html";//通过本地加载
        webView.loadUrl(url);
        
        webView.addJavascriptInterface(new CallMedia(), "callMedia");
    }

[7]  webView内存泄露处理 ,具体如下:

 /**
     * 当Activity停止时
     */
    @Override
    protected void onStop() {
        webSettings.setJavaScriptEnabled(false);

        super.onStop();
    }

    /**
     * 当Activity恢复时
     */
    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onResume() {
        webSettings.setJavaScriptEnabled(true);
        super.onResume();
    }

    /**
     * Activity销毁,防止webview内存泄露
     */
    @Override
    protected void onDestroy() {
        if (webView != null) {
//            webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
//            webView.clearHistory();
            ((ViewGroup) webView.getParent()).removeView(webView);
            webView.destroy();
            webView = null;
        }
        super.onDestroy();
    }

[8]  javascript调用android客户端SA层服务代码逻辑,主要是通过 @JavascriptInterface注解实现,具体如下:

      //声明暴露类
 public class CallMedia {
        public CallMedia() {
        }
       //声明暴露方法
        @JavascriptInterface
        public void callAndroidMedia(String token, String name, String data, int uid, int type) {
            if (type != MediaParamsType.VIDEO.getState() &&
                    type != MediaParamsType.AUDIO.getState())
                return;
            Intent intent = new Intent();
            Bundle bundle = new Bundle();
            bundle.putString(MediaRoomConstants.MEDIA_ROOM_TOKEN, token);
            bundle.putString(MediaRoomConstants.MEDIA_ROOM_NAME, name);
            bundle.putString(MediaRoomConstants.MEDIA_ROOM_DATA, data);
            bundle.putInt(MediaRoomConstants.MEDIA_ROOM_ID, uid);
            intent.putExtra("media", bundle);
            if (MediaParamsType.VIDEO.getState() == type) { //视频
                intent.setClass(MainActivity.this, VideoChatViewActivity.class);
            } else if (type == MediaParamsType.AUDIO.getState()) {//音频
                intent.setClass(MainActivity.this, VoiceChatViewActivity.class);
            }
            startActivity(intent, bundle);
        }
   
    }

[8.1]  暴露android原生接口给javascript调用,具体如下:

  webView.addJavascriptInterface(new CallMedia(), "callMedia");

[8.2]  js调用android端已暴露方法,具体如下:


callMedia对应有 webView.addJavascriptInterface(new CallMedia(), "callMedia");的callMedia接口对象。
执行代码:
callMedia.callAndroidMedia(token, name, data, uid,type)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值