我们制作了一个Android应用程序,该应用程序当前使用WebView全屏显示web内容。
这是可行的,但性能在很大程度上取决于WebVeiw组件的版本,并且在Chrome浏览器更新时并不总是更新(在不同的Android版本中,WebView组件和Chrome浏览器之间存在相对复杂的关系)。从谷歌关于这个主题的演示中,我们得出结论,使用TWA,我们可能会获得更好、更一致的性能,因为TWA功能是与Chrome浏览器一起更新的。因此,当TWA不存在时(我们的应用程序在Android 4.4及更新版本上运行),我们希望使用TWA并回退到WebView。
该应用程序需要执行更多的逻辑,而不仅仅是显示web内容,因此我们无法仅在清单中定义TWA/WebView。检查是否能够使用TWA,并在MainActivity.java中启动TWA或回退到WebView。但是,使用TWA时,URL/地址栏和底部导航栏都保持可见。
URL/地址栏:据我们所知,要使TWA不显示URL/地址栏,TWA中显示的域必须有一个/.assetlinks.json文件;匹配";Android应用程序的证书。有两个页面提供了相关信息和有用的链接https://developers.google.com/web/android/trusted-web-activity/integration-guide和https://developer.android.com/training/app-links/verify-site-associations.assetlinks.json是使用https://developers.google.com/digital-asset-links/tools/generator并成功与进行了检查https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=website_url&relationship=delegate_permission/common.handle_all_urls
在虚拟设备(Android API 29级,Chrome v83)上测试期间;在非根设备上启用命令行";Chrome的标志,在一个终端上做了
$ adb shell "echo '_ --disable-digital-asset-link-verification-for-url="website_url"' > /data/local/tmp/chrome-command-line"
之后,Chrome会显示一条警告消息,但URL/地址栏仍然存在。
底部导航栏:Chrome v80及更新版本应支持使用沉浸式选项删除底部导航栏:https://bugs.chromium.org/p/chromium/issues/detail?id=965329#c18尽管使用了创建全屏应用程序所述的选项(https://developer.android.com/training/system-ui/immersive#java)底部导航栏仍然显示。
我们如何删除URL/地址栏和底部导航栏,基本上是如何使TWA中显示的web内容全屏显示
我们查看了以下示例应用程序,以了解我们需要做些什么才能使TWA发挥作用,但没有发现任何作用(尽管我们错过了一些重要的东西并非不可想象):
- https://github.com/GoogleChromeLabs/svgomg-twa
- https://github.com/tsuyosh/TrustedWebActivitiesDemo
- https://github.com/GoogleChrome/android-browser-helper
- https://github.com/elliatab/TwaDemo
- https://github.com/thanhtungka91/TwaDemoJava
我们项目文件的相关内容:
Manifest.json
<application
android:name="main_application"
android:hardwareAccelerated="true"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:banner="@drawable/banner"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme" >
<activity
android:name="main_activity"
android:hardwareAccelerated="true"
android:label="@string/app_name"
android:launchMode = "singleInstance"
android:keepScreenOn="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
主要活动.java
public class MainActivity extends Activity implements IServiceCallbacks {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up looks of the view
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
// Note that system bars will only be "visible" if none of the
// LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
// bars are visible => user touched the screen, make the bars disappear again in 2 seconds
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
hideBars();
}
}, 2000);
} else {
// The system bars are NOT visible => do nothing
}
}
});
decorView.setKeepScreenOn(true);
setContentView(R.layout.activity_main);
// create Trusted Web Access or fall back to a WebView
String chromePackage = CustomTabsClient.getPackageName(this, TrustedWebUtils.SUPPORTED_CHROME_PACKAGES, true);
if (chromePackage != null) {
if (!chromeVersionChecked) {
TrustedWebUtils.promptForChromeUpdateIfNeeded(this, chromePackage);
chromeVersionChecked = true;
}
if (savedInstanceState != null && savedInstanceState.getBoolean(MainActivity.TWA_WAS_LAUNCHED_KEY)) {
this.finish();
} else {
this.twaServiceConnection = new MainActivity.TwaCustomTabsServiceConnection();
CustomTabsClient.bindCustomTabsService(this, chromePackage, this.twaServiceConnection);
}
} else {
// set up WebView
}
}
private class TwaCustomTabsServiceConnection extends CustomTabsServiceConnection {
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
CustomTabsSession session = MainActivity.this.getSession(client);
CustomTabsIntent intent = MainActivity.this.getCustomTabsIntent(session);
Uri url = Uri.parse("http://our_url");
TrustedWebUtils.launchAsTrustedWebActivity(MainActivity.this, intent, url);
MainActivity.this.twaWasLaunched = true;
}
public void onServiceDisconnected(ComponentName componentName) {
}
}
protected void hideBars() {
if (getWindow() != null) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
// Hide the nav bar and status bar
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
// Enables regular immersive mode
| View.SYSTEM_UI_FLAG_IMMERSIVE
);
}
// Remember that you should never show the action bar if the
// status bar is hidden, so hide that too if necessary.
ActionBar actionBar = getActionBar();
if (actionBar != null) {
actionBar.hide();
}
}
}
build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
allprojects {
repositories {
jcenter()
google()
maven { url "https://jitpack.io" }
}
}
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
applicationId application_id
minSdkVersion 19
targetSdkVersion 29
}
buildTypes {
release {
minifyEnabled true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
jniDebuggable true
renderscriptDebuggable true
renderscriptOptimLevel 3
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.leanback:leanback:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.webkit:webkit:1.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.github.GoogleChrome.custom-tabs-client:customtabs:master'
}
/.知名/assetlinks.json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target" : { "namespace": "android_app", "package_name": "our package name",
"sha256_cert_fingerprints": ["11:22:33:44"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target" : { "namespace": "android_app", "package_name": "our other package name",
"sha256_cert_fingerprints": ["11:22:33:44"]
}
}
]
关于数字资产链接验证,我建议安装Peter的资产链接工具并使用它来检查配置。请务必仔细检查快速入门指南中关于应用程序签名的部分,因为签名在使用时会发生变化,导致验证失败——应用程序是从Play Store下载的(您必须更新assetlinks.json
才能使其工作)。
您似乎也在使用custom-tabs-client
库,该库已被弃用,并建议移动到android-browser-helper
,这是Trusted Web Activity的推荐库。如果您确实想使用较低级别的库,androidx.browser
将是您要使用的库(我真的建议使用android-browser-helper
.
android-browser-helper
包括一个LauncherActivity
,它使事情变得非常简单,因为大多数方面都可以从AndroidManifest.xml
进行配置,但它也希望从主屏幕启动Trusted Web Activity。twa-basic
演示演示了如何使用LauncherActivity
。
对于其他用例,可以使用TwaLauncher
(它由LauncherActivity
本身使用)。twa-custom-launcher
演示演示了如何使用它。LauncherActivity
的源代码也很有用。
最后,如果目标是简单地从主屏幕启动Progressive Web应用程序,那么Bubblewrap是一个Node.js命令行工具,可以自动执行该过程。
关于身临其境模式,以下是它在twa基本演示中的设置方式。如果使用TwaLauncher
,则此处为要使用的LauncherActivity
代码。
使用Bubblewrap时,在创建项目时选择fullscreen
作为"显示模式",以在沉浸式模式下启动应用程序。
奖金:由于您提到想要使用WebView回退实现,您可能有兴趣了解android-browser-helper
附带了WebView回退(默认情况下禁用)。twa-webview-fallback
演示演示了如何使用它