Android項目實戰(三十四):藍牙4.0 BLE 多設備連接

来源:http://www.cnblogs.com/xqxacm/archive/2017/09/07/7488444.html
-Advertisement-
Play Games

最近項目有個需求,手機設備連接多個藍牙4.0 設備 並獲取這些設備的數據。 查詢了很多資料終於實現,現進行總結。 從零開始實現一個連接多個藍牙4.0 設備並獲取數據的 Demo 註:如果不想看實現過程的,直接看最下麵的demo源碼即可,或每一步後相關操作步驟的完整代碼。 一、Demo需求 1、搜索設 ...


  最近項目有個需求,手機設備連接多個藍牙4.0 設備 並獲取這些設備的數據。

  查詢了很多資料終於實現,現進行總結。

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------

    從零開始實現一個連接多個藍牙4.0 設備並獲取數據的 Demo

  註:如果不想看實現過程的,直接看最下麵的demo源碼即可,或每一步後相關操作步驟的完整代碼。

  

  一、Demo需求

    1、搜索設備 , 選擇多個要連接的設備。

    2、開始連接,顯示數據。

  

  二、項目知識儲備

    項目中需要用到的三方:

    1、RecyclerView 

       列表,用於顯示掃描得到的所有藍牙設備

    2、BaseRecyclerViewAdapterHelper

       Recyclerview 幫助框架,快速實現列表操作 

    3、eventbus

      用於消息傳遞,獲取到藍牙傳送的數據之後,刷新界面顯示數據時使用

    4、bluetooth-manager

      藍牙4.0框架

    5、permissionsdispatcher

     許可權管理,適配6.0+設備

     

    添加依賴 gradle.bulld文件

    compile 'com.android.support:appcompat-v7:25.3.1'    compile 'com.blakequ.androidblemanager:bluetooth-manager-lib:2.1.5'
    compile 'com.github.hotchemi:permissionsdispatcher:2.1.3'
    compile 'de.greenrobot:eventbus:2.4.0'
    compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.18'
    compile 'com.android.support:design:25.3.1'

 

  三、項目實現,佈局文件

    1、demo中一共用到兩個activity 對應兩個佈局文件

 

     先看掃描設備界面

     包含:

      1、一個列表,顯示 所有掃描到的設備的MAC地址,點擊狀態在 ''已選擇' or '‘未選擇’ 之間改變,表明當前設備有沒有加入到需要連接的設備集合中

     2、掃描按鈕

     3、結束掃描按鈕

     4、完成選擇按鈕,將選擇的設備MAC地址傳回 

     

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_select_device"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.maiji.magkareble40.SelectDeviceActivity">


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

    </android.support.v7.widget.RecyclerView>


    <Button
        android:id="@+id/btnScan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="開始掃描"
        />

    <Button
        android:id="@+id/btnStopScan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止掃描"
        />
    <Button
        android:id="@+id/btnOk"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="完成選擇設備"
        />
</LinearLayout>

     

    連接界面。

    包含:

    1、選擇需要連接的感測器設備 按鈕

    2、開始連接 按鈕

    3、數據展示

    

    

 

    佈局文件代碼:

  

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.maiji.magkareble40.XBleActivity">


    <Button
        android:id="@+id/btnSelectDevice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="選擇需要連接的感測器設備"
        />

    <Button
        android:id="@+id/btnStartConnect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="開始連接"
        />

    <TextView
        android:id="@+id/txtContentMac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text=""/>

</LinearLayout>

 

    四、Activity實現

    1、掃描 設備 選擇設備Activity

    (1)、變數聲明

   private Button btnScan;        //開始掃描按鈕
    private Button btnStopScan;   //停止掃描按鈕
    private Button btnOk;   //選擇好了需要連接的mac設備

    BluetoothScanManager scanManager ;  // 設備掃描管理器


    /* 列表相關 */
    private RecyclerView recyclerView ; //列表
    private ScanDeviceAdapter adapter;  //設備掃描適配器
    private ArrayList<String> deviceMacs ; // 數據源 : 所有掃描到的設備mac地址

    private ArrayList<String> selectDeviceMacs; // 選擇的需要連接的設備的mac集合

 

    關鍵代碼:

    (1)、藍牙掃描的初始化設置

/**
     * 初始化藍牙相關配置
     */
    private void initBle() {
        scanManager = BleManager.getScanManager(this);

        scanManager.setScanOverListener(new ScanOverListener() {
            @Override
            public void onScanOver() {
            }
        });


        scanManager.setScanCallbackCompat(new ScanCallbackCompat() {
            @Override
            public void onBatchScanResults(List<ScanResultCompat> results) {
                super.onBatchScanResults(results);
            }

            @Override
            public void onScanFailed(final int errorCode) {
                super.onScanFailed(errorCode);

            }

            @Override
            public void onScanResult(int callbackType, ScanResultCompat result) {
                super.onScanResult(callbackType, result);
                //scan result
                // 只有當前列表中沒有該mac地址的時候 添加
                if (!deviceMacs.contains(result.getDevice().getAddress())) {
                    deviceMacs.add(result.getDevice().getAddress());
                    adapter.notifyDataSetChanged();
                }
            }
        });

    }
藍牙掃描設置初始化

    (2)、開始掃描按鈕 操作

//                scanManager.startCycleScan(); //不會立即開始,可能會延時
                scanManager.startScanNow(); //立即開始掃描

    (3)、停止掃描按鈕 操作

 // 如果正在掃描中 停止掃描
                if (scanManager.isScanning()) {
                    scanManager.stopCycleScan();
                }

    (4)、RecyclerView初始化 ,點擊事件操作

    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

        // 列表相關初始化
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new ScanDeviceAdapter(deviceMacs);

        adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                if (!selectDeviceMacs.contains(deviceMacs.get(position))){
                    //如果改item的mac不在已選中的mac集合中 說明沒有選中,添加進已選中mac集合中,狀態改為"已選擇"
                    selectDeviceMacs.add(deviceMacs.get(position));
                    ((TextView)view.findViewById(R.id.txtState)).setText("已選擇");
                }else {
                    selectDeviceMacs.remove(deviceMacs.get(position));
                    ((TextView)view.findViewById(R.id.txtState)).setText("未選擇");
                }
            }
        });

        recyclerView.setAdapter(adapter);

  

  activity全部代碼:

package com.maiji.magkareble40;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.blakequ.bluetooth_manager_lib.BleManager;
import com.blakequ.bluetooth_manager_lib.BleParamsOptions;
import com.blakequ.bluetooth_manager_lib.connect.ConnectConfig;
import com.blakequ.bluetooth_manager_lib.scan.BluetoothScanManager;
import com.blakequ.bluetooth_manager_lib.scan.ScanOverListener;
import com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat.ScanCallbackCompat;
import com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat.ScanFilterCompat;
import com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat.ScanResultCompat;
import com.chad.library.adapter.base.BaseQuickAdapter;

import java.util.ArrayList;
import java.util.List;

/**
* @author xqx
* @email [email protected]
* blog:http://www.cnblogs.com/xqxacm/
* createAt 2017/9/6
* description:  掃描藍牙設備  選擇需要連接的感測器
*/

public class SelectDeviceActivity extends Activity implements View.OnClickListener {

    private Button btnScan;        //開始掃描按鈕
    private Button btnStopScan;   //停止掃描按鈕
    private Button btnOk;   //選擇好了需要連接的mac設備

    BluetoothScanManager scanManager ;


    /* 列表相關 */
    private RecyclerView recyclerView ; //列表
    private ScanDeviceAdapter adapter;
    private ArrayList<String> deviceMacs ; // 數據源 : 所有掃描到的設備mac地址

    private ArrayList<String> selectDeviceMacs; // 選擇的需要連接的設備的mac集合

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_select_device);

        deviceMacs = new ArrayList<>();
        selectDeviceMacs = new ArrayList<>();
        initView();
        initEvent();
        initBle();


    }

    /**
     * 初始化藍牙相關配置
     */
    private void initBle() {
        scanManager = BleManager.getScanManager(this);

        scanManager.setScanOverListener(new ScanOverListener() {
            @Override
            public void onScanOver() {
            }
        });


        scanManager.setScanCallbackCompat(new ScanCallbackCompat() {
            @Override
            public void onBatchScanResults(List<ScanResultCompat> results) {
                super.onBatchScanResults(results);
            }

            @Override
            public void onScanFailed(final int errorCode) {
                super.onScanFailed(errorCode);

            }

            @Override
            public void onScanResult(int callbackType, ScanResultCompat result) {
                super.onScanResult(callbackType, result);
                //scan result
                // 只有當前列表中沒有該mac地址的時候 添加
                if (!deviceMacs.contains(result.getDevice().getAddress())) {
                    deviceMacs.add(result.getDevice().getAddress());
                    adapter.notifyDataSetChanged();
                }
            }
        });

    }

    private void initEvent() {
        btnScan.setOnClickListener(this);
        btnStopScan.setOnClickListener(this);
        btnOk.setOnClickListener(this);
    }

    private void initView() {
        btnScan = (Button) findViewById(R.id.btnScan);
        btnStopScan = (Button) findViewById(R.id.btnStopScan);
        btnOk = (Button) findViewById(R.id.btnOk);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

        // 列表相關初始化
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new ScanDeviceAdapter(deviceMacs);

        adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                if (!selectDeviceMacs.contains(deviceMacs.get(position))){
                    //如果改item的mac不在已選中的mac集合中 說明沒有選中,添加進已選中mac集合中,狀態改為"已選擇"
                    selectDeviceMacs.add(deviceMacs.get(position));
                    ((TextView)view.findViewById(R.id.txtState)).setText("已選擇");
                }else {
                    selectDeviceMacs.remove(deviceMacs.get(position));
                    ((TextView)view.findViewById(R.id.txtState)).setText("未選擇");
                }
            }
        });

        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btnScan:
                //開始 掃描
//                scanManager.startCycleScan(); //不會立即開始,可能會延時
                scanManager.startScanNow(); //立即開始掃描
                break;

            case R.id.btnStopScan:
                // 如果正在掃描中 停止掃描
                if (scanManager.isScanning()) {
                    scanManager.stopCycleScan();
                }
                break;
            case R.id.btnOk:
                Intent intent = new Intent();
                intent.putExtra("data",selectDeviceMacs);                // 設置結果,併進行傳送
                this.setResult(1, intent);
                this.finish();
                break;

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 如果正在掃描中 停止掃描
        if (scanManager.isScanning()) {
            scanManager.stopCycleScan();
        }
    }
}
SelectDeviceActivity.class

  適配器相關代碼:

package com.maiji.magkareble40;

import android.widget.ImageView;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;

import java.util.ArrayList;

/**
* @author xqx
* @email [email protected]
* blog:http://www.cnblogs.com/xqxacm/
* createAt 2017/9/6
* description:  掃描得到的藍牙設備列表適配器
*/

public class ScanDeviceAdapter extends BaseQuickAdapter<String , BaseViewHolder> {

    public ScanDeviceAdapter(ArrayList<String> datas) {
        super(R.layout.item_device, datas);
    }

    @Override
    protected void convert(BaseViewHolder helper, String item) {
        helper.setText(R.id.txtMac,item);

    }
}
ScanDeviceAdapter.class

  適配器佈局代碼:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:layout_marginTop="16dp"
    android:layout_marginBottom="16dp"
    >

    <TextView
        android:id="@+id/txtMac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:layout_centerVertical="true"
        />

    <TextView
        android:id="@+id/txtState"
        android:text="未選擇"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#fff"
        android:layout_alignParentBottom="true"
        ></View>
</RelativeLayout>
item_device.xml

 

    2、連接多設備,獲取數據並展示Activity 

    (1)、變數聲明

    private Button btnSelectDevice ;  //選擇需要綁定的設備
    private Button btnStartConnect ;  //開始連接按鈕

    private TextView txtContentMac ; //獲取到的數據解析結果顯示

    private final int REQUEST_CODE_PERMISSION = 1; // 許可權請求碼  用於回調

    MultiConnectManager multiConnectManager ;  //多設備連接
    private BluetoothAdapter bluetoothAdapter;   //藍牙適配器


    private ArrayList<String> connectDeviceMacList ; //需要連接的mac設備集合
    ArrayList<BluetoothGatt> gattArrayList; //設備gatt集合

  

    2、關鍵代碼

    1、許可權適配

    註意:不止藍牙許可權,位置許可權也需要打開

/**
     * @author xqx
     * @email [email protected]
     * blog:http://www.cnblogs.com/xqxacm/
     * createAt 2017/8/30
     * description:  許可權申請相關,適配6.0+機型 ,藍牙,文件,位置 許可權
     */

    private String[] allPermissionList = {Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN,
            Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE
    };


    /**
     * 遍歷出需要獲取的許可權
     */
    private void requestWritePermission() {
        ArrayList<String> permissionList = new ArrayList<>();
        // 將需要獲取的許可權加入到集合中  ,根據集合數量判斷 需不需要添加
        for (int i = 0; i < allPermissionList.length; i++) {
            if (PackageManager.PERMISSION_DENIED == ContextCompat.checkSelfPermission(this, allPermissionList[i])){
                permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
        }

        String permissionArray[] = new String[permissionList.size()];
        for (int i = 0; i < permissionList.size(); i++) {
            permissionArray[i] = permissionList.get(i);
        }
        if (permissionList.size() > 0)
            ActivityCompat.requestPermissions(this, permissionArray, REQUEST_CODE_PERMISSION);
    }


    /**
     * 許可權申請的回調
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == REQUEST_CODE_PERMISSION){
            if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                //用戶同意使用write

            }else{
                //用戶不同意,自行處理即可
                Toast.makeText(XBleActivity.this,"您取消了許可權申請,可能會影響軟體的使用,如有問題請退出重試",Toast.LENGTH_SHORT).show();
            }
        }
    }
許可權適配

    2、藍牙開啟、連接等 初始化設置

/**
     * 對藍牙的初始化操作
     */
    private void initConfig() {
        multiConnectManager = BleManager.getMultiConnectManager(this);
        // 獲取藍牙適配器

        try {
            // 獲取藍牙適配器
            bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            //
            if (bluetoothAdapter == null) {
                Toast.makeText(this, "藍牙不可用", Toast.LENGTH_LONG).show();
                return;
            }

            // 藍牙沒打開的時候打開藍牙
            if (!bluetoothAdapter.isEnabled())
                bluetoothAdapter.enable();
        }catch (Exception err){};
        BleManager.setBleParamsOptions(new BleParamsOptions.Builder()
                .setBackgroundBetweenScanPeriod(5 * 60 * 1000)
                .setBackgroundScanPeriod(10000)
                .setForegroundBetweenScanPeriod(2000)
                .setForegroundScanPeriod(10000)
                .setDebugMode(BuildConfig.DEBUG)
                .setMaxConnectDeviceNum(7)            //最大可以連接的藍牙設備個數
                .setReconnectBaseSpaceTime(1000)
                .setReconnectMaxTimes(Integer.MAX_VALUE)
                .setReconnectStrategy(ConnectConfig.RECONNECT_LINE_EXPONENT)
                .setReconnectedLineToExponentTimes(5)
                .setConnectTimeOutTimes(20000)
                .build());
    }
initBle

    3、開始連接操作

 /**
     * 連接需要連接的感測器
     * @param
     */
    private void connentBluetooth(){

        String[] objects = connectDeviceMacList.toArray(new String[connectDeviceMacList.size()]);
        multiConnectManager.addDeviceToQueue(objects);
        multiConnectManager.addConnectStateListener(new ConnectStateListener() {
            @Override
            public void onConnectStateChanged(String address, ConnectState state) {
                switch (state){
                    case CONNECTING:
                        Log.i("connectStateX","設備:"+address+"連接狀態:"+"正在連接");
                        break;
                    case CONNECTED:
                        Log.i("connectStateX","設備:"+address+"連接狀態:"+"成功");
                        break;
                    case NORMAL:
                        Log.i("connectStateX","設備:"+address+"連接狀態:"+"失敗");
                        break;
                }
            }
        });

        /**
         * 數據回調
         */

        multiConnectManager.setBluetoothGattCallback(new BluetoothGattCallback() {
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);
                dealCallDatas(gatt , characteristic);
            }
        });

        multiConnectManager.setServiceUUID("0000ffe5-0000-1000-8000-00805f9a34fb");
        multiConnectManager.addBluetoothSubscribeData(
                new BluetoothSubScribeData.Builder().setCharacteristicNotify(UUID.fromString("0000ffe4-0000-1000-8000-00805f9a34fb")).build());

        //還有讀寫descriptor
        //start descriptor(註意,在使用時當回調onServicesDiscovered成功時會自動調用該方法,所以只需要在連接之前完成1,3步即可)
        for (int i = 0; i < gattArrayList.size(); i++) {
            multiConnectManager.startSubscribe(gattArrayList.get(i));
        }

        multiConnectManager.startConnect();

    }

    /**
     * 處理回調的數據
     * @param gatt
     * @param characteristic
     */

    float[][] floats = new float[7][30];

    private void dealCallDatas(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        int position = connectDeviceMacList.indexOf(gatt.getDevice().getAddress());
        //第一個感測器數據
        byte[] value = characteristic.getValue();
        if (value[0] != 0x55) {
            //開頭不是0x55的數據刪除
            return;
        }
        switch (value[1]) {
            case 0x61:
                //加速度數據
                floats[position][3] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f * 16;   //x軸
                floats[position][4] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f * 16;   //y軸
                floats[position][5] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f * 16;   //z軸
                //角速度數據
                floats[position][6] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f * 2000;  //x軸
                floats[position][7] = ((((short) value[11]) << 8) | ((short) value[10] & 0xff)) / 32768.0f * 2000;  //x軸
                floats[position][8] = ((((short) value[13]) << 8) | ((short) value[12] & 0xff)) / 32768.0f * 2000;  //x軸
                break;
            case 0x62:
                //四元素
                floats[position][13] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f; // q1
                floats[position][14] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2
                floats[position][15] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3
                floats[position][16] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f; // q4
                //電池電壓
                floats[position][21] = (float) 1.2 * 4 * (((value[11] << 8) | value[10]) + 	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JSON的android應用實例 Json線上解析器 1、普通鍵值對象 2、Json數組對象 3、Json數組對象 ...
  • Android icon尺寸 密度範圍 切圖比例(以mdpi為基準) 切圖比例(以xxxhdpi為基準) 圖標尺寸 外間距 ((圖標尺寸-圖片尺寸)/2) ...
  • JSON 教程 說明: 可以自己百度JSON解析器來查看自己寫的JSON是否正確。 參考: JSON 教程http://www.w3school.com.cn/json/index.asp JSON 教程 | 菜鳥教程http://www.runoob.com/json/json-tutorial. ...
  • 一、安裝環境 1、安裝VS2017 2、安裝Xamarin擴展包 3、安裝NDK 工具欄中,一個為AVD(模擬器管理)一個為NDK(管理Andoid SDK) 3.1 先打開NDK,在Tools\Options中設置代理(因是要從Goolge下載,所以需要設置代理) ,這裡我使用的是mirrors. ...
  • 1.Manifest合併 在Android studio編譯項目時,無論你使用了幾個Module都會把所有Manifest最終合併成一個,需要我們註意的是application標簽下這個幾個屬性引用的值。 如果多個Module有相同名字的資源,在編譯時會不知道引用哪個資源而導致Manifest合併失 ...
  • post方式請求數據 分析: 1、將請求方式改成post conn.setRequestMethod("POST"); 2、設置連接可以輸出 conn.setDoOutput(true); 3、告訴伺服器客戶端提交的數據類型(表單數據) conn.setRequestProperty("Conten ...
  • 在這個競爭激烈的設計行業里,只滿足自己當下的成績是愚昧的,比你優秀的人還在努力,你怎麼可以躺屍呢?作為一名UX設計師,提高自己的設計水平是為了給自己增值,讓更多人能看到你優秀的作品。然而要如何提高自身的設計水平呢? 1. 好記性不如爛筆頭—多畫圖 畫圖不是為了好看美觀,是為了能保留設計師腦袋裡的想法 ...
  • FileUriExposedException 在給app做版本升級的時候,先從伺服器下載新版本的apk文件到sdcard路徑,然後調用安裝apk的代碼,一般寫法如下: 這樣的寫法在Android7.0版本之前是沒有任何問題,只要給一個apk文件路徑就能打開安裝。但是在Android7.0版本上會報 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...