這週來寫關於Android 中 Wifi操作的部分

操作Wifi連線的APP商店裡面偏少並不多

想當然爾自然這項功能也沒有很多人寫...應該啦

 

至少我找資料都是對岸的資料壓倒性多數QQQQQQQQQ(跟繁體中文比)

那麼今天的內容就是要來實做一個Wifi搜尋、連線與斷線的功能

但是因為如果一篇全講因為想偷懶篇幅過長,所以將會分為上下兩篇敘述

上篇的內容會講述如何讓手機搜尋Wifi,並顯示搜尋結果在畫面上

下篇則是如何將使用者所選取的Wifi進行連線,並在跳出視窗後即刻斷線

 

 

那麼來看一下今天的示範

Screenshot_20200919-200505_WifiSearch&ConnectDemo

功能描述的話大致就是按下搜尋wifi的按鈕後,程式就會開始掃描Wifi

並且每5秒鐘會更新一次列表

最後如果退出程序的話掃描機制則會停止,直到下次掃描

 

理解的話Github奉上

->https://github.com/thumbb13555/WifiSearchConnectDemo

 

此外,今天的以及接下來下篇的內容將會使用到一些RecyclerView的操作

如果還不了解RecyclerView操作的朋友們,建議可以先參考我以前寫的有關RecyclerView的文章後再回來看喔

->碼農日常-『Android studio』基本RecyclerView用法

->碼農日常-『Android studio』基本RecyclerView 用法-2 基本版下拉更新以及點擊事件

->碼農日常-『Android studio』基本RecyclerView 用法-3 RecyclerView上下滑動排序與側滑刪除(RecyclerView Swipe)

->碼農日常-『Android studio』基本RecyclerView 用法-4 左滑顯示Button Menu

->碼農日常-『Android studio』進階RecyclerView 用法-5 RecyclerView item混合介面

 

還有,在執行掃描時會用到Handler重複執行的機制

不了解的同學也可以參考一下我這篇喔

->碼農日常-『Android studio』Android背景執行之Thread、Handler及AsyncTask的基礎用法

 

 


 

1. 準備工作

 

這是今天的專案內容

截圖 2020-09-19 下午9.01.56

 

其實都跟Wifi搜尋沒有什麼關係,主要都是給RecyclerView用的

比較需要注意的是請打開AndroidManifest.xml

然後加入粉底字的部分

 

AndroidManifest.xml

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jetec.wifisearchconnectdemo">

    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <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" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

這邊是宣告這隻程式需要的權限

其中比較需要注意的是,現在搜尋Wifi都需要開啟定位權限

所以也記得幫使用者檢查是否有開啟Wifi喔

 

然後是介面...真的沒有特別哪裡要注意

直接給你吧

 

activity_main.xml

截圖 2020-09-19 下午9.23.50

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="搜尋Wifi"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerVIew_SearchResult"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

result_item.xml

截圖 2020-09-19 下午9.23.23

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/textView_SSID"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="SSID"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        android:textStyle="bold"
        app:layout_constraintEnd_toStartOf="@+id/textView_Level"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView_BSSID"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:text="BSSID"
        app:layout_constraintEnd_toEndOf="@+id/textView_Level"
        app:layout_constraintStart_toStartOf="@+id/textView_SSID"
        app:layout_constraintTop_toBottomOf="@+id/textView_SSID" />

    <TextView
        android:id="@+id/textView_Cap"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Capabilities"
        app:layout_constraintEnd_toEndOf="@+id/textView_Level"
        app:layout_constraintStart_toStartOf="@+id/textView_BSSID"
        app:layout_constraintTop_toBottomOf="@+id/textView_BSSID" />

    <TextView
        android:id="@+id/textView_Frequency"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:text="Frequency"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@+id/textView_Level"
        app:layout_constraintStart_toStartOf="@+id/textView_BSSID"
        app:layout_constraintTop_toBottomOf="@+id/textView_Cap" />

    <TextView
        android:id="@+id/textView_Level"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:text="Level"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/textView_BSSID"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@+id/textView_SSID" />
</androidx.constraintlayout.widget.ConstraintLayout>

 


 

2. 設置RecyclerAdapter

 

設置RecyclerView部分請容我再重申一次~

不會的請參考我之前寫的文章XDDDD

->碼農日常-『Android studio』基本RecyclerView用法

 

所以RecyclerView的Adapter的部分我就直接貼了

 

RecyclerViewAdapter.java

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    List<ScanResult> list = new ArrayList<>();

    public void addItem(List<ScanResult> list){
        this.list = list;
        notifyDataSetChanged();
    }


    public class ViewHolder extends RecyclerView.ViewHolder {

        private TextView tvSSID,tvBSSID,tvCap,tvFren,tvLevel;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            tvSSID = itemView.findViewById(R.id.textView_SSID);
            tvBSSID = itemView.findViewById(R.id.textView_BSSID);
            tvCap = itemView.findViewById(R.id.textView_Cap);
            tvFren = itemView.findViewById(R.id.textView_Frequency);
            tvLevel = itemView.findViewById(R.id.textView_Level);

        }
    }
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.result_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        ScanResult scanResult = list.get(position);
        holder.tvSSID.setText(scanResult.SSID);
        holder.tvBSSID.setText("位址: "+scanResult.BSSID);
        holder.tvCap.setText("加密方式: "+scanResult.capabilities);
        holder.tvFren.setText("訊號頻率: "+scanResult.frequency);
        holder.tvLevel.setText("訊號強度: "+scanResult.level);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }


}

 


 

3. 好啦,這裡才是重點

 

哈哈,真的這裡才是重點:P

因為前面的部分其實就是寫關於顯示的部分而已

這裏MainActivity的部分才是今天的重點

來吧~上全部再回頭解釋

 

MainActivity.java

 

public class MainActivity extends AppCompatActivity {
    WifiManager wifiManager;
    RecyclerViewAdapter mAdapter;
    /**賦予handler重複執行掃描工作*/
    @SuppressLint("HandlerLeak")
    Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            wifiScan();
        }
    };
    /**建立Runnable, 使掃描可被重複執行*/
    Runnable searchWifi = new Runnable() {
        @Override
        public void run() {
            handler.sendEmptyMessage(1);
            handler.postDelayed(this,5000);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**取得定位權限(搜尋Wifi要記得開定位喔)*/
        getPermission();
        /**取得WifiManager*/
        wifiManager  = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
        /**設置顯示回傳的Recyclerview*/
        RecyclerView recyclerView = findViewById(R.id.recyclerVIew_SearchResult);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        /**為Recyclerview每個項目之間加入分隔線*/
        recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.HORIZONTAL));
        mAdapter = new RecyclerViewAdapter();
        recyclerView.setAdapter(mAdapter);
        /**按下按鈕則執行掃描*/
        Button btScan = findViewById(R.id.button);
        btScan.setOnClickListener(v -> {
            handler.post(searchWifi);
        });
    }

    @Override
    protected void onStop() {
        super.onStop();
        /**跳出畫面則停止掃描*/
        handler.removeCallbacks(searchWifi);
    }

    private void getPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                        != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                    this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 100
            );
        }//if
    }

    private void wifiScan(){
        new Thread(()->{
            /**設置Wifi回傳可被使用*/
            wifiManager.setWifiEnabled(true);
            /**開始掃描*/
            wifiManager.startScan();
            /**取得掃描到的Wifi*/
            List<ScanResult> wifiList = wifiManager.getScanResults();
            runOnUiThread(()->{
                /**更新掃描後的列表*/
                mAdapter.addItem(wifiList);
            });
        }).start();
        /**以下為掃描到的Wifi可被取得的相關資訊, 供參考*/

//                for (int i = 0; i <wifiList.size() ; i++) {
//                    ScanResult s = wifiList.get(i);
//                    Log.d(TAG, "run: "+s.SSID+"\n"
//                    +s.BSSID+"\n"
//                    +s.capabilities+"\n"
//                    +s.centerFreq0+"\n"
//                    +s.centerFreq1+"\n"
//                    +s.channelWidth+"\n"
//                    +s.frequency+"\n"
//                    +s.level+"\n"
//                    +s.operatorFriendlyName+"\n"
//                    +s.timestamp+"\n"
//                    +s.venueName+"\n");
//
//                }
    }

 

好,來挑重點講了

 

首先在搜尋Wifi前,要先取得Wifi管理者的這個類別

wifiManager  = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);

 

然後開始掃描Wifi

/**設置Wifi回傳可被使用*/
wifiManager.setWifiEnabled(true);
/**開始掃描*/
wifiManager.startScan();

 

最後,取得掃描結果

/**取得掃描到的Wifi*/
List<ScanResult> wifiList = wifiManager.getScanResults();

 

至於能取得什麼樣的回傳,在這邊便可參考

->https://blog.csdn.net/male09/article/details/70792657

 

我在註解裡也有寫,打開註解也可以看到摟

for (int i = 0; i <wifiList.size() ; i++) {
    ScanResult s = wifiList.get(i);
    Log.d(TAG, "run: "+s.SSID+"\n"
    +s.BSSID+"\n"
    +s.capabilities+"\n"
    +s.centerFreq0+"\n"
    +s.centerFreq1+"\n"
    +s.channelWidth+"\n"
    +s.frequency+"\n"
    +s.level+"\n"
    +s.operatorFriendlyName+"\n"
    +s.timestamp+"\n"
    +s.venueName+"\n");

}

Okay, 理論上寫到這邊應該就可以操作了

如果按搜尋後搜不到,也有可能是定位沒開啟的緣故

正如我前面所述,記得要幫使用者開啟定位哦:D

 


 

結語

 

今天的內容是上篇,算是比較簡單的部分

至於下篇的部分就會比較難囉呵呵呵呵

老實說,我當時自己在做的時候我也很驚訝居然這麼簡單

當時一開始覺得大概要寫什麼監聽器啊...或者一堆麻煩的操作

 

結果沒想到只有一行...呃

List<ScanResult> wifiList = wifiManager.getScanResults();

呃...哈哈哈XD

說真的,我下次開始才是我當時覺得很痛苦的部分

這部分真的是很簡單的部分了

那希望大家可以在這篇(跟下篇)拿到自己想要的資訊

下篇見~感謝各位點閱 :D

 

Best 30+ Thank You fun on 9GAG

 
arrow
arrow

    碼農日常 發表在 痞客邦 留言(8) 人氣()