Android 同一网段下进行组播

本项目是一个安卓对讲机应用的项目源码,代码规范注释清晰,功能完整界面简单。无论是做二次开发还是参考借鉴都是绝佳的项目源码。一个简单的按钮,按下就可以实时对讲,而且通过标识包头来解决自己听到自己说的话,使用方法:把AppConfig.java中的IP设为自己WiFi的网段x.x.x.0,(不必管安卓设备的具体IP,只填写设备所在IP的网段即可,例如我的手机连接wifi后IP是192.168.1.102,这里的网段就是IP的前三段+0。就是192.168.1.0),不过因为线程的启动方式有点问题所以项目有个小BUG,需要在对讲之前先按一下按钮,让线程启动,再按住按钮说话。javaapk.com测试完美运行,需要更多对讲机源码可以在例子大全 http://www.javaapk.com/demo 里搜“对讲机”就可以找到。

--------------权限-------------

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />

---------------------activity_main.xml---------------------

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#1D1E1D"
    tools:context="com.my.zubodemo.MainActivity">

    <Button
        android:id="@+id/speakButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/speak_bt_select"
        android:textColor="#fff"
        android:textSize="16sp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="140dp"
        android:textColor="#ff3333"
        android:text="网段是第四段换成0,    例如: ip :192.168.1.245   则网段是: 192.168.1.0"/>

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginLeft="40dp"
        android:layout_marginRight="40dp"
        android:layout_marginTop="190dp"
        android:background="#cccc"
        android:hint="请先在这里输入网段"
         />

    <TextView
        android:id="@+id/Message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="28dp"
        android:text="按住说话"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="#fff"/>

</RelativeLayout>

----------------------speak_bt_select.xml-------------------

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/speak2" android:state_pressed="true"/>
    <item android:drawable="@drawable/speak1" android:state_pressed="false"/>

</selector>

----------------------MainActivity--------------------- 

@SuppressLint("ClickableViewAccessibility")
public class MainActivity extends AppCompatActivity {

    private Button speakButton;// 按住说话
    private TextView message;
    private SendSoundsThread sendSoundsThread = new SendSoundsThread();
    private ReceiveSoundsThread receiveSoundsThread = new ReceiveSoundsThread();
    private boolean isFirst = true;
    private boolean isReceive = true;
    private static final String TAG = "MainActivity";
    // 设备信息:手机名+Android版本
    private String DevInfo = android.os.Build.MODEL + " Android " + android.os.Build.VERSION.RELEASE;

    private String tv;
    private boolean shortPress = false;

    private int index;
    private int count;
    private String ip;
    //初始化一个空数组
    private int[] arry={};
    private String substring;

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

        startService(new Intent(getApplicationContext(), DaemonService.class));
        Log.d("MainActivity", "onCreate(): savedInstanceState = [" + savedInstanceState + "]");
        Toast.makeText(this, "开启后台守护服务", Toast.LENGTH_SHORT).show();
        //finish();

        //获取ip
        ip = getIP(MainActivity.this);
        int[] arr=count(ip,'.');
        for (int i = 0; i <arr.length ; i++) {
            count = arr[i];
        }
        substring = ip.substring(0,count+1);
        Log.d("哈哈", "onCreate: "+ substring);
        Log.d(TAG, "onCreate: -----ip:----------"+ ip);


        //锁屏状态Activity不销毁
        //判断是否是锁屏
        KeyguardManager mKeyguardManager = (KeyguardManager) this.getSystemService(Context.KEYGUARD_SERVICE);
        boolean flag = mKeyguardManager.inKeyguardRestrictedInputMode();
        Log.d(TAG, "onCreate: " + "-----锁屏-----" + flag);


        //屏幕是否亮屏
        PowerManager powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
        //true为打开,false为关闭
        boolean ifOpen = powerManager.isScreenOn();
        Log.d(TAG, "onCreate: "+"------亮屏-----"+ifOpen);

        receiveSoundsThread.start();
        receiveSoundsThread.setRunning(true);


        message = (TextView) findViewById(R.id.Message);
        speakButton = (Button) findViewById(R.id.speakButton);


        //6.0动态权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0以上
            int checkPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO);
            if (checkPermission != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{ Manifest.permission.RECORD_AUDIO }, 1); //后面的1为请求码

                return;

            } else {

                speakButton.setOnTouchListener(new View.OnTouchListener() {

                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        if (event.getAction() == MotionEvent.ACTION_DOWN) {

                            message.setText("松开结束");
                            if (isFirst) {
                                sendSoundsThread.start();
                                isFirst = false;
                            }

                            sendSoundsThread.setRunning(true);
                            receiveSoundsThread.setRunning(false);
                        } else if (event.getAction() == MotionEvent.ACTION_UP) {

                            message.setText("按住说话");
                            sendSoundsThread.setRunning(false);
                            receiveSoundsThread.setRunning(true);

                        }

                        return false;
                    }
                });

            }

        } else {

            speakButton.setOnTouchListener(new View.OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {

                        message.setText("松开结束");
                        if (isFirst) {
                            sendSoundsThread.start();
                            isFirst = false;
                        }

                        sendSoundsThread.setRunning(true);
                        receiveSoundsThread.setRunning(false);
                    } else if (event.getAction() == MotionEvent.ACTION_UP) {

                        message.setText("按住说话");
                        sendSoundsThread.setRunning(false);
                        receiveSoundsThread.setRunning(true);
                    }

                    return false;
                }
            });

        }

    }

    //方法的定义
    public  int[] count(String str,char ch){

        for(int i=0;i<str.length();i++){

            char c=str.charAt(i);
            if(c==ch){
                //数组长度arry.length,字符串长度str.length().Arrays.copyOf(复制的数组,数组的长度)
                arry= Arrays.copyOf(arry,arry.length+1);
                //数组的最后一个元素,arry[arry.length-1]
                arry[arry.length-1]=i;

                Log.d(TAG, "count: -----arry:------"+i);
            }

        }
        return arry;
    }


    /**
     * 按音量下键进行组播
     *
     * @param keyCode
     * @param event
     * @return
     */

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        //scanner key
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                event.startTracking(); //只有执行了这行代码才会调用onKeyLongPress
                if (event.getRepeatCount() == 0) {
                    shortPress = true;  //短按事件逻辑
                }
                return true;
            }
        }

        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Intent i = new Intent(Intent.ACTION_MAIN);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            i.addCategory(Intent.CATEGORY_HOME);
            startActivity(i);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }


    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            //长按事件的逻辑
            Log.d(TAG, "onKeyLongPress: " + "长按");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0以上
                int checkPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO);
                if (checkPermission != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 1);//后面的1为请求码

                    return true;
                } else {

                    //按键弹起事件逻辑
                    if (shortPress) {

                        message.setText("松开结束");
                        if (isFirst) {
                            sendSoundsThread.start();
                            isFirst = false;
                        }

                        sendSoundsThread.setRunning(true);
                        receiveSoundsThread.setRunning(false);
                    }

                }

            } else {

                //按键弹起事件逻辑
                if (shortPress) {

                    message.setText("松开结束");
                    if (isFirst) {
                        sendSoundsThread.start();
                        isFirst = false;
                    }

                    sendSoundsThread.setRunning(true);
                    receiveSoundsThread.setRunning(false);
                }

            }

            return true;
        }
        //Just return false because the super call does always the same (returning false)
        return false;
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)  //此处是按键的对应键值
        {
            //抬起事件的逻辑
            Log.d(TAG, "onKeyLongPress: " + "抬起");

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0以上
                int checkPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO);
                if (checkPermission != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 1);//后面的1为请求码

                    return true;
                } else {

                    message.setText("按住说话");
                    sendSoundsThread.setRunning(false);
                    receiveSoundsThread.setRunning(true);

                }

            } else {

                message.setText("按住说话");
                sendSoundsThread.setRunning(false);
                receiveSoundsThread.setRunning(true);

            }

        }
        return super.onKeyUp(keyCode, event);
    }

    /**
     * 获取本机V4地址
     * @param context
     * @return
     */
    public static String getIP(Context context){

        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
                {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address))
                    {
                        return inetAddress.getHostAddress().toString();
                    }
                }
            }
        }
        catch (SocketException ex){
            ex.printStackTrace();
        }
        return null;
    }

    class SendSoundsThread extends Thread {
        private AudioRecord recorder = null;
        private boolean isRunning = false;
        private byte[] recordBytes = new byte[640];

        public SendSoundsThread() {
            super();

            // 录音机
            int recordBufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);
            recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, recordBufferSize);
        }

        @Override
        public synchronized void run() {
            super.run();
            recorder.startRecording();

            while (true) {
                if (isRunning) {
                    try {
                        DatagramSocket clientSocket = new DatagramSocket();


                        InetAddress IP = InetAddress.getByName(substring+"0");// 向这个网络广播

                        // 获取音频数据
                        recorder.read(recordBytes, 0, recordBytes.length);

                        // 构建数据包 头+体
                        dataPacket dataPacket = new dataPacket(DevInfo.getBytes(), recordBytes);


                        // 构建数据报
                        DatagramPacket sendPacket = new DatagramPacket(dataPacket.getAllData(),
                                dataPacket.getAllData().length, IP, AppConfig.Port);

                        // 发送
                        clientSocket.send(sendPacket);
                        clientSocket.close();
                    } catch (SocketException e) {
                        e.printStackTrace();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }
    }

    private DatagramPacket receivePacket;

    class ReceiveSoundsThread extends Thread {
        private AudioTrack player = null;
        private boolean isRunning = false;
        private byte[] recordBytes = new byte[670];


        public ReceiveSoundsThread() {
            // 播放器
            int playerBufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);
            player = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, playerBufferSize, AudioTrack.MODE_STREAM);
        }

        @Override
        public synchronized void run() {
            super.run();

            try {
                @SuppressWarnings("resource")
                DatagramSocket serverSocket = new DatagramSocket(AppConfig.Port);
                while (true) {
                    if (isRunning) {
                        receivePacket = new DatagramPacket(recordBytes, recordBytes.length);
                        serverSocket.receive(receivePacket);

                        byte[] data = receivePacket.getData();

                        byte[] head = new byte[30];
                        byte[] body = new byte[640];

                        // 获得包头
                        for (int i = 0; i < head.length; i++) {
                            head[i] = data[i];
                        }

                        // 获得包体
                        for (int i = 0; i < body.length; i++) {
                            body[i] = data[i + 30];
                        }

                        // 获得头信息 通过头信息判断是否是自己发出的语音
                        String thisDevInfo = new String(head).trim();
                        System.out.println(thisDevInfo);

                        if (!thisDevInfo.equals(DevInfo)) {
                            player.write(body, 0, body.length);
                            player.play();
                        }
                    }
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();

        android.os.Process.killProcess(android.os.Process.myPid());

    }
}

----------------------------AppConfig ---------------------

public class AppConfig {

//    public static String IPAddress = "192.168.30.0";// 局域网
    public static int Port = 8000;


}

---------------------dataPacket--------------------

public class dataPacket {

    public static int head = 30;// 头信息大小
    public static int body = 640;// 语音包大小
    private byte[] recordBytes = new byte[head + body];

    public dataPacket(byte[] headInfo, byte[] bodyBytes)
    {
        for (int i = 0; i < headInfo.length; i++)
        {
            recordBytes[i] = headInfo[i];
        }
        for (int i = 0; i < bodyBytes.length; i++)
        {
            recordBytes[i + 30] = bodyBytes[i];
        }
    }

    public byte[] getHeadInfo()
    {
        byte[] head = new byte[30];
        for (int i = 0; i < head.length; i++)
        {
            head[i] = recordBytes[i];
        }
        return head;
    }

    public byte[] getBody()
    {
        byte[] body = new byte[640];
        for (int i = 0; i < body.length; i++)
        {
            body[i] = recordBytes[i + 30];
        }
        return body;
    }

    public byte[] getAllData()
    {
        byte[] data = new byte[head + body];
        for (int i = 0; i < data.length; i++)
        {
            data[i] = recordBytes[i];
        }
        return data;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40116418/article/details/81029825