Android 开发进程 0.36 原生与H5交互

Posted 百密一疏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 开发进程 0.36 原生与H5交互相关的知识,希望对你有一定的参考价值。

JS调用原生方法

H5调用android方法基本有两种,第一步通用的初始化webview

WebSettings settings = webview.getSettings();
settings.setjavascriptEnabled(true); //允许在WebView中使用js

2 创建桥接类,这是第一种方法

private Context mContext;
private WebView mWebView;
public static final String javaInterface= "javaInterface";
public JavaScriptMethod(Context context, WebView webView) {
    mContext = context;
    mWebView = webView;
}
@JavascriptInterface
public void showToast(String json){
    Toast.makeText(context, json, Toast.LENGTH_SHORT).show();
}

这种方法就是在JavaScript中直接调用桥接类定义的方法,在初始化webview的同时执行

JavaScriptMetod m = new JavaScriptMetod(this, webview);
webview.addJavascriptInterface(m, JavaScriptMetod.javaInterface);

如果现在在 tomcat的webapp下文件夹简单填写一个html文件,添加一个按钮并在script标签中添加以下JavaScript方法

  function jscallandroid(){
        var jsondata={"toast":"js调用了Android方法"};
        window.jsInterface.showToast(JSON.stringify(jsondata));
    }

运行tomcat服务,在webview中点击按钮即可执行原生方法。
第二种方法使用范围更加普遍,如果在javascript的方法中使用以下方式:

   var json = {"data": "I am a toast"};
   window.location.href="protocol://android?code=toast&data="+JSON.stringify(json);

webview会重加载给到的链接,再重写webview类的 shouldOverrideUrlLoading方法可以拦截返回的url,从而做出相应。url应提前规定,如开头写作上文的“protocol://android?”,后面的code和data可以区分请求的方法和传输的数据。如以下实例:

webView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            String pre = "protocol://android";
            if (!url.contains(pre)) {
                return false;
            }
          //执行过滤
            return true;
        }
    });

setWebViewClient方法返回false是默认不执行过滤,继续加载。返回true则会拦截uri不直接加载。可以使用uri字符串匹配的方法过滤提取code和data
需注意的是shouldOverrideUrlLoading方法参数为view和url的方法已经弃用,在Lollipop及以上版本不会默认执行,所以需要重写同名的另一个参数为view和request的方法,在重写的方法中调用上述方法。

Android调用JS方法

这个使用较少,可以直接使用webview的loadurl方法。如Javascript中已经有以下代码:

 var showMessage=function (json) {
        alert(JSON.stringify(json))
    }

android中(如在点击事件方法中)可以使用:调用js方法需要在主线程中。

JSONObject json=new JSONObject();
json.put("alert","loading");
webView.loadUrl("javascript:showMessage("+json.toString()+")");
吾生也有涯,而知也無涯。以有涯隨無涯,殆已

简单聊聊AndroidIPC机制

前言:  

首先,我们来看一下IPC是个啥意思。

IPC是英文Inter-process communication的缩写,顾名思义就是进程间通信或者说跨进程通信

没错,是进程。所以咱们今天主要说的就是有关进程之间的事儿。

  在Android中,因为开发的需要我们可能需要跨应用去操作一些业务,或者项目较大的时候我们需要开启多个进程去完成。但是,Android中为了安全考虑呢,进程之间却又是不能进行数据交互的,为了解决这个问题所以就有了所谓的IPC机制。

 

  其次,我们来说一下今天都会接触到哪些东西呢?

IPC的有哪些方式(一笔带过)

序列化(点到为止,不细说)

Binder机制(今天的主角儿)

AIDL(专为主角设计的模板)

一句话概括,在Android中实现进程间通信的方式有不少,但是最主要的就是Binder机制,同时Android为了快速实现这种功能专门设计了一种模板,叫做AIDL(Android Interface Definition Language 翻译为:Android接口定义语言)。

 

IPC有哪些方式:

  1、通过Intent的putExtras()方法传递一个Bundle对象(如果只是简单的在进程间来回传递一些基本数据,这是一个很便捷的办法)。

  2、通过共享文件来实现数据的共享(如果对该文件没有较高的数据同步要求的话,这也不失为一种很好的办法)。

  3、使用四大组件之一的ContentProvider(Android专为进程间数据共享而生的,它的底层原理就是咱们今天的主角儿Binder,但它需要借助其他的数据存储方式来使用)。

  4、使用信使Messenger(通过Messenger可以在进程间传递Message对象,因为它是串行的方式处理信息的,没有并发需求的话采用这个很不错,它的底层同样是今天的主角儿Binder)

  5、使用Socket(它不但可以跨进程还可以跨设备,但是内存开销比较大,也不太安全)

  6、Binder机制(今天的主角儿,性能高、安全)

 

序列化:

  序列化也叫持久化。进程之间不可以传递对象,Binder也不行。我们要想传递,只能将对象做一下持久化,以便它可以写入到流。详细的咱就不说了,这里主要提一下两种序列化的方式。

  1、实现Serializable接口(使用简单,但是开销较大)

  2、实现Parcelable接口(使用较为复杂,一般用在内存序列化上,但是效率高)

 

Binder机制:

 咱们先来介绍一下进程空间,以及它的特点。

一个进程空间是分为两个部分的,分别是用户空间内核空间。其中,用户空间之间数据是不可共享的,内核空间是可以数据共享的。

这样说可能比较抽象,我来举个例子。

我们可以把进程想象成一棵棵的大树,其中树地面以上部分我们可以理解成为用户空间,地下的或者说整个大地我们可以理解为内核空间。每棵大树都是独立的存在,但是大地却是大家所共享的。

在一个进程空间中,用户空间和内核空间之间可以通过ioctl等方法来进行数据的交互的。(copy_to_user/copy_from_user)

这样来回拷贝就需要两次。如图

技术图片

 

 而Binder 则是基于C/S架构( Client - Server),并且通过内存映射来实现IPC通信的,下面咱们具体的介绍一下。

内存映射:

   Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现的,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
 

Binder IPC 通信过程:

  1、首先 Binder 驱动在内核空间创建一个数据接收缓存区
  2、接着在内核空间开辟一块内核缓存区
  3、建立内核缓存区内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区接收进程用户空间地址映射关系。(在此之前都没有数据的拷贝操作)
  4、发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

 

Binder通讯模型:

  1、Client:客户端(发送请求的进程)

  2、Server:服务端(接受请求的进程)

  3、Binder驱动:类似网络通信中的路由器,负责将Client的请求转发到具体的Server中执行,并将Server返回的数据传回给Client。

  4、ServiceManager:管理所有的需要提供Binder服务的Server的注册与查询,将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。

  这里咱们着重说一下ServiceManager:

  流程:

  1、Server向ServiceManager注册。Server通过Binder驱动向ServiceManager注册,声明可以对外提供服务。ServiceManager中会保留一份映射表。

  2、Client向ServiceManager请求Server的Binder引用。Client想要请求Server的数据时,需要先通过Binder驱动向ServiceManager请求Server的Binder引用(代理对象)。再向具体的Server发送请求。Client拿到这个Binder代理对象后,就可以通过Binder驱动和Server进行通信了。

  3、Server返回结果。Server响应请求后,需要再次通过Binder驱动将结果返回给Client。

  原理:

  ServiceManager是一个单独的进程,那么Server与ServiceManager通讯是靠什么呢?当Android系统启动后,会创建一个名称为Servicemanager的进程,这个进程通过一个约定的命令BINDERSETCONTEXT_MGR向Binder驱动注册,申请成为为ServiceManager,Binder驱动会自动为ServiceManager创建一个Binder实体。并且这个Binder实体的引用在所有的Client中都为0,也就说各个Client通过这个0号引用就可以和ServiceManager进行通信。Server通过0号引用向ServiceManager进行注册,Client通过0号引用就可以获取到要通信的Server的Binder引用。

  这里我们用一个形象的实例,来讲讲这一块过程:
  1、 Server进程向ServiceManager注册,告诉ServiceManager我是谁,我有什么,我能做什么。就好比徐同学(Server进程)有一台笔记本(computer对象),这台笔记本有个add方法。这时映射关系表就生成了。
  2、 Client进程向ServiceManager查询,我要调用Server进程的computer对象的add方法,可以看到这个过程经过Binder驱动,这时候Binder驱动就开始发挥他的作用了。当向ServiceManager查询完毕,是返回一个computer对象给Client进程吗?其实不然,Binder驱动将computer对象转换成了computerProxy对象,并转发给了Client进程,因此,Client进程拿到的并不是真实的computer对象,而是一个代理对象,即computerProxy对象。很容易理解这个computerProxy对象也是有add方法,(如果连add方法都没有,岂不是欺骗了Client?),但是这个add方法只是对参数进行一些包装而已。
  3、当Client进程调用add方法,这个消息发送给Binder驱动,这时驱动发现,原来是computerProxy,那么Client进程应该是需要调用computer对象的add方法的,这时驱动通知Server进程,调用你的computer对象的add方法,将结果给我。然后Server进程就将计算结果发送给驱动,驱动再转发给Client进程,这时Client进程还蒙在了鼓里,他以为自己调用的是真实的computer对象的add方法,其实他只是调用了代理而已。不过Client最终还是拿到了计算结果。
 

AIDL: 

  AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数,来完成进程间通信。
  咱们简单说一下AIDL使用过程
  1、首先创建我们需要的实体类,并且该实体类必须要是继承Parcelable接口。
  2、创建一个aidl文件。
  3、创一个和实体类同名的aidl文件,并在其中声明它为Parcelable类型
  4、执行一下make project操作,这时候SDK会为我们生成对应的Binder类
  5、在服务端创建一个用于接收请求的service,并在内部实现Binder类中的Stub类,并在对应的接口中作出具体的实现。
  6、将服务端工程中的aidl文件夹下的内容整个拷贝到客户端工程的对应位置下。
  7、客户端通过Intent进行bindService绑定服务端的Service
  8、实例化ServiceConnection,在内部就可以拿到我们需要的数据了。
 
 

感谢巨人的肩膀

  本文也就是作者学习当中,做的一个随笔,借鉴了很多优秀的文章,算是自己IPC这块的一个笔记吧,当然啦,在这些博文里或多或少的都向我们展示代码,大家多读读代码理解的肯定会更加的深刻。(见下面的参考)  
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

以上是关于Android 开发进程 0.36 原生与H5交互的主要内容,如果未能解决你的问题,请参考以下文章

Android与H5交互 -- 点击H5跳转到 Android原生 页面

安卓混合开发——原生Java和H5交互,保证你一看就懂!

android与H5数据交互(kotlin)

关于原生android与H5交互的方法

android与h5交互原理,h5与原生 app 交互的原理

androd H5混合开发 当无网络下,android怎么加载H5界面