Hybrid App体验(Android)
很早以前就听说过Hybrid App(混合模式移动应用),也大概了解它使用哪些技术,今天自己动手体验一把。大厂一般都是自己编译Chromium / Blink,还添加自己的定制。没有那个实力的话就选择别人提供好的SDK,比如腾讯浏览服务X5。我本次主要为了体验技术应用,不需要考虑不同Android版本的差异,就使用Android SDK中自带的WebView。
1. 新建Android工程,添加WebView
在Android Studio中新建Empty Activity的项目,再添加WebView。
activity_main.xml的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:id="@+id/blog_webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml中需要添加网络权限与修改布局:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.qinzhiqiang.webview1">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.NoActionBar">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
MainActivity.java中添加WebView,并开始浏览我们的网站:
package cn.qinzhiqiang.webview1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
WebView webView;
private long exitTime = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initWebView();
webView.loadUrl("https://micro.qinzhiqiang.cn");
}
private void initWebView() {
webView = (WebView)findViewById(R.id.blog_webview);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSupportZoom(false);
settings.setBuiltInZoomControls(false);
settings.setDefaultFontSize(16);
//设置缓存模式
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// 开启DOM storage API 功能
settings.setDomStorageEnabled(true);
webView.setWebViewClient(new WebViewClient() {
//在webview里打开新链接,否则会使用系统中浏览器打开新链接
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
webView.addJavascriptInterface(new JavaScriptInjectedNative(), "injectedNative");
}
private void sendMessageToWeb(String msgId, String payload) {
String jsContent = "if (this.nativeMessageHandler) { this.nativeMessageHandler("
+ Util.toJsString(msgId) + "," + Util.toJsString(payload) + ")}";
webView.loadUrl("javascript:" + jsContent);
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
if ((System.currentTimeMillis() - exitTime) > 2000) {
Toast.makeText(getApplicationContext(), "再按一次退出程序",
Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
finish();
}
}
}
}
2. JS Bridge连接Native与Web之间的通信
因Web中JS在一个独立的沙盒中运行,跟Android Native是不能直接通信的,我们需要给Web与Native提供通信桥接,否则都没法使用相机等设备。目前流行的技术是JS Bridge,其中也有几种实现机制。本文中,我们只看其中的一种。
2.1. Web向Native发送消息
只需要给WebView注入一个对象,在JS中调用该对象的方法injectedNative.sendMessage(msgId, payload)
即可。注入对象的代码如下:
package cn.qinzhiqiang.webview1;
import android.webkit.JavascriptInterface;
public class JavaScriptInjectedNative {
@JavascriptInterface
public String sendMessage(String msgId, String payload) {
return "Native processed: " + msgId + (payload != null ? ", " + payload : "");
}
}
注入方法,见:
webView.addJavascriptInterface(new JavaScriptInjectedNative(), "injectedNative");
在真实项目中,我们可以让Java中的代码来订阅与并处理这些消息。
2.2. Native向Web发送消息
Native向Web发送消息,其解决方案是直接让WebView执行一段JS代码。
private void sendMessageToWeb(String msgId, String payload) {
String jsContent = "if (this.nativeMessageHandler) { this.nativeMessageHandler("
+ Util.toJsString(msgId) + "," + Util.toJsString(payload) + ")}";
webView.loadUrl("javascript:" + jsContent);
}
我们可以在Web中直接定义function nativeMessageHandler(msgId, payload)
函数,或者在启动WebView时给它注入一段JS代码来完成这件事情。
3. 总结
Hybrid App中比较新颖的地方,就是JS Bridge。只要有通信的桥梁,不同的软件体系就可以融合到一块。
Hybrid App,一般还提供离线运行、在线升级的功能,Web文件会缓存到手机中。应用要加载本地资源时,务必进行数字签名校验,确保客户端的安全。