You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android开发:如何从PhoneStateListener返回值并获取当前数据传输小区信息

解决Android Q+单次获取实时Earfcn的问题

我完全懂你的需求——你不需要一直监听小区信息的变化,只是想在调用方法时一次性拿到当前的Earfcn值,但Android Q之后必须依赖onCellInfoChanged这个异步回调,没法直接在getEarfcn()方法里返回结果,这确实有点麻烦。

下面给你两种可行的实现方式,都是基于同步阻塞或异步回调的思路,帮你拿到想要的结果:

方法一:使用CountDownLatch(推荐单次获取场景)

CountDownLatch可以帮你阻塞当前线程,直到回调拿到Earfcn值再继续执行,完美适配你“单次获取”的需求。

完整代码示例

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.CountDownLatch;
import android.os.TimeUnit;
import android.telephony.CellInfo;
import android.telephony.CellInfoLte;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import androidx.core.app.ActivityCompat;

public int getEarfcn(TelephonyManager telephonyManager) {
    // 先检查权限,Android Q+需要ACCESS_FINE_LOCATION权限
    if (ActivityCompat.checkSelfPermission(telephonyManager.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return CellInfo.UNAVAILABLE;
    }

    final int[] earfcnResult = {CellInfo.UNAVAILABLE};
    final CountDownLatch latch = new CountDownLatch(1);

    PhoneStateListener listener = new PhoneStateListener() {
        @Override
        public void onCellInfoChanged(List<CellInfo> cellInfoList) {
            super.onCellInfoChanged(cellInfoList);
            if (cellInfoList == null || cellInfoList.isEmpty()) {
                latch.countDown();
                return;
            }

            for (CellInfo cellInfo : cellInfoList) {
                // 只取已注册的LTE小区信息
                if (cellInfo instanceof CellInfoLte && cellInfo.isRegistered()) {
                    earfcnResult[0] = ((CellInfoLte) cellInfo).getCellIdentity().getEarfcn();
                    break; // 拿到目标值就停止遍历
                }
            }
            latch.countDown(); // 释放锁,结束阻塞
            // 取消监听,避免后续不必要的回调和内存泄漏
            telephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
        }
    };

    // 开始监听小区信息变化
    telephonyManager.listen(listener, PhoneStateListener.LISTEN_CELL_INFO);

    try {
        // 最多阻塞5秒,避免无限等待(可根据需求调整超时时间)
        if (!latch.await(5, TimeUnit.SECONDS)) {
            telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);
            return CellInfo.UNAVAILABLE;
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);
        return CellInfo.UNAVAILABLE;
    }

    return earfcnResult[0];
}

关键注意事项

  • 禁止在主线程调用latch.await()会阻塞线程,主线程调用会触发ANR(应用无响应),必须在子线程执行。
  • 权限提前申请:Android Q及以上版本,获取小区信息必须申请ACCESS_FINE_LOCATION权限,确保权限已授予后再调用方法。
  • 超时机制必须加:防止设备无信号或回调不触发时,线程一直处于阻塞状态。
  • 及时取消监听:拿到结果或超时后,一定要取消监听,避免内存泄漏。

方法二:使用自定义回调(适合非阻塞场景)

如果你的业务场景不允许阻塞线程,可以自定义回调接口,在拿到Earfcn值后异步返回:

代码示例

// 自定义回调接口
public interface EarfcnCallback {
    void onEarfcnReceived(int earfcn);
    void onFailed();
}

public void getEarfcnAsync(TelephonyManager telephonyManager, EarfcnCallback callback) {
    if (ActivityCompat.checkSelfPermission(telephonyManager.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        callback.onFailed();
        return;
    }

    PhoneStateListener listener = new PhoneStateListener() {
        @Override
        public void onCellInfoChanged(List<CellInfo> cellInfoList) {
            super.onCellInfoChanged(cellInfoList);
            int earfcn = CellInfo.UNAVAILABLE;
            if (cellInfoList != null && !cellInfoList.isEmpty()) {
                for (CellInfo cellInfo : cellInfoList) {
                    if (cellInfo instanceof CellInfoLte && cellInfo.isRegistered()) {
                        earfcn = ((CellInfoLte) cellInfo).getCellIdentity().getEarfcn();
                        break;
                    }
                }
            }
            telephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
            // 通过回调返回结果
            if (earfcn != CellInfo.UNAVAILABLE) {
                callback.onEarfcnReceived(earfcn);
            } else {
                callback.onFailed();
            }
        }
    };

    telephonyManager.listen(listener, PhoneStateListener.LISTEN_CELL_INFO);
}

使用方式

getEarfcnAsync(telephonyManager, new EarfcnCallback() {
    @Override
    public void onEarfcnReceived(int earfcn) {
        // 在这里处理拿到的Earfcn值
    }

    @Override
    public void onFailed() {
        // 处理获取失败的情况
    }
});

额外说明

  • 为什么放弃getAllCellInfo()?Android Q开始,系统限制了该方法的返回结果,只有通过onCellInfoChanged回调才能拿到实时有效的小区信息,这是系统权限管控导致的。
  • 优先推荐CountDownLatch方案:它完全贴合你“调用方法时直接获取实时值”的需求,逻辑更直观;如果业务不允许阻塞线程,再考虑异步回调方案。

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

火山引擎 最新活动