集成腾讯bugly的热修复功能sdk步骤

本文转载自:点击打开链接

首先为什么要集成bugly热修复。市面上有其他的热修复框架,为什么就用bugly?这里给出2张图大家就明白了。




引用腾讯bugly官网的一段话:

  • 无需关注Tinker是如何合成补丁的
  • 无需自己搭建补丁管理后台
  • 无需考虑后台下发补丁策略的任何事情
  • 无需考虑补丁下载合成的时机,处理后台下发的策略
  • 我们提供了更加方便集成Tinker的方式
  • 我们提供应用升级一站式解决方案
进入正题:接入流程主要是以下几个步骤:
  • 打基准包安装并上报联网(注:填写唯一的tinkerId)
  • 对基准包的bug修复(可以是Java代码变更,资源的变更)
  • 修改基准包路径、填写补丁包tinkerId、mapping文件路径、resId文件路径
  • 执行tinkerPatchRelease打Release版本补丁包
  • 选择app/build/outputs/patch目录下的补丁包并上传(注:不要选择tinkerPatch目录下的补丁包,不然上传会有问题)
  • 编辑下发补丁规则,点击立即下发
  • 重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
  • 再次重启基准包,检验补丁应用结果

1:新建基准包工程项目(人为制造有BUG的app版本)

[java] view plain copy
  1. btn.setOnClickListener(new View.OnClickListener() {  
  2.            @Override  
  3.            public void onClick(View view) {  
  4. /                String str = LoadBugClass.getBugString();  
  5.                String str = BugClass.bug();  
  6.                Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();;  
  7.            }  
  8.        });  
[java] view plain copy
  1. public class BugClass {  
  2.   
  3.     public static String bug(){  
  4.         String str = null;  
  5.         int str_length = str.length();  
  6.         return "this is bug class";  
  7.     }  
  8. }  
这个可以看出点击一个按钮会报空指针异常。

2:接着就是配置相关属性和添加一个插件依赖了。

官方教程地址:点击打开链接

下面也给出我自己配置的过程。

首先在最外层的build.gradle文件中添加依赖,看下图:



其次新建sampleapplication和sampleapplicationLike两个java类

[java] view plain copy
  1. package com.henry.testappbugly;  
  2.   
  3. import android.annotation.TargetApi;  
  4. import android.app.Application;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.res.AssetManager;  
  8. import android.content.res.Resources;  
  9. import android.os.Build;  
  10. import android.support.multidex.MultiDex;  
  11.   
  12. import com.tencent.bugly.Bugly;  
  13. import com.tencent.bugly.beta.Beta;  
  14. import com.tencent.tinker.loader.app.DefaultApplicationLike;  
  15.   
  16. /** 
  17.  * Created by W61 on 2016/11/29. 
  18.  */  
  19.   
  20. public class SampleApplicationLike extends DefaultApplicationLike {  
  21.   
  22.     public static final String TAG = "Tinker.SampleApplicationLike";  
  23.   
  24.     public SampleApplicationLike(Application application, int tinkerFlags,  
  25.                                  boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,  
  26.                                  long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources,  
  27.                                  ClassLoader[] classLoader, AssetManager[] assetManager) {  
  28.         super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,  
  29.                 applicationStartMillisTime, tinkerResultIntent, resources, classLoader,  
  30.                 assetManager);  
  31.     }  
  32.   
  33.   
  34.     @Override  
  35.     public void onCreate() {  
  36.         super.onCreate();  
  37.         // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId  
  38.         Bugly.init(getApplication(), ""true);  
  39.     }  
  40.   
  41.   
  42.     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)  
  43.     @Override  
  44.     public void onBaseContextAttached(Context base) {  
  45.         super.onBaseContextAttached(base);  
  46.         // you must install multiDex whatever tinker is installed!  
  47.         MultiDex.install(base);  
  48.   
  49.         // 安装tinker  
  50.         // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法  
  51.         Beta.installTinker(this);  
  52.     }  
  53.   
  54.     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)  
  55.     public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {  
  56.         getApplication().registerActivityLifecycleCallbacks(callbacks);  
  57.     }  
  58.   
  59. }  

[java] view plain copy
  1. package com.henry.testappbugly;  
  2.   
  3. import com.tencent.tinker.loader.app.TinkerApplication;  
  4. import com.tencent.tinker.loader.shareutil.ShareConstants;  
  5.   
  6. /** 
  7.  * Created by W61 on 2016/11/29. 
  8.  */  
  9.   
  10. public class SampleApplication extends TinkerApplication {  
  11.     public SampleApplication() {  
  12.         super(ShareConstants.TINKER_ENABLE_ALL, "SampleApplicationLike所在的包名路径",  
  13.                 "com.tencent.tinker.loader.TinkerLoader"false);  
  14.     }  
  15. }  

在在Androidmanifest.xml文件中配置权限及application类名
[java] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.henry.testappbugly">  
  4.   
  5.     <application  
  6.         android:name=".SampleApplication"  
  7.         android:allowBackup="true"  
  8.         android:icon="@mipmap/ic_launcher"  
  9.         android:label="@string/app_name"  
  10.         android:supportsRtl="true"  
  11.         android:theme="@style/AppTheme">  
  12.         <activity android:name=".MainActivity">  
  13.             <intent-filter>  
  14.                 <action android:name="android.intent.action.MAIN" />  
  15.   
  16.                 <category android:name="android.intent.category.LAUNCHER" />  
  17.             </intent-filter>  
  18.         </activity>  
  19.   
  20.   
  21.         <!--API 24以上配置-->  
  22.         <provider  
  23.             android:name="android.support.v4.content.FileProvider"  
  24.             android:authorities="com.tencent.bugly.hotfix.fileProvider"  
  25.             android:exported="false"  
  26.             android:grantUriPermissions="true">  
  27.             <meta-data  
  28.                 android:name="android.support.FILE_PROVIDER_PATHS"  
  29.                 android:resource="@xml/provider_paths"/>  
  30.         </provider>  
  31.   
  32.     </application>  
  33.   
  34.   
  35.     <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
  36.     <uses-permission android:name="android.permission.INTERNET" />  
  37.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  38.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
  39.     <uses-permission android:name="android.permission.READ_LOGS" />  
  40.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
  41.     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>  
  42.   
  43. </manifest>  

在到res目录下:



[java] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <paths xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <!-- 这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径 -->  
  4.     <!-- /storage/emulated/0/Download/com.bugly.upgrade.demo/.beta/apk-->  
  5.     <external-path name="beta_external_path" path="Download/"/>  
  6.     <!--/storage/emulated/0/Android/data/com.bugly.upgrade.demo/files/apk/-->  
  7.     <external-path name="beta_external_files_path" path="Android/data/"/>  
  8. </paths>  


在到app目录下新建:

[java] view plain copy
  1. # you can copy the tinker keep rule at  
  2. # build/intermediates/tinker_intermediates/tinker_multidexkeep.pro  
  3.   
  4. -keep class com.tencent.tinker.loader.** {  
  5.     *;  
  6. }  
  7.   
  8. -keep class com.tencent.bugly.hotfix.SampleApplication {  
  9.     *;  
  10. }  
  11.   
  12. -keep public class * implements com.tencent.tinker.loader.app.ApplicationLifeCycle {  
  13.     *;  
  14. }  
  15.   
  16. -keep public class * extends com.tencent.tinker.loader.TinkerLoader {  
  17.     *;  
  18. }  
  19.   
  20. -keep public class * extends com.tencent.tinker.loader.app.TinkerApplication {  
  21.     *;  
  22. }  
  23.   
  24. # here, it is your own keep rules.  
  25. # you must be careful that the class name you write won't be proguard  
  26. # but the tinker class above is OK, we have already keep for you!  

然后在混淆文件.pro中添加这几句代码(bugly都有说明解释)
[java] view plain copy
  1. -dontwarn com.tencent.bugly.**  
  2. -keep public class com.tencent.bugly.**{*;}  


最后就是app目录下的build.gradle文件配置了:
[java] view plain copy
  1. apply plugin: 'com.android.application'  
  2.   
  3. dependencies {  
  4.     compile fileTree(include: ['*.jar'], dir: 'libs')  
  5.     compile 'com.android.support:appcompat-v7:24.1.1'  
  6.   
  7.     // 多dex配置  
  8.     compile "com.android.support:multidex:1.0.1"  
  9.     // 集成Bugly热更新aar(灰度时使用方式)  
  10. //    compile(name: 'bugly_crashreport_upgrade-1.2.0', ext: 'aar')  
  11.     compile "com.tencent.bugly:crashreport_upgrade:1.2.0"  
  12. }  
  13.   
  14.   
  15. android {  
  16.     compileSdkVersion 23  
  17.     buildToolsVersion "23.0.2"  
  18.   
  19.     // 编译选项  
  20.     compileOptions {  
  21.         sourceCompatibility JavaVersion.VERSION_1_7  
  22.         targetCompatibility JavaVersion.VERSION_1_7  
  23.     }  
  24.   
  25.     // recommend  
  26.     dexOptions {  
  27.         jumboMode = true  
  28.     }  
  29.     // 签名配置  
  30.     signingConfigs {  
  31.         // 签名配置  
  32.         signingConfigs {  
  33.             release {  
  34.                 try {  
  35.                     storeFile file("./keystore/release.keystore")  
  36.                     storePassword "testres"  
  37.                     keyAlias "testres"  
  38.                     keyPassword "testres"  
  39.                 } catch (ex) {  
  40.                     throw new InvalidUserDataException(ex.toString())  
  41.                 }  
  42.             }  
  43.   
  44.             debug {  
  45.                 storeFile file("./keystore/debug.keystore")  
  46.             }  
  47.         }  
  48.     }  
  49.   
  50.     defaultConfig {  
  51.         applicationId "com.henry.testappbugly"  
  52.         minSdkVersion 14  
  53.         targetSdkVersion 23  
  54.         versionCode 2  
  55.         versionName "2.0"  
  56.   
  57.         // 开启multidex  
  58.         multiDexEnabled true  
  59.         // 以Proguard的方式手动加入要放到Main.dex中的类  
  60.         multiDexKeepProguard file("keep_in_main_dex.txt")  
  61.     }  
  62.     buildTypes {  
  63.   
  64.         release {  
  65.             minifyEnabled true  
  66.             signingConfig signingConfigs.release  
  67.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
  68.         }  
  69.         debug {  
  70.             debuggable true  
  71.             minifyEnabled false  
  72.             signingConfig signingConfigs.debug  
  73.         }  
  74.     }  
  75.     sourceSets {  
  76.         main {  
  77.             jniLibs.srcDirs = ['libs']  
  78.         }  
  79.     }  
  80.   
  81.     repositories {  
  82.         flatDir {  
  83.             dirs 'libs'  
  84.         }  
  85.     }  
  86.   
  87.     lintOptions {  
  88.         checkReleaseBuilds false  
  89.         abortOnError false  
  90.     }  
  91. }  
  92.   
  93.   
  94. def gitSha() {  
  95.     try {  
  96.         String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()  
  97.         if (gitRev == null) {  
  98.             throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")  
  99.         }  
  100.         return gitRev  
  101.     } catch (Exception e) {  
  102.         throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")  
  103.     }  
  104. }  
  105.   
  106.   
  107. def bakPath = file("${buildDir}/bakApk/")  
  108.   
  109. /** 
  110.  * you can use assembleRelease to build you base apk 
  111.  * use tinkerPatchRelease -POLD_APK=  -PAPPLY_MAPPING=  -PAPPLY_RESOURCE= to build patch 
  112.  * add apk from the build/bakApk 
  113.  */  
  114. ext {  
  115.     // for some reason, you may want to ignore tinkerBuild, such as instant run debug build?  
  116.     tinkerEnabled = true  
  117.   
  118.     // for normal build  
  119.     // old apk file to build patch apk  
  120.     tinkerOldApkPath = "${bakPath}/app-release-1201-09-46-25.apk"  
  121.     // proguard mapping file to build patch apk  
  122.     tinkerApplyMappingPath = "${bakPath}/app-release-1201-09-46-25-mapping.txt"  
  123.     // resource R.txt to build patch apk, must input if there is resource changed  
  124.     tinkerApplyResourcePath = "${bakPath}/app-release-1201-09-46-25-R.txt"  
  125.   
  126.     // only use for build all flavor, if not, just ignore this field  
  127.     tinkerBuildFlavorDirectory = "${bakPath}/app-release-1201-09-46-25"  
  128. }  
  129.   
  130. def getOldApkPath() {  
  131.     return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath  
  132. }  
  133.   
  134. def getApplyMappingPath() {  
  135.     return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath  
  136. }  
  137.   
  138. def getApplyResourceMappingPath() {  
  139.     return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath  
  140. }  
  141.   
  142. def getTinkerIdValue() {  
  143.     return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()  
  144. }  
  145.   
  146. def buildWithTinker() {  
  147.     return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled  
  148. }  
  149.   
  150. def getTinkerBuildFlavorDirectory() {  
  151.     return ext.tinkerBuildFlavorDirectory  
  152. }  
  153.   
  154. /** 
  155.  * 更多Tinker插件详细的配置,参考:https://github.com/Tencent/tinker/wiki 
  156.  */  
  157. if (buildWithTinker()) {  
  158.     // 依赖tinker插件  
  159.     apply plugin: 'com.tencent.tinker.patch'  
  160.     apply plugin: 'com.tencent.bugly.tinker-support'  
  161.   
  162.     tinkerSupport {  
  163.     }  
  164.   
  165.     // 全局信息相关配置项  
  166.     tinkerPatch {  
  167.         oldApk = getOldApkPath() //必选, 基准包路径  
  168.   
  169.         ignoreWarning = false // 可选,默认false  
  170.   
  171.         useSign = true // 可选,默认true, 验证基准apk和patch签名是否一致  
  172.   
  173.         // 编译相关配置项  
  174.         buildConfig {  
  175.             applyMapping = getApplyMappingPath() //  可选,设置mapping文件,建议保持旧apk的proguard混淆方式  
  176.             applyResourceMapping = getApplyResourceMappingPath() // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配  
  177.             tinkerId = "可以是签名版本号字符串等等比如:assdhfkdshfksdhfuksfhuk" // 必选,默认为null  
  178.         }  
  179.   
  180.         // dex相关配置项  
  181.         dex {  
  182.             dexMode = "jar" // 可选,默认为jar  
  183.             usePreGeneratedPatchDex = true // 可选,默认为false  
  184.             pattern = ["classes*.dex",  
  185.                        "assets/secondary-dex-?.jar"]  
  186.             // 必选  
  187.             loader = ["com.tencent.tinker.loader.*",  
  188.                       "SampleApplication所在的全路径",  
  189.             ]  
  190.         }  
  191.   
  192.         // lib相关的配置项  
  193.         lib {  
  194.             pattern = ["lib/armeabi/*.so"]  
  195.         }  
  196.   
  197.         // res相关的配置项  
  198.         res {  
  199.             pattern = ["res/*""assets/*""resources.arsc""AndroidManifest.xml"]  
  200.             ignoreChange = ["assets/sample_meta.txt"]  
  201.             largeModSize = 100  
  202.         }  
  203.   
  204.         // 用于生成补丁包中的'package_meta.txt'文件  
  205.         packageConfig {  
  206.             configField("patchMessage""tinker is sample to use")  
  207.   
  208.             configField("platform""all")  
  209.   
  210.             configField("patchVersion""1.0")  
  211.         }  
  212.   
  213.         // 7zip路径配置项,执行前提是useSign为true  
  214.         sevenZip {  
  215.             zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // optional  
  216.             //  path = "/usr/local/bin/7za" // optional  
  217.         }  
  218.     }  
  219.   
  220.     List<String> flavors = new ArrayList<>();  
  221.     project.android.productFlavors.each { flavor ->  
  222.         flavors.add(flavor.name)  
  223.     }  
  224.     boolean hasFlavors = flavors.size() > 0  
  225.     /**  
  226.      * bak apk and mapping  
  227.      */  
  228.     android.applicationVariants.all { variant ->  
  229.         /** 
  230.          * task type, you want to bak 
  231.          */  
  232.         def taskName = variant.name  
  233.         def date = new Date().format("MMdd-HH-mm-ss")  
  234.   
  235.         tasks.all {  
  236.             if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {  
  237.   
  238.                 it.doLast {  
  239.                     copy {  
  240.                         def fileNamePrefix = "${project.name}-${variant.baseName}"  
  241.                         def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"  
  242.   
  243.                         def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath  
  244.                         from variant.outputs.outputFile  
  245.                         into destPath  
  246.                         rename { String fileName ->  
  247.                             fileName.replace("${fileNamePrefix}.apk""${newFileNamePrefix}.apk")  
  248.                         }  
  249.   
  250.                         from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"  
  251.                         into destPath  
  252.                         rename { String fileName ->  
  253.                             fileName.replace("mapping.txt""${newFileNamePrefix}-mapping.txt")  
  254.                         }  
  255.   
  256.                         from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"  
  257.                         into destPath  
  258.                         rename { String fileName ->  
  259.                             fileName.replace("R.txt""${newFileNamePrefix}-R.txt")  
  260.                         }  
  261.                     }  
  262.                 }  
  263.             }  
  264.         }  
  265.     }  
  266.     project.afterEvaluate {  
  267.         //sample use for build all flavor for one time  
  268.         if (hasFlavors) {  
  269.             task(tinkerPatchAllFlavorRelease) {  
  270.                 group = 'tinker'  
  271.                 def originOldPath = getTinkerBuildFlavorDirectory()  
  272.                 for (String flavor : flavors) {  
  273.                     def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")  
  274.                     dependsOn tinkerTask  
  275.                     def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")  
  276.                     preAssembleTask.doFirst {  
  277.                         String flavorName = preAssembleTask.name.substring(78).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)  
  278.                         project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"  
  279.                         project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"  
  280.                         project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"  
  281.   
  282.                     }  
  283.   
  284.                 }  
  285.             }  
  286.             task(tinkerPatchAllFlavorDebug) {  
  287.                 group = 'tinker'  
  288.                 def originOldPath = getTinkerBuildFlavorDirectory()  
  289.                 for (String flavor : flavors) {  
  290.                     def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")  
  291.                     dependsOn tinkerTask  
  292.                     def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")  
  293.                     preAssembleTask.doFirst {  
  294.                         String flavorName = preAssembleTask.name.substring(78).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)  
  295.                         project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"  
  296.                         project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"  
  297.                         project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"  
  298.                     }  
  299.   
  300.                 }  
  301.             }  
  302.         }  
  303.     }  
  304.   
  305.   
  306. }  

最后run as生成有bug的基准包app


在去腾讯bugly官网将这个基准包上传上去即可。



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

接下来,制作补丁包。

由于刚才点击按钮报空指针,下面将代码稍做改动如下:

[java] view plain copy
  1.     protected void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3.         setContentView(R.layout.activity_main);  
  4.   
  5.         btn = (Button) findViewById(R.id.btn);  
  6.         btn.setOnClickListener(new View.OnClickListener() {  
  7.             @Override  
  8.             public void onClick(View view) {  
  9.                 String str = LoadBugClass.getBugString();  
  10. //                String str = BugClass.bug();  
  11.                 Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();;  
  12.             }  
  13.         });  
  14.     }  
[java] view plain copy
  1. public class LoadBugClass {  
  2.     /** 
  3.      * 获取bug字符串. 
  4.      * 
  5.      * @return 返回bug字符串 
  6.      */  
  7.     public static String getBugString() {  
  8. //        BugClass bugClass = new BugClass();  
  9.         return "iS OK";  
  10.     }  
  11. }  
这样点击按钮就会弹出is ok了不会报错。

修改配置文件:



这里注意点,补丁包是基于基准包所生成的patch文件并不是版本升级,所以此处补丁包不需要修改versioncode,versionname

然后双击下图中所指地方:


稍等片刻就会出现下图中类容:


其中的patch_signed_7zip.apk就是补丁包了。将这个补丁包上传到腾讯bugly即可。

注意:上传完补丁包点击了立即下发,就需要重新启动基准包策略。从有bug版本的app到修复有一个时间差的。估计1到2分钟左右才能看到效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现送礼物功能,需要结合腾讯云TRTCVoiceRoom语音直播SDK和自己的后台服务器进行开发。 以下是一般的开发流程: 1. 首先需要在自己的后台服务器上创建一个礼物列表,包括礼物ID、名称、价格、图片等信息。 2. 在客户端中,可以使用RecyclerView展示礼物列表,当用户点击某个礼物时,可以通过TRTCVoiceRoom SDK提供的发送自定义消息接口向直播间内的其他用户发送礼物消息。在发送礼物消息时,需要携带礼物ID和数量等信息。 3. 接收礼物消息的用户可以在接收到消息时播放相应的礼物动画,并将礼物数量加入自己的礼物数量中。 下面是大致的代码实现: 发送礼物消息: ``` // 在客户端中选中某个礼物后,构造礼物消息 JSONObject giftMsg = new JSONObject(); giftMsg.put("type", "gift"); giftMsg.put("gift_id", giftId); giftMsg.put("gift_num", num); // 将礼物消息发送到直播间内的其他用户 mTRTCVoiceRoom.sendRoomCustomMsg(giftMsg.toString(), new TRTCVoiceRoomCallback.ActionCallback() { @Override public void onCallback(int code, String msg) { if (code == 0) { // 礼物消息发送成功 } else { // 礼物消息发送失败 } } }); ``` 接收礼物消息: ``` // 在接收到自定义消息时,判断消息类型是否为礼物消息 JSONObject jsonMsg = new JSONObject(msg); String type = jsonMsg.optString("type"); if ("gift".equals(type)) { String giftId = jsonMsg.optString("gift_id"); int num = jsonMsg.optInt("gift_num"); // 播放礼物动画 playGiftAnimation(giftId); // 将礼物数量加入自己的礼物数量中 mMyGiftNum += num; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值