私のアプリは、C/C++ を使用したネイティブ アプリで部分的に書かれています。問題は、C/C++ 部分が何らかの理由でクラッシュするたびに、アプリが停止して自動的に再起動することです。これにより、さまざまな厄介な問題が発生します。
もちろん、それはすべきネイティブ部分でクラッシュしないように、クラッシュが発生する理由をすべて排除しようとしています。ただし、クラッシュが発生した場合は、次のことを実行したいと思います。
- 潔く辞める
- 万が一電源が落ちた場合でも、少なくとも自動的に再起動しようとしないでください。
なぜこのような動作が起こるのか興味があります。少し調べた後、AndroidManifest.xml のメイン アクティビティ要素に次の行を追加してみました。
android:finishOnTaskLaunch="true"
ただし、自動復元は引き続き実行されます。
なぜこのようなことが起こるのか、またどうすれば変更できるのか知っている人はいますか?
アップデート:もっと根本的な疑問は、
ネイティブクラッシュが発生した場合、コールバックに似たものはありますか?
回答の 1 つに「クラッシュ信号の処理」が提案されています。アプリケーション レベルまたはモジュール レベルでこれをどのように実行できるかに関するリンクがあれば、教えていただけると幸いです。
現状では、クラッシュが発生するとアプリは消えてしまい、logcat には何も残らないため、デバッグは不可能です。
ベストアンサー1
クラッシュ シグナル (SIGSEGV など) を処理して、シグナル ハンドラーで自分自身に kill を送信してみてください。このトリックは役に立ちます。
例:
#include <signal.h>
#include <unistd.h>
static void signal_handler(int signal, siginfo_t *info, void *reserved)
{
kill(getpid(),SIGKILL);
}
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
struct sigaction handler;
memset(&handler, 0, sizeof(handler));
handler.sa_sigaction = signal_handler;
handler.sa_flags = SA_SIGINFO;
sigaction(SIGILL, &handler, NULL);
sigaction(SIGABRT, &handler, NULL);
sigaction(SIGBUS, &handler, NULL);
sigaction(SIGFPE, &handler, NULL);
sigaction(SIGSEGV, &handler, NULL);
sigaction(SIGSTKFLT, &handler, NULL);
return(JNI_VERSION_1_6);
}
アップデート2
Android logcatでクラッシュログを確認したい場合は、このシグナルハンドラを使用する必要があります。
static void signal_handler(int signal, siginfo_t *info, void *reserved)
{
struct sockaddr_un addr;
size_t namelen;
socklen_t alen;
int s, err;
char name[] = "android:debuggerd";
namelen = strlen(name);
// Test with length +1 for the *initial* '\0'.
if ((namelen + 1) > sizeof(addr.sun_path)) {
errno = EINVAL;
return;
}
/* This is used for abstract socket namespace, we need
* an initial '\0' at the start of the Unix socket path.
*
* Note: The path in this case is *not* supposed to be
* '\0'-terminated. ("man 7 unix" for the gory details.)
*/
memset (&addr, 0, sizeof addr);
addr.sun_family = AF_LOCAL;
addr.sun_path[0] = 0;
memcpy(addr.sun_path + 1, name, namelen);
alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
s = socket(AF_LOCAL, SOCK_STREAM, 0);
if(s < 0) return;
RETRY_ON_EINTR(err,connect(s, (struct sockaddr *) &addr, alen));
if (err < 0) {
close(s);
s = -1;
}
pid_t tid = gettid();
if(s>=0)
{
/* debugger knows our pid from the credentials on the
* local socket but we need to tell it our tid. It
* is paranoid and will verify that we are giving a tid
* that's actually in our process
*/
int ret;
RETRY_ON_EINTR(ret, write(s, &tid, sizeof(unsigned)));
if (ret == sizeof(unsigned)) {
/* if the write failed, there is no point to read on
* the file descriptor. */
RETRY_ON_EINTR(ret, read(s, &tid, 1));
//notify_gdb_of_libraries();
}
close(s);
}
wait(NULL);
kill(getpid(),SIGKILL);
}
私はこれを Android ソースから取得しました (android.git.kernel.org がダウンしているためリンクを挿入できません)。ただし、将来の Android リリースで動作するかどうかはわかりません。