KtvSDK

您所在的位置:网站首页 全民k歌打分规则 KtvSDK

KtvSDK

2024-07-09 18:31| 来源: 网络整理| 查看: 265

1. sdk集成 1.1 sdk及demo包下载

sdk只支持本地引入aar,不支持maven方式接入demo下载地址sdk下载地址release-notes(每次版本修改点):

企业微信截图_d740816b-b610-464b-98e3-c3f7b0f42388.png

若有权限问题,请查看SDK合作流程及规范。

1.2 demo包运行说明

在local.properties中设置K歌开放平台的appId与appKey:

KTV_SDK_APP_ID= KTV_SDK_APP_KEY=

推荐运行环境:

jdk1.8 gradle-wrapper.properties中的gradle版本: gradle-6.9 1.3 sdk集成

KtvSdk通过aar的方式对外提供,三方按照aar的方式集成即可:

将 AAR 包复制到项目的 libs 目录下 在项目的 build.gradle 文件中添加以下代码: repositories { flatDir { dirs 'libs' } } 在项目的 app 模块的 build.gradle 文件中添加以下代码: dependencies { implementation(name: 'library-name', ext: 'aar') }

sync之后即可使用;

1.3 添加三方依赖

由于sdk依赖部分三方库,但未打包到sdk中,需要宿主在项目的build.gradle 文件中添加以下依赖

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3" implementation "com.squareup.okhttp3:okhttp:3.12.0" implementation "com.google.zxing:core:3.3.3" implementation "com.tencent:mmkv:1.0.23" implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.recyclerview:recyclerview:1.1.0" implementaion "androidx.constraintlayout:constraintlayout:2.0.1" implementation "com.google.android.exoplayer:exoplayer-core:2.14.0" implementation "com.google.code.gson:gson:2.6.2" implementation "androidx.fragment:fragment-ktx:1.2.5" 1.4 添加混淆配置

由于sdk已做混淆,因此app配置sdk相关类不混淆即可:

-keep class com.tme.**{*;} -keep class com.tencent.karaoke.**{*;} -keep class com.tencent.qmsq.**{*;} -keep class com.tencent.news.**{*;} -keep class com.tencent.threadpool.**{*;} -keep class com.tencent.libunifydownload.**{*;} -keep class com.tencent.karaoketv.**{*;} -keep class com.tencent.qqmusic.business.lyricnew.**{*;} -keep class com.ola.star.**{*;} -keep class com.ola.qmsq.**{*;} -keep class ksong.support.**{*;} -keep class easytv.common.**{*;}

其余需要app主动依赖的三方开源库,按照三方库要求的混淆规则配置即可

sdk初始化

建议在application中初始化,初始化接口为:KtvSdk.init(application: Application, ktvInitParams: KtvInitParams)其中ktvInitParams是sdk初始化需要传入的参数,KtvInitParams包含两部分:

hostInfo:宿主需提供的信息

hostInfo参数说明IKAppInfo 用于声明宿主app的基本信息。

public interface IKAppInfo { /** * appId,可向K歌开放平台申请获得 * 必传 * @return */ String getAppId(); /** * 开放平台appkey,可向K歌开放平台申请获得 * 使用sdk提供的登录功能时必传 * @return */ String getAppKey(); /** * 平台标识 * 车机:52 * 智能音箱或其它:53 * 必传 * @return */ String getPlatformId(); /** * 设备唯一id,必传 */ String getImei(); /** * 用户id,可选 */ String getUid(); /** * 渠道号,需传车企/厂商名称 */ String getChannelId(); /** * SDK是否默认为正式环境 */ boolean isProductEnv(); /** * 是否为开发调试模式,若是,sdk内部应用一些开发调试配置,有助于问题定位,但对sdk性能可能有影响。 */ boolean isDevMode(); }

HostInfo 参照demo中SampleHostInfo:继承KAppInfoAdapter,提供宿主信息,如果方法返回类型不可空,则属于必填参数,可空则不强制要求,如果有其他需要传入的设备信息,通过getExtraInfo的Map传入。如果需要手动切换sdk的环境,可以使用KtvSdk.setProductEnv(true)方法来切换 。 3. AppRequestProvider:需要宿主实现的方法请求 某些场景下sdk需要宿主提供方法实现,供sdk使用,这里定义了一个通用请求接口,方法签名如下:

fun request( groupId: String, eventType: String, status: Any?, extra: Map? ): Any?

参数说明:

参数 类型 说明 groupId String 请求大类,标识属于哪一类请求,比如登陆、录唱、支付等 eventType String 请求类型,区分具体什么请求,如"guideUrl"代表获取mic引导链接 status Any? 请求状态,通用类型的请求参数,可为空 extra Map? 其他额外的请求参数放在Map中传入,可为空 需要业务方根据传参提供具体实现。

初始化的方式参考demo中HostApp的initKG方法,但是AppRequestProvider需要业务方根据具体场景实现。如播放歌曲阻断(如会员歌曲/免费歌曲达到次数上限等)时,sdk会通过此接口通知到app端,app侧可以做相应的业务逻辑(如跳转会员页等)这种场景下:groudId: "type_play" eventType: "play_intercept" status: 阻断原因,可能有如下值

String FREE_TRAIL = "free_limit"; //限免阻断 String VIP = "vip_limit"; //VIP阻断 String LOGIN = "login_limit"; //登录阻断

示例:

override fun request( context: Context, groupId: String, eventType: String, status: Any?, extra: Map? ): Any? { if (groupId == "type_play" && eventType == "play_intercept") { status.startActivity(Intent(this, SamplePlayListActivity::class.java)) } return true }

注意:另外需要在播放页的初始化时增加如下代码PlayerManager.getPlayPermissionProvider()?.attachPage(activity)

如需要跳转到宿主的播放页的场景,此时:groudId: "type_play" eventType: "goto_play_page" status: PendSong 客户端收到此回调时,主动跳转到播放页,并播放当前mid的歌曲4. imageLoader: 宿主需实现图片下载接口5. logger: 宿主日志实现

Application中实现样例代码如下:

fun initKG() { val params = KtvInitParams( hostInfo = SampleHostInfo(), appRequestProvider = SampleAppRequestProvider(), imageLoader = GlideImageLoaderImpl(), logger = SimpleLog(), ) KtvSdk.init(this, params) customConfig() } /** * 播放可选配置,相关配置项都有默认值,一般不需要单独配置。 */ private fun customConfig() { KtvPlayerConfig.getInstance() .init(context) // // .useTextureView() // .setCacheStrategyFactory(object : CacheStrategyFactory() { // override fun createCacheStrategy(cacheDirPath: String): AbstractCacheStrategy { // return LruCacheStrategy(cacheDirPath, 1024 * 1024 * 1024 * 2L) // } // }) // .forceUseSystemAudioOutput(true) // // .installAudioOutput(new PuremicOutputInstaller()) //接入炉石音频输出 // // .installAudioInput(new PuremicReceiverInstaller()) //接入炉石音频输入 // .setKtvPlayerEventReportCallback { mid: String?, event: Int -> } // .setSupportAudioRecordReceiver(true) // .enableDebugLog(true) // .enableDownloadLog(true) // .downloadRetryCount(3) //下载重试次数 // .setUserAgent("Demo") //User-Agent // .useOpenGL(false) //是否使用GLSurfaceView .commit() //此方法必须被调用 } 2. 登录

sdk必须登录态才能唱歌。登录集成方式详见:https://apifox.com/apidoc/project-951819/doc-2986762

3. 播放API

播放器为KaraokePlayer,播放界面自行开发。

3.3 KaraokePlayer Api KaraokePlayer Api Describe void play(SongInfoObject object) 播放歌曲 void pause() 暂停播放 void resume() 恢复播放 void stop() 停止播放 void replay() 重新播放 int getPlayState() 播放状态 {VideoState.STATE_IDLE:0, VideoState.STATE_START:1, VideoState.STATE_PLAYING:2, VideoState.STATE_PAUSE:3, VideoState.STATE_ENDED:4} KaraokePlayer setPlayMode(PlayMode playMode) 设置播放模式KTV KaraokePlayer setDataSourceType(DataSourceType sourceType) 原唱 消声伴奏 KaraokePlayer setLoopMode(LoopMode mode) Loop or Once KaraokePlayer setTonePitchShift(int offset) 设置变调 level int getTonePitchShift( ) 获取变调 level PlayMode getPlayMode() 获取播放模式 DataSourceType getDataSourceType() 获取伴奏类型 KaraokePlayer setVolume(float volume) 设置伴奏音量 KaraokePlayer setMicVolume(float volume) 设置mic 音量 float getVolume() 获取伴奏音量 float getMicVolume() 获取mic 音量 KaraokePlayer addCallback(KaraokePlayerCallback callback) 添加播放回调 KaraokePlayer removeCallback(KaraokePlayerCallback callback) 添加播放回调 KaraokePlayer addOnScoreListener(OnScoreListener listener) 添加打分回调 KaraokePlayer removeOnScoreListener(OnScoreListener listener) 删除打分回调 KaraokePlayer setFadeAudioVolumeTime(long time) 渐变音时间 KaraokePlayer setFadeAudioEnable(boolean flag) 是否允许渐变音 long getCurrentTime() 当前解码时间, sdk内部使用,获取当前播放时间建议用getTimeLineTime() long getTimeLineTime() 当前播放时间 long getDuration() 歌曲总时长 void playNext() 播放下一首 3.4 播放及预加载

布局中加入视频View KtvVideoLayout:

不要对KtvVideoLayout进行多次addView/removeView操作,不然会出现视频黑屏现象。

3.4.1 使用sdk请求歌曲信息(推荐)

使用sdk的扫码登录服务,或将自定义扫码登录后的token传给sdk,则可以只提供歌曲id,即可实现播放功能。如果是预加载,只调用PlayManager.preload()接口即可。

//KtvVideoLayout为视频View val videoLayout : KtvVideoLayout = findViewById(R.id.ktv_video_layout) //创建播放器实例 currentPlayer = KaraokePlayerIml(videoLayout); ... val builder = SongInfoObject.Builder() builder.setMid(currentMid) val songInfoObject = builder.build() PlayerManager.createPreLoader() .preload(songInfoObject) { event: PlayPreLoaderEvent -> when (event) { //预加载完成 is PlayPreLoaderEvent.Success -> { event.songInfoObject?.let { //将待播放歌曲加入到播放列表头部 PlayerManager.playList.addFront( PendSong(currentMid) .songName(songInfoObject.songName) .singerName(songInfoObject.singerName) ) //调用播放下一首,可使用addSongListObserver监听到播放列表变更 PlayerManager.playList.next() //主动调用播放歌曲 playSong(it) } } //预见加载的一些日志打印 is PlayPreLoaderEvent.Trace -> { KGToast.show(event.tracePrint()) } //歌曲加载进度 is PlayPreLoaderEvent.Progress -> { playInfoVM.mPreLoadProgress.value = event.progress } //加载失败 is PlayPreLoaderEvent.Failed -> { Logger.i(TAG, "preload $event") } } } private fun playSong(songInfo: SongInfoObject) { val player = currentPlayer player.setFadeAudioEnable(false) .setSupportPitchShiftWhenNormalMode(true) .setPlayMode(KaraokePlayer.PlayMode.KTV) .setDataSourceType(KaraokePlayer.DataSourceType.ACCOMPANY) .addCallback(KtvCallbackImpl()) .setVideoScaleType(VideoScaleType.FILL_FIT) .setLoopMode(KaraokePlayer.LoopMode.ONCE) .setMicVolume(1F) .setVolume(1F) .play(songInfo) }

SDK内有公版播放页KtvPlayFragment,可参见其实现。

3.4.2 自己请求歌曲信息(仅供旧厂商兼容)

如果使用的应用级鉴权方式,客户端没有扫码登录后的token,则需要自己请求歌曲信息,并传给播放器。请求示例如下:

private fun getSongUrl(appId: String, accessToken: String) { val json = JSONObject() json.put("song_id", "000wJa2G49MNiv") json.put("mv_quality", 720) json.put("need_songinfo", true) json.put("need_note", true) json.put("need_lyric", true) //MediaType 设置Content-Type 标头中包含的媒体类型值 val requestBody = RequestBody.create(MediaType.parse("application/json; "), json.toString()) val request = Request.Builder() .url("$mBaseUrl/karaoke/base/v2/get_song_url") .addHeader("X-Open-Access-Token", accessToken) .addHeader("X-Open-App-ID", appId) .addHeader("Device-ID", KtvSdk.getSdkImei()) .post(requestBody) .build() val call = mClient.newCall(request) call.enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { onError("加载失败: $e") } override fun onResponse(call: Call, response: Response) { safeRun { val responseString = response.body()!!.string() val responseJSON = JSONObject(responseString) val dataJson: JSONObject = responseJSON.optJSONObject("data") val songBean = mGson.fromJson(dataJson.toString(), SongBean::class.java) //获取歌曲信息Model val songInfo = songBean.toSongInfo() updateText("请求成功,mv地址:${songInfo.mVideoUrl}") loadSong(songInfo) } } }) } private fun loadSong(songInfoObject: SongInfoObject) { //如果自己请求歌曲详细请求,此项设为true songInfoObject.isSkipQuerySong = true //其它步骤如3.4.1所示 PlayerManager.createPreloader() .preload(songInfoObject) { event: PlayPreLoaderEvent -> when (event) { is PlayPreLoaderEvent.Success -> { //playSong(songInfoObject) } else -> {} } } }

注意:填充的SongInfoObject信息必须完整,可参考SongBean.toSongInfo的实现。

3.5 歌曲模型:SongInfoObject public class SongInfoObject { public String mMid; // 歌曲mid public String mOriUrl; // 歌曲原唱URL public String mAccUrl; // 歌曲消音伴奏URL public String mVideoUrl; // MV URL public String mAlbumUrl; // 封面 URL public byte[] mNodeBytes; // 打分 Note 数据 public byte[] mQycBytes; // 加密 QRC 歌词 public byte[] mLrcBytes; // 加密 LRC 歌词 public int mMvHasLyric; // MV是否带歌词 .... 3.6 播放参数 播放模式:PlayMode public enum PlayMode { NORMAL(1), // 听歌 KTV(2); // 唱歌 } LoopMode public enum LoopMode { LOOP, /*循环*/ ONCE, /*循环单次*/ } 默认播放类型:DataSourceType public enum DataSourceType { ORIGINAL, //原唱 ACCOMPANY //伴奏 } 视频填充模式:VideoScaleType public enum VideoScaleType { FILL, //填充满屏幕 FILL_FIT, //比例填充 FILL_WIDTH, //比例填充,填充满宽度 FIT //根据视频尺寸来显示 } 3.7 设置音频、视频清晰度 KtvPlayerConfig.getInstance().setAudioQuality(SongInfoModel.AUDIO_QUALITY_NORMAL); KtvPlayerConfig.getInstance().setVideoQuality(SongInfoModel.MV_720P); int AUDIO_QUALITY_NORMAL = 1; //标准音质 int AUDIO_QUALITY_HQ = 3; //HQ音质,需要是会员 int MV_480P = 480; int MV_720P = 720; int MV_1080P = 1080; //需要是会员

在预加载前通过KtvPlayerConfig设置音频视频资源的清晰度,在预加载时会加载指定的清晰度,如果没有对应资源,会返回其他等级清晰度的资源

3.8 KaraokePlayerCallback public static abstract class KaraokePlayerCallback { //回调分发Handler,提升性能用。使用主线程则返回null public Handler getDispatchHandler() { return null; } // 歌曲缓冲进度 public void onPrepareResourceBufferingChange(int bufferPercent); // 歌曲准备完成,回调当前歌曲信息 abstract public void onPlayPrepare(SongInfoObject song); // 播放开始 abstract public void onPlayStart(SongInfoObject song); //调用setTimeArray()后,歌曲信息解析完成后的回调,可以在此初始化打分View public void onLyricResourceChanged(SongInfoObject songInfoObject) // accomPath: 伴奏pcm, micPath: 人声pcm, playFinish:是否已播放完成。 abstract public void onPlayEnd(String accomPath, String micPath, boolean playFinish); abstract public void onError(Throwable throwable); abstract public void onResume(); abstract public void onPause(); //当视频多次缓冲失败等手动切大图后,会调用此接口让界面。显示封面图片 abstract public void onShowPicture(String url, boolean isError); //缓存开始 abstract public void onBufferingStart(); //缓存结束 abstract public void onBufferingEnd(); // 当前播放进度 //currentTime 当前播放时间 ms , duration 播放总时长ms abstract public void onPlayPositionChange(int currentTime, int duration); // 视频缓冲进度 abstract public void onBufferPercentChange(int percent); /** * 歌词信息已加载完成 */ public void onLyricResourceChanged(SongInfoObject songInfoObject) { } }

生命周期:

onPlayPrepare onBufferingStart onPrepareResourceBufferingChange // 音频缓冲进度 100% 以后 onPlayMediaMetaReady, 如果音频快速 起播选项,不到100% onPlayMediaMetaReady onPlayMediaMetaReady // Audio ready 播放时长 onBufferingEnd // 缓冲结束 onPlayStart onPlayEnd 4. 歌词 LyricView

如果SongInfoObject.isMvHasLyric()不为true(即MV没有自带歌词),则可显示歌词View。

1). 如果使用K歌的默认播放页,且不需要更改歌词样式:无需更改,K歌的播放页默认支持歌词显示;2). 如果使用K歌默认播放页,但是需要改变歌词组件的样式 在播放前通过设置歌词View参数改变歌词样式,需要在onPlayStart回调前设置例子:

val builder = LyricParam.Builder() .setIsShadow(true) .setLayoutMode(LAYOUT_MODE_LINES) .setUpdateTime(60) .setLightClipOffsetX(0) .setLightTextColor(resources.getColor(R.color.colorForgiven)) .setTextColor(resources.getColor(R.color.red_orange)) .setTextSize(200) .setLightUnselectTextStrokeWidth(30) .setLightTextSize(150) .setLineMargin(30) .setStrokeWidth(30) .setLyricBackgroundColor(resources.getColor(R.color.colorGray)) .setLightUnselectTextColor(resources.getColor(R.color.colorLightBlack)) .build() LyricController.getInstance().lyricParam = builder

3). 如果业务方不使用K歌的播放页,需要主动初始化歌词逻辑:step1:在播放页的布局中添加LyricView,样式可以通过布局中的参数控制例子:

step2:在onPlayPrepare后初始化Lyric:例子:

//歌词初始化有轻微耗时,可异步加载(以kotlin flow为例)。 flow { val player = viewModel.currPlayer() LyricInitializer().setLyricView(lyricView) .setLyricData(songInfo?.mQrcBytes, songInfo?.mLrcBytes) .setLyricTimeLine { player?.timeLineTime?.toInt() ?: 0 } .setIsShowLyric(songInfo?.mMvHasLyric != 1).build() player?.timeArray = LyricController.getInstance().timeArray }.flowOn(Dispatchers.IO).map { val player = viewModel.currPlayer() player?.addCallback(LyricController.getInstance()) LyricController.getInstance().addLyricListener(object : LyricListener { override fun OnLyricTextUpdate(text: String?) { //歌词单句回调 Logger.d(TAG, "OnLyricTextUpdate $text") } override fun OnLyricCountDown(countDown: Int) { //跳过前奏倒计时 比如5000代表 还有5s开始显示歌词 Logger.d(TAG, "OnLyricCountDown $countDown") } }) }.flowOn(Dispatchers.Main).launchIn(owner.lifecycleScope)

歌词默认刷新频率为64ms, 如果觉得流畅度不够,可改为16ms(cpu占用会升高):

com.tme.ktv.lyric.widget.LyricView#setLyricUpdateTime(64)

需要在播放界面销毁时主动释放歌词view 否则会导致内存泄漏

LyricInitializer().setLyricView(null) 5. 打分

如果使用K歌的播放页,则默认支持打分UI如果使用自定义的播放页,需要主动添加打分UI

5.1 播放页布局中添加MidiScoreView 5.2 初始化 配置Activity

​ mMidiScoreView.setActivity(this) 这个Api 需要设置,不然影响动画

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mMidiScoreView = this.findViewById(R.id.midi_view); mMidiScoreView.setActivity(this); 5.2 Callback 事件中 MidiScoreView 响应 private class ScoreListenerImpl extends KaraokePlayer.OnScoreListener{ @Override protected void onPrepareScore(SingCompetitor competitor, int nums) { //获取到节拍数据,设置给midiView mMidiScoreView.initData(competitor); } @Override protected void onDoingScore(int score, String level, int timeStamp) { //单句实时打分结果,数值和等级 } @Override protected void onFinishScore(int totalScore, String level) { //演唱结束后的总分结果 } } inner class CallbackImpl : KaraokePlayer.KaraokePlayerCallback() { private fun startMidiView() { if (KtvPlayerConfig.getInstance().isEnableScore) { midiView.stopMidi() midiView.startMidi(viewModel.currPlayer()?.timeLineTime ?: 0) } } //歌词资源加载完成 override fun onLyricResourceChanged(songInfoObject: SongInfoObject?) { super.onLyricResourceChanged(songInfoObject) startMidiView() } override fun onResume() { Logger.d(TAG, "call onResume") startMidiView() } //歌曲已到达起播条件,但歌词资源还没有 override fun onPlayPrepare(request: SongInfoObject?) { Logger.d(TAG, "onPlayPrepare ${request?.simpleInfo}") } override fun onPause() { Logger.d(TAG, "onPause") midiView.stopMidi() } override fun onPlayEnd(accomPath: String?, micPath: String?, isFinish: Boolean) { Log.e(TAG, "onPlayEnd") midiView.stopMidi() midiView.releaseMidi() val openScore = viewModel.currSongInfo().value?.mNoteBytes != null && viewModel.currPlayer()?.playMode == KaraokePlayer.PlayMode.KTV && PlayConsole.get().openScore.value == true if (!openScore && isFinish) { playNextIfNeed() } } override fun onSeekComplete(position: Long) { super.onSeekComplete(position) Logger.d(TAG, "onPlayPositionChange currentTime $position ") midiView.seekMidi(position) } override fun onBufferingStart() { super.onBufferingStart() midiView.stopMidi() } override fun onBufferingEnd() { super.onBufferingEnd() startMidiView() } } 需要把两个实现添加到播放器中 karaokePlayer.addCallback(CallbackImpl()) karaokePlayer.addOnScoreListener(ScoreListenerImpl()) 5.4 MidiView 修改配置 打分条 打分条可以根据 KaraokePlayer 的打分回调 OnScoreListener自定义实现 ScoreBarView.setLayoutResId 支持应用传入layout, layout 布局参考 layout_karaoke_score_level.xml,可以在这个layout 基础上修改,只支持修改布局属性,控件ID 需要保持不变。 MidiScoreView.getScoreBarView() 获取ScroeBarView, 动态修改属性 public void setScoreBarBarBackground(@DrawableRes int resourceId); public View getScoreView(); public View[] getLevelImageViews(); 音高数据

音高数据支持修改 背景和画笔风格

MidiScoreView.getIntonationViewGroup().getIntonationView().getIntonationViewerParam();

IntonationViewerParam 画笔如下

/** * 用于画打点线的 Paint */ public final Paint mLinePaint; /** * 用于错误的画音符的 Paint */ public final Paint mNoteMissPaint; /** * 用于画正确的音符条的 Paint */ public final Paint mNoteHitPaint; /** * 打点线左侧区域的背景的 Paint */ public final Paint mLeftAreaBackgroundPaint; /** * 画用户音高的 Paint */ public final Paint mGrovePaint;

更改midiView的高度 背景色 打分条的画笔颜色 粗细例如:在midiView的界面内,获取到MidiScoreView后:

val intonationViewerParam = midiView.intonationViewGroup.intonationView.intonationViewerParam midiView.midiViewHeight = DisplayUtil.dp2px(140f) midiView.midiViewBackgroundColor = getActivity()?.resources?.getColor(R.color.ktv_brown) ?: -1 val missPaint = Paint() missPaint.color = Color.parseColor("#234512") missPaint.strokeWidth = DisplayUtil.dp2px(8.0F).toFloat() intonationViewerParam.noteMissPaint = missPaint val hitPaint = Paint() hitPaint.color = Color.parseColor("#666666") hitPaint.strokeWidth = DisplayUtil.dp2px(8.0F).toFloat() intonationViewerParam.noteHitPaint = hitPaint 6. 如何使用开放平台接口 使用sdk提供的登录模块:此时sdk内有鉴权信息,所以可参照demo中的接口访问示例,看如何声明接口和发送请求,不用手动处理接口中的鉴权字段。 未使用sdk提供的登录模块:按接口文档自己请求,此时需要集成方自己添加接口中需要的鉴权字段(X-Open-App-ID、X-Open-Access-Token、Device-ID等)。 7. FAQ 编译不通过gradle.properties增加编译配置:android.enableDexingArtifactTransform=false 登录报3003错误,扫码后提示"Sign check failed"出现这个错误码就是下面两种原因之一: 设备本地时间不对 配置的appkey与sdk的当前环境不一致。如appkey是正式环境的,但sdk是基准环境。 demo未启动成功,报错“appid isNullOrEmpty”。查看文档1.1节的配置是否已处理。


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3