Android Bluetooth蓝牙开发(三)

编程入门 行业动态 更新时间:2024-10-08 08:23:19

Android Bluetooth<a href=https://www.elefans.com/category/jswz/34/1768306.html style=蓝牙开发(三)"/>

Android Bluetooth蓝牙开发(三)

上一节,我们将连接设备放在了对话框中进行罗列显示,下面我们将选择一个蓝牙设备进行连接。

1、首先我们添加一个列表单击事件,发送内容。
2、在onActivityResult中获取传来的内容。
3、进行连接

1、DeviceListActivity.java

package com.sangbo.bluetooth;import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;import com.sangb.projecttest.R;import java.util.Set;/*** Created by 桑博 on 2015/10/29.*/
public class DeviceListActivity extends Activity {private BluetoothAdapter m_BtAdapter;private Button m_btnFindDevice;private ListView m_pairedListView;private ListView m_newListView;private ArrayAdapter<String> m_pairedAdapter;private ArrayAdapter<String> m_newAdapter;public final static String BLUETOOTH_DEVICE_ADDRESS = "device_address";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置窗口requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);setContentView(R.layout.activity_blue_tooth_device_list);//设置取消后,返回setResult(Activity.RESULT_CANCELED);//初始化一些控件initViews();//获取默认的蓝牙适配器m_BtAdapter = BluetoothAdapter.getDefaultAdapter();//注册相应的监听IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);this.registerReceiver(mReceiver, filter);filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);this.registerReceiver(mReceiver, filter);//寻找设备按钮事件m_btnFindDevice = (Button) findViewById(R.id.btn_find_device);m_btnFindDevice.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//隐藏按钮v.setVisibility(View.GONE);//显示进度条setProgressBarIndeterminateVisibility(true);//如果当前本地蓝牙适配器处于搜索设备中if(m_BtAdapter.isDiscovering()){//那么取消搜索m_BtAdapter.cancelDiscovery();}//先把数据清除一下,以免重复m_newAdapter.clear();//开始搜索蓝牙设备m_BtAdapter.startDiscovery();}});Set<BluetoothDevice> pairedDevices = m_BtAdapter.getBondedDevices();if(pairedDevices.size() > 0){for (BluetoothDevice device: pairedDevices) {m_pairedAdapter.add(device.getName()+"\n" +device.getAddress());}}else{m_pairedAdapter.add("没有配对的设备");}}public void initViews() {m_pairedListView = (ListView) findViewById(R.id.paired_devices);m_newListView = (ListView) findViewById(R.id.new_devices);m_pairedAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1);m_newAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1);m_pairedListView.setAdapter(m_pairedAdapter);m_newListView.setAdapter(m_newAdapter);m_pairedListView.setOnItemClickListener(mDeviceClickListener);m_newListView.setOnItemClickListener(mDeviceClickListener);}//接收消息的一个监听private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();//如果发现了一个蓝牙设备if (BluetoothDevice.ACTION_FOUND.equals(action)) {//我们拿到这个蓝牙设备BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//打印出蓝牙的名称和蓝牙地址Log.i("info", "devie 蓝牙名称:" + device.getName() + ",蓝牙地址:" + device.getAddress());m_newAdapter.add(device.getName()+"\n" +device.getAddress());}//搜索完成后else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//打印搜索完成Log.i("info", "devie : 搜索结束");//显示按钮m_btnFindDevice.setVisibility(View.VISIBLE);//取消进度条setProgressBarIndeterminateVisibility(false);}}};//列表单击事件private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {//取消搜索,我们都找到要连接的设备了, 就没必要搜索了。m_BtAdapter.cancelDiscovery();//获取蓝牙的MAC地址String info = ((TextView)view).getText().toString();String address = info.substring(info.length() - 17);//装载蓝牙的MAC地址Intent intent = new Intent();intent.putExtra(BLUETOOTH_DEVICE_ADDRESS,address);//传输地址,并关闭当前ActivitysetResult(Activity.RESULT_OK, intent);finish();}};@Overrideprotected void onDestroy() {super.onDestroy();//取消搜索if (m_BtAdapter != null) {m_BtAdapter.cancelDiscovery();}//接触事件的监听this.unregisterReceiver(mReceiver);}}

2、BlueToothActivity.java

package com.sangbo.bluetooth;import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import com.sangb.projecttest.BaseActivity;
import com.sangb.projecttest.R;/*** Created by 桑博 on 2015/10/28.*/
public class BlueToothActivity extends BaseActivity{private Button m_btnConnDevice;private TextView m_tvDeviceName;private BluetoothAdapter m_BtAdapter;private final int REQUEST_ENABLE = 0;private final int REQUEST_CONNECT_DEVICE = 1;private BluetoothChatService mChatService = null;private String mConnectedDeviceName = "";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_blue_tooth);initViews();//获取默认的本地蓝牙适配器,参考①m_BtAdapter = BluetoothAdapter.getDefaultAdapter();//获取本地蓝牙适配器的状态,是否启用if(!m_BtAdapter.isEnabled()){//如果没有启用,发出提示进行启用。Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent,REQUEST_ENABLE);//当然你也可以不提示,强行打开(如果没有root权限,系统会提示获取蓝牙的root权限)
//            m_BtAdapter.enable();}else if (mChatService == null){//初始化连接线程setupChat();}//连接列表对话框m_btnConnDevice.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent serverIntent = new Intent(BlueToothActivity.this,DeviceListActivity.class);startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);}});}//初始化连接线程private void setupChat() {mChatService = new BluetoothChatService(this, mHandler);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode){//选取了mac地址后返回case REQUEST_CONNECT_DEVICE:if(resultCode == Activity.RESULT_OK){String address = data.getExtras().getString(DeviceListActivity.BLUETOOTH_DEVICE_ADDRESS);BluetoothDevice device = m_BtAdapter.getRemoteDevice(address);mChatService.connect(device,true);}break;//返回打开了蓝牙适配器case REQUEST_ENABLE:if(resultCode == Activity.RESULT_OK){setupChat();}break;}}@Overridepublic void initViews(){m_btnConnDevice = (Button) findViewById(R.id.btn_conn_device);m_tvDeviceName = (TextView) findViewById(R.id.tv_device_name);}//蓝牙连接线程private final Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {//蓝牙连接状态改变时case Constants.MESSAGE_STATE_CHANGE:switch (msg.arg1) {//已连接case BluetoothChatService.STATE_CONNECTED://显示连接设备m_tvDeviceName.setText("连接设备: " +mConnectedDeviceName);break;//连接中case BluetoothChatService.STATE_CONNECTING:m_tvDeviceName.setText("正在连接中。。。");break;//未连接成功case BluetoothChatService.STATE_LISTEN:case BluetoothChatService.STATE_NONE:m_tvDeviceName.setText("未连接设备");Toast.makeText(BlueToothActivity.this, "连接失败", Toast.LENGTH_SHORT).show();break;}break;//连接成功,获取设备名称case Constants.MESSAGE_DEVICE_NAME:// save the connected device's namemConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);break;//连接过程中发来的消息case Constants.MESSAGE_TOAST:if (null != BlueToothActivity.this) {Toast.makeText(BlueToothActivity.this, msg.getData().getString(Constants.TOAST),Toast.LENGTH_SHORT).show();}break;}}};}

3、BluetoothChatService.java

/** Copyright (C) 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      .0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.sangbo.bluetooth;import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;/*** This class does all the work for setting up and managing Bluetooth* connections with other devices. It has a thread that listens for* incoming connections, a thread for connecting with a device, and a* thread for performing data transmissions when connected.*/
public class BluetoothChatService {// Debuggingprivate static final String TAG = "BluetoothChatService";// Name for the SDP record when creating server socketprivate static final String NAME_SECURE = "BluetoothChatSecure";private static final String NAME_INSECURE = "BluetoothChatInsecure";// Unique UUID for this applicationprivate static final UUID MY_UUID_SECURE =UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");private static final UUID MY_UUID_INSECURE =UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");// Member fieldsprivate final BluetoothAdapter mAdapter;private final Handler mHandler;private AcceptThread mSecureAcceptThread;private AcceptThread mInsecureAcceptThread;private ConnectThread mConnectThread;private ConnectedThread mConnectedThread;private int mState;// Constants that indicate the current connection statepublic static final int STATE_NONE = 0;       // we're doing nothingpublic static final int STATE_LISTEN = 1;     // now listening for incoming connectionspublic static final int STATE_CONNECTING = 2; // now initiating an outgoing connectionpublic static final int STATE_CONNECTED = 3;  // now connected to a remote device/*** Constructor. Prepares a new BluetoothChat session.** @param context The UI Activity Context* @param handler A Handler to send messages back to the UI Activity*/public BluetoothChatService(Context context, Handler handler) {mAdapter = BluetoothAdapter.getDefaultAdapter();mState = STATE_NONE;mHandler = handler;}/*** Set the current state of the chat connection** @param state An integer defining the current connection state*/private synchronized void setState(int state) {Log.d(TAG, "setState() " + mState + " -> " + state);mState = state;// Give the new state to the Handler so the UI Activity can updatemHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();}/*** Return the current connection state.*/public synchronized int getState() {return mState;}/*** Start the chat service. Specifically start AcceptThread to begin a* session in listening (server) mode. Called by the Activity onResume()*/public synchronized void start() {Log.d(TAG, "start");// Cancel any thread attempting to make a connectionif (mConnectThread != null) {mConnectThread.cancel();mConnectThread = null;}// Cancel any thread currently running a connectionif (mConnectedThread != null) {mConnectedThread.cancel();mConnectedThread = null;}setState(STATE_LISTEN);// Start the thread to listen on a BluetoothServerSocketif (mSecureAcceptThread == null) {mSecureAcceptThread = new AcceptThread(true);mSecureAcceptThread.start();}if (mInsecureAcceptThread == null) {mInsecureAcceptThread = new AcceptThread(false);mInsecureAcceptThread.start();}}/*** Start the ConnectThread to initiate a connection to a remote device.** @param device The BluetoothDevice to connect* @param secure Socket Security type - Secure (true) , Insecure (false)*/public synchronized void connect(BluetoothDevice device, boolean secure) {Log.d(TAG, "connect to: " + device);// Cancel any thread attempting to make a connectionif (mState == STATE_CONNECTING) {if (mConnectThread != null) {mConnectThread.cancel();mConnectThread = null;}}// Cancel any thread currently running a connectionif (mConnectedThread != null) {mConnectedThread.cancel();mConnectedThread = null;}// Start the thread to connect with the given devicemConnectThread = new ConnectThread(device, secure);mConnectThread.start();setState(STATE_CONNECTING);}/*** Start the ConnectedThread to begin managing a Bluetooth connection** @param socket The BluetoothSocket on which the connection was made* @param device The BluetoothDevice that has been connected*/public synchronized void connected(BluetoothSocket socket, BluetoothDevicedevice, final String socketType) {Log.d(TAG, "connected, Socket Type:" + socketType);// Cancel the thread that completed the connectionif (mConnectThread != null) {mConnectThread.cancel();mConnectThread = null;}// Cancel any thread currently running a connectionif (mConnectedThread != null) {mConnectedThread.cancel();mConnectedThread = null;}// Cancel the accept thread because we only want to connect to one deviceif (mSecureAcceptThread != null) {mSecureAcceptThread.cancel();mSecureAcceptThread = null;}if (mInsecureAcceptThread != null) {mInsecureAcceptThread.cancel();mInsecureAcceptThread = null;}// Start the thread to manage the connection and perform transmissionsmConnectedThread = new ConnectedThread(socket, socketType);mConnectedThread.start();// Send the name of the connected device back to the UI ActivityMessage msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);Bundle bundle = new Bundle();bundle.putString(Constants.DEVICE_NAME, device.getName());msg.setData(bundle);mHandler.sendMessage(msg);setState(STATE_CONNECTED);}/*** Stop all threads*/public synchronized void stop() {Log.d(TAG, "stop");if (mConnectThread != null) {mConnectThread.cancel();mConnectThread = null;}if (mConnectedThread != null) {mConnectedThread.cancel();mConnectedThread = null;}if (mSecureAcceptThread != null) {mSecureAcceptThread.cancel();mSecureAcceptThread = null;}if (mInsecureAcceptThread != null) {mInsecureAcceptThread.cancel();mInsecureAcceptThread = null;}setState(STATE_NONE);}/*** Write to the ConnectedThread in an unsynchronized manner** @param out The bytes to write* @see ConnectedThread#write(byte[])*/public void write(byte[] out) {// Create temporary objectConnectedThread r;// Synchronize a copy of the ConnectedThreadsynchronized (this) {if (mState != STATE_CONNECTED) return;r = mConnectedThread;}// Perform the write unsynchronizedr.write(out);}/*** Indicate that the connection attempt failed and notify the UI Activity.*/private void connectionFailed() {// Send a failure message back to the ActivityMessage msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);Bundle bundle = new Bundle();bundle.putString(Constants.TOAST, "Unable to connect device");msg.setData(bundle);mHandler.sendMessage(msg);// Start the service over to restart listening modeBluetoothChatService.this.start();}/*** Indicate that the connection was lost and notify the UI Activity.*/private void connectionLost() {// Send a failure message back to the ActivityMessage msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);Bundle bundle = new Bundle();bundle.putString(Constants.TOAST, "Device connection was lost");msg.setData(bundle);mHandler.sendMessage(msg);// Start the service over to restart listening modeBluetoothChatService.this.start();}/*** This thread runs while listening for incoming connections. It behaves* like a server-side client. It runs until a connection is accepted* (or until cancelled).*/private class AcceptThread extends Thread {// The local server socketprivate final BluetoothServerSocket mmServerSocket;private String mSocketType;public AcceptThread(boolean secure) {BluetoothServerSocket tmp = null;mSocketType = secure ? "Secure" : "Insecure";// Create a new listening server sockettry {if (secure) {tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,MY_UUID_SECURE);} else {tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, MY_UUID_INSECURE);}} catch (IOException e) {Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);}mmServerSocket = tmp;}public void run() {Log.d(TAG, "Socket Type: " + mSocketType +"BEGIN mAcceptThread" + this);setName("AcceptThread" + mSocketType);BluetoothSocket socket = null;// Listen to the server socket if we're not connectedwhile (mState != STATE_CONNECTED) {try {// This is a blocking call and will only return on a// successful connection or an exceptionsocket = mmServerSocket.accept();} catch (IOException e) {Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);break;}// If a connection was acceptedif (socket != null) {synchronized (BluetoothChatService.this) {switch (mState) {case STATE_LISTEN:case STATE_CONNECTING:// Situation normal. Start the connected thread.connected(socket, socket.getRemoteDevice(),mSocketType);break;case STATE_NONE:case STATE_CONNECTED:// Either not ready or already connected. Terminate new socket.try {socket.close();} catch (IOException e) {Log.e(TAG, "Could not close unwanted socket", e);}break;}}}}Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);}public void cancel() {Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);try {mmServerSocket.close();} catch (IOException e) {Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);}}}/*** This thread runs while attempting to make an outgoing connection* with a device. It runs straight through; the connection either* succeeds or fails.*/private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;private String mSocketType;public ConnectThread(BluetoothDevice device, boolean secure) {mmDevice = device;BluetoothSocket tmp = null;mSocketType = secure ? "Secure" : "Insecure";// Get a BluetoothSocket for a connection with the// given BluetoothDevicetry {if (secure) {tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);} else {tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);}} catch (IOException e) {Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);}mmSocket = tmp;}public void run() {Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);setName("ConnectThread" + mSocketType);// Always cancel discovery because it will slow down a connectionmAdapter.cancelDiscovery();// Make a connection to the BluetoothSockettry {// This is a blocking call and will only return on a// successful connection or an exceptionmmSocket.connect();} catch (IOException e) {// Close the sockettry {mmSocket.close();} catch (IOException e2) {Log.e(TAG, "unable to close() " + mSocketType +" socket during connection failure", e2);}connectionFailed();return;}// Reset the ConnectThread because we're donesynchronized (BluetoothChatService.this) {mConnectThread = null;}// Start the connected threadconnected(mmSocket, mmDevice, mSocketType);}public void cancel() {try {mmSocket.close();} catch (IOException e) {Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);}}}/*** This thread runs during a connection with a remote device.* It handles all incoming and outgoing transmissions.*/private class ConnectedThread extends Thread {private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket, String socketType) {Log.d(TAG, "create ConnectedThread: " + socketType);mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;// Get the BluetoothSocket input and output streamstry {tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch (IOException e) {Log.e(TAG, "temp sockets not created", e);}mmInStream = tmpIn;mmOutStream = tmpOut;}public void run() {Log.i(TAG, "BEGIN mConnectedThread");byte[] buffer = new byte[1024];int bytes;// Keep listening to the InputStream while connectedwhile (true) {try {// Read from the InputStreambytes = mmInStream.read(buffer);// Send the obtained bytes to the UI ActivitymHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer).sendToTarget();} catch (IOException e) {Log.e(TAG, "disconnected", e);connectionLost();// Start the service over to restart listening modeBluetoothChatService.this.start();break;}}}/*** Write to the connected OutStream.** @param buffer The bytes to write*/public void write(byte[] buffer) {try {mmOutStream.write(buffer);// Share the sent message back to the UI ActivitymHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();} catch (IOException e) {Log.e(TAG, "Exception during write", e);}}public void cancel() {try {mmSocket.close();} catch (IOException e) {Log.e(TAG, "close() of connect socket failed", e);}}}
}

4、activity_blue_tooth.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><TextView
        android:id="@+id/tv_device_name"android:layout_width="fill_parent"android:text="设备未连接"android:layout_height="wrap_content" /><Button
        android:id="@+id/btn_conn_device"android:layout_width="wrap_content"android:text="@string/conn_blue_toolth_device"android:layout_height="wrap_content" /></LinearLayout>

5、activity_blue_tooth_device_list

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><TextView
        android:layout_width="wrap_content"android:text="@string/title_paired_devices"android:layout_height="wrap_content" /><ListView
        android:id="@+id/paired_devices"android:layout_width="fill_parent"android:layout_weight="1"android:layout_height="wrap_content"></ListView><TextView
        android:layout_width="wrap_content"android:text="@string/title_other_devices"android:layout_height="wrap_content" /><ListView
        android:id="@+id/new_devices"android:layout_width="fill_parent"android:layout_weight="2"android:layout_height="wrap_content"></ListView><Button
        android:id="@+id/btn_find_device"android:layout_width="wrap_content"android:text="@string/find_blue_toolth_device"android:layout_height="wrap_content" /></LinearLayout>

6、strings.xml

    <string name="find_blue_toolth_device">查找蓝牙设备</string><string name="title_paired_devices">配对的设备</string><string name="title_other_devices">其他可用设备</string><string name="conn_blue_toolth_device">连接蓝牙设备</string>

转载请注明: SangBigYe:

更多推荐

Android Bluetooth蓝牙开发(三)

本文发布于:2024-02-06 11:47:14,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1748828.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:蓝牙   Android   Bluetooth

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!