這週來寫關於Android 中 Wifi操作的部分
操作Wifi連線的APP商店裡面偏少並不多
想當然爾自然這項功能也沒有很多人寫...應該啦
至少我找資料都是對岸的資料壓倒性多數QQQQQQQQQ(跟繁體中文比)
那麼今天的內容就是要來實做一個Wifi搜尋、連線與斷線的功能
但是因為如果一篇全講因為想偷懶篇幅過長,所以將會分為上下兩篇敘述
上篇的內容會講述如何讓手機搜尋Wifi,並顯示搜尋結果在畫面上
下篇則是如何將使用者所選取的Wifi進行連線,並在跳出視窗後即刻斷線
那麼來看一下今天的示範
功能描述的話大致就是按下搜尋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. 準備工作
這是今天的專案內容
其實都跟Wifi搜尋沒有什麼關係,主要都是給RecyclerView用的
比較需要注意的是請打開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喔!
然後是介面...真的沒有特別哪裡要注意
直接給你吧
<?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>
<?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的部分我就直接貼了
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的部分才是今天的重點
來吧~上全部再回頭解釋
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
留言列表