【HMS Core】构建SplitBill应用集成多个HMS Core服务,助力您更好的了解华为生态组成

news2024/10/7 8:23:32

一、介绍

Duration: 3:00

总览

通过构建本次的SplitBill应用,您可以更好地了解华为生态的组成部分,包括认证服务、云存储和云数据库等Serverless服务。此外您还可以了解如何使用近距离数据通信服务的Nearby Connection功能分享文件。无需使用现金,SplitBill应用能够实现用户与其他任意用户共同支付账单。

您将建立什么

在本次的Codelab中,您将建立一款SplitBill应用,并使用到华为认证服务、Network Kit、近距离数据通信服务、云数据库和云存储的接口。该应用为用户提供端到端的群收款服务。使用该应用,多位用户可以组成一个群,共同完成账单支付。

您将学到什么

  • 使用认证服务登录应用(华为账号和手机号认证方式)

  • 创建群。

  • 添加成员到群。

  • 添加账单。

  • 上传用户头像至云存储和在云数据库中更新头像。

  • 使用近距离通信服务分享账单文件。

二、您需要什么

Duration: 2:00

开发环境

  • 一台装有Windows10操作系统的台式或笔记本电脑。

  • 搭载HMC Core(APK)5.0.0.300或以上版本的华为手机。

  • 通过验证的华为账号。

设备要求

一部用于测试的安卓手机或模拟器。

三、集成准备

Duration: 10:00

在集成SDK之前,您需要完成以下准备:

  • 在AppGallery Connect上创建应用。

  • 创建一个安卓项目。

  • 生成签名证书。

  • 生成签名证书指纹。

  • 配置签名证书指纹。

  • 添加应用包名,保存配置文件。

  • 在项目级build.gradle文件中添加AppGallery Connect插件和Maven仓。

  • 在Android Studio中配置签名证书。

详情请见HUWEI HMS Core集成准备。

说明:在上述准备工作前您需要注册成为一名开发者。

四、开通服务

Duration: 4:00

首先,您需要在AppGallery Connect上启用HMS Core的相关服务。启用前,请完成以下准备工作:

1、登录AppGallery Connect,点击“项目设置”中“API管理”页签,开通如下服务的API权限。

  • 认证服务(华为账号认证方式)

  • 云数据库

  • 云存储

  • 近距离通信服务

  • Network Kit

cke_22898.png

cke_24882.png

cke_27208.png

cke_29271.png

说明:以上API权限默认已开通。如未开通,请手动开通。

2、在弹出的页面设置数据处理位置。

cke_37905.png

五、集成SDK

Duration: 4:00

您需要将云数据库SDK集成到您的Android Studio项目中。

1、登录AppGallery Connect,点击“我的项目”。

2、点击您的项目,在顶部的应用下拉列表中选择应用。

3、点击“项目设置”>“常规”。在“应用”区域,下载agconnect-services.json文件。

cke_64778.png

4、将agconnect-services.json文件复制到您的项目中。

cke_71966.png

5、在Android Studio中,打开项目级build.gradle文件,前往allprojects > repositories,在buildscript的repositories部分配置Maven仓地址。

cke_80468.png

cke_84522.png

6、在buildscript的dependencies部分配置AppGallery Connect插件。

buildscript { 
    dependencies { 
        classpath 'com.huawei.agconnect:agcp:<version>'
    } 
}

7、在应用级build.gradle文件中添加AppGallery Connect插件。

apply plugin: 'com.huawei.agconnect'

8、(可选)在Application类中的onCreate方法里添加初始化代码。

if (AGConnectInstance.getInstance() == null) {
AGConnectInstance.initialize(getApplicationContext()); 
}

9、在应用级build.gradle文件里的dependencies部分添加所需依赖路径。

implementation 'com.huawei.agconnect:agconnect-auth:<version>'
implementation 'com.huawei.hms:hwid:<version>'
implementation 'com.huawei.hms:nearby: <version>'
implementation"com.huawei.agconnect:agconnect-storage:<version>"
 implementation'com.huawei.agconnect:agconnect-cloud-database:<version>'

六、设计UI

Duration: 5:00

创建以下界面:登录、建群、群列表、添加账单、账单和收支标签页、账单详情、账单分享详情和收到的账单文件列表。

用户登录界面

cke_136790.png

群列表和建群界面

cke_147222.pngcke_150144.pngcke_154068.pngcke_159027.png

七、前提准备

Duration: 5:00

认证服务

认证服务SDK能够让您快速便捷地在您的应用上实现用户注册和登录功能。

  1. 登录AppGallery Connect,点击“我的项目”。

  2. 点击您的项目。

  3. 选择“构建”,单击“认证服务”。若您首次使用认证服务,点击“立即开通”。

  4. 单击“认证方式”,在“操作”栏中启用华为账号认证方式。

cke_185653.png

Cloud DB

使用云数据库服务,您需要启用云数据库,创建存储区和云数据库对象所需字段。

1、登录AppGallery Connect,点击“我的项目”。

2、点击您的项目。

3、选择“构建”,点击“云数据库”。若您首次使用云数据库,点击“立即开通”。

4、在弹出页面设置数据处理位置。

5、单击“新增”,进入创建对象类型页面。

cke_218809.png

6、设置对象类型,单击“下一步”。

7、单击“+新增字段”添加字段,单击“下一步”。

8、添加所需索引。

9、设置各角色权限。

cke_248030.png

10、单击“确定”。返回对象类型列表,查看已创建的对象类型。

11、单击“导出”。

cke_282257.png

12、选择文件格式,此处选择文件类型为Java,文件格式为Android。输入包名,点击“确定”。对象类型文件将被作为zip文件下载

13、提取zip中的文件至项目的model包里。

1)点击“存储区”页签。

2)单击“新增”,进入创建存储区页面。

cke_345998.png

云存储

使用云存储服务,您需要启用云存储,并在开发前完成下述操作。

1、启用云存储后,创建存储实例,单击“下一步”。

2、创建安全策略,控制是否允许未经认证的用户访问存储。单击“完成”。

cke_365965.png

3、完成上述操作后,您即可使用云存储服务。

八、实现功能

Duration: 15:00

完成准备工作后,集成认证服务、云数据库、云存储、Network Kit和近距离通信服务到您的应用中。

在已设计好的登录页面添加如下代码,实现华为账号登录按钮。

activityAuthBinding.tvGetcode.setOnClickListener(this);
activityAuthBinding.btnHuaweiId.setOnClickListener(this);
HuaweiIdAuthParamsHelper huaweiIdAuthParamsHelper = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM);
scopeList = new ArrayList<>();
scopeList.add(new Scope(HwIDConstant.SCOPE.ACCOUNT_BASEPROFILE));
scopeList.add(new Scope(HwIDConstant.SCOPE.SCOPE_ACCOUNT_EMAIL));
scopeList.add(new Scope(HwIDConstant.SCOPE.SCOPE_MOBILE_NUMBER));
scopeList.add(new Scope(HwIDConstant.SCOPE.SCOPE_ACCOUNT_PROFILE));
huaweiIdAuthParamsHelper.setScopeList(scopeList);
HuaweiIdAuthParams authParams = huaweiIdAuthParamsHelper.setAccessToken().setMobileNumber().createParams();
 service = HuaweiIdAuthManager.getService(this, authParams);

 Button Click:
loginViewModel.signInWithHuaweiId(AuthActivity.this, service).observe(AuthActivity.this, new Observer<SignInResult>() {
     @Override
     public void onChanged(SignInResult user) {
         activityAuthBinding.authProgressBar.setVisibility(View.VISIBLE);
         progress();
         loginSuccess (user);
     }
 });

实现OnActivityResult。

huaweiSignIn.launch(service.getSignInIntent());
ActivityResultLauncher<Intent> huaweiSignIn = AuthActivity.authActivity.registerForActivityResult(
         new ActivityResultContracts.StartActivityForResult(),
         new ActivityResultCallback<ActivityResult>() {
             @Override
             public void onActivityResult(ActivityResult result) {

                 if (result.getResultCode() == Activity.RESULT_OK) {

                     Task<AuthAccount> authAccountTask = AccountAuthManager.parseAuthResultFromIntent(result.getData());
                     if (authAccountTask.isSuccessful()) {
                         AuthAccount authAccount = authAccountTask.getResult();

                         AGConnectAuthCredential credential = HwIdAuthProvider.credentialWithToken(authAccount.getAccessToken());
                         AGConnectAuth.getInstance().signIn(credential).addOnSuccessListener(new OnSuccessListener<SignInResult>() {
                             @Override
                             public void onSuccess(SignInResult signInResult) {
                                 // onSuccess
                                 Common.showToast("log in", AuthActivity.authActivity);
                                 authenticatedUserMutableLiveData.setValue(signInResult);

                             }
                         })
                                 .addOnFailureListener(new OnFailureListener() {
                                     @Override
                                     public void onFailure(Exception e) {
                                         Common.showToast("fail", AuthActivity.authActivity);
                                     }
                                 });
                     }
                 }
             }
         });

创建wrapper类,用于初始化云数据库存储区,插入新用户和验证已知用户等数据库操作。

public class CloudDBZoneWrapper {      private static final String TAG = "CloudDBZoneWrapper";     private static final String DB_NAME = "SplitBillSampleApp";     private AGConnectCloudDB mCloudDB;     private CloudDBZone mCloudDBZone;     private CloudDBZoneConfig mConfig;  /**
  SplitBillApplication
* Get instance of Cloud DB zone wrapper to initiate cloud DB  *  * @return mCloudDBZoneWrapper  */ public CloudDBZoneWrapper getCloudDBZoneWrapper() {     if (mCloudDBZoneWrapper != null) {         return mCloudDBZoneWrapper;     }     mCloudDBZoneWrapper = new CloudDBZoneWrapper();     return mCloudDBZoneWrapper; }
 
/**  * Get CloudDB task to open AGConnectCloudDB  */ public Task<CloudDBZone> openCloudDBZoneV2() {     mConfig = new CloudDBZoneConfig(DB_NAME,             CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,             CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);     mConfig.setPersistenceEnabled(true);     Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true);     return openDBZoneTask; }
/**
  * 调用AGConnectCloudDB.closeCloudDBZone
  */
public void closeCloudDBZone() {
     try {
         mRegister.remove();
         mCloudDB.closeCloudDBZone(mCloudDBZone);
     } catch (AGConnectCloudDBException e) {
         Log.w(TAG, "CloudDBZone: " + e.getMessage());
     }
}
 
/**
  * 云数据库中插入好友数据
  */
public MutableLiveData<Boolean> upsertExpenseData(Friends friends) {
     if (mCloudDBZone == null) {
         Log.w(TAG, "CloudDBZone is null, try re-open it");
     }

     Task<Integer> upsertTask = mCloudDBZone.executeUpsert(friends);
     upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
         @Override
         public void onSuccess(Integer cloudDBZoneResult) {
             friendsUpdateSuccess.postValue(true);
         }
     }).addOnFailureListener(new OnFailureListener() {
         @Override
         public void onFailure(Exception e) {
             friendsUpdateSuccess.postValue(false);
         }
     });
     return friendsUpdateSuccess;
 }
/**
  * 云数据库中获取好友列表
  */
 
public MutableLiveData<Integer> getFriendsIdLiveData() {
     CloudDBZoneQuery<Friends> snapshotQuery = CloudDBZoneQuery.where(Friends.class);
     Task<Long> countQueryTask = mCloudDBZone.executeCountQuery(snapshotQuery, FriendsEditFields.CONTACT_ID,
             CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
     countQueryTask.addOnSuccessListener(new OnSuccessListener<Long>() {
         @Override
         public void onSuccess(Long aLong) {
             Log.i(TAG, "The total number of groups is " + aLong);
             friendsIdLiveData.postValue((int) (aLong + 1));
         }
     }).addOnFailureListener(new OnFailureListener() {
         @Override
         public void onFailure(Exception e) {
             Log.w(TAG, "Count query is failed: " + Log.getStackTraceString(e));
         }
     });

     return friendsIdLiveData;
 }
/**
  * 云数据库中获取好友列表
 
  * 从数据库中添加,获取群或账单数据使用相似方法。
*/
private void insertGroupDataInDB(Group group){
     groupViewModel.upsertGroupData(group).observe(getActivity(), new Observer<Boolean>() {
         @Override
         public void onChanged(Boolean aBoolean) {
             if (aBoolean.equals(true)) {
                 progressStats = true;
                 Toast.makeText(getContext(), getString(R.string.add_group_success) + " " + group.getName(), Toast.LENGTH_LONG).show();
                 Navigation.findNavController(fragmentCreateGroupBinding.getRoot()).navigate(R.id.navigation_activity);
             } else {
                 Toast.makeText(getContext(), getString(R.string.add_group_failed), Toast.LENGTH_LONG).show();
                 progressStats = false;
             }
         }
     });
 }
 
 
 
   
添加账单数据:
private void addExpenseData() {
     Expense expense = new Expense();
     String strExpenseName = expenseName.getText().toString();
     String strPaidBy = spinner.getSelectedItem().toString();
     String strExpenseAmount = expenseDesc.getText().toString();
     expense.setId(0);
     expense.setName(strExpenseName);

     expense.setAmount(Float.parseFloat((strExpenseAmount) + ".00"));
     expense.setPaid_user_id(0);
     expense.setStatus(1);
     expenseViewModel.upsertExpenseData(expense).observe(getActivity(), new Observer<Boolean>() {
         @Override
         public void onChanged(Boolean aBoolean) {
             if (aBoolean.equals(true)) {
                 Toast.makeText(getContext(), getString(R.string.add_expense_success) + " " + strExpenseName, Toast.LENGTH_LONG).show();
                 //TODO : back key
                 
             } else {
                 Toast.makeText(getContext(), getString(R.string.add_expense_failed), Toast.LENGTH_LONG).show();
             }
         }
     });
 }

数据插入完成后,接着使用近距离通信服务提供的类。

NearbyAgent:
/*
  * 华为技术有限公司版权所有 保留一切权利,
     授权于Apache License 2.0版本(以下简称“许可证”)。
    使用此许可证时,须遵循其规定。
     您可以通过以下途径获取许可证的副本:
      http://www.apache.org/licenses/LICENSE-2.0
    除非得到适用法律或书面同意,
    许可证许可范围内所提供的软件均按“现状”提供,
    而不做任何明示或暗示的保证。
    关于许可证中特定语言的权限和限制,
    请参见许可证。
  */

package com.huawei.codelabs.splitbill.ui.main.helper;

import android.Manifest;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.pdf.PdfDocument;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.view.View;


import androidx.core.app.ActivityCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.huawei.codelabs.splitbill.R;
import com.huawei.codelabs.splitbill.databinding.FragmentAccountBinding;
import com.huawei.codelabs.splitbill.databinding.FragmentFileDetailsBinding;
import com.huawei.codelabs.splitbill.databinding.FragmentSendExpenseDetailsBinding;
import com.huawei.codelabs.splitbill.ui.main.activities.MainActivity;
import com.huawei.codelabs.splitbill.ui.main.adapter.DeviceAdapter;
import com.huawei.codelabs.splitbill.ui.main.adapter.FilesAdapter;
import com.huawei.codelabs.splitbill.ui.main.adapter.FriendsListAdapter;
import com.huawei.codelabs.splitbill.ui.main.models.Device;
import com.huawei.codelabs.splitbill.ui.main.models.Files;
import com.huawei.hms.hmsscankit.ScanUtil;
import com.huawei.hms.hmsscankit.WriterException;
import com.huawei.hms.ml.scan.HmsBuildBitmapOption;
import com.huawei.hms.ml.scan.HmsScan;
import com.huawei.hms.ml.scan.HmsScanAnalyzerOptions;
import com.huawei.hms.nearby.Nearby;
import com.huawei.hms.nearby.StatusCode;
import com.huawei.hms.nearby.discovery.BroadcastOption;
import com.huawei.hms.nearby.discovery.ConnectCallback;
import com.huawei.hms.nearby.discovery.ConnectInfo;
import com.huawei.hms.nearby.discovery.ConnectResult;
import com.huawei.hms.nearby.discovery.DiscoveryEngine;
import com.huawei.hms.nearby.discovery.Policy;
import com.huawei.hms.nearby.discovery.ScanEndpointCallback;
import com.huawei.hms.nearby.discovery.ScanEndpointInfo;
import com.huawei.hms.nearby.discovery.ScanOption;
import com.huawei.hms.nearby.transfer.Data;
import com.huawei.hms.nearby.transfer.DataCallback;
import com.huawei.hms.nearby.transfer.TransferEngine;
import com.huawei.hms.nearby.transfer.TransferStateUpdate;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class NearbyAgent {
     public static final int REQUEST_CODE_SCAN_ONE = 0X01;
     private static final String[] REQUIRED_PERMISSIONS =
             new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
                     Manifest.permission.ACCESS_FINE_LOCATION,
                     Manifest.permission.ACCESS_WIFI_STATE,
                     Manifest.permission.CHANGE_WIFI_STATE,
                     Manifest.permission.BLUETOOTH,
                     Manifest.permission.BLUETOOTH_ADMIN,
                     Manifest.permission.READ_EXTERNAL_STORAGE,
                     Manifest.permission.WRITE_EXTERNAL_STORAGE,
                     Manifest.permission.CAMERA};
     private static final int REQUEST_CODE_REQUIRED_PERMISSIONS = 1;
     private final String TAG = "Nearby_Agent";
     private final String mFileServiceId = "NearbyAgentFileService";
     private final String mEndpointName = android.os.Build.DEVICE;
     int lineYAxis = 350;
     FragmentAccountBinding fragmentAccountBinding;
     FragmentSendExpenseDetailsBinding fragmentSendExpenseDetailsBinding;
     FragmentFileDetailsBinding fragmentFileDetailsBinding;
     ArrayList<Files> filesArrayList;
     FilesAdapter groupAdapter;
     private MainActivity mContext = null;
     private TransferEngine mTransferEngine = null;
     private DiscoveryEngine mDiscoveryEngine = null;
     private List<File> mFiles = new ArrayList<>();
     private String mRemoteEndpointId;
     private String mRemoteEndpointName;
     private String mScanInfo;
     private final ScanEndpointCallback mDiscCb =
             new ScanEndpointCallback() {
                 @Override
                 public void onFound(String endpointId, ScanEndpointInfo discoveryEndpointInfo) {
                     if (discoveryEndpointInfo.getName().equals(mScanInfo)) {

                         Log.d(TAG, "Found endpoint:" + discoveryEndpointInfo.getName() + ". Connecting.");
                         mDiscoveryEngine.requestConnect(mEndpointName, endpointId, mConnCbRcver);
                     }
                 }

                 @Override
                 public void onLost(String endpointId) {
                     Log.d(TAG, "Lost endpoint.");
                 }
             };
     private String mRcvedFilename = null;
     private Bitmap mResultImage;
     private String mFileName;
     private long mStartTime = 0;
     private float mSpeed = 60;
     private String mSpeedStr = "60";
     private boolean isTransfer = false;
     private final DataCallback mDataCbSender =
             new DataCallback() {
                 @Override
                 public void onReceived(String endpointId, Data data) {
                     if (data.getType() == Data.Type.BYTES) {
                         String msg = new String(data.asBytes(), UTF_8);
                         if (msg.equals("Receive Success")) {
                             Log.d(TAG, "Received ACK. Send next.");
                             sendOneFile();
                         }
                     }
                 }

                 @Override
                 public void onTransferUpdate(String string, TransferStateUpdate update) {
                     if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS) {
                     } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_IN_PROGRESS) {
                         showProgressSpeedSender(update);
                     } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_FAILURE) {
                         Log.d(TAG, "Transfer failed.");
                     } else {
                         Log.d(TAG, "Transfer cancelled.");
                     }
                 }
             };
     private final ConnectCallback mConnCbRcver =
             new ConnectCallback() {
                 @Override
                 public void onEstablish(String endpointId, ConnectInfo connectionInfo) {
                     Log.d(TAG, "Accept connection.");
                     mRemoteEndpointName = connectionInfo.getEndpointName();
                     mRemoteEndpointId = endpointId;
                     mDiscoveryEngine.acceptConnect(endpointId, mDataCbRcver);
                 }

                 @Override
                 public void onResult(String endpointId, ConnectResult result) {
                     if (result.getStatus().getStatusCode() == StatusCode.STATUS_SUCCESS) {
                         Log.d(TAG, "Connection Established. Stop Discovery.");
                         mDiscoveryEngine.stopBroadcasting();
                         mDiscoveryEngine.stopScan();
                         fragmentFileDetailsBinding.tvMainDesc.setText("Connected.");
                     }
                 }

                 @Override
                 public void onDisconnected(String endpointId) {
                     Log.d(TAG, "Disconnected.");
                     if (isTransfer == true) {
                         fragmentSendExpenseDetailsBinding.tvMainDesc.setVisibility(View.GONE);
                         fragmentSendExpenseDetailsBinding.tvMainDesc.setText("Connection lost.");
                     }
                 }
             };
     private Data incomingFile = null;
     private final DataCallback mDataCbRcver =
             new DataCallback() {
                 @Override
                 public void onReceived(String endpointId, Data data) {
                     if (data.getType() == Data.Type.BYTES) {
                         String msg = new String(data.asBytes(), UTF_8);
                         mRcvedFilename = msg;
                         Log.d(TAG, "received filename: " + mRcvedFilename);
                         isTransfer = true;
                         fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder("Receiving file ").append(mRcvedFilename).append(" from ").append(mRemoteEndpointName + ".").toString());
                         fragmentFileDetailsBinding.pbMainDownload.setVisibility(View.VISIBLE);
                     } else if (data.getType() == Data.Type.FILE) {
                         incomingFile = data;
                     } else {
                         Log.d(TAG, "received stream. ");
                     }
                 }

                 public  File getLastModified(String directoryFilePath)
                 {
                     File directory = new File(directoryFilePath);
                     File[] files = directory.listFiles(File::isFile);
                     long lastModifiedTime = Long.MIN_VALUE;
                     File chosenFile = null;

                     if (files != null)
                     {
                         for (File file : files)
                         {
                             if (file.lastModified() > lastModifiedTime)
                             {
                                 chosenFile = file;
                                 lastModifiedTime = file.lastModified();
                             }
                         }
                     }

                     return chosenFile;
                 }
                 @Override
                 public void onTransferUpdate(String string, TransferStateUpdate update) {
                     if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS) {
                     } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_IN_PROGRESS) {
                         showProgressSpeedReceiver(update);
                         if (update.getBytesTransferred() == update.getTotalBytes()) {
                             Log.d(TAG, "File transfer done. Rename File.");
                             renameFile();
                             Log.d(TAG, "Send Ack.");
                             fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder("Transfer success. Speed: ").append(mSpeedStr).append("MB/s. \nView the File at /Sdcard/Download/Nearby"));
                             mTransferEngine.sendData(mRemoteEndpointId, Data.fromBytes("Receive Success".getBytes(StandardCharsets.UTF_8)));
                             isTransfer = false;
                             Files files = new Files();
                             File file=  getLastModified(Environment.getExternalStorageDirectory().getPath() + Constants.DOWNLOAD_PATH);
                             files.setFileName(file.getName());
                             files.setFilePath(new File(file.getAbsolutePath()));
                             filesArrayList.add(files);
                             groupAdapter.notifyDataSetChanged();
                         }
                     } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_FAILURE) {
                         Log.d(TAG, "Transfer failed.");
                     } else {
                         Log.d(TAG, "Transfer cancelled.");
                     }
                 }
             };
     private ArrayList<Device> deviceList;
     private final ConnectCallback mConnCbSender =
             new ConnectCallback() {
                 @Override
                 public void onEstablish(String endpointId, ConnectInfo connectionInfo) {
                     Log.d(TAG, "Accept connection.");
                     mDiscoveryEngine.acceptConnect(endpointId, mDataCbSender);
                     fragmentSendExpenseDetailsBinding.rcDevice.setHasFixedSize(true);
                     fragmentSendExpenseDetailsBinding.rcDevice.setLayoutManager(new LinearLayoutManager(mContext));
                     Device device = new Device();
                     device.setDeviceName(connectionInfo.getEndpointName());
                     deviceList.add(device);
                     DeviceAdapter deviceAdapter = new DeviceAdapter(deviceList);
                     fragmentSendExpenseDetailsBinding.rcDevice.setAdapter(deviceAdapter);
                     deviceAdapter.notifyDataSetChanged();
                     mRemoteEndpointName = connectionInfo.getEndpointName();
                     mRemoteEndpointId = endpointId;
                 }

                 @Override
                 public void onResult(String endpointId, ConnectResult result) {
                     if (result.getStatus().getStatusCode() == StatusCode.STATUS_SUCCESS) {
                         Log.d(TAG, "Connection Established. Stop discovery. Start to send file.");
                         mDiscoveryEngine.stopScan();
                         mDiscoveryEngine.stopBroadcasting();
                         sendOneFile();
                         fragmentSendExpenseDetailsBinding.barcodeImage.setVisibility(View.GONE);
                         fragmentSendExpenseDetailsBinding.tvMainDesc.setText(new StringBuilder("MB/s. \nView the File at /Sdcard/Download/Nearby").append(mFileName).append(" to ").append(mRemoteEndpointName).append("."));
                         fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
                     }
                 }

                 @Override
                 public void onDisconnected(String endpointId) {
                     Log.d(TAG, "Disconnected.");
                     if (isTransfer == true) {
                         fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
                         fragmentSendExpenseDetailsBinding.tvMainDesc.setText("Connection lost.");
                     }
                 }
             };

     public NearbyAgent(MainActivity context) {
         mContext = context;
         mDiscoveryEngine = Nearby.getDiscoveryEngine(context);
         deviceList = new ArrayList<>();
         mTransferEngine = Nearby.getTransferEngine(context);
         if (context instanceof MainActivity) {
             ActivityCompat.requestPermissions(context, REQUIRED_PERMISSIONS, REQUEST_CODE_REQUIRED_PERMISSIONS);
         }
     }

     public NearbyAgent(MainActivity context, FragmentAccountBinding fragmentAccountBinding) {
         this.fragmentAccountBinding = fragmentAccountBinding;
         this.mContext = context;
         mDiscoveryEngine = Nearby.getDiscoveryEngine(context);
         mTransferEngine = Nearby.getTransferEngine(context);
         if (context instanceof MainActivity) {
             ActivityCompat.requestPermissions(context, REQUIRED_PERMISSIONS, REQUEST_CODE_REQUIRED_PERMISSIONS);
         }


     }

     public static String getFileRealNameFromUri(Context context, Uri fileUri) {
         if (context == null || fileUri == null) {
             return Constants.UnknownFile;
         }
         DocumentFile documentFile = DocumentFile.fromSingleUri(context, fileUri);
         if (documentFile == null) {
             return Constants.UnknownFile;
         }
         return documentFile.getName();
     }

     private void showProgressSpeedSender(TransferStateUpdate update) {
         long transferredBytes = update.getBytesTransferred();
         long totalBytes = update.getTotalBytes();
         long curTime = System.currentTimeMillis();
         Log.d(TAG, "Transfer in progress. Transferred Bytes: "
                 + transferredBytes + " Total Bytes: " + totalBytes);
         fragmentSendExpenseDetailsBinding.pbMainDownload.setProgress((int) (transferredBytes * 100 / totalBytes));
         if (mStartTime == 0) {
             mStartTime = curTime;
         }
         if (curTime != mStartTime) {
             mSpeed = ((float) transferredBytes) / ((float) (curTime - mStartTime)) / 1000;
             java.text.DecimalFormat myformat = new java.text.DecimalFormat("0.00");
             mSpeedStr = myformat.format(mSpeed);
             fragmentSendExpenseDetailsBinding.tvMainDesc.setText(new StringBuilder("Transfer in Progress. Speed: ").append(mSpeedStr).append("MB/s."));
         }
         if (transferredBytes == totalBytes) {
             mStartTime = 0;
         }
     }

     public void loadScanCode(Bitmap mResultImage) {
         
         fragmentSendExpenseDetailsBinding.barcodeImage.setVisibility(View.VISIBLE);
         fragmentSendExpenseDetailsBinding.barcodeImage.setImageBitmap(mResultImage);
     }

     public File createPdf(List<FriendsListAdapter.FriendsUI> friendsUIList, Bitmap scaledImageBitmap, FragmentActivity activity) {
         //创建一个新的document。
         PdfDocument document = new PdfDocument();
         //创建页面描述。
         PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(Constants.PAGEWIDTH, Constants.PAGEHEIGHT, 1).create();
         //开启一个页面。
         PdfDocument.Page page = document.startPage(pageInfo);
         Canvas canvas = page.getCanvas();
         Paint paint = new Paint();
         canvas.drawBitmap(scaledImageBitmap, 0, 0, paint);
         paint.setTextAlign(Paint.Align.CENTER);
         paint.setTextSize(50);
         paint.setColor(activity.getResources().getColor(android.R.color.black));
         paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
         canvas.drawText("Invoice" + System.currentTimeMillis(), Constants.PAGEWIDTH / 2, 260, paint);
         paint.setStrokeWidth(2f);
         
         canvas.drawLine(Constants.PAGEWIDTH / 2, 300, 550, 300, paint);
         canvas.drawText("Amount" + "     " + "Participants", Constants.PAGEWIDTH / 2, 400, paint);
         lineYAxis = 450;
         for (FriendsListAdapter.FriendsUI friendsUI : friendsUIList) {
             canvas.drawText(friendsUI.getFriendsName() + "     " + friendsUI.getAmount() + "", Constants.PAGEWIDTH / 2, Constants.LINEYAXIS + 50, paint);
             lineYAxis = lineYAxis + 50;
         }
         
         document.finishPage(page);
         
         pageInfo = new PdfDocument.PageInfo.Builder(Constants.PAGEWIDTH, Constants.PAGEHEIGHT, 2).create();
         page = document.startPage(pageInfo);
         document.finishPage(page);
         //写入document内容。
         String directory_path = Environment.getExternalStorageDirectory().getPath() + Constants.CREATEINVOICE;
         File file = new File(directory_path);
         if (!file.exists()) {
             file.mkdirs();
         }
         String targetPdf = new StringBuilder().append(directory_path).append("invoice").append(System.currentTimeMillis()).append(".pdf").toString();
         File filePath = new File(targetPdf);
         try {
             document.writeTo(new FileOutputStream(filePath));

         } catch (IOException e) {
             Log.e("main", "error " + e.toString());

         }
         //关闭document。
         document.close();
         return filePath;
     }

     public void sendFile(File file, FragmentSendExpenseDetailsBinding fragmentSendExpenseDetailsBinding) {
         this.fragmentSendExpenseDetailsBinding = fragmentSendExpenseDetailsBinding;

         init();
         mFiles.add(file);
         sendFilesInner();
     }

     public void sendFiles(List<File> files) {
         init();
         mFiles = files;
         sendFilesInner();
     }

     public void sendFolder(File folder) {
         init();
         File[] subFile = folder.listFiles();
         for (int i = 0; i < subFile.length; i++) {
             if (!subFile[i].isDirectory()) {
                 mFiles.add(subFile[i]);
                 Log.d(TAG, "Travel folder: " + subFile[i].getName());
             }
         }
         sendFilesInner();
     }

     private void sendFilesInner() {

         /* 生成bitmap */
         try {
             //生成barcode。
             HmsBuildBitmapOption options = new HmsBuildBitmapOption.Creator().setBitmapMargin(1).setBitmapColor(Color.BLACK).setBitmapBackgroundColor(Color.WHITE).create();
             mResultImage = ScanUtil.buildBitmap(mEndpointName, HmsScan.QRCODE_SCAN_TYPE, Constants.BARCODE_SIZE, Constants.BARCODE_SIZE, options);
             loadScanCode(mResultImage);
         } catch (WriterException e) {
             Log.e(TAG, e.toString());
         }
         /* 开始广播 */
         BroadcastOption.Builder advBuilder = new BroadcastOption.Builder();
         advBuilder.setPolicy(Policy.POLICY_P2P);
         mDiscoveryEngine.startBroadcasting(mEndpointName, mFileServiceId, mConnCbSender, advBuilder.build());
         Log.d(TAG, "Start Broadcasting.");

     }

     public void receiveFile(FragmentFileDetailsBinding fragmentFileDetailsBinding, ArrayList<Files> filesArrayList, FilesAdapter groupAdapter) {
         /* 扫描bitmap */
         this.fragmentFileDetailsBinding = fragmentFileDetailsBinding;
         this.filesArrayList=filesArrayList;
         this.groupAdapter=groupAdapter;
         Log.d("TAG", "start");
         init();
         HmsScanAnalyzerOptions options = new HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.QRCODE_SCAN_TYPE, HmsScan.DATAMATRIX_SCAN_TYPE).create();
         ScanUtil.startScan(mContext, REQUEST_CODE_SCAN_ONE, options);
         Log.d("TAG", "Sent");
     }

     public void onScanResult(Intent data) {
         if (data == null) {
             Log.d("TAG", "fail");
             fragmentFileDetailsBinding.tvMainDesc.setText("Scan Failed.");
             return;
         }
         /* 保存设备名称*/
         HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT);
         mScanInfo = obj.getOriginalValue();
         /* 开始扫描*/
         ScanOption.Builder scanBuilder = new ScanOption.Builder();
         scanBuilder.setPolicy(Policy.POLICY_P2P);
         mDiscoveryEngine.startScan(mFileServiceId, mDiscCb, scanBuilder.build());
         Log.d(TAG, "Start Scan.");
         fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder().append("Connecting to ").append(mScanInfo).append("..."));
     }

     private void sendOneFile() {
         Data filenameMsg = null;
         Data filePayload = null;
         isTransfer = true;
         Log.d(TAG, "Left " + mFiles.size() + " Files to send.");
         if (mFiles.isEmpty()) {
             Log.d(TAG, "All Files Done. Disconnect");
             fragmentSendExpenseDetailsBinding.tvMainDesc.setText(R.string.all_files_sent);
             fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
             fragmentSendExpenseDetailsBinding.tvHeading.setVisibility(View.GONE);
             mDiscoveryEngine.disconnectAll();
             isTransfer = false;
             return;
         }
         try {
             mFileName = mFiles.get(0).getName();
             filePayload = Data.fromFile(mFiles.get(0));
             mFiles.remove(0);
         } catch (FileNotFoundException e) {
             Log.e(TAG, "File not found", e);
             return;
         }
         filenameMsg = Data.fromBytes(mFileName.getBytes(StandardCharsets.UTF_8));
         Log.d(TAG, "Send filename: " + mFileName);
         mTransferEngine.sendData(mRemoteEndpointId, filenameMsg);
         Log.d(TAG, "Send Payload.");
         mTransferEngine.sendData(mRemoteEndpointId, filePayload);
     }

     private void renameFile() {
         if (incomingFile == null) {
             Log.d(TAG, "incomingFile is null");
             return;
         }
         File rawFile = incomingFile.asFile().asJavaFile();
         Log.d(TAG, "raw file: " + rawFile.getAbsolutePath());
         File targetFileName = new File(rawFile.getParentFile(), mRcvedFilename);
         Log.d(TAG, "rename to : " + targetFileName.getAbsolutePath());
         Uri uri = incomingFile.asFile().asUri();
         if (uri == null) {
             boolean result = rawFile.renameTo(targetFileName);
             if (!result) {
                 Log.e(TAG, "rename failed");
             } else {
                 Log.e(TAG, "rename Succeeded ");
             }
         } else {
             try {
                 openStream(uri, targetFileName);
             } catch (IOException e) {
                 Log.e(TAG, e.toString());
             } finally {
                 delFile(uri, rawFile);
             }
         }
     }

     private void openStream(Uri uri, File targetFileName) throws IOException {
         InputStream in = mContext.getContentResolver().openInputStream(uri);
         Log.e(TAG, "open input stream successfuly");
         try {
             copyStream(in, new FileOutputStream(targetFileName));
             Log.e(TAG, "copyStream successfuly");
         } catch (IOException e) {
             Log.e(TAG, e.toString());
         } finally {
             in.close();
         }
     }

     private void copyStream(InputStream in, OutputStream out) throws IOException {
         try {
             byte[] buffer = new byte[1024];
             int read;
             while ((read = in.read(buffer)) != -1) {
                 out.write(buffer, 0, read);
             }
             out.flush();
         } finally {
             out.close();
         }
     }

     private void delFile(Uri uri, File payloadfile) {
         //删除源文件。
         mContext.getContentResolver().delete(uri, null, null);
         if (!payloadfile.exists()) {
             Log.e(TAG, "delete original file by uri successfully");
         } else {
             Log.e(TAG, "delete  original file by uri failed and try to delete it by File delete");
             payloadfile.delete();
             if (payloadfile.exists()) {
                 Log.e(TAG, "fail to delete original file");
             } else {
                 Log.e(TAG, "delete original file successfully");
             }
         }
     }

     private void showProgressSpeedReceiver(TransferStateUpdate update) {
         long transferredBytes = update.getBytesTransferred();
         long totalBytes = update.getTotalBytes();
         long curTime = System.currentTimeMillis();
         Log.d(TAG, "Transfer in progress. Transferred Bytes: "
                 + transferredBytes + " Total Bytes: " + totalBytes);
         fragmentFileDetailsBinding.pbMainDownload.setProgress((int) (transferredBytes * 100 / totalBytes));
         if (mStartTime == 0) {
             mStartTime = curTime;
         }
         if (curTime != mStartTime) {
             mSpeed = ((float) transferredBytes) / ((float) (curTime - mStartTime)) / 1000;
             java.text.DecimalFormat myformat = new java.text.DecimalFormat("0.00");
             mSpeedStr = myformat.format(mSpeed);
             fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder().append("Transfer in Progress. Speed: ").append(mSpeedStr).append("MB/s."));
         }
         if (transferredBytes == totalBytes) {
             mStartTime = 0;
         }
     }

     private void init() {
         if (fragmentSendExpenseDetailsBinding != null) {
             fragmentSendExpenseDetailsBinding.pbMainDownload.setProgress(0);
             fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
             fragmentSendExpenseDetailsBinding.tvMainDesc.setText("");
             fragmentSendExpenseDetailsBinding.barcodeImage.setVisibility(View.GONE);
         }
         mDiscoveryEngine.disconnectAll();
         mDiscoveryEngine.stopScan();
         mDiscoveryEngine.stopBroadcasting();
         mFiles.clear();
     }
 }
初始化用于MainActivity的NearByAgent类:
nearbyAgent = new NearbyAgent(this);
 
在SendExpenseDetailsFragment中发送文件码:
 
((MainActivity) getActivity()).nearbyAgent.sendFile(new File(getArguments().getString("mValues")), fragmentSendExpenseDetailsBinding);
 
 
 
在FileDetailsFragment中接收文件:
((MainActivity) getActivity()).nearbyAgent.receiveFile(fragmentFileDetailsBinding, filesArrayList, groupAdapter);
 
在MainActivity的onActivityResult中获取结果。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
     switch (requestCode) {
         case NearbyAgent.REQUEST_CODE_SCAN_ONE:
             Log.d("data:", "1");
          nearbyAgent.onScanResult(data);
             break;
         default:
             break;
     }
     super.onActivityResult(requestCode, resultCode, data);
 }
 
 
使用近距离数据通信服务下载数据:
 
 
/**
AccountRepository类
  * 初始化Network Kit
 */
@Override
protected void initManager() {
     //下载manager。
     downloadManager = new DownloadManager.Builder("downloadManager")
             .build(context);

     callback = new FileRequestCallback() {
         @Override
         public GetRequest onStart(GetRequest request) {
             return request;
         }

         @Override
         public void onProgress(GetRequest request, Progress progress) {
             Log.i(TAG, "onProgress:" + progress);
         }

         @Override
         public void onSuccess(Response<GetRequest, File, Closeable> response) {
             String filePath = "";
             if (response.getContent() != null) {
                 filePath = response.getContent().getAbsolutePath();
             }
             Log.i(TAG, "onSuccess" + " for " + filePath);
         }

         @Override
         public void onException(GetRequest getRequest, NetworkException e, Response<GetRequest, File, Closeable> response) {
             if (e instanceof Exception) {
                 String errorMsg = "download exception for paused or canceled";
                 Log.w(TAG, errorMsg);
             } else {
                 String errorMsg = "download exception for request:" + getRequest.getId() +
                         "\n\ndetail : " + e.getMessage();
                 if (e.getCause() != null) {
                     errorMsg += " , cause : " +
                             e.getCause().getMessage();
                 }
                 Log.e(TAG, errorMsg);
             }
         }
     };
 }

 
 
@Override
public void download() {
     imageDownload(context);
 }

private void imageDownload(Context context) {
     if (downloadManager == null) {
         Log.e(TAG, "can not download without init");
         return;
     }
     
     String downloadFilePath = context.getObbDir().getPath() + File.separator + "acc111.jpg";
     getRequest = DownloadManager.newGetRequestBuilder()
             .filePath(downloadFilePath)
             .url(Common.getUrlRequest())
             .build();
     Result result = downloadManager.start(getRequest, callback);
     checkResult(result);
 }

九、打包和测试

1、启动Android Studio,点击运行按钮,在手机或模拟器上运行您的应用。点击登录按钮登录您的应用。

cke_498185.pngcke_533595.png

2、登录成功后,展示群界面。点击“+“图标新建群。

cke_589817.pngcke_624120.png

3、群在创建完成后将被插入到云数据库中,用户进入主界面,显示最新群列表。

4、在列表中点击一个群,打开群详情界面。

cke_775841.png

5、点击任意一条列表中的账单或收支数据,进入详情界面。

cke_830907.pngcke_866251.png

6、点击分享按钮,将账单详情以文件形式发送给附近的好友。该操作使用近距离数据通信服务,无需使用到手机流量和wifi。

cke_916339.png

7、查看收到的账单文件。

cke_969577.png

8、点击接收按钮接收文件。

cke_1025092.png

十、恭喜您

祝贺您,您已成功构建一款SplitBill应用并学会了:

  • 在AppGallery Connect中配置云数据库和云存储。

  • 在Android Studio中集成多个HMS Core服务并构建一款SplitBill应用。

十一、参考

参考如下文档获取更多信息:

  • Auth Service

  • Cloud DB

  • Cloud Storage

  • Network Kit

  • Nearby Service

点击此处下载源码。

声明:本codelab实现多个HMS Core服务在单个项目中的集成,供您参考。您需要验证确保相关开源代码的安全合法合规。

欲了解更多更全技术文章,欢迎访问https://developer.huawei.com/consumer/cn/forum/?ha_source=zzh

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/28005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重回synchronized(源码解读与实战解析篇)

读前必知 文中锁&#xff0c;也称为对象锁&#xff0c;而锁对象就是指的承载这个锁的对象&#xff0c;如下面&#xff0c;用法中所指的Object o&#xff0c;在print3中就是锁对象。 以下源码分析仅适用于jdk8&#xff0c;版本未知&#xff0c;因为源码提供者的源码版本访问地址…

金仓数据库KingbaseES GIN 索引

目录 一、索引的逻辑结构 二、索引的物理结构 三、GIN索引使用例子 1、前后模糊查询 2、全文检索 四、gin 索引可用于超长的字段 GIN(Generalized Inverted Index, 通用倒排索引) 是一个存储对(key, posting list)集合的索引结构&#xff0c;其中key是一个键值&#xff0c…

【uniapp】利用Vuex实现购物车功能

实战项目名称&#xff1a;实现购物车功能 文章目录一、实战步骤1. 先编辑store.js文件2. 定义方法和基本的结构3. 编写SETSHPPING二、在项目中调用1. 触发相应的mutations2. 利用computed计算数量和总价的方法提示&#xff1a;本实战内容大部分为具体实现的思路&#xff0c;界面…

C语言 2 —— 常量

常量是什么&#xff1f; 常量就是在程序运行过程中&#xff0c;值不会发生改变&#xff0c;而且一眼可以辨识出值的量。 如&#xff1a; 20&#xff0c;‘a’ , 3.1415926 , "helloworld" 常量的分类&#xff1a; 整形&#xff0c;浮点型&#xff0c;字符型&#…

成电860考研专业课考前划重点-学长课程音频转文字-用科大讯飞花钱买的-三万五千字

成电860考研专业课考前划重点-学长课程音频转文字。 这个是我2021年把视频的音频扒下来后用科大讯飞音频转文字网站上花了几十块钱买的。 说话人1 03:04 对有回放&#xff0c;我这边开回放了&#xff0c;大家可以让大家下载&#xff0c;然后我怕这边回放有问题&#xff0c;大家…

Airtest poco 入门小结

目录 一、poco介绍 1、poco能做什么 2、三种定位方式 1&#xff09;基本选择器 2&#xff09;相对选择器 3&#xff09;空间顺序选择器 4&#xff09;正则表达式方式 3、poco支持平台 二、Airtest介绍 1、Airtest能做什么 2、Airtest不能做什么 3、Airtest的图像识别…

发挥数字化平台优势,电子元器件采购商城系统助力企业改变固有低效流程

我国是全球最大的电子元器件生产国&#xff0c;电子元器件在国民经济发展中占据着重要地位。近年来&#xff0c;随着数字经济的快速发展&#xff0c;电子元器件的需求量也在不断升高&#xff0c;但疫情与国际环境对电子元器件产业要素流通仍然造成了一定困扰。在此背景下&#…

win10系统下使用onnxruntime部署yolov5模型

文章目录前言一、环境1、硬件2、软件二、YOLO模型三、新建Qt项目1、pro文件2、mainwindow.h3、mainwindow.cpp四、YOLO 类封装1、yolov5.h2、yolov5.cpp3、class.names五、效果前言 上一篇介绍过使用opencv-dnn模块实现模型推理部署&#xff0c;但视频效果较差&#xff0c;本篇…

ps2023最新版免费滤镜插件Exposure安装下载教程

滤镜插件是ps的重要功能之一&#xff0c;它主要是用来制作不同的图片特效。那么&#xff0c;ps滤镜插件哪些好用&#xff0c;ps滤镜插件如何获取&#xff0c;下面我们一起来学习这些内容。 ps滤镜插件是比较多的&#xff0c;下面对几款常见的ps滤镜插件进行讲解&#xff0c;看…

TIA博途中的TRACE功能具体使用方法示例

TIA博途中的TRACE功能具体使用方法示例 我们可以利用TRACE曲线来监控程序、排查故障,那么具体怎样使用呢,可以参考以下内容。 如下图所示,打开TIA博途,新建项目后,在左侧项目树中可以看到TRACES, 如下图所示,双击添加新轨迹,然后在右侧窗口中,添加需要监视的信号,…

TOUGH2系列建模方法及在CO2地质封存、水文地球化学、地热、地下水污染等领域中的技术

TOUGH2系列软件是由美国劳伦斯伯克利实验室开发的&#xff0c;旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同&#xff0c;TOUGH2系列软件采用模块化设计和有限积分差网格剖分方法&#xff0c;通过配合不同EOS模块&#xff0c;软件…

【矩阵论】3. 矩阵函数——矩阵函数求导

3.6 矩阵函数求导 3.6.1 积分与求导定义 设 mnm\times nmn 阶矩阵 A(x)(aij(x))mnA(x)\left(a_{ij}(x)\right)_{m\times n}A(x)(aij​(x))mn​ 中的元素都是 x 的可导函数&#xff0c;则 A(x)A(x)A(x) 为关于 xxx 的求导为&#xff1a; A′(A)dA(x)dx(daij(x)dx)mnA(A)\frac{…

正点原子stm32F407学习笔记5——串口通信实验

一、串口通信实验1 上位机给开发板发送数据&#xff0c;开发板将收到的数据发回给上位机 串口设置的一般步骤可以总结为如下几个步骤&#xff1a; 串口时钟使能&#xff0c;GPIO 时钟使能。设置引脚复用器映射&#xff1a;调用 GPIO_PinAFConfig 函数。GPIO 初始化设置&#…

数据库性能翻3倍:Redis on Flash分层存储技术是如何做到的?

Redis on flash简介&#xff1a;Redis on Flash 涉及到的是Redis的分层存储技术&#xff0c;即将数据存放在不同地方。Redis自2016年以来支持Redis on Flash。从2019年开始, Redis企业版&#xff08;Redis Enterprise&#xff09;宣布支持英特尔Optane DC持久性内存&#xff0c…

基于NB-IoT的智能垃圾桶系统设计与实现

本设计是基于物联网的智能垃圾桶&#xff0c;主要实现以下功能&#xff1a; 1&#xff0c;压力传感器模块采集垃圾重量数据&#xff1b; 2&#xff0c;GPS定位模块采集垃圾桶所在的经纬度数据&#xff1b; 3&#xff0c;人体红外模块检测人体并返回是否有人通过的数据&#xf…

会议管理系统SSM记录(二)

目录&#xff1a; &#xff08;1&#xff09;整合Freemarker &#xff08;2&#xff09;用户登录 &#xff08;3&#xff09;提取头部 &#xff08;4&#xff09;提取菜单抽取 &#xff08;1&#xff09;整合Freemarker 在pom.xml中加入Freemark依赖&#xff1a; 创建free…

HTML+CSS大作业:基于HMTL校园学校网页设计题材【我的学校网站】

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

专精特新企业三个层级

专精特新企业也是分层级的。工信部2022年6月印发《优质中小企业梯度培育管理暂行办法》里面明确提出中小企业培育的3个梯度&#xff0c;分别是创新型中小企业、专精特新中小企业和专精特新小巨人企业&#xff0c;刚好构成中小企业发展层级金字塔。这就意味着企业想要发展崛起&a…

19 04-读取DTC快照信息

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;19服务作为UDS中子功能最多的服务&#xff0c;一共有28种子功能&#xff0c;本文将介绍常用的19 04服务&#xff1a;读取快照信息。 关联文章&#xff1a; 19服务List 19 01-通过状态掩码读取DTC数目 $19服务:DTC…

1.2 C++编译器对指针的解释方式(深度理解c++指针)

1.2 指针 1.2.1 指针解释方式 从内存的角度&#xff0c;一个指向类对象的指针与一个指向整数类型的指针或一个指向数组的指针&#xff0c;三者之间是没有任何区别的&#xff0c;它们内部都只存储了一个机器地址值(word)。不同类型指针的区别仅在于其寻址出来的object类型的不…