当屏幕方向发生变化并且多线程正在运行时,我刚刚发现 Adview 出现问题。我认为这可能是 Adview 的错误。这个问题会影响应用程序中的所有 webviews。webviews 停止加载任何内容,即它们要么显示空白,要么显示之前显示的内容。
测试环境是
平台:Google Nexus S + Android 4.1.2(不只是这个平台,其他很多平台,连模拟器都受影响)
Ads SDK:6.4.1(我们也测试了 Ads SDK 4.1.1 也有同样的问题)
重现此问题的方法是
使用以下清单、main.xml 布局和 helloAndroid.java 以及 Ads SDK(6.4.1 或更低版本)构建一个 android 项目;
在上述平台上运行HelloAndroid项目,或者其他平台甚至模拟器也可能受到影响;
应用程序启动后,显示“已调用 OnCreate 函数”。在 webview 中,以及加载内容的时间。同时,广告显示在屏幕顶部;
现在点击“开始计算线程”按钮,同时旋转屏幕改变方向,然后立即再次改变方向,等等……同时,在最后一个计算任务完成后立即连续点击“开始计算线程”按钮。重复这些动作几次,然后你会注意到 webview 没有重新加载。无论您是否旋转屏幕,它的文本始终是“后台计算线程完成”。(无论您是否单击“开始计算线程”按钮,其文本也可能是“配置更改”)。webview 中显示的时间也不会更新。不显示广告。
从日志文件中,我们可以看到很多广告错误,例如:
11-17 11:51:11.254: I/Ads(7411): AdLoader 在获取 URL 时在 60000 毫秒后超时。
11-17 11:51:11.254: I/Ads(7411): onFailedToReceiveAd(发生网络错误。)
我们也得到了一个异常,但是这个异常出现在上面的错误出来之后。
11-17 11:53:14.597: W/Ads(7411): 在 AdWebView 中加载数据时出错:
11-17 11:53:14.609: I/Ads(7411): 捕获并处理了以下内容:
11-17 11:53:14.609: I/Ads(7411): java.lang.NullPointerException
11-17 11:53:14.609: I/Ads(7411): 在 android.webkit.WebViewClassic.loadDataWithBaseURL(WebViewClassic.java:2564)
11-17 11:53:14.609: I/Ads(7411): 在 android.webkit.WebView.loadDataWithBaseURL(WebView.java:842)
11-17 11:53:14.609: I/Ads(7411): at com.google.ads.internal.AdWebView.loadDataWithBaseURL(SourceFile:229)
11-17 11:53:14.609: I/Ads(7411): at com.google.ads.internal.c$c.run(SourceFile:164)
11-17 11:53:14.609: I/Ads(7411): 在 android.os.Handler.handleCallback(Handler.java:615)
11-17 11:53:14.609: I/Ads(7411): 在 android.os.Handler.dispatchMessage(Handler.java:92)
11-17 11:53:14.609: I/Ads(7411): 在 android.os.Looper.loop(Looper.java:137)
11-17 11:53:14.609: I/Ads(7411): 在 android.app.ActivityThread.main(ActivityThread.java:4745)
11-17 11:53:14.609: I/Ads(7411): 在 java.lang.reflect.Method.invokeNative(Native Method)
11-17 11:53:14.609: I/Ads(7411): 在 java.lang.reflect.Method.invoke(Method.java:511)
11-17 11:53:14.609: I/Ads(7411): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
11-17 11:53:14.609: I/Ads(7411): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-17 11:53:14.609: I/Ads(7411): at dalvik.system.NativeStart.main(Native Method)
我们认为这个问题是由 AdView 引起的,而不是由 webView 或基于以下观察的线程编程引起的:
我们发现,即使我们把webView放到另外一个activity中,即多线程activity中只包含一个button和adView,经过上面的复现步骤,还是会出现这个问题。在问题出现后,我们移动了包含 webView 的其他活动,它仍然停止加载任何内容。换句话说,不是 webView 触发了问题。
计算线程非常简单,不包含任何UI操作。开始和停止加载广告总是在主线程中完成。
代码非常简单。清单文件是:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.helloandroid"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="7" />
<!-- uses-permission android:name="android.permission.INTERNET"/-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".HelloAndroid"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.google.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
</application>
</manifest>
唯一的布局文件是:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:id="@+id/adsHolder" >
</LinearLayout>
<android.webkit.WebView
android:id="@+id/webview_show_something"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@+id/button1"
android:layout_below="@+id/adsHolder"
></android.webkit.WebView>
<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="Start Calculating Thread" />
</RelativeLayout>
HelloAndroid.java 文件是:
package com.example.helloandroid;
import java.text.DateFormat;
import java.util.Date;
import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
public class HelloAndroid extends Activity {
// Need handler for callbacks to the UI thread
public Handler mHandler;
public WebView mwebView;
public AdView madView;
public Thread mthread = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mHandler = new Handler(); // handler is attached to the UI thread.
Button btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
final ProgressDialog dlgProgress = ProgressDialog.show(HelloAndroid.this, "Please wait",
"Background calculating ...", true);
mthread = new Thread(new Runnable(){
@Override
public void run() {
for (int idx = 0; idx < 10000; idx ++) {
for (int idx1 = 0; idx1 < 10; idx1 ++) {
int a = idx + idx1;
a ++;
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
dlgProgress.dismiss();
if (madView != null) {
madView.loadAd(new AdRequest());
}
String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
mwebView.loadDataWithBaseURL("http://",
"<html><body><h1>Background calculating thread done.</h1><p>" + currentDateTimeString + "</p></body></html>",
"text/html","utf-8","");
mthread = null;
}
});
}
});
if (madView != null) {
madView.stopLoading();
}
mthread.start();
}
});
mwebView = (WebView)findViewById(R.id.webview_show_something);
mwebView.setVerticalScrollBarEnabled(true);
mwebView.setHorizontalScrollBarEnabled(true);
mwebView.getSettings().setBuiltInZoomControls(true);
mwebView.setWebViewClient(new WebViewClient());
String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
mwebView.loadDataWithBaseURL("http://",
"<html><body><h1>OnCreate function called.</h1><p>" + currentDateTimeString + "</p></body></html>",
"text/html","utf-8","");
startLoadingAds();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// because it is smart_banner, adview has to be dynamically generated to fit landscape or portrait screen.
stopLoadingAds();
if (mthread == null) { // only if calculating thread finishes we start to load ads.
startLoadingAds();
}
String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
mwebView.loadDataWithBaseURL("http://",
"<html><body><h1>Configuration changed.</h1><p>" + currentDateTimeString + "</p></body></html>",
"text/html","utf-8","");
}
public void stopLoadingAds() {
// reset the adView.
if (madView != null) {
madView.stopLoading();
ViewGroup viewGroup = (ViewGroup) madView.getParent();
if (viewGroup != null)
{
viewGroup.removeView(madView);
}
madView.removeAllViews();
madView.destroy(); // not required.
madView = null;
}
}
public void startLoadingAds() {
LinearLayout layoutAdView = (LinearLayout)findViewById(R.id.adsHolder);
layoutAdView.removeAllViews();
madView = new AdView(this, AdSize.SMART_BANNER, "ca-app-pub-9004844319824679/3238161316");
if (madView != null) {
layoutAdView.addView(madView, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
madView.loadAd(new AdRequest());
}
}
}