zoukankan      html  css  js  c++  java
  • Android与WebView的插件管理机制

    上一篇文章说到,当利用WebViewClient或者WebChromeClient来处理由html页面传过来的请求的时候,都会将相应的服务名称,操作方法和相应的參数数据传给一个叫PluginManager的类。

    PluginManager类的作用是什么?

    大家知道,当利用Android原生环境的功能。比方照像机。比方相冊等,这些功能都是非常分散的,说不清楚什么时候是须要这些功能,什么时候是不须要这些功能的,所以我们希望可以像插件一样。须要的时候就载入进来,不须要的时候不去理他,而PluginManager类就是一个这种管理类。

    它主要负责几件事情:

    1)进入HTML页面的时候,去载入我们定义好的控件。

    mPluginManager = new PluginManager(this);
    mPluginManager.loadPlugin();

    那么PluginManager怎么知道本个应用要载入多少plugin来去响应由Html页面来的请求呢?

    我们是通过一个叫plugin.xml配置文件来定义的。

    <plugins>
        <plugin name="App" class="com.lms.xxx.bridge.plugin.App" />
        <plugin name="Toast" class="com.lms.xxx.plugin.Toast" />
        <plugin name="Dialog" class="com.lms.xxx.bridge.plugin.Dialog" />   
        <plugin name="User" class="com.lms.xxx.bridge.plugin.User" />
    </plugins>


    比方在上面的配置文件里,我们会载入App, Toast, Dialog 和 User 这几个plugin。

    能够联想到,Toast和Dialog都是Android原生环境下的显示窗体,我们尽管用html页面来实现界面,可是为了保持整个应用的一致性,我们就会用到原生环境中的Toast或者我们自己定义的对话框等控件。

    须要用到什么,就在这里定义什么。

    我们再来看一下loadPlugin方法:

    	public void loadPlugin() {
    		int identifier = context.getResources().getIdentifier("plugins", "xml",
    				context.getPackageName());
    		if (identifier == 0) {
    			pluginConfigurationMissing();
    		}
    
    		XmlResourceParser xml = context.getResources().getXml(identifier);
    		try {
    
    			int eventType = -1;
    			while ((eventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
    				if (eventType == XmlResourceParser.START_TAG) {
    					String name = xml.getName();
    					if ("plugin".equals(name)) {
    						String pluginName = xml.getAttributeValue(null, "name");
    						String className = xml.getAttributeValue(null, "class");
    						configs.put(pluginName, className);
    					}
    
    				}
    			}
    
    		} catch (XmlPullParserException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}

    能够看到,这就是解析plugins.xml文件。然后将相应的插件类名给放到configs中,configs定义例如以下:

    private HashMap<String, String> configs = new HashMap<String, String>();
    private HashMap<String, IPlugin> plugins = new HashMap<String, IPlugin>();

    通过loadPlugin方法,我们就将在plugins.xml中定义好的插件,给载入到configs中去了。configs里存放的仅仅是类名。而plugins存放的才是实现,只是我们这里不须要关心这个。

    在这里。在plugins.xml文件里定义的name属性就是这个服务名称。

    2)依据请求的服务名称和操作方法等,为这个请求找到相应的Plugin去处理。

    String execResult = mPluginManager.exec("service", "action", args);

    看一下exec方法,

    public String exec(String service, String action, JSONObject args) throws PluginNotFoundException {
            IPlugin plugin = getPlugin(service);
    	...	
    	PluginResult result = plugin.exec(action, args);
    	...		
    }

    在上面的逻辑能够看到。PluginManager会利用getPlugin方法拿出相应的服务,例如以下:

    	public IPlugin getPlugin(String pluginName) throws PluginNotFoundException {
    		String className = configs.get(pluginName);
    		if(className==null){
    			throw new PluginNotFoundException(pluginName);
    		}
    		if (plugins.containsKey(className)) {
    			return plugins.get(className);
    		} else {
    			return addPlugin(className);
    		}
    	}


    这样,我们就拿到了一个实现了IPlugin接口中的Plugin实现类。

    IPlugin是一个接口,其定义例如以下:

    public interface IPlugin {
    	
    	public static final String SERVICE = "service";
    	public static final String ACTION = "action";
    	public static final String ARGS = "args";
    
    	/**
    	 * 运行请求
    	 * 
    	 * @param action
    	 *            功能
    	 * @param args
    	 *            參数
    	 * @return pluginResult 结果
    	 */
    	public PluginResult exec(String action, JSONObject args)throws ActionNotFoundException;

    里面定义的最重要的方法就是exec方法。每个我们自己定义的插件都要实现这个接口,只是在这里。我们先实现了一个抽象基类Plugin。在里面实现一些公共的逻辑,而对于详细的实现,再由Plugin的子类去继承。

    public abstract class Plugin implements IPlugin {
    
    	protected DroidHtml5 context;

    比方,我们拿上面的Toast类。其就会继承Plugin。然后依据相应的服务去实现相应的逻辑,调用原生环境的Toast。

    public class Toast extends Plugin {
    
    	@Override
    	public PluginResult exec(String action, JSONObject args)
    			throws ActionNotFoundException {
    		if ("makeTextShort".equals(action)) {
    			return makeTextShort(args);
    		}else if ("makeTextLong".equals(action)) {
    			return makeTextLong(args);
    		} else {
    			throw new ActionNotFoundException("Toast", action);
    		}
    
    	}
    
    	private PluginResult makeTextShort(JSONObject args) {
    
    		try {
    			String text = args.getString("text");			
    			android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT).show();
    		} catch (JSONException e) {
    			e.printStackTrace();
    			return PluginResult.newErrorPluginResult(e.getMessage());
    		}
    		return PluginResult.newEmptyPluginResult();
    	}
    	
    	private PluginResult makeTextLong(JSONObject args) {
    
    		try {
    			String text = args.getString("text");			
    			android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_LONG).show();
    		} catch (JSONException e) {
    			e.printStackTrace();
    			return PluginResult.newErrorPluginResult(e.getMessage());
    		}
    		return PluginResult.newEmptyPluginResult();
    	}
    
    }


    从上面的代码。相信大家非常easy就行理解了Plugin机制了。

    3)从Html页面来调用。

    我们在Android原生环境定义了这么一套Plugin机制。那么在Html里面,也能够有这种一套接口方法,来相应不同的Plugin,所以我们在javascript中也会定义各种各样的对象。

    比方上面描写叙述的Toast插件,我们能够在javascript中定义一个相应的对象。例如以下:

    var Toast = {
    
    	makeTextShort : function(text) {
    
    		return exec("Toast", "makeTextShort", JSON.stringify(text));
    	},
    	makeTextLong : function(text) {
    
    		return exec("Toast", "makeTextLong", JSON.stringify(text));
    	}
    
    }

    这里,我们能够看到Toast的makeTextShort方法,会调用上一篇文章中讲到的exec方法,由于弹窗显示这样的东西肯定是同步的,不会说做了一会流程,突然间就跑出一个框来。告诉我,你刚才做错了。对吧。

    而在这里,我们就会将服务名(Toast)。操作方法(makeTextShort)。还有显示的内容(JSON.stringfy(text))等通过exec方法,然后利用WebChromeClient的onJsPrompt方法。将命令传递给PluginManager,由PluginManager来处理。

    		public boolean onJsPrompt(WebView view, String url, String message,
    				String defaultValue, JsPromptResult result) {
    		
    			System.out.println("onJsPrompt:defaultValue:" + defaultValue + "|" + url + "," + message);
    			JSONObject args = null;
    			JSONObject head = null;
    			try {
    				// message:{"service" : "XX", "action" : "xx"}
    				head = new JSONObject(message);
    				if (defaultValue != null && !defaultValue.equals("")) {
    					try {
    						args = new JSONObject(defaultValue);
    					} catch (Exception e) {
    						e.printStackTrace();
    					}
    				}
    
    				String execResult = mPluginManager.exec(head.getString(IPlugin.SERVICE),
    						head.getString(IPlugin.ACTION), args);
    
    				result.confirm(execResult);
    				return true;

    4)我们会把这些定义的插件对象,还有同步(exec),异步运行(exec_sync)的方法都写到一个javascript文件里,方便统一管理,所以一般这个文件内容就会像以下这样:

    var Toast = {
    	makeTextShort : function(text) {
    
    		return exec("Toast", "makeTextShort", JSON.stringify(text));
    	},
    	makeTextLong : function(text) {
    
    		return exec("Toast", "makeTextLong", JSON.stringify(text));
    	}
    }
    var Dialog = {
        ...
    }
    var AndroidHtml5 = {
    	....
    	/*
    	 * exec_asyn调用的方法 @params {JSONObject} cmd 服务名和动作命令 @params {String} args 參数
    	 */
    	callNative : function(cmd, args, success, fail) {
    		....
    	},
    <span style="white-space:pre">	</span>...
    	callBackJs : function(result,key) {
    		...
    	}
    };
    
    /*
     * Html5与Android同步交互接口
     */
    var exec = function(service, action, args) {
    	var json = {
    		"service" : service,
    		"action" : action
    	};
    	var result_str = prompt(JSON.stringify(json), args);
    
    	var result;
    	try {
    		result = JSON.parse(result_str);
    	} catch (e) {
    		console.error(e.message);
    	}
    	...
    }
    /*
     * Html5与Android异步交互接口
     */
    var exec_asyn = function(service, action, args, success, fail) {
    	var json = {
    		"service" : service,
    		"action" : action
    	};
    		
    	var result = AndroidHtml5.callNative(json, args, success, fail);	
    
    }

    结束。





  • 相关阅读:
    COJ 1002 WZJ的数据结构(二)(splay模板)
    生成网络流图
    最小费用最大流MCMF zkw费用流
    COJ 2003 选根 (树的重心)
    最小费用最大流MCMF 最小增广
    PDO 基础知识
    使 用 Jquery 全选+下拉+单选+事件+挂事件
    搜 房 网 站 设 计 练 习
    百分比进度条
    在PHP系统里连接MySQL 数据访问,+ + + + + 数据删除
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7214511.html
Copyright © 2011-2022 走看看