因隐私协议内容太多,用TextView,展示内容会变成STRING_TOO_LARGE;且协议中带有表格,故使用WebView加载html的方式展示。
在页面展示时,创建WebView、初始化,用户体验不佳,故创建WebView池,提前准备好WebView对象
object WebViewPools {
private const val TAG = "WebViewPools"
private val webViewCacheStack = Stack<WebView>()
private const val CACHED_WEB_VIEW_MAX_NUM = 4
private lateinit var application: Application
fun init(application: Application) {
this.application = application
prepareWebView()
}
private fun prepareWebView() {
if (webViewCacheStack.size < CACHED_WEB_VIEW_MAX_NUM) {
Looper.myQueue().addIdleHandler {
Log.w(TAG, "prepareWebView ori Size: " + webViewCacheStack.size)
if (webViewCacheStack.size < CACHED_WEB_VIEW_MAX_NUM) {
for (i in 0 until (CACHED_WEB_VIEW_MAX_NUM - webViewCacheStack.size)) {
val webView = createWebView(MutableContextWrapper(application))
webView.loadUrl("file:///android_asset/user_agreement.html")
webViewCacheStack.push(webView)
}
Log.w(TAG, "prepareWebView Size: ${webViewCacheStack.size}, $webViewCacheStack")
}
//返回值为 false,即只会执行一次;返回值为 true,即每次当消息队列内没有需要立即执行的消息时,都会触发该方法
false
}
}
}
fun acquireWebViewInternal(context: Context?): WebView {
Log.w(TAG, "acquireWebViewInternal context = $context")
val webView: WebView
if (webViewCacheStack.isEmpty()) {
webView = createWebView(MutableContextWrapper(context ?: application))
Log.w(TAG, "acquireWebViewInternal: webViewCacheStack isEmpty")
} else {
webView = webViewCacheStack.pop()
Log.w(TAG, "acquireWebViewInternal: webView = $webView, ${webView.url}")
}
if (null != context) {
val contextWrapper = webView.context as MutableContextWrapper
contextWrapper.baseContext = context
}
return webView
}
private fun createWebView(context: Context): WebView {
return WebView(context)
}
fun recycle(webView: WebView) {
try {
Log.w(TAG, "recycle webView = ${webView.url}")
//根据池容量判断是否销毁
val contextWrapper = webView.context as MutableContextWrapper
contextWrapper.baseContext = webView.context.applicationContext
if (webViewCacheStack.size < CACHED_WEB_VIEW_MAX_NUM) {
webViewCacheStack.push(webView)
} else {
webView.destroy()
}
} catch (e: Exception) {
Log.e(TAG, "recycle: ${e.message}")
}
}
}
//使用
mWebView = WebViewPools.INSTANCE.acquireWebViewInternal(requireContext());
//回收
WebViewPools.INSTANCE.recycle(mWebView);
因WebViewPools在子模块,则要在app模块的application#onCreate中调用子模块的application#onCreate方法,来初始化WebViewPools。同时,防止app模块的application#onCreate调用多次,要使用当前进程和包名匹配的方式
/**
* 子模块的Application
*/
public class SubMoudleApplication extends Application {
static SubMoudleApplication mApp;
@Override
public void onCreate() {
super.onCreate();
init();
}
private void init(){
mApp = this;
WebViewPools.INSTANCE.init(mApp);
}
public static SubMoudleApplication getApp(){
return mApp;
}
}
/**
* app模块的Application
*/
public class application extends Application {
private SubMoudleApplication mSubMoudleApplication;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
getSettingApplicationInstance(this);
if (null == mSubMoudleApplication){
return;
}
try {
//通过反射调用moduleApplication的attach方法
Method method = Application.class.getDeclaredMethod("attach", Context.class);
if (method != null) {
method.setAccessible(true);
method.invoke(mSubMoudleApplication, getBaseContext());
}
} catch (Exception e) {
e.printStackTrace();
}
}
//映射获取SubMoudleApplication
private void getSettingApplicationInstance(Context paramContext) {
try {
if (mSubMoudleApplication == null) {
ClassLoader classLoader = paramContext.getClassLoader();
if (classLoader != null) {
Class<?> mClass = classLoader.loadClass(SubMoudleApplication.class.getName());
if (mClass != null)
mSubMoudleApplication = (SubMoudleApplication) mClass.newInstance();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取当前的进程名
*
* @param context:上下文
* @return :返回值
*/
public String getCurrentProcessName(Context context) {
int pid = android.os.Process.myPid();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
@Override
public void onCreate() {
super.onCreate();
//执行settings模块的application,且只执行一次
if (null != mSubMoudleApplication && getCurrentProcessName(this).equals(getPackageName())){
mSubMoudleApplication.onCreate();
}
}
}
参考如下:
android 组件化开发——多个Module的Application初始化共存问题_android 何如实现多个moduleapplication共存-CSDN博客
Android始化Application两次或者多次的问题_安卓 application 为什么会启动2次-CSDN博客
IdleHandler,页面启动优化神器 - 掘金 (juejin.cn)
Android MutableContextWrapper、IdelHandler实践之预加载View工具类-CSDN博客