Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android與WebView的插件管理機制

Android與WebView的插件管理機制

編輯:關於Android編程

上一篇文章說到,當利用WebViewClient或者WebChromeClient來處理由html頁面傳過來的請求的時候,都會將對應的服務名稱,操作方法和對應的參數數據傳給一個叫PluginManager的類。

PluginManager類的作用是什麼?

大家知道,當利用Android原生環境的功能,比如照像機,比如相冊等,這些功能都是很分散的,說不清楚什麼時候是需要這些功能,什麼時候是不需要這些功能的,所以我們希望能夠像插件一樣,需要的時候就加載進來,不需要的時候不去理他,而PluginManager類就是一個這樣的管理類。

它主要負責幾件事情:

1)進入HTML頁面的時候,去加載我們定義好的控件。

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

那麼PluginManager怎麼知道本個應用要加載多少plugin來去響應由Html頁面來的請求呢?

我們是通過一個叫plugin.xml配置文件來定義的。


    
    
       
    


比如在上面的配置文件中,我們會加載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 configs = new HashMap();
private HashMap plugins = new HashMap();

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

}


從上面的代碼,相信大家很容易就能夠理解了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) {
		....
	},
	...
	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);	

}

結束。





  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved