很多問題,自己測試可能測不出來,但是跑到了線上,就有可能出現問題,比如多線程問題。
錯誤堆棧:
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. 線程開啓問題,線程啓動最好都有多線程處理。