欢迎来到山村网

android基于socket的局域网内服务器与客户端加密通信

2019-03-09 11:56:46浏览:208 来源:山村网   
核心摘要:  实现了基本的socket通信(即两台设备,一台用作服务器,一台用作客户端),服务器进行监听,客户端发送加密数据到服务器,服务

  实现了基本的socket通信(即两台设备,一台用作服务器,一台用作客户端),服务器进行监听,客户端发送加密数据到服务器,服务器进行解密得到明文。

  注意:本项目中使用了ButterKnife及EventBus作为辅助工具,通信建立时默认网络正常(未做局域网网络环境检测),加密方式为AES加密

  1.效果图:

  (1)客户端

android基于socket的局域网内服务器与客户端加密通信 山村

  (2)服务器端

服务器端

  2.界面布局部分

  (1)服务器端布局 function_socket_server.xml

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 <?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="match_parent" android:orientation="vertical"> <RelativeLayout style="@style/ToolBar"> <TextView style="@style/ToolBar_tv_Title" android:text="网络加密-服务器端" /> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_startListener" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="启动监听" /> <Button android:id="@+id/btn_stopListener" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="停止监听" /> <Button android:id="@+id/btn_getUser" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="刷新用户" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="本机地址:" /> <TextView android:id="@+id/tv_localAddress" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" /> </LinearLayout> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="接收到的明文:" android:textColor="@color/black" /> <TextView android:id="@+id/tv_receivedContent" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="解密后的明文:" android:textColor="@color/black" /> <TextView android:id="@+id/tv_decryptContent" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" /> </LinearLayout> </ScrollView> </LinearLayout>

  (2)客户端布局 function_socket_client.xml

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <?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="match_parent" android:orientation="vertical"> <RelativeLayout style="@style/ToolBar"> <TextView style="@style/ToolBar_tv_Title" android:text="网络加密-客户端" /> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="服务器地址:" /> <EditText android:id="@+id/edtTxt_serverAddress" android:layout_width="match_parent" android:text="192.168.43.1" android:layout_height="wrap_content" android:singleLine="true" /> </LinearLayout> <ScrollView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="文本内容:" android:textColor="@color/black" /> <EditText android:id="@+id/edtTxt_Content" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/main_background" android:padding="10dp" android:text="123木头人" /> </LinearLayout> </ScrollView> <Button android:id="@+id/btn_encryptAndSend" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:text="加密并发送" /> </LinearLayout>

  (3)用到的style

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <!--通用Title的右侧按钮--> <style name="ToolBar_iv_Right"> <item name="android:layout_width">@dimen/toolbar_icon_dimen</item> <item name="android:layout_height">@dimen/toolbar_icon_dimen</item> <item name="android:layout_alignParentRight">true</item> <item name="android:layout_gravity">end</item> <item name="android:clickable">true</item> <item name="android:background">?android:actionBarItemBackground</item> <item name="android:padding">15dp</item> </style> <!--通用Title的TextView--> <style name="ToolBar_tv_Title"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_centerVertical">true</item> <item name="android:layout_marginLeft">@dimen/toolbar_title_haveBack_marginStart</item> <item name="android:layout_marginRight">@dimen/toolbar_title_haveBack_marginEnd</item> <item name="android:gravity">center</item> <item name="android:singleLine">true</item> <item name="android:textColor">@color/white</item> <item name="android:textSize">20sp</item> </style>

  3.功能代码

  (1)基类 baseEventActivity.Java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import org.greenrobot.eventbus.EventBus; import butterknife.ButterKnife; public abstract class baseEventActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getIntentData(); setContentView(getLayoutResId()); ButterKnife.bind(this); EventBus.getDefault().register(this); init(); } protected void getIntentData() { } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } protected abstract void init(); protected abstract int getLayoutResId(); }

  (2)服务器主界面 Function_Socket.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.view.View; import android.widget.TextView; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import butterknife.BindView; import butterknife.OnClick; public class Function_Socket_Server extends baseEventActivity { @BindView(R.id.tv_localAddress) TextView tv_localAddress; @BindView(R.id.tv_receivedContent) TextView tv_receivedContent; @BindView(R.id.tv_decryptContent) TextView tv_decryptContent; private LocalService localService;//用于启动监听的服务 private ServiceConnection sc;//服务连接 @Override protected void init() { tv_localAddress.setText(ToolUtil.getHostIP()); sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { LocalService.LocalBinder localBinder = (LocalService.LocalBinder) service; localService = localBinder.getService(); localService.startWaitDataThread(); ToastUtil.showToast(Function_Socket_Server.this, "监听已启动"); } @Override public void onServiceDisconnected(ComponentName name) { } }; connection(); } @Subscribe(threadMode = ThreadMode.MAIN) public void getData(String data) { tv_receivedContent.setText(data); tv_decryptContent.setText(AESUtil.decrypt(ConstantUtil.password, data)); } private void connection() { Intent intent = new Intent(this, LocalService.class); bindService(intent, sc, BIND_AUTO_CREATE); } @Override protected int getLayoutResId() { return R.layout.function_socket_server; } private ArrayList<String> getConnectedIP() { ArrayList<String> connectedIP = new ArrayList<>(); try { //通过读取配置文件实现 BufferedReader br = new BufferedReader(new FileReader( "/proc/net/arp")); String line; while ((line = br.readLine()) != null) { String[] splitted = line.split(" +"); if (splitted.length >= 4) { String ip = splitted[0]; connectedIP.add(ip); } } } catch (Exception e) { e.printStackTrace(); } return connectedIP; } @OnClick({R.id.btn_startListener, R.id.btn_stopListener, R.id.btn_getUser}) public void onClick(View v) { switch (v.getId()) { case R.id.btn_startListener://启动监听 connection(); break; case R.id.btn_stopListener://停止监听 if (sc != null) unbindService(sc); break; case R.id.btn_getUser://刷新连接到此设备的IP并清空之前接收到的数据 ArrayList<String> connectedIP = getConnectedIP(); StringBuilder resultList = new StringBuilder(); for (String ip : connectedIP) { resultList.append(ip); resultList.append("n"); } ToastUtil.showToast(this, "连接到手机上的Ip是:" + resultList.toString()); tv_decryptContent.setText(""); tv_receivedContent.setText(""); break; } } public void onDestroy() { super.onDestroy(); if (sc != null) unbindService(sc); } }

  (3)客户端主界面 Function_Socket_Client.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 import android.app.ProgressDialog; import android.util.Log; import android.view.View; import android.widget.EditText; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import butterknife.BindView; import butterknife.OnClick; public class Function_Socket_Client extends baseEventActivity { @BindView(R.id.edtTxt_Content) EditText edtTxt_Content; @BindView(R.id.edtTxt_serverAddress) EditText edtTxt_serverAddress; private ProgressDialog mProgressDialog;//加载的小菊花 @Override protected void init() { } @Override protected int getLayoutResId() { return R.layout.function_socket_client; } @OnClick(R.id.btn_encryptAndSend) public void onClick(View v) { switch (v.getId()) { case R.id.btn_encryptAndSend: String s = edtTxt_Content.getText().toString().trim(); String ip = edtTxt_serverAddress.getText().toString().trim(); if (ToolUtil.IsIpv4(ip)) { new SendDataThread(ip, AESUtil.encrypt(ConstantUtil.password, s), ConstantUtil.port).start();//消息发送方启动线程发送消息 showProgressDialog("尝试发送数据到ntt" + ip, true); } else { ToastUtil.showToast(this, "IP不合法!"); } break; } } @Subscribe(threadMode = ThreadMode.MAIN) public void sendResult(Integer resultCode) { Log.i("succ", "=" + resultCode); dismissProgressDialog(); switch (resultCode) { case ConstantUtil.CODE_SUCCESS: ToastUtil.showToast(this, "发送成功"); break; case ConstantUtil.CODE_TIMEOUT: ToastUtil.showToast(this, "连接超时"); break; case ConstantUtil.CODE_UNKNOWN_HOST: ToastUtil.showToast(this, "错误-未知的host"); break; } } public void showProgressDialog(final String msg, final boolean isCancel) { runonUiThread(new Runnable() { @Override public void run() { try { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(Function_Socket_Client.this); } if (mProgressDialog.isShowing()) { return; } mProgressDialog.setMessage(msg); mProgressDialog.setCancelable(isCancel); mProgressDialog.setCanceledonTouchOutside(false); mProgressDialog.setonCancelListener(null); mProgressDialog.show(); } catch (Exception e) { e.printStackTrace(); } } }); } public void dismissProgressDialog() { try { if (mProgressDialog != null && mProgressDialog.isShowing()) { runonUiThread( new Runnable() { @Override public void run() { mProgressDialog.dismiss(); } } ); } } catch (Exception e) { e.printStackTrace(); } } }

  (4)LocalService.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class LocalService extends Service { private IBinder iBinder = new LocalService.LocalBinder(); @Override public IBinder onBind(Intent intent) { return iBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } public void startWaitDataThread() { new ListenThread(ConstantUtil.port).start(); } //定义内容类继承Binder public class LocalBinder extends Binder { //返回本地服务 public LocalService getService() { return LocalService.this; } } }

  (5)ListenThread.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import org.greenrobot.eventbus.EventBus; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class ListenThread extends Thread { private ServerSocket serverSocket; public ListenThread(int port) { try { serverSocket = new ServerSocket(port); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while (true) { try { if (serverSocket != null) { Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); if (inputStream != null) { BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String str; str = in.readLine(); EventBus.getDefault().post(str); socket.close(); } } } catch (IOException e) { e.printStackTrace(); } } } }

  (6)SendDataThread.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import android.util.Log; import org.greenrobot.eventbus.EventBus; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; public class SendDataThread extends Thread { private Socket socket; private String ip;//接收方的IP private int port;//接收方的端口号 private String data;//准备发送的数据 public SendDataThread(String ip, String data, int port) { this.ip = ip; this.data = data; this.port = port; } @Override public void run() { try { socket = new Socket(); socket.connect(new InetSocketAddress(ip,port),ConstantUtil.TIME_MILLIS);//设置超时时间 } catch (UnknownHostException e) { EventBus.getDefault().post(ConstantUtil.CODE_UNKNOWN_HOST); Log.d("error", "SendDataThread.init() has UnknownHostException" + e.getMessage()); } catch (SocketTimeoutException e) { EventBus.getDefault().post(ConstantUtil.CODE_TIMEOUT); Log.d("error", "SendDataThread.init() has TimeoutException:" + e.getMessage()); }catch (IOException e){ Log.d("error", "SendDataThread.init() has IOException:" + e.getMessage()); } if (socket != null&&socket.isConnected()) { try { OutputStream ops = socket.getOutputStream(); OutputStreamWriter opsw = new OutputStreamWriter(ops); BufferedWriter writer = new BufferedWriter(opsw); writer.write(data + "rnrn");//由于socket使用缓冲区进行读写数据,因此使用rnrn用于表明数据已写完.不加这个会导致数据无法发送 EventBus.getDefault().post(ConstantUtil.CODE_SUCCESS); writer.flush(); } catch (IOException e) { e.printStackTrace(); } } } }

  (7)AESUtil.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 import android.util.Log; import java.io.UnsupportedEncodingException; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class AESUtil { // private static final String CipherMode = "AES/ECB/PKCS5Padding";使用ECB加密,不需要设置IV,但是不安全 private static final String CipherMode = "AES/CFB/NoPadding";//使用CFB加密,需要设置IV private static SecretKeySpec createKey(String password) { byte[] data = null; if (password == null) { password = ""; } StringBuilder sb = new StringBuilder(32); sb.append(password); while (sb.length() < 32) { sb.append("0"); } if (sb.length() > 32) { sb.setLength(32); } try { data = sb.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new SecretKeySpec(data, "AES"); } // private static byte[] encrypt(byte[] content, String password) { try { SecretKeySpec key = createKey(password); System.out.println(key); Cipher cipher = Cipher.getInstance(CipherMode); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec( new byte[cipher.getBlockSize()])); return cipher.doFinal(content); } catch (Exception e) { e.printStackTrace(); } return null; } // public static String encrypt(String password, String content) { Log.d("加密前", "seed=" + password + "ncontent=" + content); byte[] data = null; try { data = content.getBytes("UTF-8"); } catch (Exception e) { e.printStackTrace(); } data = encrypt(data, password); String result = byte2hex(data); Log.d("加密后", "result=" + result); return result; } // private static byte[] decrypt(byte[] content, String password) { try { SecretKeySpec key = createKey(password); Cipher cipher = Cipher.getInstance(CipherMode); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec( new byte[cipher.getBlockSize()])); return cipher.doFinal(content); } catch (Exception e) { e.printStackTrace(); } return null; } // public static String decrypt(String password, String content) { Log.d("解密前", "seed=" + password + "ncontent=" + content); byte[] data = null; try { data = hex2byte(content); } catch (Exception e) { e.printStackTrace(); } data = decrypt(data, password); if (data == null) return null; String result = null; try { result = new String(data, "UTF-8"); Log.d("解密后", "result=" + result); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } // private static String byte2hex(byte[] b) { // 一个字节的数, StringBuilder sb = new StringBuilder(b.length * 2); String tmp ; for (byte aB : b) { // 整数转成十六进制表示 tmp = (Integer.toHexString(aB & 0xFF)); if (tmp.length() == 1) { sb.append("0"); } sb.append(tmp); } return sb.toString().toUpperCase(); // 转成大写 } // private static byte[] hex2byte(String inputString) { if (inputString == null || inputString.length() < 2) { return new byte[0]; } inputString = inputString.toLowerCase(); int l = inputString.length() / 2; byte[] result = new byte[l]; for (int i = 0; i < l; ++i) { String tmp = inputString.substring(2 * i, 2 * i + 2); result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF); } return result; } }

  (8)ConstantUti.java

  ?

1 2 3 4 5 6 7 8 9 10 11 public class ConstantUtil { public static final int TIME_MILLIS = 5 * 1000;//连接超时时间 public static final int port = 25256;//端口号 public static final String password = "123456885";//加密所使用的密钥 public static final int CODE_TIMEOUT = 0;//连接超时 public static final int CODE_SUCCESS = 1;//连接成功 public static final int CODE_UNKNOWN_HOST = 2;//错误-未知的host }

  (9)ToolUtil.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; public class ToolUtil { public static String getHostIP() { String hostIp = null; try { Enumeration nis = NetworkInterface.getNetworkInterfaces(); InetAddress ia; while (nis.hasMoreElements()) { NetworkInterface ni = (NetworkInterface) nis.nextElement(); Enumeration<InetAddress> ias = ni.getInetAddresses(); while (ias.hasMoreElements()) { ia = ias.nextElement(); if (ia instanceof Inet6Address) { continue;// skip ipv6 } String ip = ia.getHostAddress(); if (!"127.0.0.1".equals(ip)) { hostIp = ia.getHostAddress(); break; } } } } catch (SocketException e) { Log.i("error", "SocketException"); e.printStackTrace(); } return hostIp; } public static boolean IsIpv4(String ipv4) { if (ipv4 == null || ipv4.length() == 0) { return false;//字符串为空或者空串 } String[] parts = ipv4.split(".");//因为java doc里已经说明, split的参数是reg, 即正则表达式, 如果用"|"分割, 则需使用"|" if (parts.length != 4) { return false;//分割开的数组根本就不是4个数字 } for (String part : parts) { try { int n = Integer.parseInt(part); if (n < 0 || n > 255) { return false;//数字不在正确范围内 } } catch (NumberFormatException e) { return false;//转换数字不正确 } } return true; } }

  (10)ToastUtil.java

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import android.content.Context; import android.widget.Toast; public class ToastUtil { private static Toast mToast = null; public static void showToast(Context context, String text) { if (text != null) { if (mToast == null) { mToast = Toast.makeText(context, text, Toast.LENGTH_SHORT); } else { mToast.setText(text); mToast.setDuration(Toast.LENGTH_SHORT); } mToast.show(); } } }

  3.权限及声明

  ?

1 2 3 4 5 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <!--service部分--> <service android:name="com.test.test.LocalService"/>
(责任编辑:豆豆)
下一篇:

wifi共享精灵出现错误代码1的提示怎么解决?

上一篇:

华硕RT-AC5300内部做工怎么样?华硕RT-AC5300路由器拆解评测

  • 信息二维码

    手机看新闻

  • 分享到
打赏
免责声明
• 
本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们 xfptx@outlook.com