1. Android studio grade配置relesase/debug版本的签名文件,需要放在defaultConfig和buildTypes 之前
signingConfigs {
release {
keyAlias 'xierapp'
keyPassword 'xierandroid123'
storeFile file('release.jks')
storePassword 'xierandroid123'
}
debug {
keyAlias 'xierapp'
keyPassword 'xierandroid123'
storeFile file('release.jks')
storePassword 'xierandroid123'
}
}
2. jni了解
第一步先在类中创建javanative 方法
public static native String getStringFromNDK();
第二步build make project. 然后就能在build中找到这些文件
第三步在命令行切换到debug目录 执行 javah -classpath . -jni 你的native方法所在类的路径,生成.h文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_scy_android_ndkdemo_NDKTools */
#ifndef _Included_com_scy_android_ndkdemo_NDKTools
#define _Included_com_scy_android_ndkdemo_NDKTools
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_scy_android_ndkdemo_NDKTools
* Method: getStringFromNDK
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_scy_android_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
第四部创建jni 文件夹 把生成的.h文件放进来,再创建Android.mk 文件 和一个随意取名(我这里是ndkdemotest)的.c 文件
Android.mk
LOCAL_PATH := $(call my-dir)//$(call my-dir)调用NDK内部的函数获得当前.mk文件的路径
include $(CLEAR_VARS) //清空了除了LOCAL_PATH之外的所有LOCAL_xxx变量的值
LOCAL_MODULE := ndkdemotest-jni//表示Android.mk中的每一个模块。名字必须唯一且不包含空格。
LOCAL_SRC_FILES := ndkdemotest.c//必须包含将要打包如模块的C/C++ 源码
include $(BUILD_SHARED_LIBRARY)//编译为动态库
ndkdemotest.c (注意要引入你自己的.h文件)
#include "com_scy_android_ndkdemo_NDKTools.h"
JNIEXPORT jstring JNICALL Java_com_scy_android_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env, "scy jni blog article");
}
最后配置buidl.grade 再在native方法里引入
//AndroidStudio 指定引入jniLibs的特定CPU架构的so库文件
ndk{
moduleName "ndkdemotest-jni"
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
//cmake 工具编译选项
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
sourceSets.main {
jni.srcDirs = []
}
}
//在包含native的Java方法类中引入代码
static {
System.loadLibrary("ndkdemotest-jni");
}
3. 在jni 的基础之上开始了解串口通信
在调用native方法上与jni相似的,官方提供了两个打开和关闭串口的c方法,我们只需要调用即可,别忘记加上一些引入和配置。
// JNI(调用java本地接口,实现串口的打开和关闭)
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
//构造器供外部调用开启串口
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
//检查访问权限,如果没有读写权限,进行文件操作,修改文件访问权限
// if (!device.canRead() || !device.canWrite()) {
//
// try {
// //通过挂载到linux的方式,修改文件的操作权限
// Process su = Runtime.getRuntime().exec("/system/xbin/su");
// String cmd = "chmod 777 " + device.getAbsolutePath() + "\n" + "exit\n";
// su.getOutputStream().write(cmd.getBytes());
//
// if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) {
// Log.e("SerialPort", "SerialPort: 没有权限");
// throw new SecurityException();
// }
// } catch (Exception e) {
// e.printStackTrace();
// throw new SecurityException();
// }
// }
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
接下来就是一个封装的SerialPortUtil类了
public SerialPort openSerialPort(String path,int baudrate){
try {
serialPort = new SerialPort(new File(path),baudrate,0);// TODO: 2019/7/28 打开串口异常,path需修改,流报空指针
this.serialPortStatus = true;
threadStatus = false; //线程状态
//获取打开的串口中的输入输出流,以便于串口数据的收发
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
new ReadThread().start(); //开始线程监控是否有数据要接收
} catch (IOException e) {
Log.d(TAG, "openSerialPort: 打开串口异常:" + e.toString());
return serialPort;
}
Log.d(TAG, "openSerialPort: 打开串口");
return serialPort;
}
/**
* 关闭串口
*/
public void closeSerialPort(){
try {
inputStream.close();
outputStream.close();
this.serialPortStatus = false;
this.threadStatus = true; //线程状态
serialPort.close();
} catch (IOException e) {
Log.e(TAG, "closeSerialPort: 关闭串口异常:"+e.toString());
return;
}
Log.d(TAG, "closeSerialPort: 关闭串口成功");
}
/**
* 发送串口指令(字符串)
* @param data String数据指令
*/
public void sendSerialPort(String data){
Log.d(TAG, "sendSerialPort: 发送数据");
try {
byte[] sendData = data.getBytes(); //string转byte[]
this.data_ = new String(sendData); //byte[]转string
if (sendData.length > 0) {
outputStream.write(sendData);
outputStream.write('\n');
//outputStream.write('\r'+'\n');
outputStream.flush();
Log.d(TAG, "sendSerialPort: 串口数据发送成功");
}
} catch (IOException e) {
Log.e(TAG, "sendSerialPort: 串口数据发送失败:"+e.toString());
}
}
/**
* 单开一线程,来读数据
*/
private class ReadThread extends Thread{
@Override
public void run() {
super.run();
//判断进程是否在运行,更安全的结束进程
while (!threadStatus){
Log.d(TAG, "进入线程run");
//64 1024
byte[] buffer = new byte[64];
int size; //读取数据的大小
try {
size = inputStream.read(buffer);
if (size > 0){
Log.d(TAG, "run: 接收到了数据:" + changeTool.ByteArrToHex(buffer));
Log.d(TAG, "run: 接收到了数据大小:" + String.valueOf(size));
//提供回调接口,告知调用方收到了信息
onDataReceiveListener.onDataReceive(buffer,size);
}
} catch (IOException e) {
Log.e(TAG, "run: 数据读取异常:" +e.toString());
}
}
}
}
4. 学习Android wifi 开发
SSID 网络名字
RSSID/level wifi信号强度
capabilities wifi的加密类型
BSSID wifi的物理地址
加入权限,动态申请ACCESS_FINE_LOCATION,提示打开GPS。
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
//检查GPS是否开启
private boolean checkGpsIsOpen() {
boolean isOpen;
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
isOpen = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
return isOpen;
}
//未开启则请求跳转GPS开启界面
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, Constants.LOCATION_REQUEST_2);
获取WifiManager对象
mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
判断当前wifi是否开启
if (!mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(true);
}
判断wifi是否保存
/**
* descirption: 判断wifi是否已保存
*/
public static WifiConfiguration isWifiSave(String SSID) {
if (mWifiConfigurations != null) {
for (WifiConfiguration existingConfig : mWifiConfigurations) {
if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
return existingConfig;
}
}
}
return null;
}
注册广播接收wifi的一些信息再继续做操作
//注册广播
public void initReceiver() {
wifiBroadCastReceiver = new WifiBroadCastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
if (wifiBroadCastReceiver != null) {
registerReceiver(wifiBroadCastReceiver, filter);
}
}
class WifiBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
//当扫描到结果后
if (loadingLl.getVisibility() == View.VISIBLE) {
loadingLl.setVisibility(View.GONE);
}
initRecyclerView();
} else if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
//wifi连接网络状态
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
NetworkInfo.DetailedState state = info.getDetailedState();
if (state == state.SCANNING) {
ToastUtils.showShort("正在扫描");
} else if (state == state.AUTHENTICATING) {
ToastUtils.showShort("正在验证身份信息");
} else if (state == state.OBTAINING_IPADDR) {
ToastUtils.showShort("正在获取IP地址");
} else if (state == state.CONNECTING) {
ToastUtils.showShort("正在连接");
} else if (state == state.CONNECTED) {
ToastUtils.showShort("建立连接");
if (adapter != null) {
adapter.notifyDataSetChanged();
}
} else if (state == state.DISCONNECTING) {
ToastUtils.showShort("正在断开连接");
} else if (state == state.DISCONNECTED) {
ToastUtils.showShort("已断开连接");
if (adapter != null) {
adapter.notifyDataSetChanged();
}
} else if (state == state.FAILED) {
ToastUtils.showShort("连接失败");
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
} else if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
这个监听wifi的打开与关闭,与wifi的连接无关
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
switch (wifiState) {
case WifiManager.WIFI_STATE_DISABLED:
Log.d("WIFI状态", "wifiState:WIFI_STATE_DISABLED");
break;
case WifiManager.WIFI_STATE_DISABLING:
Log.d("WIFI状态", "wifiState:WIFI_STATE_DISABLING");
break;
case WifiManager.WIFI_STATE_ENABLED:
Log.d("WIFI状态", "wifiState:WIFI_STATE_ENABLED");
//监听到WIFI 已经开启,开始扫描
netSettingSwitch.setChecked(true);
results = WifiUtil.getScanResult(mWifiManager);
mWifiConfigurations = mWifiManager.getConfiguredNetworks();
loadingLl.setVisibility(View.VISIBLE);
break;
case WifiManager.WIFI_STATE_ENABLING:
Log.d("WIFI状态", "wifiState:WIFI_STATE_ENABLING");
ToastUtils.showShort("正在打开wifi...");
break;
case WifiManager.WIFI_STATE_UNKNOWN:
Log.d("WIFI状态", "wifiState:WIFI_STATE_UNKNOWN");
break;
}
} else if (intent.getAction().equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
SupplicantState state = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
int error = intent.getIntExtra(EXTRA_SUPPLICANT_ERROR, 0);
if (error == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE) {
ToastUtils.showShort("密码有误");
}
}
}
}
扫描wifi
mWifiManager.startScan();
List<ScanResult> results = mWifiManager.getScanResults();
获取当前Wifi的信息
/**
* descirption: 获取当前连接wifi名字 内存溢出?
*/
public static String getCurrentWifiSSID(Context context) {
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo info = wifiManager.getConnectionInfo();
return info != null ? info.getSSID() : "null";
}
连接WIfi,先根据传入的自定义对象保存的名字,密码,加密类型创建WifiConfiguration对象,再连接。
/**
* descirption: 创建要连接的WifiConfiguration
*/
public WifiConfiguration createConfiguration(AccessPoint ap) {
String SSID = ap.getSsid();
String password = ap.getPassword();
String encryptionType = ap.getEncryptionType();
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + SSID + "\"";
//判断当前连接的wifi保存了密码,清除wifi保存信息
WifiConfiguration tempConfig = isWifiSave(SSID);
if (tempConfig != null) {
mWifiManager.removeNetwork(tempConfig.networkId);
mWifiManager.saveConfiguration();
Log.d(TAG, "createConfiguration: 清除wifi保存信息");
}
if (encryptionType.contains("WEP")) {
Log.d(TAG, "createConfiguration: wep");
/**
* special handling according to password length is a must for wep
*/
int i = password.length();
if (((i == 10 || (i == 26) || (i == 58))) && (password.matches("[0-9A-Fa-f]*"))) {
config.wepKeys[0] = password;
} else {
config.wepKeys[0] = "\"" + password + "\"";
}
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
} else if (encryptionType.contains("WPA")) {
Log.d(TAG, "createConfiguration: wpa");
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
} else {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
}
return config;
}
//去连接
AccessPoint accessPoint = new AccessPoint(results.get(position).SSID, results.get(position).capabilities, et.getText().toString());
WifiConfiguration wifiConfiguration = createConfiguration(accessPoint);
//如果你设置的wifi是设备已经存储过的,那么这个networkId会返回小于0的值。
int networkId = mWifiManager.addNetwork(wifiConfiguration);
mWifiManager.enableNetwork(networkId, true);
注意点
WIfi开启需要时间,没有等到Wifi完全开启,立即去获取扫描列表是获取不到的。
Wifi扫描的列表会有重复和空的情况,需要处理。