一、问题背景
最近遇到了StackOverflowError的异常问题,具体信息如下:
StackTrace=nulljava.lang.StackOverflowError
at java.lang.IntegralToString.convertInt(IntegralToString.java:216)
at java.lang.IntegralToString.appendInt(IntegralToString.java:173)
at java.lang.StringBuilder.append(StringBuilder.java:139)
at com.jb.gokeyboard.theme.b.a(InterprocessDataUtils.java:509)
at com.jb.gokeyboard.ad.j$a.a(GiftBoxAdManager.java:470)
at com.jb.gokeyboard.gostore.GoKeyboardProviderEmoji.a(GoKeyboardProviderEmoji.java:421)
at com.jb.gokeyboard.gostore.GoKeyboardProviderEmoji.query(GoKeyboardProviderEmoji.java:92)
at android.content.ContentProvider.query(ContentProvider.java:857)
at android.content.ContentProvider$Transport.query(ContentProvider.java:200)
at android.content.ContentResolver.query(ContentResolver.java:464)
at android.content.ContentResolver.query(ContentResolver.java:407)
at com.jb.gokeyboard.theme.b.a(InterprocessDataUtils.java:511)
at com.jb.gokeyboard.ad.j$a.a(GiftBoxAdManager.java:470)
at ...
二、问题分析
根据异常信息,我们明显可以知道这是由于死循环调用导致栈溢出的问题。接下来,通过代码进一步分析问题的原因。
class GiftBoxAdManager :
public static class AvailableAdVerify {
...
public static boolean getAdAvaliablity(int virtualID) {
if (!PackageUtil.isMainProcess(GoKeyboardApplication.getContext())) {
return InterprocessDataUtils.getAdAvaliability(GoKeyboardApplication.getContext(),
virtualID);
}
synchronized (sLock) {
return isAdAvaliabele.get(virtualID, false);
}
}
...
}
class InterprocessDataUtils :
public static boolean getAdAvaliability(Context context,int virtualID) {
...
try {
cursor = context.getApplicationContext().getContentResolver()
.query(InterprocessDataUtils.sAdAvaliabilityUri, projection, projection[0], null, null);
if (cursor != null && cursor.moveToFirst()) {
value = cursor.getInt(0);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
if (value == 1) {
return true;
}
return false;
}
class GoKeyboardProviderEmoji extends ContentProvider :
private Cursor isAdAvaliable(String[] projection,int virtualID){
...
if (AvailableAdVerify.getAdAvaliablity(virtualID)) {
objects[0] = 1;
}else {
objects[0] = 0;
}
MatrixCursor cursor = new MatrixCursor(projection, projection.length);
cursor.addRow(objects);
return cursor;
}
当调用接口AvailableAdVerify.getAdAvaliablity()的时候,会通过PackageUtil.isMainProcess()判断是否为主进程,不是则调用接口InterprocessDataUtils.getAdAvaliability(),再通过ContentProvider跨进程共享数据。这时,由于ContentProvider处于主进程,此时调用接口PackageUtil.isMainProcess()时,正常情况下应该返回true,然而,如果返回false则会出现死循环调用的情况。
三、问题处理
针对该问题,原因是判断是否为主进程的接口出现了错误,然而目前未找出异常情况出现的路径,而从统计上该问题出现的概率也非常小。解决该问题,主要是防止死循环的调用,而我们已经知道ContentProvider一定是处于主进程,因此在ContentProvider中则不需要判断是否为主进程。修改后,如下:
class GiftBoxAdManager :
public static class AvailableAdVerify{
...
public static boolean getAdAvaliablity(int virtualID){
if (!PackageUtil.isMainProcess(GoKeyboardApplication.getContext())) {
return InterprocessDataUtils.getAdAvaliability(GoKeyboardApplication.getContext(),virtualID);
}
return getAdAvaliablityByMainProcess(virtualID);
}
//主进程中调用
public static boolean getAdAvaliablityByMainProcess(int virtualID) {
synchronized (sLock) {
return isAdAvaliabele.get(virtualID, false);
}
}
...
}
class GoKeyboardProviderEmoji extends ContentProvider :
private Cursor isAdAvaliable(String[] projection,int virtualID){
...
if (AvailableAdVerify.getAdAvaliablityByMainProcess(virtualID)) {
objects[0] = 1;
}else {
objects[0] = 0;
}
MatrixCursor cursor = new MatrixCursor(projection, projection.length);
cursor.addRow(objects);
return cursor;
}