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

Android开发:智能TV发现、配对及遥控器功能实现求助

实现智能TV发现、配对与遥控功能步骤

一、局域网智能TV设备发现

Android平台推荐用**Network Service Discovery(NSD)**实现同局域网设备发现,步骤如下:

  1. 初始化NSD管理器
private NsdManager nsdManager;
private NsdManager.DiscoveryListener discoveryListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    nsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
    initializeDiscoveryListener();
}
  1. 实现DiscoveryListener监听设备发现事件
private void initializeDiscoveryListener() {
    discoveryListener = new NsdManager.DiscoveryListener() {
        @Override
        public void onDiscoveryStarted(String serviceType) {
            // 开始发现设备的回调
        }

        @Override
        public void onServiceFound(NsdServiceInfo serviceInfo) {
            // 筛选智能TV(可根据服务类型或设备名称过滤)
            if (serviceInfo.getServiceType().contains("_androidtv._tcp.") || 
                serviceInfo.getServiceName().contains("TV")) {
                // 将设备信息存入自定义实体类列表
                tvDeviceList.add(new TvDevice(serviceInfo.getServiceName(), 
                                              serviceInfo.getHost().getHostAddress(), 
                                              serviceInfo.getPort()));
                // 更新RecyclerView
                tvAdapter.notifyDataSetChanged();
            }
        }

        @Override
        public void onServiceLost(NsdServiceInfo serviceInfo) {
            // 设备离开网络,移除对应列表项
        }

        @Override
        public void onDiscoveryStopped(String serviceType) {
            // 停止发现的回调
        }

        @Override
        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
            // 发现启动失败的回调
        }

        @Override
        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
            // 发现停止失败的回调
        }
    };
}
  1. 启动/停止设备发现
@Override
protected void onResume() {
    super.onResume();
    nsdManager.discoverServices("_androidtv._tcp.", NsdManager.PROTOCOL_DNS_SD, discoveryListener);
}

@Override
protected void onPause() {
    super.onPause();
    if (nsdManager != null) {
        nsdManager.stopServiceDiscovery(discoveryListener);
    }
}

二、用RecyclerView展示TV列表

  1. 创建自定义TvDevice实体类
public class TvDevice implements Serializable {
    private String name;
    private String ip;
    private int port;

    public TvDevice(String name, String ip, int port) {
        this.name = name;
        this.ip = ip;
        this.port = port;
    }

    // getter方法
    public String getName() { return name; }
    public String getIp() { return ip; }
    public int getPort() { return port; }
}
  1. 实现RecyclerView Adapter
public class TvListAdapter extends RecyclerView.Adapter<TvListAdapter.TvViewHolder> {
    private List<TvDevice> tvDeviceList;
    private OnTvItemClickListener listener;

    public interface OnTvItemClickListener {
        void onTvItemClick(TvDevice device);
    }

    public TvListAdapter(List<TvDevice> tvDeviceList, OnTvItemClickListener listener) {
        this.tvDeviceList = tvDeviceList;
        this.listener = listener;
    }

    @NonNull
    @Override
    public TvViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_tv_device, parent, false);
        return new TvViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull TvViewHolder holder, int position) {
        TvDevice device = tvDeviceList.get(position);
        holder.tvName.setText(device.getName());
        holder.tvIp.setText(device.getIp());
        holder.itemView.setOnClickListener(v -> listener.onTvItemClick(device));
    }

    @Override
    public int getItemCount() {
        return tvDeviceList.size();
    }

    static class TvViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;
        TextView tvIp;

        public TvViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tv_device_name);
            tvIp = itemView.findViewById(R.id.tv_device_ip);
        }
    }
}
  1. 列表项布局item_tv_device.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_device_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tv_device_ip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="@android:color/darker_gray"/>
</LinearLayout>
  1. 初始化RecyclerView
private RecyclerView rvTvList;
private TvListAdapter tvAdapter;
private List<TvDevice> tvDeviceList = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    rvTvList = findViewById(R.id.rv_tv_list);
    rvTvList.setLayoutManager(new LinearLayoutManager(this));
    tvAdapter = new TvListAdapter(tvDeviceList, this::onTvItemClick);
    rvTvList.setAdapter(tvAdapter);
}

// 点击TV项触发配对流程
private void onTvItemClick(TvDevice device) {
    startPairing(device);
}

三、设备配对流程

以Socket连接+PIN码验证为例(通用方案):

  1. 建立Socket连接到TV
private Socket tvSocket;
private DataOutputStream outputStream;

private void startPairing(TvDevice device) {
    new Thread(() -> {
        try {
            tvSocket = new Socket(device.getIp(), device.getPort());
            outputStream = new DataOutputStream(tvSocket.getOutputStream());
            
            // 发送配对请求(自定义协议)
            outputStream.writeUTF("PAIR_REQUEST");
            outputStream.flush();

            // 接收TV返回的PIN码
            DataInputStream inputStream = new DataInputStream(tvSocket.getInputStream());
            String tvPin = inputStream.readUTF();
            
            // 主线程显示PIN输入对话框
            runOnUiThread(() -> showPinInputDialog(tvPin, device));
        } catch (IOException e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(this, "配对连接失败", Toast.LENGTH_SHORT).show());
        }
    }).start();
}
  1. PIN码输入与验证
private void showPinInputDialog(String tvPin, TvDevice device) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("输入配对PIN码");
    builder.setMessage("TV显示的PIN码:" + tvPin);
    
    EditText input = new EditText(this);
    builder.setView(input);
    
    builder.setPositiveButton("确认", (dialog, which) -> {
        String userPin = input.getText().toString();
        if (userPin.equals(tvPin)) {
            new Thread(() -> {
                try {
                    outputStream.writeUTF("PAIR_SUCCESS");
                    outputStream.flush();
                    runOnUiThread(() -> {
                        Toast.makeText(this, "配对成功", Toast.LENGTH_SHORT).show();
                        // 跳转至遥控界面
                        startActivity(new Intent(this, RemoteControlActivity.class)
                                .putExtra("tv_device", device));
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        } else {
            Toast.makeText(this, "PIN码错误", Toast.LENGTH_SHORT).show();
        }
    });
    builder.setNegativeButton("取消", null);
    builder.show();
}

四、遥控器功能实现

配对成功后通过Socket发送按键指令:

  1. 遥控界面布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="↑"/>

    <Button
        android:id="@+id/btn_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/btn_ok"
        android:layout_centerVertical="true"
        android:text="←"/>

    <Button
        android:id="@+id/btn_ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="确认"/>

    <Button
        android:id="@+id/btn_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/btn_ok"
        android:layout_centerVertical="true"
        android:text="→"/>

    <Button
        android:id="@+id/btn_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/btn_ok"
        android:text="↓"/>
</RelativeLayout>
  1. 遥控逻辑实现
public class RemoteControlActivity extends AppCompatActivity {
    private Socket tvSocket;
    private DataOutputStream outputStream;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_remote_control);

        TvDevice device = (TvDevice) getIntent().getSerializableExtra("tv_device");
        // 重新建立Socket连接
        new Thread(() -> {
            try {
                tvSocket = new Socket(device.getIp(), device.getPort());
                outputStream = new DataOutputStream(tvSocket.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 绑定按键事件
        findViewById(R.id.btn_up).setOnClickListener(v -> sendKeyCommand("KEY_UP"));
        findViewById(R.id.btn_down).setOnClickListener(v -> sendKeyCommand("KEY_DOWN"));
        findViewById(R.id.btn_left).setOnClickListener(v -> sendKeyCommand("KEY_LEFT"));
        findViewById(R.id.btn_right).setOnClickListener(v -> sendKeyCommand("KEY_RIGHT"));
        findViewById(R.id.btn_ok).setOnClickListener(v -> sendKeyCommand("KEY_OK"));
    }

    private void sendKeyCommand(String command) {
        new Thread(() -> {
            try {
                if (outputStream != null) {
                    outputStream.writeUTF(command);
                    outputStream.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
                runOnUiThread(() -> Toast.makeText(this, "指令发送失败", Toast.LENGTH_SHORT).show());
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 关闭连接释放资源
        try {
            if (tvSocket != null) tvSocket.close();
            if (outputStream != null) outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:不同品牌TV的服务类型、配对协议、遥控指令存在差异,实际开发需参考对应厂商开放API。通用Android TV也可通过ADB命令实现遥控(需TV开启ADB调试),示例命令:adb shell input keyevent KEYCODE_DPAD_UP,可通过Java执行shell命令发送。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 16:27:23