1. 程式人生 > >Android wifi通訊 開發(wifi列表 +連線具體的wifi熱點+socket的獲取) 之 解決熱點連成功的時延問題

Android wifi通訊 開發(wifi列表 +連線具體的wifi熱點+socket的獲取) 之 解決熱點連成功的時延問題

寫這篇部落格前,說一些題外話:樓主是在一家做嵌入式研發的公司當然也有軟體研發,總之就是以軟硬體通訊為主把資料展示到前端,所
以不可避免的有 wifi 藍芽 網口 串列埠 等這些通訊媒介,網上的wifi通訊千篇一律下面我來總結我的wifi通訊 ,可以負責的告訴大家這是公
司目前在用的,起碼穩定是可以維持住的,歡迎大家指證。
轉載請附上本文連結squery的部落格連結地址: http://blog.csdn.net/shentanweilan9

wifi通訊設計到以下3點, 當然 肯定要建立一個工具類來對外暴露這三個以上的方法

  • wifi列表獲取展示
  • wifi連線
  • 獲取熱點ip建立socket

wifi連線通訊的工具類WifiAdmin

這個工具類中主要方法如下:

  1. startScan() 掃描wifi
  2. getWifiList() 獲取wifi列表
  3. connectToTarget() 連線wifi 這裡包含兩個一種是不知道加密方式的 另一種加密方式固定 兩種都需要密碼
  4. intToIp() 轉換成ip地址

整個類程式碼如下:

package com.pswx.squery.wifi_phone.utils;

import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;

import java.lang.reflect.Method;
import java.util.List;

/**
 * Created by squery on 2017/8/17.
 */
public class WifiAdmin {
    public static final String SSID = "test";
    public static final String PassWord = "12345678";
    private final DhcpInfo mDhcpInfo;
    private WifiManager mWifiManager;//wifimanager 物件
    private WifiInfo mWifiInfo; // 定義WifiInfo物件
    private List<ScanResult> mWifiList;  // 掃描出的網路連線列表
    private List<WifiConfiguration> mWifiConfiguration;  // 網路連線列表
    WifiManager.WifiLock mWifiLock;  // 定義一個WifiLock
    private static final int NOPASSWORD = 0;
    private static final int PASSWORD_WPA = 1;
    private static final int PASSWORD_WEP = 2;
    private static final int PASSWORD_WPA2 = 3;

    // 構造器
    public WifiAdmin(Context context) {
        // 取得WifiManager物件
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        // 取得WifiInfo物件
        mWifiInfo = mWifiManager.getConnectionInfo();
        mDhcpInfo = mWifiManager.getDhcpInfo();
    }

    //開啟wifi
    public void openWifi() {
        if (!mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(true);
        }
    }

    //關閉WIFI
    public void closeWifi() {
        if (mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(false);
        }
    }

    //建立熱點
    public void createAp() {
        if (mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(false);
        }
        try {
            WifiConfiguration apConfiguration = new WifiConfiguration();
            apConfiguration.SSID = WifiAdmin.SSID;
            apConfiguration.preSharedKey = WifiAdmin.PassWord;
            apConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
            method.invoke(mWifiManager, apConfiguration, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //關閉WiFi熱點
    public void closeWifiAp() {
        if (isWifiApEnabled()) {
            try {
                Method method = mWifiManager.getClass().getMethod("getWifiApConfiguration");
                method.setAccessible(true);
                WifiConfiguration config = (WifiConfiguration) method.invoke(mWifiManager);
                Method method2 = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
                method2.invoke(mWifiManager, config, false);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    //熱點開關是否開啟
    public boolean isWifiApEnabled() {
        try {
            Method method = mWifiManager.getClass().getMethod("isWifiApEnabled");
            method.setAccessible(true);
            return (Boolean) method.invoke(mWifiManager);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    // 檢查當前WIFI狀態
    public int checkState() {
        return mWifiManager.getWifiState();
    }

    // 鎖定WifiLock
    public void acquireWifiLock() {
        mWifiLock.acquire();
    }

    // 解鎖WifiLock
    public void releaseWifiLock() {
        // 判斷時候鎖定
        if (mWifiLock.isHeld()) {
            mWifiLock.acquire();
        }
    }


    // 建立一個WifiLock
    public void creatWifiLock() {
        mWifiLock = mWifiManager.createWifiLock("Test");
    }

    // 得到配置好的網路
    public List<WifiConfiguration> getConfiguration() {
        return mWifiConfiguration;
    }


    // 指定配置好的網路進行連線
    public void connectConfiguration(int index) {
        // 索引大於配置好的網路索引返回
        if (index > mWifiConfiguration.size()) {
            return;
        }
        // 連線配置好的指定ID的網路
        mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId, true);
    }

    /**
     * 掃描WIFI
     */
    public void startScan() {
        mWifiManager.startScan();
        // 得到掃描結果
        mWifiList = mWifiManager.getScanResults();
        // 得到配置好的網路連線
        mWifiConfiguration = mWifiManager.getConfiguredNetworks();
    }

    // 得到網路列表
    public List<ScanResult> getWifiList() {
        return mWifiList;
    }

    // 檢視掃描結果
    public StringBuilder lookUpScan() {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < mWifiList.size(); i++) {
            stringBuilder.append("Index_" + new Integer(i + 1).toString() + ":");
            // 將ScanResult資訊轉換成一個字串包
            // 其中把包括:BSSID、SSID、capabilities、frequency、level
            stringBuilder.append((mWifiList.get(i)).toString());
            stringBuilder.append("/n");
        }
        return stringBuilder;
    }

    // 得到接入點的BSSID
    public String getSSID() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getSSID();
    }

    // 得到MAC地址
    public String getMacAddress() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getMacAddress();
    }

    // 得到接入點的BSSID
    public String getBSSID() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getBSSID();
    }


    // 得到IP地址
    public int getIPAddress() {
        return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();
    }

    public int getServerIPAddress() {
        return (mWifiInfo == null) ? 0 : mDhcpInfo.serverAddress;
    }


    // 得到連線的ID
    public int getNetworkId() {
        return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();
    }

    // 得到WifiInfo的所有資訊包
    public String getWifiInfo() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.toString();
    }


    // 新增一個網路並連線
    public int addNetwork(WifiConfiguration wcg) {
        int wcgID = mWifiManager.addNetwork(wcg);
        boolean b = mWifiManager.enableNetwork(wcgID, true);
        mWifiManager.reassociate();
        LogUtils.e("b--" + b);
        return wcgID;
    }


    // 建立wificonfig
    public WifiConfiguration createWifiConfig(String SSID, String Password, int Type) {
        WifiConfiguration config = new WifiConfiguration();
        config.allowedAuthAlgorithms.clear();
        config.allowedGroupCiphers.clear();
        config.allowedKeyManagement.clear();
        config.allowedPairwiseCiphers.clear();

        config.allowedProtocols.clear();
        config.SSID = "\"" + SSID + "\"";


        //如果裝置大於6.0配置的時候就不需要雙引號,加了就連線不上了
        if (Build.VERSION.SDK_INT >= 23) {
            config.SSID = SSID;
        } else {
            config.SSID = "\"" + SSID + "\"";
        }

        WifiConfiguration tempConfig = isExsits(SSID);
        if (tempConfig != null) {// 去除自動儲存的 wifi
            disconnectWifi(tempConfig.networkId);
        }

        if (Type == NOPASSWORD) // WIFICIPHER_NOPASS
        {
            config.hiddenSSID = true;
            //config.wepKeys[0] = "";
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            //config.wepTxKeyIndex = 0;
        }
        if (Type == PASSWORD_WPA) // WIFICIPHER_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.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            config.status = WifiConfiguration.Status.ENABLED;
        }
        if (Type == PASSWORD_WEP) // WIFICIPHER_WEP
        {
            config.hiddenSSID = true;
            config.wepKeys[0] = "\"" + Password + "\"";
            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;
        }
        return config;
    }

    /**
     * 判斷wifi是否存在
     *
     * @param SSID
     * @return
     */
    public WifiConfiguration isExsits(String SSID) {
        List<WifiConfiguration> existingConfigs = mWifiManager.getConfiguredNetworks();
        if (!ArrayUtils.isEmpty(existingConfigs)) {
            for (WifiConfiguration existingConfig : existingConfigs) {
                if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
                    return existingConfig;
                }
            }
        }
        return null;
    }


    /**
     * 連線目標熱點
     *
     * @param scanResult 熱點的加密方式
     */
    public int connectToTarget(ScanResult scanResult, String password) {
        int mNetworkID = 0;
        int password_type = 0;
        WifiConfiguration mTargetWifiCfg;
        if (scanResult != null) {
            if (scanResult.capabilities.contains("WPA") || scanResult.capabilities.contains("wpa")) {
                password_type = PASSWORD_WPA;
            } else if (scanResult.capabilities.contains("WEP") || scanResult.capabilities.contains("wep")) {
                password_type = PASSWORD_WEP;
            } else if (scanResult.capabilities.contains("WPA2") || scanResult.capabilities.contains("wpa2")) {
                password_type = PASSWORD_WPA2;
            } else {
                password_type = NOPASSWORD;
            }
        }
        //LogUtils.e(scanResult.SSID+"::::::::::::::::::::" + password_type); //password_type=1 WPA
        mTargetWifiCfg = createWifiConfig(scanResult.SSID, password, password_type);// 獲得wificonfig
        mNetworkID = addNetwork(mTargetWifiCfg);
        return mNetworkID;
    }

    public int connectToTarget(String SSID, String password) {
        int mNetworkID = 0;
        int password_type = PASSWORD_WPA;
        WifiConfiguration mTargetWifiCfg;
        mTargetWifiCfg = createWifiConfig(SSID, password, password_type);// 獲得wificonfig
        mNetworkID = addNetwork(mTargetWifiCfg);
        return mNetworkID;
    }


    // 斷開指定ID的網路
    public void disconnectWifi(int netId) {
        mWifiManager.disableNetwork(netId);
        mWifiManager.disconnect();
        mWifiManager.removeNetwork(netId);
    }


    /**
     * 轉換IP地址
     *
     * @param i
     * @return
     */
    public String intToIp(int i) {
        return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF)
         + "." + ((i >> 24) & 0xFF);
    }
}

wifi列表獲取與展示

wifi列表的獲取,大致思路是:掃描附近wifi這個時候android系統會廣播一條通知SCAN_RESULTS_AVAILABLE_ACTION
注意:** 每掃描一次會廣播單條具體的通知,這個不同於wifi連線的通知(wifi連線通知 不同手機廣播的通知條數不同 通知型別也不同) ** 然後就是在廣播監聽裡面獲取到wifi列表 並更新adapter 也就是更新ui

傳送廣播 可以在一進入頁面就去掃描 也可以通過按鈕點選事件來觸發 程式碼如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setLayoutId(R.layout.activity_wifi_list);
        super.onCreate(savedInstanceState);
        initTitle(R.mipmap.left_arrow, R.string.back, "wifi列表", R.string.showelctric, 0);
        mWifiAdmin.openWifi();// 第一次進來時候顯示列表
        scanFlag = 1;
        mWifiAdmin.startScan();//開始掃描  傳送通知
        AppUtils.getInstance().showLoading(this);
    }

@OnClick({R.id.lear_left, R.id.btn_scan})
    void onClicks(View v) {
        int flag = 0;
        switch (v.getId()) {
            case R.id.lear_left:
                mConnectThread = null;
                finish();
                break;
            case R.id.btn_scan:
                mWifiAdmin.openWifi();
                mWifiAdmin.startScan();//開始掃描  傳送通知
                AppUtils.getInstance().showLoading(this);
                scanFlag = 1;
                break;
        }
    }

獲取wifi列表 並更新adapter 當然要註冊廣播監聽 頁面銷燬時候要登出廣播監聽 程式碼如下:

  @Override
    public void onResume() {// 註冊廣播
        super.onResume();
        registerReceiver(wifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

    }

    @Override
    public void onPause() {// 登出廣播
        super.onPause();
        unregisterReceiver(wifiScanReceiver);
    }

    private BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {//掃描完畢 更新Ui
                    AppUtils.getInstance().dismissLoading(WifiListActivity.this);
                    mWifiList.clear();
                    for (ScanResult scanResult : mWifiAdmin.getWifiList()) {
                        if (scanResult.SSID.startsWith("orbis_"))  // 過濾掉其他的,新增自己需要的wifi熱點
                            mWifiList.add(new CustomScan(false, scanResult));
                    }
                    //LogUtils.e("掃描的wifi列表:::\t" + mWifiAdmin.lookUpScan().toString());
                    mCommonAdapter.notifyDataSetChanged();
            }
        }
    };

wifi連線以及處理連線時延問題

wifi連線:就是連線具體的wifi熱點 這個很好理解
連線時延: 執行了連線程式碼 但是系統反應需要一段時間並不是馬上連線上wifi熱點的,這個時間就是連線時延
注意:如果大家不去考慮這個時延,那樣會出現很大的問題,那樣我們通過ip獲取建立socket時候會報一個socket untouched異常 具體異常名字我沒有詳記

  1. 首先我們不能通過mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");傳送的通知 以及捕
    獲具體通知進行wifi連線的處理 因為上述我已經闡明 通知的種類以及條數都不是固定的
  2. 我們不能認為mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");執行完瞬間就可以連
    接上wifi了不能單純的只根據這個 返回值是不是-1 來判斷連線wifi成功與否 當然返回-1 肯定是失敗 但是返回!-1的時
    候,由於連線是需要一段時間的,所以我們要加一個迴圈判斷是否連線成功的執行緒 這樣就可以在連線成功後進行我們自己的業務處理了.
  3. 迴圈判斷wifi連線成功的執行緒需要注意 1.迴圈次數 2.失敗後的介面反饋 可以起執行緒 那樣需要handler來傳遞
    失敗後的資訊 也可以用非同步任務

1和2 有兩種可能 通過wifi列表連線具體wifi的 也可以 直接連線某個wifi熱點的 程式碼如下:

@OnClick({R.id.btn_jump_temp, R.id.btn_scan})
    void onClicks(View v) {
        int flag = 0;
        switch (v.getId()) {
            case R.id.btn_scan:
                mWifiAdmin.openWifi();
                mWifiAdmin.startScan();//開始掃描  傳送通知
                AppUtils.getInstance().showLoading(this);
                scanFlag = 1;
                break;
        case R.id.btn_jump_temp:
            if (!ArrayUtils.isEmpty(mWifiList)) {
                for (CustomScan customScan : mWifiList) {
                    if (customScan.isSelected()) {
                        flag = 1;
                        if (!TextUtils.isEmpty(mEdiPhoneNum.getText())) {
                            int i = mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");// 傳送通知
                            if (i != -1) {
                                AppUtils.getInstance().showLoading(WifiListActivity.this);
                                jumpNum = 1;
                                mConnectThread = new ConnectThread();
                                mConnectThread.start();
                                mLearRight.setClickable(true);
                            } else {
                                Toast.makeText(WifiListActivity.this, "連線失敗", Toast.LENGTH_LONG).show();
                                mLearRight.setClickable(true);
                            }
                        }
                    }
                }
                if (flag == 0) {
                    Toast.makeText(this, "請先選擇一個wifi熱點", Toast.LENGTH_LONG).show();
                    mLearRight.setClickable(true);
                    return;
                }
            } else {
                Toast.makeText(this, "沒有wifi列表", Toast.LENGTH_LONG).show();
                mLearRight.setClickable(true);
                return;
            }
            break;
        }
    }

@OnClick({R.id.btn_jump_temp, R.id.btn_scan})
    void onClicks(View v) {
        int flag = 0;
        switch (v.getId()) {
            case R.id.btn_scan:
                mWifiAdmin.openWifi();
                mWifiAdmin.startScan();//開始掃描  傳送通知
                AppUtils.getInstance().showLoading(this);
                scanFlag = 1;
                break;
        case R.id.btn_jump_temp:
            if (!DeviceUtils.isWifiConnected(WifiListNullActivity.this)
                    || TextUtils.isEmpty(PreferenceSettingUtils.getIP(this))) {
                jumpNum = 1;
                AppUtils.getInstance().showLoading(WifiListNullActivity.this, "分機連線中...");
                 int i = mWifiAdmin.connectToTarget(PreferenceSettingUtils.getWifiName(WifiListNullActivity.this),
                                    PreferenceSettingUtils.getWifiPassword(WifiListNullActivity.this));// 傳送通知
                    if (i == -1) {
                    Toast.makeText(WifiListNullActivity.this, "附近沒有找到指定的分機", Toast.LENGTH_LONG).show();
                    }
                mConnectThread = new ConnectThread();
                mConnectThread.start();
            } else {
                startActivity(new Intent(this, DetctTempActivity.class));
            }
            break;
        }
    }

3.也有兩種 起執行緒非同步任務 程式碼如下:

    public class ConnectThread extends Thread {// 連線Thread
        @Override
        public void run() {
            int count = 0;
            while (true) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (DeviceUtils.isWifiConnected(WifiListActivity.this)) {//  連線成功
                    WifiAdmin wifiAdmin = new WifiAdmin(WifiListActivity.this);
                    String ip = wifiAdmin.intToIp(wifiAdmin.getServerIPAddress());
                    PreferenceSettingUtils.setIP(WifiListActivity.this, ip);
                    // 啟動net work
                    try {
                        Message msg = new Message();
                        msg.obj = "WIFI連線成功!";
                        msg.what = 1;
                        LinkDetectedHandler.sendMessage(msg);
                    } catch (Exception e) {
                        Message msg = new Message();
                        msg.obj = "獲取socket失敗";
                        msg.what = 0;
                        LinkDetectedHandler.sendMessage(msg);
                        e.printStackTrace();
                    }
                    break;
                }
                if (count++ > 10) {
                    Message msg = new Message();
                    msg.obj = "WIFI連線失敗!";
                    msg.what = -1;
                    LinkDetectedHandler.sendMessage(msg);
                    break;
                }
            }
            super.run();
        }
    }



public class SocketConnectTask extends AsyncTask<Integer, Void, Integer> {
    //後面尖括號內分別是引數(執行緒休息時間),進度(publishProgress用到),返回值 型別

    private Context mContext;
    private Thread mThread;


    public SocketConnectTask(Context context, Thread thread) {
        mContext = context;
        mThread = thread;
    }

    public SocketConnectTask(Context context) {
        mContext = context;
    }
    /*
        * 第一個執行的方法
        * 執行時機:在執行實際的後臺操作前,被UI 執行緒呼叫
        * 作用:可以在該方法中做一些準備工作,如在介面上顯示一個進度條,或者一些控制元件的例項化,這個方法可以不用實現。
        * @see android.os.AsyncTask#onPreExecute()
        */
    @Override
    protected void onPreExecute() {
        //AppUtils.getInstance().showLoading(mContext);
        super.onPreExecute();
    }

     /*
     * 執行時機:在onPreExecute 方法執行後馬上執行,該方法執行在後臺執行緒中
     * 作用:主要負責執行那些很耗時的後臺處理工作。該方法是抽象方法,子類必須實現。
     * @see android.os.AsyncTask#doInBackground(Params[])
     */

    @Override
    protected Integer doInBackground(Integer... params) {
        int count = 0;
        while (true) {
            try {
                Thread.sleep(2000);
                if (DeviceUtils.isWifiConnected(mContext)) {
                    Thread.sleep(1000);
                    WifiAdmin wifiAdmin = new WifiAdmin(mContext);
                    String ip = wifiAdmin.intToIp(wifiAdmin.getServerIPAddress());
                    PreferenceSettingUtils.setIP(mContext, ip);
                    return 1;
                }
                if (count++ > 10) return -1;

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /*
    * 執行時機:在doInBackground 執行完成後,將被UI 執行緒呼叫
    * 作用:後臺的計算結果將通過該方法傳遞到UI 執行緒,並且在介面上展示給使用者
    * result:上面doInBackground執行後的返回值,所以這裡是"執行完畢"
    * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
    */
    @Override
    protected void onPostExecute(Integer result) {
        //AppUtils.getInstance().dismissLoading(mContext);
        switch (result) {
            case 1:
                if (mThread != null) {
                    mThread.start();
                }
                Toast.makeText(mContext, "wifi連線成功", Toast.LENGTH_SHORT).show();
                break;
            case -1:
                Toast.makeText(mContext, "wifi連線異常", Toast.LENGTH_SHORT).show();
                break;
        }

        super.onPostExecute(result);
    }
}

總結:wifi連線就這些了 其主要問題 就是在連線到熱點 建立socket的時候一定要注意這個時延 不能去通過通知的捕獲來處理業務邏輯socket建立的成功與否 是在wifi熱點必須連線成功的前提下。下面章節我可能會敘述一些 wifi通訊協議以及通訊中執行緒中處理業務的問題。如有疑問可以聯絡我。