主页 > app >
为 App Clips (iOS) 和 Instant Apps (Android) 启用 Cordova
25-04-30
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
我有两个版本的 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
我正在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 近期,有同事咨询如何在 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”
我们最近在Google Play上部署了Instant App进行内部测试。 如果用户在Play上点击“立即尝试”按钮,则Instant App初始屏幕将不显示任何按钮,而只是开始下载(这是我们喜欢的行为)。
但是,如果用户通过应用程序链接,则该初始屏幕具有两个按钮:OPEN APP和OPEN IN broWSER。如果用户不小心点击了“打开浏览器”,则他们将永远不会看到即时应用。只需将它们带到浏览器。
我们根据文档设置了应用程序链接。那么,可以禁用此选择吗?我们希望用户始终使用即时应用。
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
有没有人遇到过这个问题并且能够解决它。
我的工作环境是:
Mac 操作系统 10.15.7
Android Studio 4.0.1
三星 A70
Android 版本 10
解决方法
虽然这在 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 中崩溃 等相关内容,可以在本站寻找。