AndroidとWebViewのプラグイン管理メカニズム
前の記事では、WebViewClientまたはWebChromeClientを使用してhtmlページからのリクエストを処理すると、対応するサービス名、操作方法、および対応するパラメータデータがPluginManagerというクラスに渡されます.
PluginManagerクラスの役割は何ですか?
Androidのオリジナル環境を利用した機能、例えばカメラ、例えばアルバムなど、これらの機能は分散しており、いつこれらの機能が必要なのか、いつこれらの機能が必要なのか、いつ必要なのか分からないので、プラグインのように、必要なときにロードして、必要でないときに相手にしないことを望んでいます.PluginManagerクラスはこのような管理クラスです.
主にいくつかのことを担当しています.
1)HTMLページに入るときは、定義したコントロールをロードします.
では、PluginManagerは、Htmlページからのリクエストに応答するためにどのくらいのpluginをロードするかをどのように知っていますか?
私たちはpluginという人を通じてxmlプロファイルで定義します.
たとえば、上記のプロファイルでは、App、Toast、Dialog、Userのpluginをロードします.
ToastもDialogもAndroidオリジナル環境での表示ウィンドウであり,htmlページでインタフェースを実現するが,アプリケーション全体の一貫性を保つために,オリジナル環境でのToastやカスタムダイアログなどのコントロールを用いることが考えられる.
必要なものは、ここで定義します.
loadPluginの方法をもう一度見てみましょう.
これが解析pluginsですxmlファイルを使用して、対応するプラグインクラス名をconfigsに配置します.configsは次のように定義されます.
loadPluggin法によりpluinsにxmlで定義されたプラグインはconfigsにロードされ、configsに格納されているのはクラス名だけで、pluginsが格納されているのが実現ですが、私たちはこれに関心を持つ必要はありません.
ここでplugins.xmlファイルで定義されたnameプロパティがこのサービス名です.2)要求されたサービス名や操作方法などにより,その要求に対応するPluginを見つけて処理する.
execの方法を見てみましょう
上記の論理から、PluginManagerはgetPluginメソッドを使用して対応するサービスを次のように取り出します.
これにより,IPluginインタフェースを実装したPlugin実装クラスを手に入れた.
IPluginは、次のように定義されたインタフェースです.
中で定義された最も重要な方法はexec方法であり、私たちがカスタマイズしたプラグインごとにこのインタフェースを実現しなければならないが、ここでは抽象的なベースクラスPluginを実現し、中でいくつかの共通の論理を実現し、具体的な実現についてはPluginのサブクラスが継承する.
たとえば,上記のToastクラスを持つと,Pluginを継承し,対応するサービスに基づいて対応する論理を実現し,原生環境のToastを呼び出す.
上のコードから、Pluginメカニズムを簡単に理解できると信じています.
3)Htmlページから呼び出す.
Androidのオリジナル環境でこのようなPluginメカニズムを定義したので、Htmlでは、異なるPluginに対応するためのインタフェース方法もあるので、javascriptでも様々なオブジェクトを定義します.
たとえば、上記のToastプラグインでは、javascriptで次のように対応するオブジェクトを定義できます.
ここでは、ToastのmakeTextShortメソッドを見ることができます.前の文章で述べたexecメソッドを呼び出します.弾窓はこのようなものが同期しているに違いありません.しばらくの流れをしたとは言いません.突然枠を飛び出して、さっき間違っていたことを教えてください.そうでしょう.
ここでは、サービス名(Toast)、操作方法(makeTextShort)、表示される内容(JSON.stringfy(text))などをexecメソッドで、WebChromeClientのonJsPromptメソッドを利用して、PluginManagerにコマンドを渡し、PluginManagerで処理します.
4)これらの定義されたプラグインオブジェクトと同期(exec)、非同期実行(exec_sync)の方法をjavascriptファイルに書き、統一管理が容易であるため、一般的にこのファイルの内容は以下のようになります.
終わります.
PluginManagerクラスの役割は何ですか?
Androidのオリジナル環境を利用した機能、例えばカメラ、例えばアルバムなど、これらの機能は分散しており、いつこれらの機能が必要なのか、いつこれらの機能が必要なのか、いつ必要なのか分からないので、プラグインのように、必要なときにロードして、必要でないときに相手にしないことを望んでいます.PluginManagerクラスはこのような管理クラスです.
主にいくつかのことを担当しています.
1)HTMLページに入るときは、定義したコントロールをロードします.
mPluginManager = new PluginManager(this);
mPluginManager.loadPlugin();
では、PluginManagerは、Htmlページからのリクエストに応答するためにどのくらいのpluginをロードするかをどのように知っていますか?
私たちは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>();
loadPluggin法によりpluinsに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) {
....
},
<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);
}
終わります.