Use C # To develop a mobile cross-platform Hybrid App (I): Talk about the implementation of Hybrid from the Native end,
0x00 Preface
Some time ago, I read two blogs respectively, ye Xiaodi's Design and Implementation of hybridge' and Xu Leige's technical manager's calculation, how to Make an app faster and better. I have been inspired and have some opinions. Because currently, the three major platforms (although wp shares are poor compared with iOS and android) have the following development languages: Objective-C (swift), Java, and C #, regardless of other features of the platform, the development languages of each platform are different. A single App often needs to log on to different platforms at the same time, so how to quickly develop cross-platform apps and try to make code reuse a problem that developers have to face.
As Ye xiaochai said in his blog post: "It seems that the cost of using IOS and Android to develop an APP is too high, h5 features such as low cost, high efficiency, and cross-platform are immediately exploited to form a new development model: Hybrid APP." -- Hybrid APP developed using Web and Native appeared. Therefore, before officially introducing C # to develop cross-platform apps, I think it is necessary to develop the other two platforms (iOS ~ OC, Android ~ Java. Note that some of the web code I used in this article comes from the "Implementation of simple Hybrid box" code shared by brother ye Xiaodi.
The iOS-side Hybrid code used in this article can be viewed here:
Https://github.com/chenjd/iOS-Hybrid-Sample
0x01 hello, WebView
Because the Web end developed by Hybrid is essentially some Web pages, when using App-related functions, it is equivalent to using the native platform to access some Web pages. It is precisely because the content comes from the web page rather than relying on the platform that it achieves cross-platform in disguise. For iOS and Android, they need to provide a Web View container on the Native side to access the page.
IOS UIWebView
On the iOS platform, we use the UIWebView of UIKit. framework to access the page. Using the UIWebView control, the iOS platform can obtain network resources and load local HTML code. The following three methods are used:
How to load Resources in the UIWebView Control |
- (void)loadData:(NSData *)data
MIMEType:(NSString *)MIMEType
textEncodingName:(NSString *)encodingName
baseURL:(NSURL *)baseURL
- (void)loadHTMLString:(NSString *)string
baseURL:(NSURL *)baseURL
- (void)loadRequest:(NSURLRequest *)request
|
|
Next we will use these methods to use the UIWebView control on the iOS platform to access the web pages provided by brother ye.
//ViewController.h
@interface ViewController : UIViewController
{
UIWebView *webView;
}
//ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
webView = [[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//load yexiaochai's html
NSBundle *thisBundle = [NSBundle mainBundle];
NSString *path = [thisBundle pathForResource:@"webapp/hotel/index" ofType:@"html"];
NSURL *baseURL = [NSURL fileURLWithPath:path];
NSURLRequest *urlReq = [NSURLRequest requestWithURL:baseURL];
[self.view addSubview: webView];
[webView loadRequest:urlReq];
Run in the iOS simulator. We can see that the Web content has been loaded on the Native end through UIWebView.
Of course, at this time we just took the first step, that is, rendering the Web content on the Native side. However, the interaction between Native and Web is not involved yet. Since Native interacts with Web, we have to mention two other important methods:
Methods for Native and Web interaction in the UIWebView Control |
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script |
- (BOOL)webView:(UIWebView *)webViewshouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType |
In addition, iOS's UIWebView also has some attributes of whether pages can be moved forward and backward, as well as the methods to control them:
Properties and methods of page forward and backward in the UIWebView Control |
canGoBack
canGoForward
- (void)goBack
- (void)goForward
|
After preliminary introduction to iOS's UIWebView, let's take a look at the WebView container on the Android platform-WebView.
Android WebView
In Android, we first need to know that we use WebView under android. webkit to access the page on the Andorid platform. Similar to the iOS platform, the Andorid platform uses WebView to obtain network resources or load local HTML code. Note that in order for the Activity to correctly connect to the network, you must enable INTERNET permissions in the AndroidManifest file when using WebView to load remote resources:
<uses-permission android:name="android.permission.INTERNET" />
WebView mainly includes the following methods for loading resources:
WebView resource Loading Method |
Public void loadUrl (String url ); |
Public void loadUrl (String url, Map <String, String> additionalHttpHeaders ); |
Public void loadData (String data, String mimeType, String encoding ); |
Public void loadDataWithBaseURL (String baseUrl, String data, String mimeType, String encoding, String historyUrl ); |
The following describes how to use the WebView control on the Andorid platform to access the web pages provided by Ye Xiaodi.
First, we need to add a WebView control on the Native side, which can be easily implemented by adding <WebView> to the layout file.
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
Of course, in addition to adding the <WebView> element to layout, we can directly set the content as WebView in the Activity through code,
WebView webview = new WebView(this);
setContentView(webview);
However, the following assumes that the <WebView> element is added to layout.
At this point, we have prepared a container for storing web pages on the Native side. The next step is to load Html resources.
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("file:///android_asset/webapp/hotel/index.html");
Here we will render the Web page of Brother ye xiaochai again.
Similar to iOS, we just took the first step, that is, rendering Web content on the Native side. However, the interaction between Native and Web is not involved yet. However, there are also differences between Android and iOS platforms. For example, by default, Andorid WebView does not support js, therefore, if we only use a web page as a static UI without interaction, but if we want to use js to achieve interaction between Native and Web, we need to set more content. Here we need to obtain the WebSettings object of WebView and enable support for js in WebSettings.
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
Now, we have enabled support for js, that is, Native and Web interaction. The Android platform uses the following methods to call Native web and web call Native:
- Public void evaluateJavaScript (String script, ValueCallback <String> resultCallback); The method can call JS Code on the Native side. Of course, there is an Android version issue, which will be described in detail below.
- In addition, we need to use JavascriptInterface. java to implement Web call Native through this interface.
In addition, Android WebView also has some methods similar to iOS's UIWebView to control page behavior:
0x02 Native calls JS
In the previous section, I introduced how iOS and Android can access Native local resources and remote resources on the Native side. In this way, during App development, some modules that often need to be updated are created in the form of Web, which not only avoids the upgrade and replacement of the entire App package, but also enables quick implementation.Multiple platformsOfUpdate Iteration.
The interaction between Native and Web is indispensable. This interaction is nothing more than a Native method that calls the Web or a Web method that calls the Native method. In this section, I will talk to you about how iOS and Android call Web methods.
IOS Native calls JS
//ViewController.h
@interface ViewController: UIViewController
{
UIWebView * webView;
NSMutableString * msg;
UITextField * msgText;
UIButton * nativeCallJsBtn;
}
//ViewController.m
-(void) createNativeCallJsSample {
[webView loadHTMLString: @ "<html> <head> <script language = 'JavaScript'> function msg (text) {alert (text);} </ script> </ head> <body style = \" background-color: # 0ff000; color: #FFFFFF; font-family: Helvetica; font-size: 10pt; width: 300px; word-wrap: break-word; \ "> <button type = 'button' onclick = \" msg ('Js Call ') \ "style = \" margin: 30 auto; width: 100; height: 25; \ "> web button </ button> </ body> </ html>" baseURL: nil];
// Create a UITextField to pass parameters to js functions when native calls js
[self createNativeTextField];
// Create a button to demonstrate Native calling js
[self createNativeCallJsButton];
}
...
// native calls js
-(void) btnClickMsg {
[msg setString: @ "msg ('"];
[msg appendString: @ "native call js:"];
[msg appendString: [msgText text]];
[msg appendString: @ "')"];
[webView stringByEvaluatingJavaScriptFromString: msg];
}
We first use UIWebView's loadHTMLString method to directly load a piece of Html code. The JS part defines a function function msg (text) {alert (text);}, which calls the alert method. On the Native side, we use UIWebView's stringByEvaluatingJavaScriptFromString method to implement calls to JS.
The leftmost is the msg method of JS triggered after directly clicking the Button generated by the HTML code in the Web page.
In the middle and right, the user's input is obtained through the Native UITextField control, then the btnClickMsg method is called through the Native button control, and then the stringByEvaluatingJavaScriptFromString method is called, and then the msg method of JS is executed from the Native side.
Android Native calls JS
Andorid platform is very similar to the part of iOS calling JS. However, we need to remind you to pay attention to two points. One is that we mentioned above. Android WebView does not enable the function of interacting with JS by default. Here we need to get the WebSettings object of the WebView, and enable js support in WebSettings. The second point to note is the version of Android. After Kitkat (4.4), we can use the evaluateJavaScript method to call JS, while in the previous version we directly called the WebView's loadUrl method, passing in the JS code as a parameter.
@Override protected void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); ... WebView myWebView = (WebView) findViewById (R.id.webview); WebSettings webSettings = myWebView.getSettings (); // Enable JS to support webSettings .setJavaScriptEnabled (true); myWebView.loadData ("<html> <head> <script language = 'JavaScript'> function msg (text) {alert (text);} </ script> </ head> <body> <button type = 'button' onclick = 'msg ()'> web button </ button> </ body> </ html> "," text / html "," utf-8 "); evaluateJavascript (myWebView);} .. .. // Native calls JS public static void evaluateJavascript (WebView mWebview) {// Different versions call different methods if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT) {mWebview.evaluateJavascript ("msg ('native call js') ", null);} else {mWebview.loadUrl (" msg ('native call js') ");}} View Code 0x03 Web Call Native Hybrid Interactive Design
I think at this point, you should have understood how the Native end of the two major platforms (iOS and Android) call JS code. Then this section mainly talks about how the Web calls Native. The first step in the interaction between the Web and Native is to agree on the format. Since I used the web part of Brother Ye Xiaoxuan directly, so according to the web design he designed for the Native model:
requestHybrid ({
// Create a new webview dialog window
tagname: 'hybridapi',
// Request parameters, will be used by Native
param: {},
// Native callback callback method after successful processing
callback: function (data) {
}
});
// actual example in index.html
requestHybrid ({
tagname: 'forward',
param: {
topage: 'webapp / flight / index',
type: 'webview'
}
});
The Native will receive a URL, for example:
hybrid: // forward? t = 1447949881120 & param =% 7B% 22topage% 22% 3A% 22webapp% 2Fflight% 2Findex% 22% 2C% 22type% 22% 3A% 22webview% 22% 7D
It is very clear that the schema we use interactively here is hybrid: //. In the Native part, all schema: // requests issued by the Webview are monitored, and then distributed to the "controller" hybridapi handler. The Native controller will need param to provide it. Parameters. Let's take a look at how iOS and Android use the Web to call Native. (Here I just implemented the iOS part of the web interaction with Ye Xiaoxiong, and the Android part shows a simple example of calling Native on the web).
iOS Web Call Native
On the iOS platform, we will use the shoutStartLoadWithRequest method to capture requests made by the Web. Let's take a look at a simple demo.
// Get data in accordance with the format specified by the web
-(BOOL) webView: (UIWebView *) webView shouldStartLoadWithRequest: (NSURLRequest *) request navigationType: (UIWebViewNavigationType) navigationType {
NSURL * url = [request URL];
if ([[url scheme] isEqualToString: @ "hybrid"]) {
NSString * actionType = request.URL.host;
// Get the parameters from the web from the url
NSDictionary * actionDict = [self getDicFromUrl: url];
// According to the web instructions, the native end responds accordingly
[self doActionType: actionType: actionDict];
return NO;
}
return YES;
}
// Get the parameters from the web from the url
-(NSDictionary *) getDicFromUrl: (NSURL *) url {
NSArray * paramArray = [[url query] componentsSeparatedByString: @ "param ="];
NSString * paramStr = paramArray [1];
NSString * jsonDictString = [paramStr stringByRemovingPercentEncoding];
NSData * jsonData = [jsonDictString dataUsingEncoding: NSUTF8StringEncoding];
NSError * e;
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData: jsonData options: nil error: & e];
return dict;
}
// According to the web instructions, the native end responds accordingly
-(void) doActionType: (NSString *) type: (NSDictionary *) dict {
if ([type isEqualToString: @ "forward"]) {
[webView goForward];
}
// Open a new Web
if ([dict [@ "type"] isEqualToString: @ "webview"]) {
[self web2Web: dict [@ "topage"]];
}
// Open a Native page (I simplified it for the control)
else if ([dict [@ "type"] isEqualToString: @ "native"]) {
[self web2Native];
}
}
The picture on the far left is the initial web page. After clicking the "new webview ..." button, the Native side opens a new web page, as shown in the middle picture.
The picture on the far right shows that after clicking the "jump native ..." button, a Native UI control is created on the Native side.
Android Web calls Native
Web calling Native on the Android platform is slightly more complicated than iOS. Because on the Android platform we need to use JavaScriptInterface to implement the function of calling Native on the Web.
Let's demonstrate a small example, instead of letting the Web call the JS alert, instead use the Web to call the Native Dialog to display the prompt.
First we need to introduce JavaScriptInterface in the Android application.
...
...
import android.webkit.JavascriptInterface;
public class WebAppInterface {
Context mContext;
WebAppInterface(Context c) {
mContext = c;
}
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
After that, we use WebView's addJavascriptInterface () method to bind this class to the JS code as a bridge for Native to call the Web.
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
Finally, the corresponding implementation is also done on the Web side. Clicking a button in the web will call the native showToast method.
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
function showAndroidToast(toast) {
Android.showToast(toast);
}
</script>
0x04 Let the Web belong to the Web, Native to Native
At this point, the use of Hybrid on the two platforms that do not use C # as the development language to make the Web and Native interactive content is introduced. However, I wonder if readers feel the same as me? Then the line between web and native is still very clear.
In fact, I don't think Hybrid's greatest value actually lies in cross-platform. On the contrary, it is very suitable to use this Hybrid way of combining Web and Native on the issue of hot update of some frequently changing modules.
This is because it does not solve the so-called cross-platform problem in essence, but adopts a very clever way or a way to escape the problem beyond the scope of the platform. This also leads to the disadvantages of Hybrid, for example, the Hybrid experience is distanced from Native. Therefore, Hybrid is an excellent hot update technology, but also an imperfect cross-platform solution.
As for a better solution to cross-platform problems, leave it to the Native to figure it out. For example, the three major mobile platforms all use the same language or even the same IDE for the development of the Native part. It seems to be a wonderful solution. If you add the hot update blessing brought by the Hybrid method, the result is even better.
So let the Web belong to the Web and Native to Native.
To be continued ~~~~~