本文调查了Android H5的秒开方案,分析了今日头条App的秒开方案。
本文首发于:
背景
在回家的地铁上使用自己APP的H5相关功能时,可能会因为网络原因体验不佳。用微信和今日头条App,感觉很流畅,基本秒开,然后想了解一下行业内的H5秒。打开一个计划。
问题 原因 常见解决方法 WebView 缓存相关
这可以通过以下代码实现:
WebSettings webSettings = myWebView.getSettings();webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);webSettings.setDomStorageEnabled(true);webSettings.setDatabaseEnabled(true);final String dbPath = getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();webSettings.setDatabasePath(dbPath); webSettings.setAppCacheEnabled(true);final String cachePath = getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();webSettings.setAppCachePath(cachePath);webSettings.setAppCacheMaxSize(5*1024*1024);webSettings.setJavaScriptEnabled(true);
开源解决方案 今日头条解决方案
我们来看看今日头条的效果。第二次断网打开页面,秒级达到打开页面的效果:
今日头条对其平台的文章详情页做了很多优化,包括以下几点:
内置所需文件
WebView预创建、资源预加载
第一次创建 WebView 比第二次创建要慢得多。估计第一次创建WebView需要初始化一些静态资源,第二次创建不需要初始化,所以第二次创建耗时少很多。
使用Context包装类MutableContextWrapper传入Application,预先创建WebView对象,然后预先加载一个用java代码拼接的html,并提前解析js和css资源。当您获得预先创建的 WebView 时,将其替换为 Activity 的上下文。
public class PreloadWebView { private PreloadWebView(){} private static final int CACHED_WEBVIEW_MAX_NUM = 2; private static final Stack mCachedWebViewStack = new Stack<>(); public static PreloadWebView getInstance(){ return Holder.INSTANCE; } private static class Holder{ private static final PreloadWebView INSTANCE = new PreloadWebView(); } /** * 创建WebView实例 * 用了applicationContext */ public void preload() { L.d("webview preload"); Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { if (mCachedWebViewStack.size() < CACHED_WEBVIEW_MAX_NUM) { mCachedWebViewStack.push(createWebView()); } return false; } }); } private WebView createWebView() { WebView webview = new WebView(new MutableContextWrapper(App.getApp())); webview.getSettings().setJavaScriptEnabled(true); webview.loadDataWithBaseURL("file:///android_asset/article/?item_id=0&token=0",getHtml(),"text/html","utf-8","file:///android_asset/article/?item_id=0&token=0"); return webview; } private static String getHtml() { StringBuilder builder = new StringBuilder(); builder.append(""); builder.append(""); builder.append(""); builder.append(""); builder.append(""); builder.append(""); builder.append(" "); builder.append(""); builder.append(""); builder.append(""); builder.append(""); return builder.toString(); } /** * 从缓存池中获取合适的WebView * * @param context activity context * @return WebView */ public WebView getWebView(Context context) { // 为空,直接返回新实例 if (mCachedWebViewStack == null || mCachedWebViewStack.isEmpty()) { WebView web = createWebView(); MutableContextWrapper contextWrapper = (MutableContextWrapper) web.getContext(); contextWrapper.setBaseContext(context); return web; } WebView webView = mCachedWebViewStack.pop(); // webView不为空,则开始使用预创建的WebView,并且替换Context MutableContextWrapper contextWrapper = (MutableContextWrapper) webView.getContext(); contextWrapper.setBaseContext(context); return webView; }}
本地数据库缓存
使用数据库进行持久化。
图片资源展示
使用 ContentProvider 获取图片资源:
content://com.xposed.toutiao.provider.ImageProvider/getimage/origin/eJy1ku0KwiAUhm8l_F3qvuduJSJ0mRO2JtupiNi9Z4MoWiOa65cinMeX57xXVDda6QPKFld0bLQ9UckbJYlR-UpX3N5Smfi5x3JJ934YxWlKWZhEgbeLhBB-QNFyYUfL1s6uUQFgMkKMtwLA4gJSVwrndUWmUP8CC5xhm87izlKY7VDeTgLXZUtOlJzjkP6AxXfiR5eMYdMCB9PHneGHBzh-VzEje7AzV3ZvHYpjJV599w-uZWXvWadQR_vlAhtY_Bn2LKuzu_GGOscc1MfZ4veyTyNuuu4G1giVqQ==/6694469396007485965/3
上面ContentProvider的uri会调用对应ContentProvider的openFile方法,别忘了在manifest文件中注册。
public class ImageProvider extends ContentProvider { ... public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException { File file = getFile(uri); return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) ; } ...}
中间字符串使用 zip 压缩,使用以下代码解压 zip 数据:
static final byte[] buffer = new byte[4096];public static final String unzip(String str) { try { Inflater inflater = new Inflater(); inflater.setInput(Base64.decode(str, 8)); int size = inflater.inflate(buffer); inflater.end(); String temp = new String(buffer, 0, size, "UTF-8"); return temp; } catch (Exception e) { e.printStackTrace(); } return "";}
解压后的数据如下:
{ "origin": { "uri": "large/pgc-image/8e72c19ce0f2456880947531d5bbb230", "urls": ["http://p1-tt.byteimg.com/large/pgc-image/8e72c19ce0f2456880947531d5bbb230", "http://p1-tt.byteimg.com/large/pgc-image/8e72c19ce0f2456880947531d5bbb230", "http://p3-tt.byteimg.com/large/pgc-image/8e72c19ce0f2456880947531d5bbb230"] }, "webp_origin": { "uri": "details/v0/w640/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp", "urls": ["http://p99.pstatp.com/details/v0/w640/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp", "http://p6-tt.byteimg.com/details/v0/w640/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp", "http://p1-tt.byteimg.com/details/v0/w640/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp"] }, "thumb": { "uri": "thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230", "urls": ["http://p9-tt.byteimg.com/thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230", "http://p3-tt.byteimg.com/thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230", "http://p1-tt.byteimg.com/thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230"] }, "webp_thumb": { "uri": "thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp", "urls": ["http://p1-tt.byteimg.com/thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp", "http://p3-tt.byteimg.com/thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp", "http://p6-tt.byteimg.com/thumb/pgc-image/8e72c19ce0f2456880947531d5bbb230.webp"] }}
uri的最后两个片段分别代表文章id和图片索引今日头条本地怎么设置,用于通过js通知页面图片加载完成。通过解析content的uri中的数据,获取Fresco下载的缓存文件,返回一个ParcelFileDescriptor对象。结果如下图:
通过以上优化点最终达到的效果如下:
总结
通过今日头条APP的分析,针对特定平台的文章今日头条本地怎么设置,可以采用类似今日头条的方案来预加载数据,提升用户体验。由于时间关系,我们不再分析微信,猜测采用了类似的方案。通过WebView提供的缓存功能和拦截资源方式的缓存体验还是不尽如人意。或许等5G流行起来会好很多。
公众号后台回复关键词:666,即可获得程序员大礼包!
参考
Android Webview H5二次开放方案的实现
腾讯大招VasSonic,让你的H5页面首屏秒开
- -结尾 - -
选择“开发者技术前线”星标,内容触手可及。点击原文,更多惊喜!
开发者技术前沿汇聚技术前沿新闻,关注行业动态。是开发者体验和成长的绝佳指南。
历史推荐
点击观看,解锁更多惊喜!