webview系列:HTML5页面和Native App怎么进行交互
混合开发的App(Hybrid App)就是在一个App中内嵌一个轻量级的浏览器,一部分原生的功能改为html5来开发,这部分功能不仅能够在不升级App的情况下动态更新,而且可以在Android或iOS的App上同时运行,让用户的体验更好又可以节省开发的资源。
我觉得一个Hybrid开发的App中必须要要有的功能就是Html5页面和Native App怎么进行交互。比如,我点了一个Html 5页面上的一个按钮或链接,我能不能够跳转到Native App的某个页面;比如我点了Html 5页面上的分享按钮,我能不能调用Native App的分享功能;比如Html加载的时候能不能获取Native App的用户信息等等。
一般来讲,我所知道的两种主流的方式就是:
js调用Native中的代码
Schema:WebView拦截页面跳转
第2种方式实现起来很简单,但是一个致命的问题就是这种交互方式是单向的,Html5无法实现回调。如果需求变得复杂,假如Html5需要获取Native App中的用户信息,那么最好使用js调用的方式。例如我们通过淘宝客户端进入天猫的h5页面购物,在这种情况下,你就需要在webview页面获取登陆用户的信息。
一.方式1:js和Native进行交互
demo
webview相关页面配置
WebSettings webSettings = mWebview.getSettings();
//①设置WebView允许调用js
webSettings.setJavaScriptEnabled(true);
webSettings.setDefaultTextEncodingName("UTF-8");
//②将object对象暴露给Js,调用addjavascriptInterface
mWebview.addJavascriptInterface(new MyObject(TestWebViewActivity.this), "myObj");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
静态页面
<html>
<head>
<title>Js调用Android</title>
</head>
<body>
<a href="xl://goods:8888/goodsDetail?goodsId=10011002">test scheme</a>
<input type="button" value="Toast提示" onclick="myObj.showToast('曹神前来日狗~');"/>
<input type="button" value="列表对话框" onclick="myObj.showDialog();"/>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
MyObject
/**
* Created by niehongtao on 16/10/9.
*/
public class MyObject {
private Context context;
public MyObject(Context context) {
this.context = context;
}
//将显示Toast和对话框的方法暴露给JS脚本调用
@JavascriptInterface
public void showToast(String name) {
Toast.makeText(context, name, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void showDialog() {
new AlertDialog.Builder(context)
.setTitle("联系人列表").setIcon(R.mipmap.ic_launcher)
.setItems(new String[]{"基神", "B神", "曹神", "街神", "翔神"}, null)
.setPositiveButton("确定", null).create().show();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
可参考
android基础入门教程——7.5.2 WebView和JavaScrip交互基础
http://blog.csdn.net/coder_pig/article/details/48392621
这个讲解的最为具体
二.方式2:Scheme:WebView拦截页面跳转
demo
点击网页里的超链接,跳转到原生页面
webviewActivity
package com.ht.fyforandroid.test.webview;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.ht.fyforandroid.R;
import com.ht.fyforandroid.base.BaseActivity;
import butterknife.InjectView;
/**
* Created by niehongtao on 16/7/7.
* 进行仿微信加载WebView显示进度条,直接调用start()方法进行跳转.
*/
public class TestWebViewActivity extends BaseActivity {
@InjectView(R.id.webview)
WebView mWebview;
@Override
protected int getLayoutId() {
return R.layout.activity_webview;
}
@Override
protected void init(Bundle savedInstanceState) {
TestWebViewActivity.super.mLoadingDialog.hideLoading();
mWebview.loadUrl("file:///android_asset/test.html");
mWebview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getScheme().equals("xl")) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
} else {
view.loadUrl(url);
}
return true;
}
});
}
public static void startActivity(Context context) {
Intent intent = new Intent(context, TestWebViewActivity.class);
context.startActivity(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mWebview != null) {
mWebview.destroy();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
静态页面test.html
<html>
<body>
<a href="xl://goods:8888/goodsDetail?goodsId=10011002">test scheme</a>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
需要跳转到的页面
配置信息
<activity android:name=".test.scheme.Test1Activity">
<!--要想在别的App上能成功调起App,必须添加intent过滤器-->
<intent-filter>
<!--协议部分,随便设置-->
<data
android:host="goods"
android:path="/goodsDetail"
android:port="8888"
android:scheme="xl"
/>
<!--下面这几行也必须得设置-->
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
页面
package com.ht.fyforandroid.test.scheme;
import android.os.Bundle;
import com.ht.fyforandroid.R;
import com.ht.fyforandroid.base.BaseActivity;
/**
* Created by niehongtao on 16/10/8.
*/
public class Test1Activity extends BaseActivity {
@Override
protected int getLayoutId() {
return R.layout.activity_test1;
}
@Override
protected void init(Bundle savedInstanceState) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="scheme跳转测试"
/>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
应用场景:
Android中的scheme是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;
通过scheme协议,服务器可以定制化告诉App跳转那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等。
已经可以适应多数的应用场景
三.一些开源项目的实现方案
coding上的实现方案
public static boolean openActivityByUri(Context context, String uri, boolean newTask, boolean defaultIntent, boolean share) {
final String ProjectPath = "/u/([\\w.-]+)/p/([\\w\\.-]+)";
final String Host = Global.HOST;
final String UserPath = "/u/([\\w.-]+)";
Intent intent = new Intent();
if (newTask) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
final String uriString = uri.replace("/team/", "/user/").replace("/t/", "/u/"); // 添加 team 后导致的 api 失效问题
final String NAME = "([\\w.-]+)";
final String uriPath = uriString.replace(Global.HOST, "");
final String projectPattern = String.format("^/u/%s/p/%s(.*)", NAME, NAME);
Pattern pattern = Pattern.compile(projectPattern);
Matcher matcher = pattern.matcher(uriPath);
if (matcher.find()) {
String user = matcher.group(1);
String project = matcher.group(2);
String simplePath = matcher.group(3); // 去除了 /u/*/p/* 的路径
final String projectPath = String.format("/user/%s/project/%s", user, project);
// 代码中的文件 https://coding.net/u/8206503/p/TestPrivate/git/blob/master/jumpto
final String gitFile = String.format("^/git/blob/%s/(.*)$", NAME);
pattern = Pattern.compile(gitFile);
matcher = pattern.matcher(simplePath);
if (matcher.find()) {
String version = matcher.group(1);
String path = matcher.group(2);
intent.setClass(context, GitViewActivity_.class);
intent.putExtra("mProjectPath", projectPath);
intent.putExtra("mVersion", version);
intent.putExtra("mGitFileInfoObject", new GitFileInfoObject(path));
context.startActivity(intent);
return true;
}
}
// 用户名
final String atSomeOne = "^(?:https://[\\w.]*)?/u/([\\w.-]+)$";
pattern = Pattern.compile(atSomeOne);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
String global = matcher.group(1);
intent.setClass(context, UserDetailActivity_.class);
intent.putExtra("globalKey", global);
context.startActivity(intent);
return true;
}
// 项目讨论列表
// https://coding.net/u/8206503/p/TestIt2/topic/mine
final String topicList = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w.-]+)/topic/(mine|all)$";
pattern = Pattern.compile(topicList);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, ProjectActivity_.class);
ProjectActivity.ProjectJumpParam param = new ProjectActivity.ProjectJumpParam(
matcher.group(1), matcher.group(2)
);
intent.putExtra("mJumpParam", param);
intent.putExtra("mJumpType", ProjectActivity.ProjectJumpParam.JumpType.typeTopic);
context.startActivity(intent);
return true;
}
// 单个项目讨论
// https://coding.net/u/8206503/p/AndroidCoding/topic/9638?page=1
final String topic = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w.-]+)/topic/([\\w.-]+)(?:\\?[\\w=&-]*)?$";
pattern = Pattern.compile(topic);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, TopicListDetailActivity_.class);
TopicListDetailActivity.TopicDetailParam param =
new TopicListDetailActivity.TopicDetailParam(matcher.group(1),
matcher.group(2), matcher.group(3));
intent.putExtra("mJumpParam", param);
context.startActivity(intent);
return true;
}
// 项目
// https://coding.net/u/8206503/p/AndroidCoding
// https://coding.net/u/8206503/p/FireEye/git
//
final String project = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w.-]+)(/git)?$";
pattern = Pattern.compile(project);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, ProjectHomeActivity_.class);
ProjectActivity.ProjectJumpParam param = new ProjectActivity.ProjectJumpParam(
matcher.group(1), matcher.group(2)
);
intent.putExtra("mJumpParam", param);
context.startActivity(intent);
return true;
}
// 冒泡
// https://coding.net/u/8206503/pp/9275
final String maopao = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/pp/([\\w.-]+)$";
pattern = Pattern.compile(maopao);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, MaopaoDetailActivity_.class);
MaopaoDetailActivity.ClickParam param = new MaopaoDetailActivity.ClickParam(
matcher.group(1), matcher.group(2));
intent.putExtra("mClickParam", param);
context.startActivity(intent);
return true;
}
// 项目内冒泡
// https://coding.net/t/superrocket/p/TestPrivate?pp=2417
final String projectMaopao = String.format("^%s%s\\?pp=([\\d]+)", Host, ProjectPath);
pattern = Pattern.compile(projectMaopao);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, MaopaoDetailActivity_.class);
MaopaoDetailActivity.ClickParam param = new MaopaoDetailActivity.ClickParam(
matcher.group(1), matcher.group(2), matcher.group(3));
intent.putExtra("mClickParam", param);
context.startActivity(intent);
return true;
}
// 冒泡话题
// https://coding.net/u/8206503/pp/9275
final String maopaoTopic = "^(?:(?:https://[\\w.]*)?/u/(?:[\\w.-]+))?/pp/topic/([\\w.-]+)$";
pattern = Pattern.compile(maopaoTopic);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, SubjectDetailActivity_.class);
intent.putExtra("topicId", Integer.valueOf(matcher.group(1)));
context.startActivity(intent);
return true;
}
// 还是冒泡话题 https://coding.net/pp/topic/551
final String maopao2 = "^https://[\\w.]*/pp/topic/([\\w.-]+)$";
pattern = Pattern.compile(maopao2);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, SubjectDetailActivity_.class);
intent.putExtra("topicId", Integer.valueOf(matcher.group(1)));
context.startActivity(intent);
return true;
}
// 任务详情
// https://coding.net/u/wzw/p/coding/task/9220
final String task = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w\\.-]+)/task/(\\w+)$";
pattern = Pattern.compile(task);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
Log.d("", "gg " + matcher.group(1) + " " + matcher.group(2) + " " + matcher.group(3));
intent.setClass(context, TaskAddActivity_.class);
intent.putExtra("mJumpParams", new TaskJumpParams(matcher.group(1),
matcher.group(2), matcher.group(3)));
context.startActivity(intent);
return true;
}
// 我的已过期任务 "/user/tasks"
final String myExpireTask = String.format("(%s)?%s", Global.DEFAULT_HOST, "/user/tasks");
// final String myExpireTask = "/user/tasks";
pattern = Pattern.compile(myExpireTask);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, AllTasksActivity_.class);
context.startActivity(intent);
return true;
}
// 私信推送
// https://coding.net/user/messages/history/1984
final String message = PATTERN_URL_MESSAGE;
pattern = Pattern.compile(message);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
Log.d("", "gg " + matcher.group(1));
intent.setClass(context, MessageListActivity_.class);
intent.putExtra("mGlobalKey", matcher.group(1));
context.startActivity(intent);
return true;
}
// 跳转到文件夹,与服务器相同
pattern = Pattern.compile(FileUrlActivity.PATTERN_DIR);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
FileUrlActivity_.intent(context)
.url(uriString)
.start();
return true;
}
// 文件夹,这个url后面的字段是添加上去的
// https://coding.net/u/8206503/p/TestIt2/attachment/65138/projectid/5741/name/aa.jpg
final String dir = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w.-]+)/attachment/([\\w.-]+)/projectid/([\\d]+)/name/(.*+)$";
pattern = Pattern.compile(dir);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
AttachmentFolderObject folder = new AttachmentFolderObject();
folder.file_id = matcher.group(3);
folder.name = matcher.group(5);
AttachmentsActivity_.intent(context)
.mAttachmentFolderObject(folder)
.mProjectObjectId(Integer.valueOf(matcher.group(4)))
.start();
return true;
}
pattern = Pattern.compile(FileUrlActivity.PATTERN_DIR_FILE);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
FileUrlActivity_.intent(context)
.url(uriString)
.start();
return true;
}
// 文件,这个url后面的字段是添加上去的
// https://coding.net/u/8206503/p/TestIt2/attachment/65138/preview/66171/projectid/5741/name/aa.jpg
final String dirFile = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w.-]+)/attachment/([\\w.-]+)/preview/([\\d]+)/projectid/([\\d]+)/name/(.*+)$";
pattern = Pattern.compile(dirFile);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
AttachmentFolderObject folder = new AttachmentFolderObject();
folder.name = matcher.group(3);
AttachmentFileObject folderFile = new AttachmentFileObject();
folderFile.file_id = matcher.group(4);
folderFile.setName(matcher.group(6));
int projectId = Integer.valueOf(matcher.group(5));
String extension = folderFile.getName().toLowerCase();
final String imageType = ".*\\.(gif|png|jpeg|jpg)$";
final String htmlMdType = ".*\\.(html|htm|markd|markdown|md|mdown)$";
final String txtType = ".*\\.(sh|txt)$";
if (extension.matches(imageType)) {
AttachmentsPicDetailActivity_.intent(context)
.mProjectObjectId(projectId)
.mAttachmentFolderObject(folder)
.mAttachmentFileObject(folderFile)
.start();
} else if (extension.matches(htmlMdType)) {
AttachmentsHtmlDetailActivity_.intent(context)
.mProjectObjectId(projectId)
.mAttachmentFolderObject(folder)
.mAttachmentFileObject(folderFile)
.start();
} else if (extension.matches(txtType)) {
AttachmentsTextDetailActivity_.intent(context)
.mProjectObjectId(projectId)
.mAttachmentFolderObject(folder)
.mAttachmentFileObject(folderFile)
.start();
} else {
AttachmentsDownloadDetailActivity_.intent(context)
.mProjectObjectId(projectId)
.mAttachmentFolderObject(folder)
.mAttachmentFileObject(folderFile)
.start();
}
return true;
}
// 图片链接
final String imageSting = "(http|https):.*?.[.]{1}(gif|jpg|png|bmp)";
pattern = Pattern.compile(imageSting);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, ImagePagerActivity_.class);
intent.putExtra("mSingleUri", uriString);
context.startActivity(intent);
return true;
}
// 跳转图片链接
// https://coding.net/api/project/78813/files/137849/imagePreview
final String imageJumpString = Global.HOST_API + "/project/\\d+/files/\\d+/imagePreview";
pattern = Pattern.compile(imageJumpString);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, ImagePagerActivity_.class);
intent.putExtra("mSingleUri", uriString);
context.startActivity(intent);
return true;
}
// 跳转到merge或pull
final String mergeString = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w\\.-]+)/git/(merge)?(pull)?/(\\d+)";
pattern = Pattern.compile(mergeString);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, MergeDetailActivity_.class);
intent.putExtra("mMergeUrl", uriString);
context.startActivity(intent);
return true;
}
// 跳转到commit
final String commitString = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w\\.-]+)/git/commit/.+$";
pattern = Pattern.compile(commitString);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, CommitFileListActivity_.class);
intent.putExtra("mCommitUrl", uriString);
context.startActivity(intent);
return true;
}
// 跳转到branch
final String branchString = "^(?:https://[\\w.]*)?/u/([\\w.-]+)/p/([\\w\\.-]+)/git/tree/(.+)$";
pattern = Pattern.compile(branchString);
matcher = pattern.matcher(uriString);
if (matcher.find()) {
intent.setClass(context, BranchMainActivity_.class);
String userString = matcher.group(1);
String projectString = matcher.group(2);
String version = matcher.group(3);
String projectPath = String.format("/user/%s/project/%s", userString, projectString);
intent.putExtra("mProjectPath", projectPath);
intent.putExtra("mVersion", version);
context.startActivity(intent);
return true;
}
String s = PushUrl.URL_2FA;
if (uriString.equals(s)) {
intent.setClass(context, AuthListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
}
try {
if (defaultIntent) {
intent = new Intent(context, WebActivity_.class);
if (newTask) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (uri.startsWith("/u/")) {
uri = Global.HOST + uri;
}
if (share) {
intent.putExtra("share", true);
}
intent.putExtra("url", uri);
context.startActivity(intent);
}
} catch (Exception e) {
Toast.makeText(context, "" + uri, Toast.LENGTH_LONG).show();
Global.errorLog(e);
}
return false;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
coding的实现其实是和服务器商量了一种协议,看起来非常繁琐。感觉作用和scheme类似。
参考资料
Android产品研发(十一)–>应用内跳转Scheme协议
http://blog.csdn.net/qq_23547831/article/details/51685310
谈谈App混合开发
http://bxbxbai.github.io/2015/08/16/talk-about-bybird-app/
Android业务组件化之URL Schema使用
http://www.itdadao.com/articles/c15a327723p0.html
Android基础入门教程——7.5.2 WebView和JavaScrip交互基础
http://blog.csdn.net/coder_pig/article/details/48392621
【Android】Scheme详解
http://blog.csdn.net/wangkeke1860/article/details/49850997
ios使用schema协议调起APP
http://js8.in/2013/12/16/ios%E4%BD%BF%E7%94%A8schema%E5%8D%8F%E8%AE%AE%E8%B0%83%E8%B5%B7app/
Android WebView 优化之路
http://www.codeceo.com/article/android-webview-optimization.html
解耦—Hybrid H5跨平台性思考
http://www.iliunian.cn/14684843677084.html
深入浅出话iOS URL Scheme(一)理论篇
http://jobyang.com/2016/08/%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA%E8%AF%9DiOS-URL-Scheme-%E4%B8%80-%E7%90%86%E8%AE%BA%E7%AF%87/
【杂谈1月】H5与APP交互错疑
http://www.sabria.me/2016/01/22/ZTN01_5/