修改历史
版本 | 日期 | 主要作者 | |
---|---|---|---|
0.1 | 2017/09 | SDK团队 | 初始文档 |
0.5 | 2018/03 | SDK团队 | 支持清晰度切换API |
1.0.0 | 2018/12 | SDK团队 | 重构无UI版本 |
1.1.0 | 2019/05 | SDK团队 | 支持清晰度切换;增加功能键发送 |
1 SDK集成准备
1.1 导入SDK
将我们提供的SDK压缩包中libs目录下的aar文件拷贝到你工程的app/libs目录下,并在app Module的build.gradle文件中,添加repositories和dependencies,并且添加所需依赖库,如下所示:
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
compile 'com.android.volley:volley:1.1.0'
compile 'com.alibaba:fastjson:1.2.0'
compile (name: 'saas-sdk-v1.0', ext: 'aar')
}
注:
1.海马云手机客户端SDK目前只支持通过Gradle集成SDK的集成方式。
2.其中saas-sdk-v1.0是海马的sdk,其他为海马sdk用到的包。如果无法正常集成,请在Project的build.gradle文件中配置repositories,使用jcenter,如下所示:
allprojects {
repositories {
jcenter()
}
}
1.2 配置权限
在工程AndroidManifest.xml文件中添加如下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
注:
1.andriod 6.0之后,权限获取有所变动,请参考官网:https://developer.android.com/training/permissions/requesting.html
2.小技巧:当xml中的targetSdkVersion=x(x<23)时候, 可以正常获取信息(相当于跳过了6.0权限检查)
1.3 配置硬件加速
打开硬件加速可以提高渲染能力,提高用户体验。
<application ……
android:hardwareAccelerated="true">
1.4 配置渠道信息
渠道信息包括以下两个字段:
HMCP_ACCESS_KEY_ID //接入商的唯一ID,用来区分不同的接入商。该值由海马云分配。
HMCP_CHANNEL_ID //渠道号,由接入商配置。如果应用本身不区分渠道,可以设置为一个随机值的的字符串。
这两个参数需要写在AndroidManifest.xml中,代码示例如下:
<meta-data android:name="HMCP_ACCESS_KEY_ID" android:value="海马商户分配的id" />
<meta-data android:name="HMCP_CHANNEL_ID" android:value="渠道id" />
1.5 代码混淆
如果你的apk最终会经过代码混淆,请在proguard配置文件中加入以下代码:
-dontwarn org.codehaus.**
-keep class org.codehaus.** {*;}
-keep interface com.haima.hmcp.listeners.*{*;}
-keep class com.haima.hmcp.beans.*{*;}
-keep enum com.haima.hmcp.enums.*{*;}
-keep class com.haima.hmcp.**{*;}
-keep enum com.haima.hmcp.websocket.WebSocketCloseNotification{*;}
-keep interface com.haima.hmcp.websocket.WebSocket{*;}
-keep interface com.haima.hmcp.websocket.WebSocketConnectionObserver{*;}
-keep class com.haima.hmcp.websocket.WebSocketConnection{public
<methods>;}
-keep class com.haima.hmcp.websocket.WebSocketOptions{public <methods>;}
-keep class com.haima.hmcp.websocket.WebSocketException{*;}
-keep interface com.hmcp.saas.sdk.listeners.*{*;}
-keep class com.hmcp.saas.sdk.beans.*{*;}
-keep class com.hmcp.saas.sdk.enums.*{*;}
-keep class com.hmcp.saas.sdk.SaasSDK{public <methods>;}
-keep class de.tavendo.autobahn.**{*;}
-keep class tv.haima.ijk.media.player.** { *; }
-keep interface tv.haima.ijk.media.player.listeners.*{*;}
-keep interface tv.haima.ijk.media.player.IMediaPlayer{*;}
-keep class com.netease.LDNetDiagnoService.LDNetDiagnoService{public <methods>;}
-keep interface com.netease.LDNetDiagnoService.LDNetDiagnoListener{public <methods>;}
-keep class com.netease.LDNetDiagnoService.LDNetTraceRoute { *; }
-dontwarn org.openudid.**
-keep class org.openudid.**{*;}
2 SDK集成
2.1 Layout中布局组件
在布局文件中添加CloudPhoneVideoView组件
<com.haima.hmcp.widgets.CloudPhoneVideoView
android:id="@+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
CloudPhoneVideoView组件所在Activity需要以下属性
<activity
android:name=".xxxActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTask"
android:screenOrientation="landscape" />
2.2 Activity中初始化组件
以下是Activity中的示例代码,并且做了注释,后面还有详细的解释。
import com.haima.hmcp.widgets.*;
import com.haima.hmcp.listeners.*;
import com.haima.hmcp.enums.*;
import com.haima.hmcp.beans.*;
import com.haima.hmcp.utils.*;
public class HmcpPlayerActivity extends AppCompatActivity implements
HmcpPlayerListener {
@Override
Protected void onCreate(Bundle savedInstanceState) {
//获取组件
CloudPhoneVideoView VideoView= (CloudPhoneVideoView) this.findViewById(R.id.gameView);
}
//Activity需要implements HmcpPlayerListener接口
@Override
public void onError(ErrorType errorType, String s) { // 出错信息回调
}
@Override
public void onSuccess() { // SDK启动成功并且开始播流的回调
}
@Override
public void onExitQueue() {
this.finish(); // 内部弹窗退出回调
}
@Override
public void onMessage(Message message) { // 收到退出登录消息回调
}
@Override
public void onSceneChanged(String s) { // 收到场景切换的回调
}
@Override
public void onNetworkChanged(NetWorkState state) { // ⽹络发⽣变化时的回调。
}
@Override
public void onPlayerError(String errorCode, String errorMsg) {
}
@Override
public void onPlayStatus(int status, long value, String data) {
}
@Override
public void HmcpPlayerStatusCallback(String callback) {
}
@Override
public void onInputMessage(String msg) {
}
//Activity部分方法的重写
@Override
protected void onResume() {
super.onResume();
videoView.onResume();
}
@Override
protected void onPause() {
super.onPause();
videoView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (videoView != null) {
videoView.disconnect();
}
}
}
注:
1.Activity的onResume(),onPause(),onDestroy()必须重写,并在重写的方法中调用CloudPhoneVideoView的相应方法,实现可以参考上面的代码。
2.Activity需要实现 HmcpPlayerListener的接口,包括:
● onError(ErrorType type, String errorInfo) 是连接云手机失败后的回调,可以通过errorInfo弹出提示信息;
● onSuccess(),连接云手机成功后回调。
● onExitQueue()是退出云手机时的回调,需要在该回调内调用finish()退出Activity。
● onMessage(Message message)是SDK的消息通知回调。
● onSceneChanged(String sceneMessage)是SDK使用云手机过程中各状态发生变化时通知应用的接口,sceneMessage是包括sceneId和extraInfo{}两个字段的JSON串。详细请参考“参数说明”中的“场景切换”章节。
● onNetworkChanged(NetWorkState state)是SDK在监听到网络变化后通知应用的接口,NetWorkState为enum类型,现在包括ISWIFI,NOTWIFI,NO_NETWORK三种。
声明:上述所有接口全部需要在Activity的Context里执行,也就是在主线程中执行,不需要调用runOnUiThread,或者post之类的方法。
2.3 开始使用云手机
调用CloudPhoneVideoView的connect()函数,SDK会与海马云的server通讯,申请服务成功后云手机的桌面会显示在CloudPhoneViewView组件上。
代码示例如下:
UserInfo userInfo = new UserInfo();
userInfo.userId = "";
userInfo.userToken = "";
videoView.setUserInfo(userInfo);
Bundle bundle = new Bundle();
bundle.putString(CloudPhoneVideoView.PHONE_ID, phoneId);
bundle.putString(CloudPhoneVideoView.ACCESS_KEY_ID, accessKeyId);
bundle.putString(HmcpVideoView.EXTRA_ID, "cloudPhoneDemo");
bundle.putSerializable(CloudPhoneVideoView.SCREEN_ORITENTION, ScreenOrientation.PORTRAIT);
bundle.putString(CloudPhoneVideoView.TOKEN, token);
videoView.connect(getApplicationContext(), bundle, new OnInitCallBackListener() {
@Override
public void success()
{
Log.d(TAG, "InitCallback success");
}
@Override
public void fail(String msg)
{
Log.e(TAG, "InitCallback fail: " + msg);
}
});
各参数定义如下:
● phoneId: 申请云手机时服务器返回的唯一标识符。
● accessKeyId:渠道信息。
● Orientaion::标识云手机是竖屏显示还是横屏显示。
● token:用来校验参数的有效性,token的生成请参考服务端SDK文档或者联系我们获取生成方法。
● videoView: 云手机显示组件。
● OnInitCallBackListener:回调接口,用于返回云手机的连接状态。
2.4 停止使用云手机
停止使用云手机只是断开与远程云手机的连接,云手机还将继续工作,客户端还可以再次连接并且正常使用。
有两种方式停止使用云手机:
1.连续点击两次返回键退出
这种方法退出需要app在Activity重载onPause(),onDestroy(),重载可以参考以下代码
@Override
protected void onPause() {
super.onPause();
videoView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (videoView!= null) {
videoView.disconnect();
}
}
2.5 获取清晰度列表
videoView.mResolutionList; //获取清晰度列表
2.6 切换清晰度
videoView.switchResolution(String resolution); //resolution:要切换到的清晰度ID
2.7 发送功能键
videoView.onButtonClick(KeyEvent.KEYCODE_HOME); //发送HOME键videoView.onButtonClick(KeyEvent.KEYCODE_BACK); //发送返回键videoView.onButtonClick(KeyEvent.KEYCODE_MENU); //发送MENU键
3 参数说明
3.1 场景切换
使用云手机过程中的各个状态切换都会通过onSceneChanged(String sceneMessage)接口通知应用,sceneMessage字符串是有JSON串转换的,格式如下:
{
“sceneId”: “” , //切换到的场景Id
“extraInfo” {} , //场景切换的扩展信息,参考sceneId说明
}
以下是使用过程中个状态的sceneId及描述:
名称 | sceneId | extraInfo{} | 备注 |
---|---|---|---|
onSceneChanged应用场景切换详细信息 | init | 初始化中,extraInfo为空 | |
wait | 排队中,extraInfo为空 | ||
play | 开始使用,extraInfo为空 | ||
stop | 使用结束,extraInfo参考参数示例 | ||
mait | 云玩维护,extraInfo参考参数示例 | ||
crtp | 显示自动降低码率tips,extraInfo参考参数示例 | ||
crst | 开始切换码率(当前码率,目标码率,自动手动模式),extraInfo参考参数示例 | ||
cred | 切换码率完成(成功,失败,当前码率),extraInfo参考参数示例 |
以下是使用云手机过程中各状态对应的扩展信息extraInfo{}描述:
“init” : 初始化中, extraInfo 为空
“wait” : 排队中, extraInfo 为空
“play” : 开始使用, extraInfo 为空
“stop” : 使用结束
“extraInfo” : {
“interval” : 当次使用时长,单位 秒
“reason” : “time_limit” :当次时间到
“no_operation” :超时无操作
“instance_err” :实例出错
“forced” :强制下线
“token_expire” :Token失效
“log_off” :注销
“network_off” :网络不可用
“request_err” :网络请求出错
}
“mait” : 云玩维护
“extraInfo” : {
“progress” : “soon” :即将
“start”:进入
“done” :恢复
}
“crtp” : 显示自动降低码率tips
“extraInfo” : {
“minimum” : 当前码率是否最低码率 “0”-否 “1”-是
}
“crst” : 开始切换码率 (当前码率,和目标码率,自动手动模式)
“extraInfo” : {
“source” : “” 当前码率
“des” : “” 目标码率
“method” : 自动手动模式 “0”-手动 “1”-自动
}
“cred” : 切换码率完成 (成功,失败,当前码率)
“extraInfo” : {
“result” :”0”-失败 “1”-成功
“cur_rate” :当前码率
}
4 注意事项
4.1 安卓5.0+系统需要手动申请权限,申请权限代码如下:
private void requestCloudSdkPermission() {
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
String[] mPermissionList = new String[]{Manifest.permission.READ_PHONE_STATE};
ActivityCompat.requestPermissions(CloudPlayActivity.this, mPermissionList, 100);
} else {
// 不需要什么权限
}
}
4.2 关于部分手机虚拟键盘显示实效设置问题
Activity实现OnSystemUiVisibilityChangeListener, 并且重写onSystemUiVisibilityChange(int visibility)方法
在onCreate()方法中注册OnSystemUiVisibilityChangeListener,如下:
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
重写onSystemUiVisibilityChange(int visibility)方法,如下:
@Override
public void onSystemUiVisibilityChange(int visibility) {
setHideVirtualKey();
}
private void setHideVirtualKey() {
//保持布局状态
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
//布局位于状态栏下方
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
//全屏
View.SYSTEM_UI_FLAG_FULLSCREEN |
//隐藏导航栏
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
if (Build.VERSION.SDK_INT >= 19) {
uiOptions |= 0x00001000;
} else {
uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
}
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
}
4.3 推荐设置Activity界面为全屏沉浸式效果,在onCreate()方法中添加如下代码:
public static boolean setTranslucentStatus(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { //4.4以上,状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
return true;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0以上状态栏透明
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
return true;
}
return false;
}