webview系列:Html5页面和Native App怎么进行交互

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相关页面配置

?
1
2
3
4
5
6
WebSettings webSettings = mWebview.getSettings();
//①设置WebView允许调用js
webSettings.setJavaScriptEnabled( true );
webSettings.setDefaultTextEncodingName( "UTF-8" );
//②将object对象暴露给Js,调用addjavascriptInterface
mWebview.addJavascriptInterface( new MyObject(TestWebViewActivity. this ), "myObj" );

静态页面

?
1
 

MyObject

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
  * 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();
     }
}


这个讲解的最为具体

二.方式2:Scheme:WebView拦截页面跳转

demo

点击网页里的超链接,跳转到原生页面

webviewActivity

?
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
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();
         }
     }
  
}

静态页面test.html

?
1
 
test scheme

需要跳转到的页面

配置信息

?
1
2
3
4
5
6
7
8
9
10
11
     <!--要想在别的App上能成功调起App,必须添加intent过滤器-->
     <INTENT-FILTER>
         <!--协议部分,随便设置-->
         <DATA android:scheme= "xl" android:port= "8888" android:path= "/goodsDetail" android:host= "goods" >
         <!--下面这几行也必须得设置-->
         <CATEGORY android:name= "android.intent.category.DEFAULT" >
          
         <CATEGORY android:name= "android.intent.category.BROWSABLE" >
     </CATEGORY></ACTION></CATEGORY></DATA></INTENT-FILTER>
</ACTIVITY>

页面

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
<!--?xml version= "1.0" encoding= "utf-8" ?-->
<LINEARLAYOUT xmlns:android= "http://schemas.android.com/apk/res/android" android:orientation= "vertical" android:layout_width= "match_parent" android:layout_height= "match_parent" >
  
     <TEXTVIEW android:layout_width= "match_parent" android:layout_height= "match_parent" android:text= "scheme跳转测试" android:gravity= "center" >
  
</TEXTVIEW></LINEARLAYOUT>

应用场景:

Android中的scheme是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;
通过scheme协议,服务器可以定制化告诉App跳转那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等。
已经可以适应多数的应用场景

三.一些开源项目的实现方案

coding上的实现方案

?
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
     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 ;
         }
  
         // 项目讨论列表
         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 ;
         }
  
         // 单个项目讨论
         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 ;
         }
  
         // 项目
         //
         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 ;
         }
  
         // 冒泡
         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 ;
         }
  
         // 项目内冒泡
         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 ;
         }
  
             // 冒泡话题
         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 ;
         }
  
         // 任务详情
         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 ;
         }
  
         // 私信推送
         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后面的字段是添加上去的
         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后面的字段是添加上去的
         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 ;
         }
  
         // 跳转图片链接
         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 ;
     }

coding的实现其实是和服务器商量了一种协议,看起来非常繁琐。感觉作用和scheme类似

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值