本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
web和android端交互简单封装
manongba · 643浏览 · 发布于2019-10-30 +关注

背景

公司项目是通过webView.addJavascriptInterface(Object obj, String interfaceName)进行web和原生的交互的,android端通常会定义类似以下的多个方法,这样做的好处是便于阅读,一看便知方法是干什么的,需要什么参数;坏处是不便于扩展,一旦web端传错参数,或者调了一个android端没有的方法时,会导致各种问题,每当需要扩展时,web端总是需要考虑android端的版本兼容问题,因此考虑一个通用的方法来处理双方的交互(正好前些日子学了Javapoet和apt没东西练手hhhhh)。


@JavascriptInterface
    public void func(){ }

    @JavascriptInterface
    public void func(int a, int b){}

    @JavascriptInterface
    public void func1(int a, String b){ }


交互

Android端只暴露一个方法给web端调用,双方约定好数据类型,比如一个json字符串,action代替原来的方法用作请求标志,data作为扩展参数,如


{
"action" : “REQUEST_PLAY_VIDEO”,
"data" : {
"url" : "http://xxxx"
     }
}

同时暴露一个接口出来用作交互,如

@JavascriptInterface
  public void request(String request) {}

这样,交互问题就解决了,当web需要扩展时,只需往json字符串里添加额外的参数即可,android端这边解析json,由于低版本没有解析额外的参数因此会忽略掉,不会导致网页报错等问题,高版本或者补丁包添加参数处理即可。到这里基本可以了,但,还是会存在一些问题:


  • 参数是json字符串,要解析

  • 解析后要判断action,做相应的处理,如果带数据data,还要解析转成Java对象

  • 简单的判断action免不了switch,每添加一个方法就要写一个case过于麻烦

基于以上问题,把交互方式封装一下。


实现

封装一个简单的JsBridge,要实现的功能如下:

  1. 自动解析json,android端拿到的是对应的Java对象

  2. 简单的扩展action的方式

  3. 简单的绑定方式,如JsBridge.bind(webview, object)

  4. 其他,如错误处理等


思路很简单:通过注解标记与web交互的类和action对应的实现方法,通过Javapoet和apt生成相应的类,该类包含了真正的交互方法(@JavascriptInterface标记的),在该方法里解析json参数得到action,switch判断action对应的实现方法,再调用即可。


定义一些约束

注解

/**
 * js和android互相交互的js名
 * author : pxq
 * date : 19-10-24 下午9:39
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Bridge {
    //js交互名
    String name();
    //交互方法
    String jsMethod();
}
/**
 * js请求android端的action
 * author : pxq
 * date : 19-10-24 下午9:38
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface JsAction {
    //action名
    String value();
}
/**
 * Js Request异常,如解析失败等
 * author : pxq
 * date : 19-10-26 下午2:27
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface JsError {
}
/**
 * 未处理的Js Request
 * author : pxq
 * date : 19-10-26 下午2:25
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface UnHandle {
}


获取interfaceName的接口

注意到webView.addJavascriptInterface(Object obj, String interfaceName)需要一个interfaceName,为此我们还需要定义一个接口,让生成的类去实现它,以便通过强转的方式获取该值。


/**
 * 约束类,用来获取webview.addJavascriptInterface(obj, name)的name
 * author : pxq
 * date : 19-10-26 上午2:20
 * @see android.webkit.WebView#addJavascriptInterface(Object, String)
 */
public interface IJsBridge {
    //获取JavascriptInterface名
    String getName();
}


这里生成的类有点讲究,务必生成在同一个包下,同时加上后缀加以区分,接下来就是javapoet的使用了。类生成之后,最后是绑定到webview上,有了前面的基础,这个就简单了,直接看代码:

public static void bind(WebView webView, Object bridge){
        String className = bridge.getClass().getCanonicalName() + JS_BRIDGE_SUFFIX;
        Log.e(TAG, "bind: " + className);
        try {
            Class<?> jsBridgeClazz = Class.forName(className);
            IJsBridge jsBridge = (IJsBridge) jsBridgeClazz.getConstructor(bridge.getClass()).newInstance(bridge);
            Log.e(TAG, "bind: " + jsBridge.getName());
            webView.getSettings().setJavaScriptEnabled(true);
            webView.addJavascriptInterface(jsBridge, jsBridge.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

效果

定义个简单的类测试一下:

//action对应的实现方法
@Bridge(name = "android", jsMethod = "request")
public class BridgeTest {
    private static final String TAG = "BridgeTest";
    @JsAction("test")
    public void test(){
        Log.i(TAG, "test: ");
    }

    @JsAction("testData")
    public void testData(TestBean test){
        Log.i(TAG, "testData: " + test.name +" " +  test.data);
    }

    @UnHandle
    public void UnHandle(String request){
        Log.w(TAG, "UnHandle: " + request);
    }

    @JsError
    public void error(String request, Exception e){
        Log.e(TAG, "error: " +request, e);
    }
}
//生成的交互类
public class BridgeTest$$Bridge  implements IJsBridge {
  public BridgeTest mHandler;

  public BridgeTest$$Bridge(BridgeTest mHandler) {
    this.mHandler = mHandler;
  }

  @JavascriptInterface
  public void request(String request) {
    try {
      handleRequest(request);
    } catch (Exception e) {
      mHandler.error(request, e);
    }
  }

  public String getName() {
    return "android";
  }

  void handleRequest(String request) throws Exception {
    String action = JsonParser.getAction(request);
    switch(action) {
      case "test": 
          mHandler.test();
      break;
      case "testData": 
          mHandler.testData(JsonParser.parse(request, TestBean.class));
      break;
      default : mHandler.UnHandle(request);
    }
  }
}


test.html

<html>
<body>
<input type="button" value="无数据" onclick="call()" style="height:40px;" >
<p>
<input type="button" value="带数据" onclick="callWithData()" style="height:40px;">
</p>
<p>
<input type="button" value="默认处理(UnHandle)" onclick="callUnHandle()" style="height:40px;">
</p>
<p>
<input type="button" value="错误处理(Error)" onclick="callError()" style="height:40px;">
</p>
<script>
function call(){
    var x = "{\"action\":\"test\"}"
    callAndroid(x);
}
function callWithData() {
    var x = "{\"action\":\"testData\", \"data\": {\"name\" : \"pxq\", \"data\" : \"testdata\"}}"
    callAndroid(x);
}
function callUnHandle() {
    var x = "{\"action\":\"UnHandleTest\", \"data\": {\"name\" : \"pxq\"}}"
    callAndroid(x);
}
function callError() {
    var x = "{\"actions\":\"ErrorTest\", \"data\": {\"name\" : \"pxq\"}}"  //这里把action 改成了 actions会导致解析失败
    callAndroid(x);
}
function callAndroid(x) {
    window.android.request(x);
}
</script>
</body>
</html>


绑定一下

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WebView webView = new WebView(this);
        JsBridge.bind(webView, new BridgeTest());
        setContentView(webView);
        webView.loadUrl("file:///android_asset/test.html");
    }
}


相关推荐

android下vulkan与opengles纹理互通

talkchan · 1174浏览 · 2020-11-23 10:37:39
Android 使用RecyclerView实现轮播图

奔跑的男人 · 2172浏览 · 2019-05-09 17:11:13
微软发布新命令行工具 Windows Terminal

吴振华 · 867浏览 · 2019-05-09 17:15:04
Facebook 停止屏蔽部分区块链广告

· 753浏览 · 2019-05-09 17:20:08
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页