I've written an iOS app that I want to port to android. This app is a front-end for a web-based timekeeping system at work. Unfortunately, the system doesn't have an API, so I'm screen-scraping it using xpath queries in javascript. In iOS, I only have to load this page once because I get full control over when instances of my UIWebView
get destroyed.
The iOS app only has 3 use cases, each which break into s开发者_运维知识库eparate Android activities:
- Login
- View a list of all reported times
- Report a new time.
Using a naive approach, my android views (including the WebView
I need to use to interact with the timekeeping system) will be destroyed and recreated when I switch between views. If I have to reload the WebView
every time I switch activities, my app will appear very slow.
Is it possible to share a WebView
instance between multiple
activities? I know I can set the launchMode
to singleInstance
, but ideally, it would be nice to allow separate instances so that the back button would function normally.
Per a suggestion on from Eric Burke at STL Mobile Dev:
See if your Android
Application
can create theWebView
. You won't actually display theWebView
anywhere, it's just created for interaction with the web site. TheWebView
instance's lifecycle is then managed by yourApplication
rather than anActivity
.
I was able to share my WebView
between my Activity
instances. I specified a custom Application
in my manifest, and created the WebView
inside the Application
. The Application
is a Context
and it lives as long as the app does, so all Activity
objects can share the WebView
that way.
The way I solved this problem is little different. It pivots around couple of things.
- WebViews can be created with application context.
- There is something called MutableContextWrapper, we can flip the underlying context inside it.
So, we create the web view in the pool with mutable context wrapper with appContext and when an activity context asks for it, we flip the context and return it. We flip the context to appContext when we return the webView back.
Something like this
// Has a single web view, and crashes if a request to obtain comes in and the
// cached instance is already allocated.
public class SingularWebViewPool implements WebViewPool {
private WebView mCachedInstance;
private volatile int mBorrower;
private final int mSignature;
private final Context mAppCtx;
public SingularWebViewPool(Context appCtx) {
this.mAppCtx = appCtx;
this.mCachedInstance = new WebView(appCtx);
// We will match this every time, we flip to app context to ensure a different web view is
// not handed over to us.
this.mSignature = mCachedInstance.hashCode();
}
@Override
@NonNull
public WebView obtain(@NonNull Context activity) {
if (mCachedInstance != null) {
Context ctx = mCachedInstance.getContext();
if (ctx instanceof MutableContextWrapper) {
((MutableContextWrapper) ctx).setBaseContext(activity);
} else
// We should not reach here!
throw new IllegalStateException("Cached web view stored without a mutable context wrapper.");
WebView temp = mCachedInstance;
mCachedInstance = null;
this.mBorrower = activity.hashCode();
return temp;
} else
throw new IllegalStateException("Pool not having a cached web view instance when obtain() was called.");
}
@Override
public boolean release(@NonNull WebView webView, @NonNull Context borrower) {
// Validate the last borrower.
if (borrower.hashCode() != this.mBorrower) {
return false;
}
Context ctx = webView.getContext();
if (ctx instanceof MutableContextWrapper) {
((MutableContextWrapper) ctx).setBaseContext(mAppCtx);
} else
throw new IllegalStateException("Cached web view stored without a mutable context wrapper.");
// match the signature.
if (mSignature != mCachedInstance.hashCode()) {
throw new IllegalStateException("A different web view is released other than what we have given out.");
}
mCachedInstance = webView;
mBorrower = 0;
return true;
}
}
精彩评论