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

Android NSD API适配异常:setText显示null但日志能输出IP地址

问题分析与解决方案

我一眼就看出问题所在了——你界面显示null是因为列表里存储的是还没完成解析的NsdServiceInfo对象,而日志里打印的是解析完成后的对象的IP,两者不是同一个(或者说列表里的对象没被更新)。

具体原因

onServiceFound方法里,你直接把刚发现的serviceInfo添加到了services列表,但这时候这个对象的host属性还是null(NSD发现服务时只返回基本信息,IP和端口需要通过resolveService解析后才会填充)。虽然你之后调用了解析,但解析完成后拿到的nsdServiceInfo是更新后的对象,你并没有把列表里原来那个未解析的对象替换/更新,所以Adapter刷新时,取的还是host为null的原始对象,导致界面显示异常。

修复步骤

1. 调整服务添加/更新逻辑

把添加服务到列表的操作放到onServiceResolved里,同时处理重复服务的更新:

@Override
public void onServiceFound(final NsdServiceInfo serviceInfo) {
    Log.d("TAG", "Service discovery success : " + serviceInfo);
    Log.d("TAG", "Host = " + serviceInfo.getServiceName());
    Log.d("TAG", "Port = " + serviceInfo.getPort());

    // 先解析服务,不要直接添加未解析的对象到列表
    NsdManager.ResolveListener mResolveListener2 = new NsdManager.ResolveListener() {
        @Override
        public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int errorCode) {
            Log.e("TAG", "Resolved failed " + errorCode);
            Log.e("TAG", "Service = " + nsdServiceInfo);
            // 解析失败时可以添加原始对象,并显示提示
            boolean exists = false;
            for (NsdServiceInfo s : services) {
                if (s.getServiceName().equals(nsdServiceInfo.getServiceName())) {
                    exists = true;
                    break;
                }
            }
            if (!exists) {
                services.add(nsdServiceInfo);
                runOnUiThread(() -> mAdapter.notifyDataSetChanged());
            }
        }

        @Override
        public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
            Log.d("TAG", "Resolve Succeeded " + nsdServiceInfo);
            if (!nsdServiceInfo.getServiceType().equals(SERVICE_TYPE)) {
                return;
            }

            hostPort = nsdServiceInfo.getPort();
            hostAddress = nsdServiceInfo.getHost();
            Log.d("hello", String.valueOf(hostAddress));

            // 检查列表中是否已有该服务,有则更新,无则添加
            boolean serviceExists = false;
            for (int i = 0; i < services.size(); i++) {
                NsdServiceInfo existingService = services.get(i);
                if (existingService.getServiceName().equals(nsdServiceInfo.getServiceName())) {
                    // 更新已存在的服务信息
                    services.set(i, nsdServiceInfo);
                    serviceExists = true;
                    break;
                }
            }
            if (!serviceExists) {
                services.add(nsdServiceInfo);
            }

            // 在UI线程刷新Adapter
            runOnUiThread(() -> mAdapter.notifyDataSetChanged());
        }
    };
    mNsdManager.resolveService(serviceInfo, mResolveListener2);
}

2. 修复onServiceLost的判断逻辑

你原来的判断里有两个错误:用==比较字符串(应该用equals),还有端口比较写错了(currentService.getPort() == currentService.getPort()永远为true),改成下面的写法:

@Override
public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
    Log.d("TAG", "Service lost " + nsdServiceInfo);

    // 用迭代器安全删除元素
    Iterator<NsdServiceInfo> iterator = services.iterator();
    while (iterator.hasNext()) {
        NsdServiceInfo currentService = iterator.next();
        if (currentService.getServiceName().equals(nsdServiceInfo.getServiceName())
                && currentService.getPort() == nsdServiceInfo.getPort()
                && Objects.equals(currentService.getHost(), nsdServiceInfo.getHost())) {
            iterator.remove();
            break;
        }
    }

    runOnUiThread(() -> mAdapter.notifyDataSetChanged());
    Log.d("TAG", "Xd" + services);
}

3. 优化Adapter的显示逻辑

NsdServiceInfoAdaptergetView方法里,增加null判断,避免显示null,还可以同时显示服务名称:

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    View listItem = convertView;
    if (listItem == null)
        listItem = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);

    NsdServiceInfo currentService = services.get(position);

    // 显示服务名称
    TextView serviceNameTv = listItem.findViewById(R.id.TextView_serviceName);
    serviceNameTv.setText(currentService.getServiceName());

    // 显示IP地址,增加null判断
    TextView ipTv = listItem.findViewById(R.id.TextView_ip); // 假设你有这个TextView
    if (currentService.getHost() != null) {
        ipTv.setText(currentService.getHost().getHostAddress());
    } else {
        ipTv.setText("正在解析...");
    }

    Log.d("tag", "service name" + currentService.getServiceName());
    return listItem;
}

验证效果

做完这些修改后,当服务解析完成,列表里的对象会被更新为带有正确IP的NsdServiceInfo,Adapter刷新后就能正常显示IP地址了,不会再出现null的情况。

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

火山引擎 最新活动