GVKun编程网logo

为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova

1

本文的目的是介绍为AppClips(iOS)和InstantApps(Android)启用Cordova的详细情况,我们将通过专业的研究、有关数据的分析等多种方式,同时也不会遗漏关于ActionCon

本文的目的是介绍为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova的详细情况,我们将通过专业的研究、有关数据的分析等多种方式,同时也不会遗漏关于ActionController :: RoutingError没有路由与[GET]“ / vendor / assets / stylesheets / bootstrap.min.css”匹配 app / assets / stylesheets / application.css: app / assets / javascript / application.js: app / views / layout / application.html.erb: app / j、Android H5 混合开发(5):封装 Cordova View, 让 Fragment、弹框、Activity 自由使用 Cordova、Android Instant App:当用户下载Instant App时,禁用“ OPEN IN BROWSER”、Android KeyStore 在 Android Instant App 中崩溃的知识。

本文目录一览:

为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova

为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova

如何解决为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova

我有两个版本的 iOS 和 Android 混合应用程序,它们都启用了 Cordova,其中包括 Cordova 框架和插件。

在当前发布的 App Clips (iOS) 和 Instant Apps (Android) 中,我想通过 Cordova 启用它们,以利用已经内置的 Cordova 插件。

如果您有任何解决方法,或者对于 App Clips (iOS) 和 Instant Apps (Android) 用例,这是不可能/不推荐的,请告诉我。

ActionController :: RoutingError没有路由与[GET]“ / vendor / assets / stylesheets / bootstrap.min.css”匹配 app / assets / stylesheets / application.css: app / assets / javascript / application.js: app / views / layout / application.html.erb: app / j

ActionController :: RoutingError没有路由与[GET]“ / vendor / assets / stylesheets / bootstrap.min.css”匹配 app / assets / stylesheets / application.css: app / assets / javascript / application.js: app / views / layout / application.html.erb: app / j

如何解决ActionController :: RoutingError没有路由与[GET]“ / vendor / assets / stylesheets / bootstrap.min.css”匹配 app / assets / stylesheets / application.css: app / assets / javascript / application.js: app / views / layout / application.html.erb: app / j

我正在Rails上执行此应用程序,页面加载正常,但在控制台中显示:

Started GET "/vendor/assets/stylesheets/bootstrap.min.css" for ::1 at 2020-08-17 09:09:51 -0500

Started GET "/vendor/assets/javascripts/bootstrap.min.js" for ::1 at 2020-08-17 09:09:51 -0500
ActionController::RoutingError (No route matches [GET] 
"/vendor/assets/stylesheets/bootstrap.min.css"):
ActionController::RoutingError (No route matches [GET] 
"/vendor/assets/javascripts/bootstrap.min.js"):

在我的供应商文件夹中,我同时拥有文件夹,javascript和样式表,并分别位于各自的bootsrtrap.min中。 即使在application.html.erb中,我也有以下内容:

  <link href="vendor/assets/stylesheets/bootstrap.min.css" rel="stylesheet">
  <%= csrf_Meta_tags %>
  <%= stylesheet_link_tag    ''application'',media: ''all'',''data-turbolinks-track'': ''reload'' %>
  <%= javascript_include_tag ''application'',''data-turbolinks-track'': ''reload'' %>

</head>
<body>

  <!-- Navigation -->
  <nav>
    <div>
      <ahref="#">Instagram Clone</a>
      <buttontype="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span></span>
      </button>
      <divid="navbarResponsive">
        <ul>
          <li>
            <ahref="#">Home
              <span>(current)</span>
            </a>
          </li>
          <li>
            <ahref="#">About</a>
          </li>
          <li>
            <ahref="#">Services</a>
          </li>
          <li>
            <ahref="#">Contact</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <!-- Page Content -->
  <div>
    <div>
      <div>
        <%= yield %>
      </div>
    </div>
  </div>

  <!-- Bootstrap core JavaScript -->
  <!--<script src="vendor/jquery/jquery.slim.min.js"></script>-->
  <script src="vendor/assets/javascripts/bootstrap.min.js"></script>
    
</body>
</html>

在js和CSS这两个应用程序文件中,我都放置了“ require bootsrtrap.min”,即使该错误仍然出现在控制台中。 会是什么?

解决方法

RoR不使用目录作为文件夹结构指示。因为有资产装载者。您应该将5s或更少的css导入application.css中,并将js导入application.js中。并使用自己的助手添加捆绑文件:

1。用纱安装靴子

yarn add bootstrap @popperjs/core jquery

npm i bootstrap @popperjs/core jquery

栏位不超过5

2。在Assets Pipeline中导入文件

app / assets / stylesheets / application.css:

//...
require bootstrap/dist/css/bootstrap.min
//...

但是,如果需要,我建议使用SCSS并仅导入所需的样式。

app / assets / javascript / application.js:

//...
//= require jquery
//= require @popperjs/core
//= require bootstrap/dist/js/bootstrap.min
//...

3。在布局中导入资产

app / views / layout / application.html.erb:

<head>
...
  <%= stylesheet_link_tag ''application'',media: ''all'' %>
</head>
<body>
...
  <%= javascript_include_tag ''application'' %>
</body>

使用webpack滚动6

2。在application.scss和application.js中导入Bootstrap

app / javascript / stylesheets / application.scss:

//...
import ''bootstrap''
//...

app / javascript / packs / application.js:

import ''jquery''
import ''popper.js''
import ''bootstrap''
import ''../stylesheets/application''

3。在布局中导入捆绑包

<head>
...
  <%= stylesheet_pack_tag ''application'',media: ''all'',''data-turbolinks-track'': ''reload'' %>
  <%= javascript_pack_tag ''application'',''data-turbolinks-track'': ''reload'' %>
</head>

之后,您应该可以在所有应用程序中使用引导程序!

,

[解决方案]因此,在我的HTML(application.html.erb)内部,我有两个对js和css文件的调用:

<script src="assets/javascripts/bootstrap.min.js"></script>
  <%= javascript_include_tag ''application'',''data-turbolinks-track'': ''reload'' %>       
<link href="assets/stylesheets/bootstrap.min.css" rel="stylesheet">
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag    ''application'',''data-turbolinks-track'': 
  ''reload'' %>

正如您在上面看到的那样,在使用javascript的情况下,我有一个使用脚本的调用,而另一个使用了的调用,两者都造成了问题,所以您只需要评论一个,我就对该脚本进行评论对于我的java文件和CSS文件,一个。 像这样:

  <!--<link href="assets/stylesheets/bootstrap.min.css" rel="stylesheet">-->
    <!--<script src="assets/javascripts/bootstrap.min.js"></script>-->

对我来说,解决了这个问题,我真的尝试了所有事情,这就是解决方案哈哈。

Android H5 混合开发(5):封装 Cordova View, 让 Fragment、弹框、Activity 自由使用 Cordova

Android H5 混合开发(5):封装 Cordova View, 让 Fragment、弹框、Activity 自由使用 Cordova

近期,有同事咨询如何在 Fragment 中使用 Cordova,看了下 Cordova 源码,官方并没有提供包含 Cordova Webview 的 Fragment,以供我们继承。

上网查询了一下,也有几篇文章讲解 Fragment 中如何使用 Cordova,不过 Cordova 逻辑与 Fragment 逻辑耦合太深,不太适用于常规项目开发。

通过分析 CordovaActivity 的源码实现,我们只需要将 Cordova 封装成自定义 View 就可以了。后面的演示,咱们还是基于之前的工程吧,代码会在后面分享给大家的。

####CordovaView 实现的目标: 应像系统 Webview 一样,与页面逻辑解耦,且方便使用

1、CordovaView的逻辑应独立;
2、能在Fragment中使用;
3、能在Activity中使用;
4、能在弹框中使用;

CordovaView 封装

因我这边时间比较紧张,所以不带领大家去分析 CordovaActivity 的实现原理了,此处直接贴出自定义 CordovaView 的源码吧:

###### 自定义控件内容,主要包含:

1、CordovaWebView的初始化、UI、URL加载、配合Activity、Fragment生命周期等;
2、读取Cordova配置文件,设置页面相关属性;
3、Crodova接口实现(无需自己写,直接使用Cordova自带的CordovaInterfaceImpl)
4、异常消息回调,以便于使用方自己控制错误提示等;
5、返回真实Webview控件,以便于使用方自行控制goback、reload、websetting等;
6、其他...

######CordovaView.java 实现 (重要的注释都已加上了):

package com.ccc.ddd;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import org.apache.cordova.Config;
import org.apache.cordova.ConfigXmlParser;
import org.apache.cordova.CordovaInterfaceImpl;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaWebViewEngine;
import org.apache.cordova.CordovaWebViewImpl;
import org.apache.cordova.PluginEntry;
import org.apache.cordova.PluginManager;
import org.apache.cordova.engine.SystemWebView;
import org.apache.cordova.engine.SystemWebViewEngine;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Field;
import java.util.ArrayList;

/**
 * 自定义Cordova控件
 * 1、可用于Activity、Fragment集成
 * 2、可在布局xml文件中引入
 * 3、可在代码中使用new关键字创建实例
 *
 * <p>
 * 使用示例:
 * String launchUrl = "file:///android_asset/www/index.html";
 * CordovaView cordovaView = view.findViewById(R.id.cv);
 * cordovaView.initCordova(getActivity());
 * cordovaView.loadUrl(launchUrl);
 * <p>
 * 
 * 作者:齐xc
 * 日期:2019.09.15
 */
public class CordovaView extends RelativeLayout {
    //页面对象
    private Activity activity;
    //Cordova浏览器对象: 初始化、UI布局控制、url加载、生命周期(开始、暂停、销毁...)
    protected CordovaWebView appView;
    //Cordova配置对象: 各类配置信息读取、设置、使用
    protected CordovaPreferences preferences;
    //Cordova接口实现对象: 消息处理(页面跳转、页面数据存取、权限申请...)
    protected CordovaInterfaceImpl cordovaInterface;
    //是否保持运行
    protected boolean keepRunning = true;
    //是否沉浸式
    protected boolean immersiveMode;
    //默认启动url
    protected String launchUrl;
    //插件实体类集合
    protected ArrayList<PluginEntry> pluginEntries;
    //接收错误的监听器(用于回调页面加载错误,如:页面未找到等等。使用方需先调用方法:setOnReceivedErrorListener())
    private OnReceivedErrorListener errorListener;

    /**
     * 构造函数
     *
     * @param context 上下文
     */
    public CordovaView(Context context) {
        super(context);
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrs   属性
     */
    public CordovaView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 初始化Cordova
     *
     * @param activity 页面
     */
    public void initCordova(Activity activity) {
        this.activity = activity;

        //加载配置信息
        loadConfig();

        //设置页面是否全屏
        if (preferences.getBoolean("SetFullscreen", false)) {
            preferences.set("Fullscreen", true);
        }
        if (preferences.getBoolean("Fullscreen", false)) {
            if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
                immersiveMode = true;
            } else {
                activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
            }
        } else {
            activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        }

        //实例化接口实现
        cordovaInterface = makeCordovaInterface();

        //设置背景为白色
        activity.getWindow().getDecorView().setBackgroundColor(Color.WHITE);

        //初始化
        initCordova();
    }

    /**
     * 加载配置信息
     * 1.读取默认启动的url  file:///android_asset/www/index.html
     * 2.读取res/xml/config.xml文件,获得插件集合 pluginEntries
     */
    private void loadConfig() {
        ConfigXmlParser parser = new ConfigXmlParser();
        parser.parse(activity);
        preferences = parser.getPreferences();
        preferences.setPreferencesBundle(activity.getIntent().getExtras());
        launchUrl = parser.getLaunchUrl();
        pluginEntries = parser.getPluginEntries();
        try {
            //通过反射处理
            Field field = Config.class.getDeclaredField("parser");
            field.setAccessible(true);
            field.set(null, parser);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 初始化
     */
    private void initCordova() {
        //实例化webview对象
        appView = makeWebView();
        //将webview加载到页面中,并根据参数配置其属性
        createViews();
        //如果"实例化接口"为空
        if (!appView.isInitialized()) {
            //appview初始化
            //初始化插件管理
            //初始化消息队列
            //初始化桥模块
            //......
            appView.init(cordovaInterface, pluginEntries, preferences);
        }
        //设置插件管理器
        //设置onActivityResult消息回调
        //设置Activity销毁处理
        cordovaInterface.onCordovaInit(appView.getPluginManager());
    }


    /**
     * 创建views
     */
    @SuppressWarnings({"deprecation", "ResourceType"})
    private void createViews() {
        //appView.getView()指SystemWebViewEngine的SystemWebView,继承自android.webkit.WebView
        appView.getView().setId(100);
        appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        //设置当前视图为SystemWebViewEngine的SystemWebView
        //setContentView(appView.getView());
        this.removeAllViews();
        this.addView(appView.getView(), new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

        //如果preferences有配置背景色,则设置webview背景色
        if (preferences.contains("BackgroundColor")) {
            try {
                int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
                // Background of activity:
                appView.getView().setBackgroundColor(backgroundColor);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

        //webview获得焦点(不受touch限制)
        appView.getView().requestFocusFromTouch();
    }

    /**
     * 构建CordovaWebView
     *
     * @return CordovaWebView
     */
    private CordovaWebView makeWebView() {
        //1.通过preferences配置信息构建CordovaWebViewEngine
        //2.通过CordovaWebViewEngine构建CordovaWebView
        return new CordovaWebViewImpl(makeWebViewEngine());
    }

    /**
     * 通过preferences配置信息构建CordovaWebViewEngine
     *
     * @return CordovaWebViewEngine
     */
    private CordovaWebViewEngine makeWebViewEngine() {
        return CordovaWebViewImpl.createEngine(activity, preferences);
    }

    /**
     * 构建接口实现类,接收消息
     *
     * @return CordovaInterfaceImpl
     */
    private CordovaInterfaceImpl makeCordovaInterface() {
        return new CordovaInterfaceImpl(activity) {
            @Override
            public Object onMessage(String id, Object data) {
                return CordovaView.this.onMessage(id, data);
            }
        };
    }

    /**
     * 处理消息
     *
     * @param id   消息id
     * @param data 消息数据
     * @return 处理结果
     */
    public Object onMessage(String id, Object data) {
        try {
            if ("onReceivedError".equals(id)) {
                JSONObject d = (JSONObject) data;
                try {
                    //将消息透传给客户端
                    if (errorListener != null) {
                        int errorCode = d.getInt("errorCode");
                        String description = d.getString("description");
                        String failingUrl = d.getString("url");
                        errorListener.onReceivedError(errorCode, description, failingUrl);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }


    /**
     * 获得Webview组件,供客户端使用
     *
     * @return Webview组件
     */
    public SystemWebView getWebview() {
        if (appView != null && appView.getView() instanceof WebView) {
            SystemWebView webView = (SystemWebView) appView.getView();
            return webView;
        }
        return null;
    }

    /**
     * 获得系统webview引擎
     * @return 系统webview引擎
     */
    public SystemWebViewEngine getSystemWebViewEngine(){
        return (SystemWebViewEngine) appView.getEngine();
    }

    /**
     * 加载url
     *
     * @param url 地址,默认应是:file:///android_asset/www/index.html
     */
    public void loadUrl(String url) {
        if (appView == null) {
            initCordova();
        }

        // If keepRunning
        // 如果preferences配置了KeepRunning,则页面置于后台时,仍可见
        this.keepRunning = preferences.getBoolean("KeepRunning", true);

        //加载url
        //第2个参数表示重新初始化插件管理器、插件集合等
        appView.loadUrlIntoView(url, true);
    }

    /**
     * 当页面执行onPause方法时,可调用
     */
    public void onPause() {
        if (this.appView != null) {
            CordovaPlugin activityResultCallback = null;
            try {
                Field field = CordovaInterfaceImpl.class.getDeclaredField("activityResultCallback");
                field.setAccessible(true);
                activityResultCallback = (CordovaPlugin) field.get(this.cordovaInterface);
            } catch (Exception e) {
                e.printStackTrace();
            }
            boolean keepRunning = this.keepRunning || activityResultCallback != null;
            this.appView.handlePause(keepRunning);
        }
    }

    /**
     * 当页面执行onNewIntent方法时,可调用
     */
    public void onNewIntent(Intent intent) {
        if (this.appView != null)
            this.appView.onNewIntent(intent);
    }

    /**
     * 当页面执行onResume方法时,可调用
     */
    public void onResume() {
        if (this.appView == null) {
            return;
        }
        activity.getWindow().getDecorView().requestFocus();
        this.appView.handleResume(this.keepRunning);
    }

    /**
     * 当页面执行onStop方法时,可调用
     */
    public void onStop() {
        if (this.appView == null) {
            return;
        }
        this.appView.handleStop();
    }

    /**
     * 当页面执行onStart方法时,可调用
     */
    public void onStart() {
        if (this.appView == null) {
            return;
        }
        this.appView.handleStart();
    }

    /**
     * 当页面执行onDestroy方法时,可调用
     */
    public void onDestroy() {
        if (this.appView != null) {
            appView.handleDestroy();
        }
    }

    /**
     * 当页面执行onWindowFocusChanged方法时,可调用
     */
    @SuppressLint("InlinedApi")
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        //设置沉浸式与全屏
        if (hasFocus && immersiveMode) {
            final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
            activity.getWindow().getDecorView().setSystemUiVisibility(uiOptions);
        }
    }

    /**
     * 当页面执行startActivityForResult方法时,可调用
     */
    @SuppressLint({"NewApi", "RestrictedApi"})
    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        // Capture requestCode here so that it is captured in the setActivityResultCallback() case.
        cordovaInterface.setActivityResultRequestCode(requestCode);
        //super.startActivityForResult(intent, requestCode, options);
    }

    /**
     * 当页面执行onActivityResult方法时,可调用
     */
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        //super.onActivityResult(requestCode, resultCode, intent);
        cordovaInterface.onActivityResult(requestCode, resultCode, intent);
    }

    /**
     * 当页面执行onSaveInstanceState方法时,可调用
     */
    public void onSaveInstanceState(Bundle outState) {
        cordovaInterface.onSaveInstanceState(outState);
    }

    /**
     * 当页面执行onConfigurationChanged方法时,可调用
     */
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (this.appView == null) {
            return;
        }
        PluginManager pm = this.appView.getPluginManager();
        if (pm != null) {
            pm.onConfigurationChanged(newConfig);
        }
    }

    /**
     * 接口:接收到错误时的回调
     */
    public interface OnReceivedErrorListener {
        /**
         * 接收到错误
         *
         * @param errorCode   错误码(请参考Cordova官方定义)
         * @param description 错误描述
         * @param failingUrl  发生异常的url
         */
        void onReceivedError(int errorCode, String description, String failingUrl);
    }

    /**
     * 设置监听
     *
     * @param listener 监听器
     */
    public void setOnReceivedErrorListener(OnReceivedErrorListener listener) {
        this.errorListener = listener;
    }
}

如何使用

1、Fragment 中(灰常简单)

布局文件:fragment_test.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:background="#ffff00"
    tools:context=".TestFragment">

    <com.ccc.ddd.CordovaView
        android:id="@+id/cv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </com.ccc.ddd.CordovaView>

</FrameLayout>

代码:TestFragment.java

package com.ccc.ddd;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class TestFragment extends Fragment {
    public TestFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_test, container, false);
        initView(view);
        return view;
    }

    private void initView(View view) {
        String launchUrl = "file:///android_asset/www/index1.html";
        final CordovaView cordovaView = view.findViewById(R.id.cv);
        cordovaView.initCordova(getActivity());
        cordovaView.loadUrl(launchUrl);
        //如果需要处理异常,设置此回调即可;
        cordovaView.setOnReceivedErrorListener(new CordovaView.OnReceivedErrorListener() {
            @Override
            public void onReceivedError(int errorCode, String description, String failingUrl) {
                Log.i("onReceivedError", "errorCode:" + errorCode + "     description:" + description + "     failingUrl:" + failingUrl);
            }
        });
        //获得Cordova的Webview控件,执行操作,如:reload、goback、设置缓存、获得进度条等等
        //cordovaView.getWebview().reload();
        //cordovaView.getWebview().goBack();
        /*cordovaView.getWebview().setWebChromeClient(new SystemWebChromeClient(cordovaView.getSystemWebViewEngine()) {
            //监听进度
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                //设置页面加载进度
                Log.i("newProgress","newProgress: "+newProgress);
            }

            @Override
            public void onReceivedTitle(WebView view, String title) {
                super.onReceivedTitle(view, title);
                //设置标题
            }
        });*/
    }
}

####2、Activity 中(同样灰常简单) 布局文件:activity_test_cordova_view.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    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"
    tools:context=".TestCordovaViewActivity">

    <com.ccc.ddd.CordovaView
        android:id="@+id/cv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.ccc.ddd.CordovaView>

</android.support.constraint.ConstraintLayout>

代码实现:TestCordovaViewActivity.java

package com.ccc.ddd;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class TestCordovaViewActivity extends AppCompatActivity {

    private CordovaView cordovaView;
    private String launchUrl = "file:///android_asset/www/index.html";

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

        cordovaView = findViewById(R.id.cv);
        cordovaView.initCordova(this);
        cordovaView.loadUrl(launchUrl);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        cordovaView.onDestroy();
    }
}

## 其他用法 在弹框中实现,也是类似的,具体不再演示了。 ######1)生命周期 如果想关联 Activity 或 Frament 的生命周期,CordovaView 中都已经预留了生命周期方法,只需在页面生命周期方法中,关联对应的方法即可,如:

@Override
    protected void onDestroy() {
        super.onDestroy();
        cordovaView.onDestroy();
    }
...其他生命周期函数写法类似...

######2)异常消息处理 CordovaWebview 加载过程中如果遇到问题,会将错误信息回传,如果开发者需要处理其错误信息,只需设置监听即可:

cordovaView.setOnReceivedErrorListener(new CordovaView.OnReceivedErrorListener() {
            @Override
            public void onReceivedError(int errorCode, String description, String failingUrl) {
                Log.i("onReceivedError", "errorCode:" + errorCode + "     description:" + description + "     failingUrl:" + failingUrl);
            }
        });

######3)Webview 控件 通过 cordovaView.getWebview () 方法获得 Webview 控件,可用于配置 WebSetting、设置 goback、设置 reload、监听加载进度等。

cordovaView.getWebview().reload();
cordovaView.getWebview().goBack();
cordovaView.getWebview().setWebChromeClient(new SystemWebChromeClient(cordovaView.getSystemWebViewEngine()) {
            //监听进度
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                //设置页面加载进度
                Log.i("newProgress","newProgress: "+newProgress);
            }

            @Override
            public void onReceivedTitle(WebView view, String title) {
                super.onReceivedTitle(view, title);
                //设置标题
            }
        });

##Fragment 中运行效果

##Demo 源码下载

https://pan.baidu.com/s/1_6Ms-K_Wj5EjCp01LVZHhg https://github.com/qxcwanxss/CordovaDemo.git

如有不明白的地方,建议多看下 CordovaActivity 的源码实现 ,也欢迎留言,就是不一定有时间解答,哈哈。。。


Android H5 混合开发(1):构建 Cordova 项目 https://www.cnblogs.com/qixingchao/p/11654454.html

Android H5 混合开发(2):自定义 Cordova 插件 https://www.cnblogs.com/qixingchao/p/11652418.html

Android H5 混合开发(3):原生 Android 项目里嵌入 Cordova https://www.cnblogs.com/qixingchao/p/11652424.html

Android H5 混合开发(4):构建 Cordova Jar 包 https://www.cnblogs.com/qixingchao/p/11652431.html

Android H5 混合开发(5):封装 Cordova View, 让 Fragment、弹框、Activity 自由使用 Cordova https://www.cnblogs.com/qixingchao/p/11652438.html

Android Instant App:当用户下载Instant App时,禁用“ OPEN IN BROWSER”

Android Instant App:当用户下载Instant App时,禁用“ OPEN IN BROWSER”

如何解决Android Instant App:当用户下载Instant App时,禁用“ OPEN IN BROWSER”

我们最近在Google Play上部署了Instant App进行内部测试。 如果用户在Play上点击“立即尝试”按钮,则Instant App初始屏幕将不显示任何按钮,而只是开始下载(这是我们喜欢的行为)。

但是,如果用户通过应用程序链接,则该初始屏幕具有两个按钮:OPEN APP和OPEN IN broWSER。如果用户不小心点击了“打开浏览器”,则他们将永远不会看到即时应用。只需将它们带到浏览器。

我们根据文档设置了应用程序链接。那么,可以禁用此选择吗?我们希望用户始终使用即时应用。

Android KeyStore 在 Android Instant App 中崩溃

Android KeyStore 在 Android Instant App 中崩溃

如何解决Android KeyStore 在 Android Instant App 中崩溃

val masterKey =
            MasterKey
            .Builder(applicationContext)
            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
            .build()

在 Instant App 中运行此代码会导致我的应用程序崩溃并显示以下堆栈跟踪:

2020-12-21 13:07:29.654 28949-28949/com.example.instantappandroidkeystore E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.instantappandroidkeystore,PID: 28949
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.instantappandroidkeystore/com.example.instantappandroidkeystore.MainActivity}: java.lang.NullPointerException: Attempt to invoke interface method ''int android.security.keystore.IKeystoreService.exist(java.lang.String,int)'' on a null object reference
        at android.app.ActivityThread.performlaunchActivity(ActivityThread.java:3448)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2147)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:237)
        at android.app.ActivityThread.main(ActivityThread.java:7814)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068)
     Caused by: java.lang.NullPointerException: Attempt to invoke interface method ''int android.security.keystore.IKeystoreService.exist(java.lang.String,int)'' on a null object reference
        at android.security.KeyStore.contains(KeyStore.java:408)
        at android.security.keystore.AndroidKeyStoreSpi.engineContainsAlias(AndroidKeyStoreSpi.java:1038)
        at java.security.KeyStore.containsAlias(KeyStore.java:1293)
        at androidx.security.crypto.MasterKeys.keyExists(MasterKeys.java:154)
        at androidx.security.crypto.MasterKeys.getorCreate(MasterKeys.java:96)
        at androidx.security.crypto.MasterKey$Builder.buildOnM(MasterKey.java:357)
        at androidx.security.crypto.MasterKey$Builder.build(MasterKey.java:314)
        at com.example.instantappandroidkeystore.MainActivity.onCreate(MainActivity.kt:32)
        at android.app.Activity.performCreate(Activity.java:7955)
        at android.app.Activity.performCreate(Activity.java:7944)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
        at android.app.ActivityThread.performlaunchActivity(ActivityThread.java:3423)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2147) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:237) 
        at android.app.ActivityThread.main(ActivityThread.java:7814) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068) 

我没能在 StackOverflow 中找到这个问题,但我在 Google 问题跟踪器中找到了它,但没有任何回应。所以我创建了一个问题给谷歌,提供更多信息。 https://issuetracker.google.com/issues/176085956

有没有人遇到过这个问题并且能够解决它。

我的工作环境是:

解决方法

虽然这在 developer.android 论坛上没有正式记录,但 AndroidKeyStore 似乎限制了免安装应用的访问。我发现了这个链接 https://www.reddit.com/r/androiddev/comments/7a4tdw/why_no_hardware_backed_keystore_in_instant_apps/

keystore 受到限制并且没有 android 文档对此进行解释,这很奇怪。我不确定访问 KeyStore 的替代方案,因为我们对常规应用程序(非即时)的密钥库有很多依赖,我想不出更好的选择。期待建议!

关于为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于ActionController :: RoutingError没有路由与[GET]“ / vendor / assets / stylesheets / bootstrap.min.css”匹配 app / assets / stylesheets / application.css: app / assets / javascript / application.js: app / views / layout / application.html.erb: app / j、Android H5 混合开发(5):封装 Cordova View, 让 Fragment、弹框、Activity 自由使用 Cordova、Android Instant App:当用户下载Instant App时,禁用“ OPEN IN BROWSER”、Android KeyStore 在 Android Instant App 中崩溃等相关内容,可以在本站寻找。

本文标签:

上一篇升级到 Facebook Marketing API 9.0 潜在客户检索返回零潜在客户(facebook精准客户)

下一篇如何让用户将他们的自定义域连接到我在 Google App Engine 上托管的 next.js 应用程序?