很多问题,自己测试可能测不出来,但是跑到了线上,就有可能出现问题,比如多线程问题。
错误堆栈:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:437)
at com.zhangyue.we.ad.model.AdSchedule.getValidPartnersAdSource(SourceFile:347)
at com.zhangyue.module.ad.e.isPartnersAdTimeOn(SourceFile:177)
at com.zhangyue.module.ad.e.processEvent(SourceFile:50)
at com.zhangyue.iReader.module.driver.ad.a.loadAdStrategy(SourceFile:324)
at com.zhangyue.iReader.module.proxy.AdProxy.loadAdStrategy(SourceFile:72)
at com.chaozh.iReader.ui.activity.u.run(SourceFile:689)
at java.lang.Thread.run(Thread.java:764)
有问题的代码:
public class AdSchedule {
private ArrayList<String> mAdResource;
public boolean hasValidAd(String pos) {
if (!isHaveRules()) {
return false;
}
ArrayList<Bean> beans = body.rule.get(pos);
if (beans != null) {
mAdResource = new ArrayList<>();
mAdResource.clear();
for (Bean bean : beans) {
if (bean.isValid()) {
mAdResource.add(bean.source);
return true;
}
}
}
return false;
}
*
* @param pos 广告显示的位置
* @return
*/
public String getValidPartnersAdSource(String pos) {
if (!isHaveRules()) {
return "";
}
ArrayList<Bean> beans = body.rule.get(pos);
if (beans != null) {
mAdResource = new ArrayList<>();
mAdResource.clear();
int apkVersionCode = APK.getApkVersionCode(PluginRely.getAppContext());
if (Util.isRunningInVivo(PluginRely.getAppContext()) && apkVersionCode >= 7150005 && apkVersionCode != 7150009) {
for (Bean bean : beans) {
if (bean.isValid()) {
mAdResource.add(bean.source);
LOG.E(TAG, "Ad插件配置的广告资源 source : " + bean.source);
}
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < mAdResource.size(); i++) {
result.append(mAdResource.get(i));
if (i != mAdResource.size()-1) {
result.append("|");
}
}
return result.toString();
}else {
for (Bean bean : beans) {
if (bean.isValid()) {
mAdResource.add(bean.source);
LOG.E(TAG, "Ad插件配置的广告资源 source : " + bean.source);
return bean.source;
}
}
}
}
return "";
}
/**
* 获取当前要显示的广告资源。返回的是排期中第一个广告资源
*
* @return
*/
@JSONField(serialize = false)
public String getAdResource() {
if (mAdResource != null && mAdResource.size() > 0) {
String source = mAdResource.get(0);
return TextUtils.isEmpty(source) ? "三方广告" : source;
} else {
return "三方广告";
}
}
}
修改之后:
/**
* @description
*/
public class AdSchedule {
public final static String TAG = "ad_AdSchedule";
public int code;
public String msg;
public BodyBean body;
// 仅标识开屏位置的广告源
private ArrayList<String> mAdResource;
*
* @param pos 广告显示的位置
* @return
*/
public String getValidPartnersAdSource(String pos) {
if (!isHaveRules()) {
return "";
}
ArrayList<Bean> beans = body.rule.get(pos);
if (beans != null) {
List<String> adResource = new ArrayList<>();
//vivo 项目 该方法返回一个列表 里面有广告补量的逻辑,如果第一个广告展示不了,加载第二个,只在vivo 7.15.5以上版本返回,7.15.9上不返回,因为7.15.9是7.15.3的复制品
int apkVersionCode = APK.getApkVersionCode(PluginRely.getAppContext());
if (Util.isRunningInVivo(PluginRely.getAppContext()) && apkVersionCode >= 7150005 && apkVersionCode != 7150009) {
for (Bean bean : beans) {
if (bean.isValid()) {
adResource.add(bean.source);
LOG.E(TAG, "Ad插件配置的广告资源 source : " + bean.source);
}
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < adResource.size(); i++) {
result.append(adResource.get(i));
if (i != adResource.size()-1) {
result.append("|");
}
}
return result.toString();
}else {
for (Bean bean : beans) {
if (bean.isValid()) {
adResource.add(bean.source);
LOG.E(TAG, "Ad插件配置的广告资源 source : " + bean.source);
return bean.source;
}
}
}
}
return "";
}
/**
* 获取当前要显示的广告资源。返回的是排期中第一个广告资源
*
* @return
*/
@JSONField(serialize = false)
public String getAdResource() {
if (mAdResource == null) {
if (isHaveRules()) {
ArrayList<Bean> beans = body.rule.get(Const.POS_SPLASH);
if (beans != null) {
mAdResource = new ArrayList<>();
for (Bean bean : beans) {
if (bean.isValid()) {
mAdResource.add(bean.source);
}
}
}
}
}
if (mAdResource != null && mAdResource.size() > 0) {
String source = mAdResource.get(0);
return TextUtils.isEmpty(source) ? "三方广告" : source;
} else {
return "三方广告";
}
}
}
错误原因:
这个mAdResource 在多个方法里面都有赋值操作,如果在一个方法里面正在遍历,另一个方法里面直接清楚了,那么就会发现数组越界异常。
大家最好在自己方法里面,单独赋值给一个局部变量,然后对局部变量进行操作。或者加上锁。
比如一下代码,把ma 赋值给局部变量a,然后对a 进行操作,就不会有多线程问题。
Object a = ma;
a.setValue
错误堆栈:
java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:724)
at com.zhangyue.ireader.zyadsdk.comm.managers.n.b(SourceFile:101)
at com.zhangyue.ireader.zyadsdk.comm.managers.n.preLoadVideoMaterial(SourceFile:86)
at com.zhangyue.iReader.module.driver.ad.a.loadAdStrategy(SourceFile:334)
at com.zhangyue.iReader.module.proxy.AdProxy.loadAdStrategy(SourceFile:72)
at com.zhangyue.iReader.module.idriver.ad.AdUtil.updateAdSchedule(SourceFile:33)
at com.zhangyue.iReader.free.FreeModelReceiver.onReceive(SourceFile:41)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(SourceFile:313)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(SourceFile:121)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7073)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)
有问题的:
private void initHandler() {
if (mHandlerThread == null) {
mHandlerThread = new HandlerThread("LoadVideoMaterial", Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
}
修改之后:
private synchronized void initHandler() {
if (mHandlerThread == null) {
mHandlerThread = new HandlerThread("LoadVideoMaterial", Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
}
原因:
因为这个方法可能会把多个线程调用,所以,只是判空不能解决多线程的问题。所以,保险起见,所有被多个线程调用的地方,都要做
多线程处理。尤其是线程启动这种,只能调用一次的地方。
总结:
1. 变量在多个方法都有操作,容易多线程异常
2. 最好赋值给一个新的本地变量。
3. 线程开启问题,线程启动最好都有多线程处理。