You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:如何在App启动时获取位置?解决线程Handler报错

App启动时先获取位置再加载Fragment的问题解决

嘿,我看你遇到了启动时获取位置再加载Fragment的问题,还碰到了Can't create handler inside thread that has not called Looper.prepare()的报错,我来帮你梳理下问题原因,然后给出可行的解决方案:

问题场景与现有代码

你需要在MainActivity启动时先拿到经纬度,再加载依赖该信息的HomeFragment,尝试用子线程实现时触发了报错,以下是你的代码:

MainActivity代码

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
    MyLocation myLocation;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myLocation = new MyLocation(getApplicationContext());
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                myLocation.getLocation(getApplicationContext(), locationResult);
            }
        });
        t.start(); // spawn thread
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
        tx.replace(R.id.flContent, new HomeFragment());
        tx.commit();
    }
    public MyLocation.LocationResult locationResult = new MyLocation.LocationResult() {
        @Override
        public void gotLocation(Location location) {
            // TODO Auto-generated method stub
            double Longitude = location.getLongitude();
            double Latitude = location.getLatitude();
            try {
                System.out.println("lat"+Latitude);
                System.out.println("long"+Longitude);
                SharedPreferences locationpref = getApplication()
                        .getSharedPreferences("location", MODE_PRIVATE);
                SharedPreferences.Editor prefsEditor = locationpref.edit();
                prefsEditor.putString("Longitude", Longitude + "");
                prefsEditor.putString("Latitude", Latitude + "");
                prefsEditor.commit();
                System.out.println("Lat:"+Latitude);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };
}

MyLocation类代码

public class MyLocation {
    Timer timer1;
    LocationManager lm;
    LocationResult locationResult;
    boolean gps_enabled = false;
    boolean network_enabled = false;
    Context context;
    public MyLocation(Context context){
        this.context = context;
    }
    public boolean getLocation(Context context, LocationResult result) {
        //I use LocationResult callback class to pass location value from MyLocation to user code.
        locationResult = result;
        if (lm == null)
            lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        //exceptions will be thrown if provider is not permitted.
        try {
            gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
        } catch (Exception ex) {
        }
        try {
            network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        } catch (Exception ex) {
        }
        //Toast.makeText(context, gps_enabled+" "+network_enabled, Toast.LENGTH_LONG).show();
        //don't start listeners if no provider is enabled
        if (!gps_enabled && !network_enabled)
            return false;
        if (gps_enabled)
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                // ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                // public void onRequestPermissionsResult(int requestCode, String[] permissions,
                // int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return true;
            }
        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
        if (network_enabled)
            lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
        timer1 = new Timer();
        timer1.schedule(new GetLastLocation(context), 10000);
        // Toast.makeText(context, " Yaha Tak AAya", Toast.LENGTH_LONG).show();
        return true;
    }
    LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location) {
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerNetwork);
        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    };
    LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location) {
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerGps);
        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    };
    class GetLastLocation extends TimerTask {
        Context context;
        public GetLastLocation(Context context){
            this.context = context;
        }
        @Override
        public void run() {
            //Context context = getClass().getgetApplicationContext();
            Location net_loc = null, gps_loc = null;
            if (gps_enabled)
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    // ActivityCompat#requestPermissions
                    // here to request the missing permissions, and then overriding
                    // public void onRequestPermissionsResult(int requestCode, String[] permissions,
                    // int[] grantResults)
                    // to handle the case where the user grants the permission. See the documentation
                    // for ActivityCompat#requestPermissions for more details.
                    return;
                }
            gps_loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if(network_enabled)
                net_loc=lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            //if there are both values use the latest one
            if(gps_loc!=null && net_loc!=null){
                if(gps_loc.getTime()>net_loc.getTime())
                    locationResult.gotLocation(gps_loc);
                else
                    locationResult.gotLocation(net_loc);
                return;
            }
            if(gps_loc!=null){
                locationResult.gotLocation(gps_loc);
                return;
            }
            if(net_loc!=null){
                locationResult.gotLocation(net_loc);
                return;
            }
            locationResult.gotLocation(null);
        }
    }
    public static abstract class LocationResult{
        public abstract void gotLocation(Location location);
    }
}

问题原因分析

  1. Looper报错根源:你在子线程中调用了getLocation,而LocationManager的回调(如onLocationChanged)会尝试在当前线程创建Handler,但子线程默认没有初始化Looper,因此抛出异常。
  2. 主线程阻塞问题:t.join()会阻塞主线程,导致App启动时出现ANR(应用无响应),严重影响用户体验,这是绝对不能用的写法。

正确解决方案

我们需要在主线程发起位置请求,通过回调拿到结果后再加载Fragment,同时避免阻塞主线程,具体实现如下:

修改后的MainActivity代码

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
    MyLocation myLocation;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 可选:显示加载动画,提升用户体验
        // findViewById(R.id.loading_progress).setVisibility(View.VISIBLE);
        
        myLocation = new MyLocation(getApplicationContext());
        // 直接在主线程发起位置请求,无需子线程
        myLocation.getLocation(getApplicationContext(), locationResult);
    }
    
    public MyLocation.LocationResult locationResult = new MyLocation.LocationResult() {
        @Override
        public void gotLocation(Location location) {
            if (location == null) {
                // 处理获取位置失败的情况
                Toast.makeText(MainActivity.this, "获取位置失败,请检查定位权限和设置", Toast.LENGTH_SHORT).show();
                loadHomeFragment();
                return;
            }
            
            double longitude = location.getLongitude();
            double latitude = location.getLatitude();
            
            try {
                SharedPreferences locationpref = getApplication()
                        .getSharedPreferences("location", MODE_PRIVATE);
                SharedPreferences.Editor prefsEditor = locationpref.edit();
                prefsEditor.putString("Longitude", String.valueOf(longitude));
                prefsEditor.putString("Latitude", String.valueOf(latitude));
                prefsEditor.apply(); // 用apply()异步保存,比commit()更高效
                
                // 位置获取成功后加载Fragment
                loadHomeFragment();
                
            } catch (Exception e) {
                e.printStackTrace();
                // 异常情况下也加载Fragment,避免App卡住
                loadHomeFragment();
            }
        }
    };
    
    // 封装Fragment加载逻辑,复用代码
    private void loadHomeFragment() {
        // 隐藏加载动画
        // findViewById(R.id.loading_progress).setVisibility(View.GONE);
        
        FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
        tx.replace(R.id.flContent, new HomeFragment());
        tx.commit();
    }
}

MyLocation类的线程安全优化

GetLastLocation是TimerTask,运行在子线程中,直接调用locationResult.gotLocation()可能引发线程安全问题,需要切换到主线程执行回调:

class GetLastLocation extends TimerTask {
    Context context;
    public GetLastLocation(Context context){
        this.context = context;
    }
    @Override
    public void run() {
        Location net_loc = null, gps_loc = null;
        if (gps_enabled)
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
        gps_loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if(network_enabled)
            net_loc=lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        
        // 切换到主线程执行回调
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                if(gps_loc!=null && net_loc!=null){
                    if(gps_loc.getTime()>net_loc.getTime())
                        locationResult.gotLocation(gps_loc);
                    else
                        locationResult.gotLocation(net_loc);
                    return;
                }
                if(gps_loc!=null){
                    locationResult.gotLocation(gps_loc);
                    return;
                }
                if(net_loc!=null){
                    locationResult.gotLocation(net_loc);
                    return;
                }
                locationResult.gotLocation(null);
            }
        });
    }
}

额外注意事项

  • 权限处理:务必在AndroidManifest.xml中添加定位权限,并在运行时请求ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION,否则无法获取位置。
  • 异常兜底:一定要处理location == null的情况,避免App崩溃。
  • 用户体验:加载位置时显示加载动画,让用户感知到App在正常工作。

内容的提问来源于stack exchange,提问作者user8654574

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:52:20