週記1

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章