我们都知道webView是加载解析网页代码用的,但是如果webView加载的网页数据过大的话就会消耗本进程的内存空间,从而影响app的性能;因为,系统给每个app的进程分配的空间是有限的,过多的使用空间会造成进程空间资源不足;因此,遇到这种情况我们最好给webView开一个独立进程,这样的话app主进程就节省了加载webView资源的空间
webView与native通信
我们知道js要想调用native代码的话,需要在webView里面添加addJavascriptInterface;js在调用native代码的时候无非就是根据不同的行为传递不同的参数的问题,那这样的话,我们不妨用类似命令模式的方式,来定义一个命令接口,然后当js调用JavascriptInterface的方法的时候,通过传递不同的action 与params到native代码,然后通过命令分发来执行不同的命令,这样的话就实现了一个简单的通信框架,如图所示:
具体步骤如下:
创建JavascriptInterface接口
首先我们在webView初始化的时候创建出与js通信的接口:
public final class WebviewJavascriptInterface {
private final Context mContext;
private final Handler mHandler = new Handler();
private JavascriptCommand javascriptCommand;
public WebviewJavascriptInterface(Context context) {
mContext = context;
}
@JavascriptInterface
public void post(final String cmd, final String param) {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
if (javascriptCommand != null) {
javascriptCommand.exec(mContext, cmd, param);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void setJavascriptCommand(JavascriptCommand javascriptCommand) {
this.javascriptCommand = javascriptCommand;
}
public interface JavascriptCommand {
void exec(Context context, String cmd, String params);
}
}
然后将这个接口设置到webView里面就行了
命令分发
接着定义一个webViewCallBack接口,这个接口是将javascriptCommand.exec()回调给webViewCallBack,相当于一个代理吧,webViewCallBack可以让fragment实现然后去做分发处理:
public interface WebViewCallBack {
int getCommandLevel();
void pageStarted(String url);
void pageFinished(String url);
boolean overrideUrlLoading(WebView view, String url);
void onError();
void exec(Context context, int commandLevel, String cmd, String params, WebView webView);
}
可以让fragment或者activity实现这个接口,如下所示:
@Override
public void exec(Context context, int commandLevel, String cmd, String params, WebView webView) {
CommandDispatcher.getInstance().exec(context, commandLevel, cmd, params, webView, getDispatcherCallBack());
}
可以看到在这个里面需要去调命令分发:
public void exec(Context context, int commandLevel, String cmd, String params, WebView webView,
DispatcherCallBack dispatcherCallBack) {
Log.i("CommandDispatcher", "command: " + cmd + " params: " + params);
try {
if (CommandsManager.getInstance().checkHitLocalCommand(commandLevel, cmd)) {
execLocalCommand(context, commandLevel, cmd, params, webView, dispatcherCallBack);
} else {
execRemoteCommand(context, commandLevel, cmd, params, webView, dispatcherCallBack);
}
} catch (Exception e) {
Log.e("CommandDispatcher", "Command exec error!!!!", e);
}
}
首先会判断是否是在本地需要处理的命令,不是的话就去执行远程命令,因为webView通信有可能是多进程的,所以这里把命令分为了3个等级:
- LocalCommands (本地命令)
- BaseLevelCommands (基础的一些命令)
- AccountLevelCommands (与账户相关,带有头信息的)
这3个等级,是在CommandsManager里面管理的,这个里面有注册命令的方法registerCommand, 与获取命令的方法findAndExecRemoteCommand(寻找远程要执行的命令), findAndExecLocalCommnad(寻找本地要执行的命令),这些命令有一个统一的接口:
public interface Command {
String COMMAND_UPDATE_TITLE = "xiangxue_webview_update_title";
String COMMAND_UPDATE_TITLE_PARAMS_TITLE = "xiangxue_webview_update_title_params_title";
String name();
void exec(Context context, Map params, ResultBack resultBack);
}
而这些命令是被Commands这个命令集合所分类管理的:
public abstract class Commands {
private HashMap<String, Command> commands;
abstract int getCommandLevel();
public HashMap<String, Command> getCommands() {
return commands;
}
public Commands() {
commands = new HashMap<>();
}
protected void registerCommand(Command command) {
commands.put(command.name(), command);
}
}
可以看到这是一个抽象的,它的实现就是刚才说的那3个等级的命令集,它被管理在CommandsManager里面:
public class CommandsManager {
private static CommandsManager instance;
private LocalCommands localCommands;
private BaseLevelCommands baseLevelCommands;
private AccountLevelCommands accountLevelCommands;
private CommandsManager() {
localCommands = new LocalCommands();
baseLevelCommands = new BaseLevelCommands();
accountLevelCommands = new AccountLevelCommands();