Android 来自Parcel.readException的NullPointerException(etc)

Android 来自Parcel.readException的NullPointerException(etc),android,parcel,Android,Parcel,看起来像这样的异常令人困惑: FATAL EXCEPTION: main java.lang.NullPointerException at android.os.Parcel.readException(Parcel.java:1437) at android.os.Parcel.readException(Parcel.java:1385) at com.yourpackage.ipc.IYourClass$Stub$Proxy.yourMethod(IYourCla

看起来像这样的异常令人困惑:

FATAL EXCEPTION: main
java.lang.NullPointerException
    at android.os.Parcel.readException(Parcel.java:1437)
    at android.os.Parcel.readException(Parcel.java:1385)
    at com.yourpackage.ipc.IYourClass$Stub$Proxy.yourMethod(IYourClass.java:488)
    at com.yourpackage.ipc.YourClassShim.yourMethod(YourClassShim.java:269)
我找到了一大堆相关的问题,但没有一个是关于“如何调试这个”的。所以我要提出这个问题/答案

通过查看android源代码,您将看到它可以抛出其中任何一个(NullPointerException正是我所拥有的):


但是是什么导致了这些呢?

这里发生的是
readException()
正在检查IPC字节流中是否有一个报头表明发生了异常;如果找到一个异常,则抛出该类型的新异常,该异常包含相同的消息,但缺少原始堆栈跟踪。(它实际上只知道一些异常类型;其他任何类型都会转换为基本的
运行时异常

那么最初的例外来自哪里?好吧,在
YourClass.yourMethod()
的真正实现中的某个地方——不在任何parcelable或IPC代码中。因此,去那里,将整个方法包装在一个try/catch中,并记录您捕获的任何内容


(如果远程进程断点正常工作,也可以在那里设置断点。)

我认为android应该提供更多的binder远程异常信息

public final void writeException(Exception e) {
    int code = 0;
    if (e instanceof SecurityException) {
        code = EX_SECURITY;
    } else if (e instanceof BadParcelableException) {
        code = EX_BAD_PARCELABLE;
    } else if (e instanceof IllegalArgumentException) {
        code = EX_ILLEGAL_ARGUMENT;
    } else if (e instanceof NullPointerException) {
        code = EX_NULL_POINTER;
    } else if (e instanceof IllegalStateException) {
        code = EX_ILLEGAL_STATE;
    }
    writeInt(code);
    StrictMode.clearGatheredViolations();
    if (code == 0) {
        if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        }
        throw new RuntimeException(e);
    }
    // I replace writeString(e.getMessage()) with writeString(remoteExceptionToString(e))
    writeString(remoteExceptionToString(e));
}

static String remoteExceptionToString(Exception e) {
    final StringWriter sw = new StringWriter(1024);
    final PrintWriter pw = new PrintWriter(sw, true);
    pw.println();
    e.printStackTrace(pw);
    return sw.toString().replace("\n", String.format("\n(%5d %5d): ", Process.myPid(), Process.myTid()));
}
所以我修改了Parcel.java来包装更多的绑定器远程异常信息

public final void writeException(Exception e) {
    int code = 0;
    if (e instanceof SecurityException) {
        code = EX_SECURITY;
    } else if (e instanceof BadParcelableException) {
        code = EX_BAD_PARCELABLE;
    } else if (e instanceof IllegalArgumentException) {
        code = EX_ILLEGAL_ARGUMENT;
    } else if (e instanceof NullPointerException) {
        code = EX_NULL_POINTER;
    } else if (e instanceof IllegalStateException) {
        code = EX_ILLEGAL_STATE;
    }
    writeInt(code);
    StrictMode.clearGatheredViolations();
    if (code == 0) {
        if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        }
        throw new RuntimeException(e);
    }
    // I replace writeString(e.getMessage()) with writeString(remoteExceptionToString(e))
    writeString(remoteExceptionToString(e));
}

static String remoteExceptionToString(Exception e) {
    final StringWriter sw = new StringWriter(1024);
    final PrintWriter pw = new PrintWriter(sw, true);
    pw.println();
    e.printStackTrace(pw);
    return sw.toString().replace("\n", String.format("\n(%5d %5d): ", Process.myPid(), Process.myTid()));
}
序列化异常SecondService定义:

public class SerializeExceptionSecondService extends Service {
private static final String TAG = "SerializeExceptionSecondService";

public SerializeExceptionSecondService() {
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    return mServiceBinder;
}

private final ISerializeExceptionSecondService.Stub mServiceBinder = new ISerializeExceptionSecondService.Stub() {

    @Override
    public void throwException() throws RemoteException {
        // TODO Auto-generated method stub
        Log.e(TAG, "throwException");
        throw new IllegalStateException("Cause1", new IllegalStateException("Cause2", new IllegalStateException("Cause3")));
    }

    @Override
    public void noThrowException() throws RemoteException {
        // TODO Auto-generated method stub

    }
};
}
AndroidManifest.xml片段:

    <service
        android:name=".SerializeExceptionSecondService"
        android:enabled="true"
        android:exported="true"
        android:process=":second_remote" >
    </service>

这样,活页夹异常日志将如下所示: