在互联网时代,移动应用的市场越来越火爆。不仅仅是大型APP,像小程序、H5应用这种轻量级的应用越来越受到用户的欢迎。但是,由于不是原生应用,H5应用并不能和原生应用有同等的使用效果。封装H5应用为原生应用则可以弥补这一短板,本文将介绍封装H5应用为原生应用的原理及详细步骤。
一、封装H5应用的原理
封装H5应用为原生应用的原理其实很简单,这里主要分两步:
1. 将H5应用解压缩到本地目录中,并使用Webview调用index.html文件;
2. 使用桥接方式,将原生App和JS之间的交互打通。
第一步使用的是Webview技术,它是Android系统中的一种组件,可以在应用内加载网页并显示出来。我们只需要把H5应用的全部静态文件打包成一个zip压缩包,在App中解压缩,然后使用WebView控件打开H5应用中的index.html文件即可。
第二步则需要从两个应用技术角度考虑:原生应用和H5应用之间要如何通信?具体来说,就是如何实现原生App调用H5应用,以及H5应用回调原生App的功能。这一步主要实现思路是在原生App中内置一个JavaScript桥接文件,通过该文件,JS可以直接读取原生App的接口,另一方面,原生App也可以通过该文件直接调用H5中的js方法。
二、封装H5应用的具体步骤
下面详细介绍一下,封装H5应用为原生应用的具体步骤。
1. H5应用的打包和解压
我们首先需要将H5应用的代码打包成zip文件,命名为“app.zip”,然后将该zip文件复制到App的assets目录下。当App启动时,首先从资源目录中读取app.zip文件,并解压到磁盘上,如下所示:
```
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//复制app.zip到sdcard根目录下,并解压到app目录中
String zipPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/app.zip";
String appPath = getApplicationContext().getFilesDir().getAbsolutePath();
FileUtils.copyFile(zipPath, appPath + "/app.zip"); //复制
ZipUtils.unZip(appPath + "/app.zip", appPath + "/app"); //解压
}
}
```
在解压之后,我们需要将H5应用的入口文件(index.html)通过Webview调用出来,如下所示:
```
public class MainActivity extends AppCompatActivity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//找到WebView控件,并加载index.html
mWebView = (WebView) findViewById(R.id.web_view);
String appPath = getApplicationContext().getFilesDir().getAbsolutePath();
mWebView.loadUrl("file://" + appPath + "/app/index.html");
}
}
```
2. 原生App和H5应用之间的交互
下面我们将介绍如何实现App和H5应用之间的交互。
首先,创建一个JsBridge类,这个类继承自WebViewClient。按照常规做法,在加载H5应用时用该类覆盖掉WebView的默认实现。我们可以通过这个桥接文件,在App和H5应用之间建立双向通信通道。下面的示例代码演示了如何将原生App的功能暴露给JS:
```
public class JsBridge extends WebViewClient {
private WeakReference
//原生App调用H5应用
public void callJs(WebView webView, String jsFunc, String message) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("javascript:" + jsFunc + "('" + message + "')", null);
} else {
webView.loadUrl("javascript:" + jsFunc + "('" + message + "')");
}
}
//H5应用调用原生App
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("jsbridge://")) {
String[] urls = url.split("//");
String message = urls[1];
String func = urls[2];
callNative(func, message); //调用原生App的方法
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
//调用原生App的方法
private void callNative(String func, String message) {
Activity activity = mActivityRef.get();
if (activity != null) {
if ("showToast".equals(func)) {
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
} else if ("callPhone".equals(func)) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + message));
activity.startActivity(intent);
} else if ("openUrl".equals(func)) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(message));
activity.startActivity(intent);
}
}
}
}
```
上面代码中, 原生App和H5应用中的通讯协议采用“jsbridge://”形式,其中jsbridge前缀为自定义协议的名称。H5应用通过window.location.href属性调用原生App的接口,调用格式为:
```
window.location.href='jsbridge://message/callNative?abc'
```
接下来,我们在MainActivty类中将JsBridge类作为WebView的Client实现,并注册全局的桥接对象:
```
public class MainActivity extends AppCompatActivity {
private WebView mWebView;
private JsBridge mJsBridge = new JsBridge(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//找到WebView控件,并加载index.html
mWebView = (WebView) findViewById(R.id.web_view);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(mJsBridge);
mWebView.addJavascriptInterface(new JsApi(this), "JsApi");
String appPath = getApplicationContext().getFilesDir().getAbsolutePath();
mWebView.loadUrl("file://" + appPath + "/app/index.html");
}
}
```
在上面的代码中,我们使用addJavascriptInterface方法将原生功能依附到JS的window对象上,这样就可以在JS中直接调用该接口了。JsApi类的源代码如下:
```
public class JsApi {
private Activity mActivity;
public JsApi(Activity activity) {
mActivity = activity;
}
@JavascriptInterface
public void showToast(String message) {
Toast.makeText(mActivity, message, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void openUrl(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
mActivity.startActivity(intent);
}
@JavascriptInterface
public void share(String message) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, message);
mActivity.startActivity(Intent.createChooser(shareIntent, "Share this"));
}
}
```
三、总结
本文主要介绍了如何将H5应用封装为原生App,主要包括两步:将H5应用解压缩到本地,并使用Webview调用index.html文件;使用桥接方式,将原生App和JS之间的交互打通。如果你正在开发一些轻量级应用,H5应用的封装为原生App则是一个有效的方案,不仅使得用户使用起来更加方便,也增加了应用的流行度。