上篇文章已经大概介绍了crosswalk的用法,现在就来看看一个嵌套了crosswalk的app究竟有怎么的构造。
在/data/data/myapp/目录下发现多了一个app_xwalkcoew目录,在里面保存了缓存文件已经runtime,说明它不能与webview共用缓存。
由图片可以知道,本来在raw目录下的8M多的libxwalkcore.so.armeabi_v7a已经被解压成了21M的文件。一个大概12m的包runtime就占了8M多,我突然觉得,在不使用share mode的情况下能不能把crosswalk”变小”,给用户一种很好的感觉呢?答案是可以的。
那就是把libxwalkcore.so提取出来,改为打开app的时候在后台下载,这样看起来安装包就感觉变小了,但是下载的这段时间是不能使用Crosswalk的,这时就只能使用webview,下载完成后再转换成crosswalk
首先定义一个抽象类实现crosswalk和webview的集成,通过子类继承它来实现
/**
* 实现crosswalk和webview的集成,游戏界面可以继承该类可直接调用xwalkview加载网页
*@author chenjiantian1992@gmail.com
*@time 2015-7-31
*/
public abstract class CrosswalkActivity extends Activity{
private CrosswalkUtil crosswalkUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
crosswalkUtil = new CrosswalkUtil(this);
//初始化crosswalk
initCrossWalkEnv();
//配置加载crosswalk还是webview
configEnv();
}
private void configEnv() {
//判断app中是否有crosswalk runtime 即libxwalkcore.so
if(crosswalkUtil.haveCwModel()){
initCrosswalk();
}else{
initSysWebView();
loadCrosswalk();
}
}
/**
* 初始化crosswalk
*/
public abstract void initCrosswalk();
/**
* 初始化系统webview
*/
public abstract void initSysWebView();
/**
* 加载配置crosswalk环境
*/
public abstract void loadCrosswalk();
private void initCrossWalkEnv() {
//如果有crosswalk模块,初始化crosswalk环境
if(crosswalkUtil.haveCwModel()){
onXWalkCoreReady();
}
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
//不为空,可以初始化crosswalk环境,必须调用该方法
if(mActivityDelegate != null){
mActivityDelegate.onResume()
}
}
/**
* 初始化crosswalk环境,才能调用XwalkView
*/
protected void onXWalkCoreReady() {
mActivityDelegate = new XWalkActivityDelegate(this, cancelCommand, completeCommand);
}
Runnable cancelCommand = new Runnable() {
@Override
public void run() {
finish();
}
};
Runnable completeCommand = new Runnable() {
@Override
public void run() {
onXWalkReady();
}
};
private XWalkActivityDelegate mActivityDelegate;
public abstract void onXWalkReady() ;
}
然后,在MainActivity中继承这个抽象类实现它
public class MainActivity extends CrosswalkActivity{
XWalkView xWalkView;
@Override
public void onXWalkReady() {
xWalkView.load("http://play.7724.com/olgames/hdcgbt/", null);
}
@Override
public void initCrosswalk() {
Logger.showI("系统Crosswalk");
xWalkView = new XWalkView(this, (Activity)this);
setContentView(xWalkView);
}
@Override
public void initSysWebView() {
Logger.showI("系统webview");
WebViewFactory viewFactory = new WebViewFactory(this);
WebView webView = viewFactory.getWebView();
webView.loadUrl("http://www.csdn.net/");
setContentView(webView);
}
//调用webview说明没有runtime,加载它
@Override
public void loadCrosswalk() {
CrosswalkModelDownloadUtil modelDownloadUtil = new CrosswalkModelDownloadUtil(this);
//已经下载了runtime文件,下面将它解压放到指定位置
if(modelDownloadUtil.haveFileInLocal()){
Logger.showI("本地有so文件");
File file = new File(modelDownloadUtil.getFilePath());
new Thread(new DpRunnable(file)).start();
}else if(NetWorkHelp.isWifiConnected(this)){
Logger.showI("本地没有,下载");//开启一个服务下载runtime文件
startService(new Intent(this, CrosswalkDownloadService.class));
}
}
//线程解压crosswalk
public class DpRunnable implements Runnable{
File file = null;
CrosswalkUtil crosswalkUtil = new CrosswalkUtil(BaseActivity.this);
public DpRunnable(File filetmp) {
super();
file = filetmp;
}
@Override
public void run() {
Logger.showI("开始解压"+file.exists()+file.getAbsolutePath());
try {
FileInputStream inputStream = new FileInputStream(file);
crosswalkUtil.decompress(BaseActivity.this, inputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
下面看一下CrosswalkUtil工具类,它提供压缩文件解压,判断app是否有runtime模块
/**
* 判断默认目录有无libxwalkcore.so文件,有之后才能使用XwalkView
* @return
*/
public boolean haveCwModel(){
File cwModelDir = context.getDir("xwalkcore", Context.MODE_PRIVATE);
if(!cwModelDir.exists())
return false;
File soFile = new File(cwModelDir, "libxwalkcore.so");
if(soFile.exists() && (soFile.length()>=MAX_COUNT))
return true;
return false;
}
/**
* 解压文件流,并放到默认的目录
* @param context
* @param inputStream
* @return
*/
public boolean decompress(Context context,InputStream inputStream) {
ReentrantLock reentrantLock = new ReentrantLock();
File libDir = context.getDir("xwalkcore", Context.MODE_PRIVATE);
if (libDir.exists() && libDir.isFile()) libDir.delete();
if (!libDir.exists() && !libDir.mkdirs()) return false;
reentrantLock.lock();
for (String library : MANDATORY_LIBRARIES) {
File tmpfile = null;
InputStream input = null;
OutputStream output = null;
try {
File outfile = new File(libDir, library);
tmpfile = new File(libDir, library + ".tmp");
input = new BufferedInputStream(inputStream);
output = new BufferedOutputStream(new FileOutputStream(tmpfile));
decodeWithLzma(input, output);
tmpfile.renameTo(outfile);
} catch (Resources.NotFoundException e) {
return false;
} catch (Exception e) {
return false;
} finally {
reentrantLock.unlock();
if (output != null) {
try {
output.flush();
} catch (IOException e) {
}
try {
output.close();
} catch (IOException e) {
}
}
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
tmpfile.delete();
}
}
return true;
}
private void decodeWithLzma(InputStream input, OutputStream output) throws IOException {
final int propSize = 5;
final int outSizeLength = 8;
byte[] properties = new byte[propSize];
if (input.read(properties, 0, propSize) != propSize) {
throw new EOFException("Input .lzma file is too short");
}
Decoder decoder = new Decoder();
if (!decoder.SetDecoderProperties(properties)) {
Log.w(TAG, "Incorrect stream properties");
}
long outSize = 0;
for (int i = 0; i < outSizeLength; i++) {
int v = input.read();
if (v < 0) {
Log.w(TAG, "Can't read stream size");
}
outSize |= ((long)v) << (8 * i);
}
if (!decoder.Code(input, output, outSize)) {
Log.w(TAG, "Error in data stream");
}
}
上面两个方法中,通过查找app_xwalkcoew目录有无libxwalkcore.so文件来判断是否有加载runtime,而解压的方法是在源代码里面截取的,文件是以7zip提供的api解压的。
另外res\raw\libxwalkcore.so.armeabi_v7a删除的话,可能老版的crosswalk初始化的时候会报错,所以我们可以添加一个空的libxwalkcore.so.armeabi_v7a
总结一下
- 可以通过删除runtime文件改为启动时下载来“减少”apk大小
- 下载的文件解压到制定目录成功后才能调用crosswalk,故每次调用webview都需要判断