亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android性能優(yōu)化之捕獲java crash示例解析

 更新時(shí)間:2022年09月19日 09:49:28   作者:Pika  
這篇文章主要介紹了Android性能優(yōu)化之捕獲java crash示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

crash一直是影響app穩(wěn)定性的大頭,同時(shí)在隨著項(xiàng)目逐漸迭代,復(fù)雜性越來越提高的同時(shí),由于主觀或者客觀的的原因,都會造成意想不到的crash出現(xiàn)。同樣的,在android的歷史化過程中,就算是android系統(tǒng)本身,在迭代中也會存在著隱含的crash。我們常說的crash包括java層(虛擬機(jī)層)crash與native層crash,本期我們著重講一下java層的crash。

java層crash由來

雖然說我們在開發(fā)過程中會遇到各種各樣的crash,但是這個(gè)crash是如果產(chǎn)生的呢?我們來探討一下一個(gè)crash是如何誕生的!

我們很容易就知道,在java中main函數(shù)是程序的開始(其實(shí)還有前置步驟),我們開發(fā)中,雖然android系統(tǒng)把應(yīng)用的主線程創(chuàng)建封裝在了自己的系統(tǒng)中,但是無論怎么封裝,一個(gè)java層的線程無論再怎么強(qiáng)大,背后肯定是綁定了一個(gè)操作系統(tǒng)級別的線程,才真正得與驅(qū)動(dòng),也就是說,我們平常說的java線程,它其實(shí)是被操作系統(tǒng)真正的Thread的一個(gè)使用體罷了,java層的多個(gè)thread,可能會只對應(yīng)著native層的一個(gè)Thread(便于區(qū)分,這里thread統(tǒng)一只java層的線程,Thread指的是native層的Thread。其實(shí)native的Thread也不是真正的線程,只是操作系統(tǒng)提供的一個(gè)api罷了,但是我們這里先簡單這樣定義,假設(shè)了native的線程與操作系統(tǒng)線程為同一個(gè)東西)

每一個(gè)java層的thread調(diào)用start方法,就會來到native層Thread的世界

public synchronized void start() {
        throw new IllegalThreadStateException();
    group.add(this);
    started = false;
    try {
        nativeCreate(this, stackSize, daemon);
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

最終調(diào)用的是一個(gè)jni方法

private native static void nativeCreate(Thread t, long stackSize, boolean daemon);

而nativeCreate最終在native層的實(shí)現(xiàn)是

static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size,
jboolean daemon) {
    // There are sections in the zygote that forbid thread creation.
    Runtime* runtime = Runtime::Current();
    if (runtime->IsZygote() && runtime->IsZygoteNoThreadSection()) {
        jclass internal_error = env->FindClass("java/lang/InternalError");
        CHECK(internal_error != nullptr);
        env->ThrowNew(internal_error, "Cannot create threads in zygote");
        return;
    }
     // 這里就是真正的創(chuàng)建線程方法
    Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}

CreateNativeThread 經(jīng)過了一系列的校驗(yàn)動(dòng)作,終于到了真正創(chuàng)建線程的地方了,最終在CreateNativeThread方法中,通過了pthread_create創(chuàng)建了一個(gè)真正的Thread

Thread::CreateNativeThread 方法中
...
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
if (pthread_create_result == 0) {
    // pthread_create started the new thread. The child is now responsible for managing the
    // JNIEnvExt we created.
    // Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization
    //       between the threads.
    child_jni_env_ext.release();  // NOLINT pthreads API.
    return;
}
...

到這里我們就能夠明白,一個(gè)java層的thread其實(shí)真正綁定的,是一個(gè)native層的Thread,有了這個(gè)知識,我們就可以回到我們的crash主題了,當(dāng)發(fā)生異常的時(shí)候(即檢測到一些操作不符合虛擬機(jī)規(guī)定時(shí)),注意,這個(gè)時(shí)候還是在虛擬機(jī)的控制范圍之內(nèi),就可以直接調(diào)用

void Thread::ThrowNewException(const char* exception_class_descriptor,
const char* msg) {
    // Callers should either clear or call ThrowNewWrappedException.
    AssertNoPendingExceptionForNewException(msg);
    ThrowNewWrappedException(exception_class_descriptor, msg);
}

進(jìn)行對exception的拋出,我們目前所有的java層crash都是如此,因?yàn)閷rash的識別還屬于本虛擬機(jī)所在的進(jìn)程的范疇(native crash 虛擬機(jī)就沒辦法直接識別),比如我們常見的各種crash

然后就會調(diào)用到Thread::ThrowNewWrappedException 方法,在這個(gè)方法里面再次調(diào)用到Thread::SetException方法,成功的把當(dāng)次引發(fā)異常的信息記錄下來

void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
    CHECK(new_exception != nullptr);
    // TODO: DCHECK(!IsExceptionPending());
    tlsPtr_.exception = new_exception.Ptr();
}

此時(shí),此時(shí)就會調(diào)用Thread的Destroy方法,這個(gè)時(shí)候,線程就會在里面判斷,本次的異常該怎么去處理

void Thread::Destroy() {
   ...
    if (tlsPtr_.opeer != nullptr) {
        ScopedObjectAccess soa(self);
        // We may need to call user-supplied managed code, do this before final clean-up.
        HandleUncaughtExceptions(soa);
        RemoveFromThreadGroup(soa);
        Runtime* runtime = Runtime::Current();
        if (runtime != nullptr) {
                runtime->GetRuntimeCallbacks()->ThreadDeath(self);
        }

HandleUncaughtExceptions 這個(gè)方式就是處理的函數(shù),我們繼續(xù)看一下這個(gè)異常處理函數(shù)

void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) {
    if (!IsExceptionPending()) {
        return;
    }
    ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer));
    ScopedThreadStateChange tsc(this, ThreadState::kNative);
    // Get and clear the exception.
    ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred());
    tlsPtr_.jni_env->ExceptionClear();
    // Call the Thread instance's dispatchUncaughtException(Throwable)
    // 關(guān)鍵點(diǎn)就在此,回到j(luò)ava層
    tlsPtr_.jni_env->CallVoidMethod(peer.get(),
    WellKnownClasses::java_lang_Thread_dispatchUncaughtException,
    exception.get());
    // If the dispatchUncaughtException threw, clear that exception too.
    tlsPtr_.jni_env->ExceptionClear();
}

到這里,我們就接近尾聲了,可以看到我們的處理函數(shù)最終通過jni,再次回到了java層的世界,而這個(gè)連接的java層函數(shù)就是dispatchUncaughtException(java_lang_Thread_dispatchUncaughtException)

public final void dispatchUncaughtException(Throwable e) {
    // BEGIN Android-added: uncaughtExceptionPreHandler for use by platform.
    Thread.UncaughtExceptionHandler initialUeh =
            Thread.getUncaughtExceptionPreHandler();
    if (initialUeh != null) {
        try {
            initialUeh.uncaughtException(this, e);
        } catch (RuntimeException | Error ignored) {
            // Throwables thrown by the initial handler are ignored
        }
    }
    // END Android-added: uncaughtExceptionPreHandler for use by platform.
    getUncaughtExceptionHandler().uncaughtException(this, e);
}

到這里,我們就徹底了解到了一個(gè)java層異常的產(chǎn)生過程!

為什么java層異常會導(dǎo)致crash

從上面我們文章我們能夠看到,一個(gè)異常是怎么產(chǎn)生的,可能細(xì)心的讀者會了解到,筆者一直在用異常這個(gè)詞,而不是crash,因?yàn)楫惓0l(fā)生了,crash是不一定產(chǎn)生的!我們可以看到dispatchUncaughtException方法最終會嘗試著調(diào)用UncaughtExceptionHandler去處理本次異常,好家伙!那么UncaughtExceptionHandler是在什么時(shí)候設(shè)置的?其實(shí)就是在Init中,由系統(tǒng)提前設(shè)置好的!frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

protected static final void commonInit() {
    if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
    /*
     * set handlers; these apply to all threads in the VM. Apps can replace
     * the default handler, but not the pre handler.
     */
    LoggingHandler loggingHandler = new LoggingHandler();
    RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
    Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
    /*
     * Install a time zone supplier that uses the Android persistent time zone system property.
     */
    RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
    LogManager.getLogManager().reset();
    new AndroidConfig();
    /*
     * Sets the default HTTP User-Agent used by HttpURLConnection.
     */
    String userAgent = getDefaultUserAgent();
    System.setProperty("http.agent", userAgent);
    /*
     * Wire socket tagging to traffic stats.
     */
    TrafficStats.attachSocketTagger();
    initialized = true;
}

好家伙,原來是KillApplicationHandler“搗蛋”,在異常到來時(shí),就會通過KillApplicationHandler去處理,而這里的處理就是,殺死app??!

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
    private final LoggingHandler mLoggingHandler;
    public KillApplicationHandler(LoggingHandler loggingHandler) {
        this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
    }
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        try {
            ensureLogging(t, e);
            // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
            if (mCrashing) return;
            mCrashing = true;
            if (ActivityThread.currentActivityThread() != null) {
                ActivityThread.currentActivityThread().stopProfiling();
            }
            // Bring up crash dialog, wait for it to be dismissed
            ActivityManager.getService().handleApplicationCrash(
                    mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
        } catch (Throwable t2) {
            if (t2 instanceof DeadObjectException) {
                // System process is dead; ignore
            } else {
                try {
                    Clog_e(TAG, "Error reporting crash", t2);
                } catch (Throwable t3) {
                    // Even Clog_e() fails!  Oh well.
                }
            }
        } finally {
            // Try everything to make sure this process goes away.
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
    }
    private void ensureLogging(Thread t, Throwable e) {
        if (!mLoggingHandler.mTriggered) {
            try {
                mLoggingHandler.uncaughtException(t, e);
            } catch (Throwable loggingThrowable) {
                // Ignored.
            }
        }
    }
}

看到了嗎!異常的產(chǎn)生導(dǎo)致的crash,真正的源頭就是在此了!

捕獲crash

通過對前文的閱讀,我們了解到了crash的源頭就是KillApplicationHandler,因?yàn)樗J(rèn)處理就是殺死app,此時(shí)我們也注意到,它是繼承于UncaughtExceptionHandler的。當(dāng)然,有異常及時(shí)拋出解決,是一件好事,但是我們也可能有一些異常,比如android系統(tǒng)sdk的問題,或者其他沒那么重要的異常,直接崩潰app,這個(gè)處理就不是那么好了。但是不要緊,java虛擬機(jī)開發(fā)者也肯定注意到了這點(diǎn),所以提供

Thread.java
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)

方式,導(dǎo)入一個(gè)我們自定義的實(shí)現(xiàn)了UncaughtExceptionHandler接口的類

public interface UncaughtExceptionHandler {
    /**
     * Method invoked when the given thread terminates due to the
     * given uncaught exception.
     * <p>Any exception thrown by this method will be ignored by the
     * Java Virtual Machine.
     * @param t the thread
     * @param e the exception
     */
    void uncaughtException(Thread t, Throwable e);
}

此時(shí)我們只需要寫一個(gè)類,模仿KillApplicationHandler一樣,就能寫出一個(gè)自己的異常處理類,去處理我們程序中的異常(或者Android系統(tǒng)中特定版本的異常)。例子demo比如

class MyExceptionHandler:Thread.UncaughtExceptionHandler {
    override fun uncaughtException(t: Thread, e: Throwable) {
        // 做自己的邏輯
        Log.i("hello",e.toString())
    }
}

總結(jié)

到這里,我們能夠了解到了一個(gè)java crash是怎么產(chǎn)生的了,同時(shí)我們也了解到了常用的UncaughtExceptionHandler為什么可以攔截一些我們不希望產(chǎn)生crash的異常,在接下來的android性能優(yōu)化系列中,會持續(xù)帶來相關(guān)的其他分享,感謝觀看

更多關(guān)于Android捕獲java crash的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論