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掃描的列表會有重複和空的情況,需要處理。