locationManager.requestLocationUpdates()報(bào)Can't create handler inside thread that has not called Looper.prepare()

背景

long long time ago...公司出了需求,為了數(shù)據(jù)的安全性和業(yè)務(wù)的安全性,每個(gè)接口需要傳遞一些公共參數(shù),其中有一個(gè)要獲取當(dāng)前的經(jīng)緯度上傳。自己用了原生的api來獲取經(jīng)緯度。在自己的測(cè)試機(jī)子上,并沒有出現(xiàn)問題,但是一上線,有很多機(jī)型崩潰。報(bào)錯(cuò)日志如下:
調(diào)用安卓原生的定位功能,測(cè)試的時(shí)候沒有測(cè)試出來,上線卻有不同的機(jī)型報(bào)出了bug。

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    at android.os.Handler.<init>(Handler.java:200)
    at android.os.Handler.<init>(Handler.java:114)
    at android.location.LocationManager$ListenerTransport$1.<init>(LocationManager.java:221)
    at android.location.LocationManager$ListenerTransport.<init>(LocationManager.java:221)
    at android.location.LocationManager.wrapListener(LocationManager.java:844)
    at android.location.LocationManager.requestLocationUpdates(LocationManager.java:857)
    at android.location.LocationManager.requestLocationUpdates(LocationManager.java:454)
    at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:67)
    at okhttp3.RealCall.getResponseWithInterceptorChain(SourceFile:170)
    at okhttp3.RealCall.access$100(SourceFile:33)
    at okhttp3.RealCall$AsyncCall.execute(SourceFile:120)
    at okhttp3.internal.NamedRunnable.run(SourceFile:32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
    at java.lang.Thread.run(Thread.java:841)

探索

 locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener);

從日志上看出這個(gè)bug是調(diào)用這行代碼發(fā)生的。但是自己的自己上怎么沒出問題,而且出問題的機(jī)子也是很少的。點(diǎn)源碼里面看一下

 public void requestLocationUpdates(String provider, long minTime, float minDistance,
            PendingIntent intent) {
        checkProvider(provider);
        checkPendingIntent(intent);

        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                provider, minTime, minDistance, false);
        requestLocationUpdates(request, null, null, intent);
    }

調(diào)用了 requestLocationUpdates(request, null, null, intent)這個(gè)方法

//可以看出,調(diào)用這個(gè)方法的時(shí)候looper傳入的是null;
private void requestLocationUpdates(LocationRequest request, LocationListener listener,
            Looper looper, PendingIntent intent) {

        String packageName = mContext.getPackageName();

        // wrap the listener class
        ListenerTransport transport = wrapListener(listener, looper);

        try {
            mService.requestLocationUpdates(request, transport, intent, packageName);
       } catch (RemoteException e) {
           throw e.rethrowFromSystemServer();
       }
    }

繼續(xù)往下看 ListenerTransport transport = wrapListener(listener, looper);這行代碼

 private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
        if (listener == null) return null;
        synchronized (mListeners) {
            ListenerTransport transport = mListeners.get(listener);
            if (transport == null) {
                transport = new ListenerTransport(listener, looper);//這里我們looper傳入的是null
            }
            mListeners.put(listener, transport);
            return transport;
        }
    }

這個(gè)方法的目的在使用LocationRequst來解決位置更新的問題。然后看到了下面這行代碼關(guān)聯(lián)了一個(gè)looper

transport = new ListenerTransport(listener,looper);

點(diǎn)擊源碼,看實(shí)現(xiàn):

 ListenerTransport(LocationListener listener, Looper looper) {
            mListener = listener;

            if (looper == null) {//這里傳入的looper的是null,進(jìn)入if條件語句
                mListenerHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        _handleMessage(msg);
                    }
                };
            } else {
                mListenerHandler = new Handler(looper) {
                    @Override
                    public void handleMessage(Message msg) {
                        _handleMessage(msg);
                    }
                };
            }
        }

從代碼中,終于看到了handler,離錯(cuò)誤估計(jì)也就不遠(yuǎn)了,從代碼中可以看出來我們的代碼進(jìn)入了if條件語句,然后創(chuàng)建了一個(gè)handler。繼續(xù)往下跟。

 public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) { //這里看到了最開始的異常,因?yàn)閭魅氲膌ooper是null,所以出現(xiàn)了這個(gè)情況。
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

問題的源頭終于找到了,自己在子線程中獲取了經(jīng)緯度,Looper.myLooper()或者的值為null。 但是最后還是沒明白,為什么有的機(jī)型并沒有崩潰呢,是定制系統(tǒng)的原因嗎?從
Android原生的代碼看這個(gè)問題是應(yīng)該崩潰的。

解決辦法:

locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener);這個(gè)方法有重載的方法,能指定自己的looper。將代碼改成下面的代碼,搞定!

 locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener, Looper.getMainLooper());
 //或者
 locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener, Looper.prepare());
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容