locationManager.requestLocationUpdates()报Can't create handler inside thread that has not called Looper.prepare()

背景

long long time ago…公司出了需求,为了数据的安全性和业务的安全性,每个接口需要传递一些公共参数,其中有一个要获取当前的经纬度上传。自己用了原生的api来获取经纬度。在自己的测试机子上,并没有出现问题,但是一上线,有很多机型崩溃。报错日志如下:
调用安卓原生的定位功能,测试的时候没有测试出来,上线却有不同的机型报出了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);

从日志上看出这个bug是调用这行代码发生的。但是自己的自己上怎么没出问题,而且出问题的机子也是很少的。点源码里面看一下

 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);
    }

调用了 requestLocationUpdates(request, null, null, intent)这个方法

//可以看出,调用这个方法的时候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();
       }
    }

继续往下看 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;
        }
    }

这个方法的目的在使用LocationRequst来解决位置更新的问题。然后看到了下面这行代码关联了一个looper

transport = new ListenerTransport(listener,looper);

点击源码,看实现:

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

            if (looper == null) {//这里传入的looper的是null,进入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,离错误估计也就不远了,从代码中可以看出来我们的代码进入了if条件语句,然后创建了一个handler。继续往下跟。

 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) { //这里看到了最开始的异常,因为传入的looper是null,所以出现了这个情况。
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

问题的源头终于找到了,自己在子线程中获取了经纬度,Looper.myLooper()或者的值为null。 但是最后还是没明白,为什么有的机型并没有崩溃呢,是定制系统的原因吗?从
Android原生的代码看这个问题是应该崩溃的。

解决办法:

locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener);这个方法有重载的方法,能指定自己的looper。将代码改成下面的代码,搞定!

 locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener, Looper.getMainLooper());
 //或者
 locationManager.requestLocationUpdates(locationProvider, 0, 0, mListener, Looper.prepare());
    原文作者:浔它芉咟渡
    原文地址: https://www.jianshu.com/p/c9a6c73ed5ce
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞