修改历史
| 版本 | 日期 | 主要作者 | |
|---|---|---|---|
| 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:要切换到的清晰度ID2.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;
}