即构科技集成直播后,需求中需要拉流端(观众)切换到阿里云的流地址,而不是用即构返回的即构流地址,原因是即构的流比较贵,阿里云的收费非常的便宜,差距大概是阿里云的四倍吧,所以考虑到用前端进行流地址的切换,直播推流决定着观众的拉流,也就是说,主播直播的时候推的是什么流,观众拉的就是什么流了,所以在推流的时候进行切换流地址,(没有让即构在返回列表里返回多个流地址,据说即构那边是可以配置的),我是直接与后台对接,把后台拿到的即构流、阿里云流等都通过一个接口返回给我,我自己截取替换流地址;对于阿里云的流地址的使用,除了替换还有进行鉴权,下面看看具体的文档:
阿里云鉴权的说明:https://www.alibabacloud.com/help/zh/doc-detail/97943.htm?spm=a2c63.p38356.b99.29.37182eb8uQa4rL
https://www.alibabacloud.com/help/zh/doc-detail/66016.htm?spm=a2c63.p38356.b99.72.3e31zGVxzGVxCj
https://help.aliyun.com/knowledge_list/49528.html
即构相关链接:
https://doc.zego.im/CN/303.html
startPublishing 开始推流
startPublishing2 开始推流,可推两路流
startPlayingStream 开始拉流
https://doc.zego.im/CN/491.html#3
如何自定义指定的CDN拉流
如何获取转码推流地址
- 推流地址规则
直播只支持RTMP格式推流。
推流地址格式为
RTMP://推流域名/AppName/StreamName?鉴权串
- 示例
例如,推流域名是
push.aliyunlive.com
,AppName为app,StreamName为stream,鉴权key是123,则推流地址为RTMP://push.aliyunlive.com/app/stream?auth_key=timestamp-rand-uid-md5hash
- 示例
例如,播流域名是
play.aliyunlive.com
,AppName为app,StreamName为stream,鉴权key是456,使用sd模板,拼接地址如下所示:- RTMP协议地址
rtmp://play.aliyunlive.com/app/stream_sd?auth_key=timestamp-rand-uid-md5hash
- FLV协议地址
http://play.aliyunlive.com/app/stream_sd.flv?auth_key=timestamp-rand-uid-md5hash
- HLS协议地址
http://play.aliyunlive.com/app/stream_sd.m3u8?auth_key=timestamp-rand-uid-md5hash
- RTMP协议地址
鉴权串字段对照,参见鉴权地址Democode。
字段 | 描述 |
---|---|
timestamp | 失效时间,整形正数,固定长度 10,1970 年 1 月 1 日以来的秒数。用来控制失效时间,10 位整数,有效时间 1800s |
rand | 随机数,建议使用UUID(不能包含中划线“-”,例如: 477b3bbc253f467b8def6711128c7bec 格式) |
uid | 暂未使用(设置成 0 即可) |
md5hash | 通过 md5 算法计算出的验证串,数字和小写英文字母混合(0-9和a-z),固定长度 32 |
鉴权算法
未鉴权URL: http://DomainName/AppName/StreamName.flv
。
- 鉴权后的URL:
http://DomainName/Filename?auth_key=timestamp-rand-0-md5hash
。
参数说明
-
timestamp:失效时间,整形正数,固定长度 10,1970 年 1 月 1 日以来的秒数。用来控制失效时间。
-
rand:随机数,一般设成 0。
-
md5hash:通过md5算法计算出的验证串,算法(假设用户的密钥为 123456:md5hash=md5(timestamp-rand-0-123456)。
鉴权示例
用户给 a.com 这个域名配置了一个鉴权密钥:xyz,希望鉴权有效时间为:2017-07-28 05:43:20,准备用rtmp://video-center.alivecdn.com/live/abc?vhost=a.com
进行推流,相应的鉴权计算逻辑为:
推流
-
获取推流uri:
/live/abc
。 -
获取 timestamp:过期时间 2017-07-28 05:43:200 的unixtimestamp为:1501191800。
-
组装加密字符串:
/live/abc-1501191800-0-0-xyz
。 -
计算加密后的字符串:md5 (“/live/abc-1501191800-0-0-xyz”) = 7856896499b661b1123dca82d7406aa3。
-
拼接加密后的推流URL:
rtmp://video- center.alivecdn.com/live/abc?vhost=a.com?auth_key=1501191800-0-0-7856896499b661b1123dca82d7406aa3
。
播放
播放的计算方法与推流类似,以 httpflv 播放地址为例。
-
未鉴权的 URL 为:
http:// a.com/live/abc.flv
。 -
获取播放URL:
/live/abc.flv
。 -
获取 timestamp:过期时间 2017-07-28 05:43:200 的 unixtimestamp 为:1501191800。
-
组装加密字符串:
/live/abc.flv-1501191800-0-0-xyz
。 -
计算加密后的字符串:md5(“/live/abc.flv-1501191800-0-0-xyz”) = b022238fd0cd1c8efae2ba84fc0c9119。
-
拼接加密后的推流URL:
http://a.com/live/abc.flv?auth_key=1501191800-0-0-b022238fd0cd1c8efae2ba84fc0c9119
。 -
设置方法:可以在直播控制台进行设置,控制台中鉴权计算器,可以方便的生成鉴权 URL。详情参考 直播鉴权。
关键说明上面已经贴出,下面来看看代码部分:
1.在BasePublishActivity中设置流地址的切换:
(替换前)
/**
* 推流成功.
*/
protected void handlePublishSucc(String streamID, HashMap<String, Object> info) {
// ViewLive viewLivePublish = getViewLiveByStreamID(streamID);
// List<String> listUrls = getShareUrlList(info);
// if (viewLivePublish != null && listUrls.size() >= 2) {
// // 设置分享连接
// viewLivePublish.setListShareUrls(listUrls);
//
// // 将流地址发送给房间观众
// Map<String, String> mapUrls = new HashMap<>();
// mapUrls.put(Constants.FIRST_ANCHOR, String.valueOf(true));
// mapUrls.put(Constants.KEY_HLS, listUrls.get(0));
// mapUrls.put(Constants.KEY_RTMP, listUrls.get(1));
// Gson gson = new Gson();
// String json = gson.toJson(mapUrls);
// mZegoLiveRoom.updateStreamExtraInfo(json);
// }
super.handlePublishSucc(streamID);
}
上述代码注释取消就可以了,可以看到
mapUrls.put(Constants.KEY_RTMP, url1);
就是RTMP直播时候提供给观众的流地址,在这里进行替换。
(替换后)
/**
* 推流成功.
*/
protected void handlePublishSucc(String streamID, HashMap<String, Object> info) {
getRtmpUrl(streamID,info);
super.handlePublishSucc(streamID);
}
/**
* 获取直播推流的流地址
*/
private void getRtmpUrl(final String streamId, final HashMap<String, Object> info) {
HttpParam param = new HttpParam();
param.put("streamId", streamId);
HttpLoader.getInstance(this).post(UrlConstant.RTMPURL, param, UrlConstant.USER_EDIT_RECORDS_REQUEST_CODE, new HttpLoader.HttpListener() {
@Override
public void onSuccess(int resultCode, String result) {
RtmpUrlBean entity = JsonUtils.toEntity(result, RtmpUrlBean.class);
if (entity != null) {
if(!TextUtils.isEmpty(entity.getData())){
//截取流地址
String rtmpUrl = entity.getData();
if(rtmpUrl.indexOf(",") != -1)//是否包含,
{
// System.out.println("包含该字符串");
String[] splitUrl = rtmpUrl.split(",");
String url0 = splitUrl[0];
url1 = splitUrl[1];
String url2 = splitUrl[2];
}
//
}
ViewLive viewLivePublish = getViewLiveByStreamID(streamId);
List<String> listUrls = getShareUrlList(info);
if (viewLivePublish != null && listUrls.size() >= 2) {
// 设置分享连接
viewLivePublish.setListShareUrls(listUrls);
// 将流地址发送给房间观众
Map<String, String> mapUrls = new HashMap<>();
mapUrls.put(Constants.FIRST_ANCHOR, String.valueOf(true));
mapUrls.put(Constants.KEY_HLS, listUrls.get(0));
if(TextUtils.isEmpty(entity.getData())){
mapUrls.put(Constants.KEY_RTMP, listUrls.get(1));
}else {
mapUrls.put(Constants.KEY_RTMP, url1);
}
Gson gson = new Gson();
String json = gson.toJson(mapUrls);
mZegoLiveRoom.updateStreamExtraInfo(json);
}
}
}
@Override
public void onfailed(int resultCode, String error) {
}
});
}
可以看到,后台返回的字符串通过逗号截取后进行替换地址,这样就可以,接着就是要进行阿里云流地址的鉴权了:
2.在BaseLiveActivity中设置阿里云鉴权:
protected void publishStream(){}方法中修改,将原来的startPublishing修改成startPublishing2,把相应的参数加上:
protected void publishStream() {
if (TextUtils.isEmpty(mPublishStreamID)) {
return;
}
if (isStreamExisted(mPublishStreamID)) {
Toast.makeText(this, "流已存在", Toast.LENGTH_LONG).show();
return;
}
ViewLive freeViewLive = getFreeViewLive();
if (freeViewLive == null) {
return;
}
// 设置流信息
freeViewLive.setStreamID(mPublishStreamID);
freeViewLive.setPublishView(true);
// 初始化配置信息, 混流模式使用
initPublishConfigs();
// 输出发布状态
recordLog(MY_SELF + ": start publishing(" + mPublishStreamID + ")");
// 设置水印
// mZegoLiveRoom.setWaterMarkImagePath("asset:watermark.png");
// Rect rect = new Rect();
// rect.left = 50;
// rect.top = 20;
// rect.right = 200;
// rect.bottom = 170;
// mZegoLiveRoom.setPreviewWaterMarkRect(rect);
// mZegoLiveRoom.setPublishWaterMarkRect(rect);
// 开启流量自动控制
int properties = ZegoConstants.ZegoTrafficControlProperty.ZEGOAPI_TRAFFIC_FPS
| ZegoConstants.ZegoTrafficControlProperty.ZEGOAPI_TRAFFIC_RESOLUTION;
mZegoLiveRoom.enableTrafficControl(properties, true);
//开启双声道
mZegoLiveRoom.setAudioChannelCount(2);
// 开始播放
// if(PreferenceUtil.getInstance().getUseExternalRender(false)){
// if(videoRenderer == null){
// videoRenderer = new VideoRenderer();
// }
// 开启外部渲染
// videoRenderer.init();
// videoRenderer.setRendererView(freeViewLive.getTextureView());
// mZegoLiveRoom.setZegoExternalRenderCallback(videoRenderer);
// }else{
// }
mZegoLiveRoom.setPreviewView(freeViewLive.getTextureView());
mZegoLiveRoom.startPreview();
if (PreferenceUtil.getInstance().getVideoFilter(false)) {
mZegoLiveRoom.setFilter(ZegoFilter.Wine, ZegoConstants.PublishChannelIndex.MAIN);
}
mZegoLiveRoom.enableMic(mEnableMic);
mZegoLiveRoom.enableCamera(mEnableCamera);
// 阿里云主播推流未鉴权
// mZegoLiveRoom.startPublishing(mPublishStreamID, mPublishTitle, mPublishFlag);
//阿里云主播推流鉴权
//传入自定义参数,即传入应用名称和流名称
String yuMing = "ceshipushs_a.a.sallsmeanalysispxy.xyzk";
//时间戳,有效时间
// long time = (System.currentTimeMillis() + 1800) / 1000;
long time = (System.currentTimeMillis() / 1000) + 1800;
//加密key,即直播后台鉴权里面自行设置
String key = "AldNSFA6h9";
String strpush = "/" + "live" + "/" + mPublishStreamID + "-" + time + "-0-0-" + key;
// 里面的直播推流中心服务器域名、vhost域名可根据自身实际情况进行设置
String pushurl = "rtmp://ceshi.a.sallysispxy.xyzk/" + "lives" + "/" + mPublishStreamID + "?auth_key=" + time + "-0-0-" + MD5Util.MD5(strpush);
String rtmpurl = "rtmp://" + yuMing + "/" + "live" + "/" + mPublishStreamID + "?auth_key=" + time + "-0-0-" + MD5Util.MD5(strpush);
LogUtils.e("KING: " + pushurl);
LogUtils.e("KING: " + rtmpurl);
LogUtils.e("KING: " + strpush);
mZegoLiveRoom.startPublishing2(mPublishStreamID, mPublishTitle, mPublishFlag,"1024","auth_key=" + time + "-0-0-" + MD5Util.MD5(strpush),ZegoConstants.PublishChannelIndex.MAIN);
//阿里云主播推流鉴权
mZegoLiveRoom.setPreviewViewMode(ZegoVideoViewMode.ScaleAspectFill);
}
上述代码中涉及到设置时间戳:(设置十位数的时间戳)
a.十位数的UNIX时间戳:
Long newTime = System.currentTimeMillis();
String time = newTime.toString().substring(0, 10);
或:
long time = (System.currentTimeMillis() / 1000) + 1800;
或:
long timeStampSec = System.currentTimeMillis()/1000;
String timestamp = String.format("%010d", timeStampSec);
或自定义工具类,写在方法中:
/**
* 将时间转换成时间戳
* 设置即构、阿里云需要的时间戳(秒)
* @return counter
*/
public static String dataToStamp() {
long time = System.currentTimeMillis();
time += 30*1000*60;//在当前系统时间加30分钟,有效时间30分钟
String res = "";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date data = simpleDateFormat.parse(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time));
long ts = data.getTime();//得出的是毫秒,阿里云需要的是秒,所以除以1000
ts = ts/1000;
res = String.valueOf(ts);
} catch (ParseException e) {
e.printStackTrace();
}
return res;
}
// long time = (System.currentTimeMillis() + 1800) / 1000;默认是13位的。
3.MD5工具类:(最下面有视频直播Url鉴权工具类)
package com.southsummer.goddessplan.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public final static String MD5(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f','g','h','i','j','k','l','m','n',
'o','p','q','r','s','t','u','v','w','x',
'y','z'};
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
/**
* @Description: 32位小写MD5
*/
public static String parseStrToMd5L32(String str) {
String reStr = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = md5.digest(str.getBytes());
StringBuffer stringBuffer = new StringBuffer();
for (byte b : bytes) {
int bt = b & 0xff;
if (bt < 16) {
stringBuffer.append(0);
}
stringBuffer.append(Integer.toHexString(bt));
}
reStr = stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return reStr;
}
/**
* @param str
* @Description: 32位大写MD5
*/
public static String parseStrToMd5U32(String str) {
String reStr = parseStrToMd5L32(str);
if (reStr != null) {
reStr = reStr.toUpperCase();
}
return reStr;
}
/**
* @param str
* @Description: 16位小写MD5
*/
public static String parseStrToMd5U16(String str) {
String reStr = parseStrToMd5L32(str);
if (reStr != null) {
reStr = reStr.toUpperCase().substring(8, 24);
}
return reStr;
}
/**
* @param str
* @Description: 16位大写MD5
*/
public static String parseStrToMd5L16(String str) {
String reStr = parseStrToMd5L32(str);
if (reStr != null) {
reStr = reStr.substring(8, 24);
}
return reStr;
}
}
4.接下来就是在拉流的时候鉴权了,需求是拉流和推流都需要鉴权的
在BasePlayActivity中设置:
/**
* 观众登录房间成功.
*/
protected void handleAudienceLoginRoomSuccess(ZegoStreamInfo[] zegoStreamInfos) {
// 播放房间的流
if (zegoStreamInfos != null && zegoStreamInfos.length > 0) {
for (int i = 0; i < zegoStreamInfos.length; i++) {
String streamId = zegoStreamInfos[i].streamID;
LogUtils.e("流附加信息: " + zegoStreamInfos[i].extraInfo);
LogUtils.e("流附加信息: 长度: " + zegoStreamInfos.length);
ZegoStreamExtraPlayInfo info = JsonUtils.toEntity(zegoStreamInfos[i].extraInfo, ZegoStreamExtraPlayInfo.class);
LogUtils.w("流附加信息:" + info.rtmpUrls+"------"+info.flvUrls+"------"+mOldSavedStreamList);
if (mOldSavedStreamList != null && mOldSavedStreamList.contains(streamId)) {
LogUtils.w("BasePlayAct" + "Has quick start, ignore");
mOldSavedStreamList.remove(streamId);
} else {
startPlay(streamId, info);
}
}
if (mOldSavedStreamList != null && mOldSavedStreamList.size() > 0) {
for (String streamId : mOldSavedStreamList) {
LogUtils.w("BasePlayAct" + "Remove timeout stream id: " + streamId);
stopPlay(streamId);
}
mOldSavedStreamList.clear();
}
}
// 分享主播视频
if (zegoStreamInfos != null && zegoStreamInfos.length > 0) {
for (ZegoStreamInfo info : zegoStreamInfos) {
ViewLive viewLive = getViewLiveByStreamID(info.streamID);
final HashMap<String, String> mapInfo =
(new Gson()).fromJson(info.extraInfo, new TypeToken<HashMap<String, String>>() {
}.getType());
if (viewLive != null && mapInfo != null) {
boolean firstAnchor = Boolean.valueOf(mapInfo.get(Constants.FIRST_ANCHOR));
if (firstAnchor) {
List<String> listUrls = new ArrayList<>();
listUrls.add(mapInfo.get(Constants.KEY_HLS));
listUrls.add(mapInfo.get(Constants.KEY_RTMP));
viewLive.setListShareUrls(listUrls);
break;
}
}
}
}
// 打印log
recordLog(MY_SELF + ": onLoginRoom success(" + mRoomID + "), streamCounts:" + zegoStreamInfos.length);
}
根据https://doc.zego.im/CN/491.html#3 可以看出,在包房房间流的时候进行流地址的设置,调用startPlayingStream的接口可以传入完整的自定义拉流地址,参考如下:
ZegoStreamExtraPlayInfo info = new ZegoStreamExtraPlayInfo();
info.params = "";
//以下rtmp或flv的拉流方式,两个选择一个就可以
info.rtmpUrls = new String[]{"xxx", "yyy"}; //数组中的每个值都是完整的rtmp拉流,如果有鉴权参数的,将鉴权参数带上
info.flvUrls = new String[]{"xxx", "yyy"}; //数组中的每个值都是完整的flv拉流地址,如果有鉴权参数的,将鉴权参数带上
mZegoLiveRoom.startPlayingStream("streamID",inview, info);
//这里的streamID一定不能为空,可以自定义一个,inview是外部指定的渲染视图
这里的鉴权和推流的鉴权是否一致,还要等验证完才可以下结论。
鉴权注意推流、拉流的细节:
推流
-
获取推流uri:
/live/abc
。 -
获取 timestamp:过期时间 2017-07-28 05:43:200 的unixtimestamp为:1501191800。
-
组装加密字符串:
/live/abc-1501191800-0-0-xyz
。 -
计算加密后的字符串:md5 (“/live/abc-1501191800-0-0-xyz”) = 7856896499b661b1123dca82d7406aa3。
-
拼接加密后的推流URL:
rtmp://video- center.alivecdn.com/live/abc?vhost=a.com?auth_key=1501191800-0-0-7856896499b661b1123dca82d7406aa3
。
播放
播放的计算方法与推流类似,以 httpflv 播放地址为例。
-
未鉴权的 URL 为:
http:// a.com/live/abc.flv
。 -
获取播放URL:
/live/abc.flv
。 -
获取 timestamp:过期时间 2017-07-28 05:43:200 的 unixtimestamp 为:1501191800。
-
组装加密字符串:
/live/abc.flv-1501191800-0-0-xyz
。 -
计算加密后的字符串:md5(“/live/abc.flv-1501191800-0-0-xyz”) = b022238fd0cd1c8efae2ba84fc0c9119。
-
拼接加密后的推流URL:
http://a.com/live/abc.flv?auth_key=1501191800-0-0-b022238fd0cd1c8efae2ba84fc0c9119
。 -
设置方法:可以在直播控制台进行设置,控制台中鉴权计算器,可以方便的生成鉴权 URL。详情参考 直播鉴权。
视频直播Url鉴权工具类:
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Component;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
@Component
@SuppressWarnings({"FieldCanBeLocal", "SameParameterValue", "WeakerAccess"})
class LivingUrlUtil {
//app名称 自己定义
private final String appName = "dkcj";
//房间名 自己定义 随便取
private final String streamName = "room1";
//鉴权失效的超时时间(分)
private final Integer overTime = 30;
//推流域名 阿里云配置的推流域名
private final String pushDomain = "zbvip.push.haic666.com";
//拉流域名 阿里云配置的拉流域名
private final String pullDomain = "zbvip.pull.haic666.com";
//uid 暂时没用,设置为0
private final String uid = "0";
//密钥 阿里配置的鉴权key
private final String privateKey = "qihan888";
//加密随机字段,也可以设置为0
private final String randomNumber = RandomStringUtils.random(5, "1234567890");
String getPushUrl() {
String pathModel = "/%s/%s";
return getUrl(pathModel, pushDomain, "rtmp://");
}
String getPullUrl() {
String pathModel = "/%s/%s";
return getUrl(pathModel, pullDomain, "rtmp://");
}
String getPullUrlFlv() {
String pathModel = "/%s/%s.flv";
return getUrl(pathModel, pullDomain, "http://");
}
String getPullUrlM3u8() {
String pathModel = "/%s/%s.m3u8";
return getUrl(pathModel, pullDomain, "http://");
}
private String getUrl(String pathModel, String domain, String origin) {
//当前时间戳+超时时间 ,用来生成鉴权字串
Long timestampSecond = Instant.now().getEpochSecond() + overTime * 60;
System.out.println(timestampSecond);
//路径原始地址 用于生成鉴权字串
String pathUrl = String.format(pathModel, appName, streamName);
//拉流原始地址meu8版 http://{pullDomain}/{AppName}/{StreamName}
String urlBase = String.format("%s%s%s", origin, domain, pathUrl);
//获取推流鉴权字段
String pushMd5Hash = getSString(pathUrl, timestampSecond);
//格式化推流最终地址 最终推流地址模板 pushUrlBase?auth_key=timestamp-rand-uid-md5hash
return String.format("%s?auth_key=%s-%s-%s-%s", urlBase, timestampSecond, randomNumber, uid, pushMd5Hash);
}
private String getSString(String pushUrl, Long timestampSecond) {
//格式化鉴权字符串 鉴权原始字符串 URI-timestamp-rand-uid-PrivateKey
String pushSstring = String.format("%s-%s-%s-%s-%s", pushUrl, timestampSecond, randomNumber, uid, privateKey);
//鉴权字符串md5
return getMd5(pushSstring).toLowerCase();
}
private static String getMd5(String s) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(s.getBytes());
BigInteger HashInt = new BigInteger(1, messageDigest.digest());
return String.format("%1$032X", HashInt);
} catch (NoSuchAlgorithmException lException) {
throw new RuntimeException(lException);
}
}
}
随机数工具类:
package com.southsummer.goddessplan.utils;
import android.graphics.Color;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
/**
* 工具类-随机数
*/
public class RandomUtil {
private static final String ALL_CHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LETTER_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String NUMBER_CHAR = "0123456789";
/**
* 获取从a至z,长度为length随机数
*
* @return
*/
public static String getRandomStr(int length) {
String base = "abcdefghijklmnopqrstuvwxyz";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取范围内int值
*
* @param
* @return
*/
public static int getRandomRange(int max, int min) {
return (int) (Math.random() * (max - min) + min);
}
/**
* 获取随机长度随机字符
*
* @param length base
* @return
*/
public static String getRandomString(int length, String base) { // length表示生成字符串的长度
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取随机长度随机字符
*
* @param length
* @return
*/
public static String getRandomString(int length) { // length表示生成字符串的长度
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取随机长度随机数字
*
* @param length
* @return
*/
public static String getRandomNumString(int length) { // length表示生成字符串的长度
String base = "0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 返回随机数组
*
* @param start 开始值
* @param end 结束值
* @return
*/
public static int[] getRangRandom(int start, int end) {
return getRangRandom(start, end, end - start + 1);
}
/**
* 返回指定范围指定个数的不重复随机数。
*
* @param start
* @param end
* @param num
* @return
*/
public static int[] getRangRandom(int start, int end, int num) {
int length = end - start + 1;
// 参数不合法
if (length < 1 || num > length) {
return null;
} else {
int[] numbers = new int[length];
int[] result = new int[num];
// 循环赋初始值
for (int i = 0; i < length; i++) {
numbers[i] = i + start;
}
Random random = new Random();
// 取randomMax次数
for (int i = 0; i < num; i++) {
// 随机获取取数的位置
int m = random.nextInt(length - i) + i;
result[i] = numbers[m];
// 交换位置
int temp = numbers[m];
numbers[m] = numbers[i];
numbers[i] = temp;
}
return result;
}
}
/*
* 生成6位随机数验证码
*/
public static String code() {
Set<Integer> set = GetRandomNumber();
Iterator<Integer> iterator = set.iterator();
String temp = "";
while (iterator.hasNext()) {
temp += iterator.next();
}
return temp;
}
public static Set<Integer> GetRandomNumber() {
Set<Integer> set = new HashSet<Integer>();
Random random = new Random();
while (set.size() < 6) {
set.add(random.nextInt(10));
}
return set;
}
/**
* 获取定长的随机数,只包含数字
*
* @param length 随机数长度
* @return
*/
public static String generateNumberString(int length) {
StringBuffer sb = new StringBuffer();
Random random = new Random();
random.
for (int i = 0; i < length; i++) {
sb.append(NUMBER_CHAR.charAt(random.nextInt(NUMBER_CHAR.length())));
}
return sb.toString();
}
/**
* 获取定长的随机数,包含大小写字母
*
* @param length 随机数长度
* @return
*/
public static String generateMixString(int length) {
StringBuffer sb = new StringBuffer();
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(LETTER_CHAR.charAt(random.nextInt(LETTER_CHAR.length())));
}
return sb.toString();
}
/**
* 获取定长的随机数,只包含小写字母
*
* @param length 随机数长度
* @return
*/
public static String generateLowerString(int length) {
return generateMixString(length).toLowerCase();
}
/**
* 获取定长的随机数,只包含大写字母
*
* @param length 随机数长度
* @return
*/
public static String generateUpperString(int length) {
return generateMixString(length).toUpperCase();
}
static Random rd = new Random();
/**
* 产生1-9之间随机数
*/
public static int gtRdNum() {
return rd.nextInt(9) + 1; //(0-9内)+1 变相的变成了 1-10内
}
/**
* 产生(+、-、*、/)之间随机运算符
*/
public static String gtRdEx() {
int ex = rd.nextInt(4); // 0+、1-、2*、3/
if (ex == 0) {
return "+";
}
if (ex == 1) {
return "-";
}
if (ex == 2) {
return "*";
}
if (ex == 3) {
return "/";
}
return "";
}
/**
* 递归获取整数
*/
public static String gtZs() {
String str = gtEx();
if (".0".equals(str.substring(str.length() - 2))) {
return str;
} else {
System.out.println("[递归] " + str);
return gtZs();
}
}
/**
* 获取3个随机数(1-9),进行随机加减乘数运行(+、-、*、/),返回整数运算结果的表达式
*/
public static String gtEx() {
String str = gtRdNum() + gtRdEx() + gtRdNum() + gtRdEx() + gtRdNum();
double ts = 0.00;
//优先计算乘除(第一个运算符)
if ("*".equals(str.substring(1, 2)) || "/".equals(str.substring(1, 2))) {
if ("*".equals(str.substring(1, 2))) {
ts = Integer.parseInt(str.substring(0, 1)) * Integer.parseInt(str.substring(2, 3));
} else {
ts = Integer.parseInt(str.substring(0, 1)) / Integer.parseInt(str.substring(2, 3));
}
//第二个运算符
if ("+".equals(str.substring(3, 4))) {
ts = ts + Integer.parseInt(str.substring(4, 5));
} else if ("-".equals(str.substring(3, 4))) {
ts = ts - Integer.parseInt(str.substring(4, 5));
} else if ("*".equals(str.substring(3, 4))) {
ts = ts * Integer.parseInt(str.substring(4, 5));
} else if ("/".equals(str.substring(3, 4))) {
ts = ts / Integer.parseInt(str.substring(4, 5));
}
} else {
//优先计算乘除(第二个运算符)
if ("*".equals(str.substring(3, 4)) || "/".equals(str.substring(3, 4))) {
if ("*".equals(str.substring(3, 4))) {
ts = Integer.parseInt(str.substring(2, 3)) * Integer.parseInt(str.substring(4, 5));
} else {
ts = Integer.parseInt(str.substring(2, 3)) / Integer.parseInt(str.substring(4, 5));
}
//第二个运算符
if ("+".equals(str.substring(1, 2))) {
ts = Integer.parseInt(str.substring(0, 1)) + ts;
} else if ("-".equals(str.substring(1, 2))) {
ts = Integer.parseInt(str.substring(0, 1)) - ts;
} else if ("*".equals(str.substring(1, 2))) {
ts = Integer.parseInt(str.substring(0, 1)) * ts;
} else if ("/".equals(str.substring(1, 2))) {
ts = Integer.parseInt(str.substring(0, 1)) / ts;
}
} else {
//第一个运算符
if ("+".equals(str.substring(1, 2))) {
ts = Integer.parseInt(str.substring(0, 1)) + Integer.parseInt(str.substring(2, 3));
} else {
ts = Integer.parseInt(str.substring(0, 1)) - Integer.parseInt(str.substring(2, 3));
}
//第二个运算符
if ("+".equals(str.substring(3, 4))) {
ts = ts + Integer.parseInt(str.substring(4, 5));
} else {
ts = ts - Integer.parseInt(str.substring(4, 5));
}
}
}
return str + "=" + ts;
}
/**
* 获取指定长度的16进制字符串.
*/
public static String randomHexStr(int len) {
try {
StringBuffer result = new StringBuffer();
for (int i = 0; i < len; i++) {
//随机生成0-15的数值并转换成16进制
result.append(Integer.toHexString(new Random().nextInt(16)));
}
return result.toString().toUpperCase();
} catch (Exception e) {
System.out.println("获取16进制字符串异常,返回默认...");
return "00CCCC";
}
}
// 随机颜色
// Random random = new Random();
// int ranColor = 0xff000000 | random.nextInt(0x00ffffff);
// holder.iv_dynamic_pic.setBackgroundColor(ranColor);
// 随机颜色
// Random random = new Random();
// int []ranColor ={0x00FFFFCC,0xCCFFFF,0xff000000,0x00ffff00,0x00FFFFCC,0x00ff0000};
// 0xff000000 | random.nextInt(0x00ffffff);
// int randomcolor =random.nextInt(ranColor.length);
// int color = Color.argb(1, random.nextInt(255) / 255, random.nextInt(255) / 255, random.nextInt(255) / 255);
// holder.iv_dynamic_pic.setBackgroundColor(color);
// 随机颜色
// Random random = new Random();
// #FFFFF0//int ranColor = 0xff000000 | random.nextInt(0x00ffffff);
// int []ranColor ={0xffFFFFF0,0xffF5FFFA, 0xffFFE7B, 0xffF0F8FF, 0xffEECFA1, 0xffF5FFFA,0xffFFF0F5,0xffEEE9E9};
// 0xff000000 | random.nextInt(0x00ffffff); int randomcolor =random.nextInt(ranColor.length);
// int color = Color.argb(1, random.nextInt(255) / 255, random.nextInt(255) / 255, random.nextInt(255) / 255);
// holder.iv_dynamic_pic.setBackgroundColor(ranColor[randomcolor]);
// 随机颜色
// Random myRandom=new Random();
// int ranColor = 0xff000000 | mRandom.nextInt(0x00ffffff);
// Random random = new Random();
// int[] ranColor = {0xff000000, 0x00ff00ff, 0xff000000, 0x00ffff00, 0x00ffffff, 0x00ff0000};//0xff000000 | random.nextInt(0x00ffffff);
// for(int i = 0;i<ranColor.length;i++)
// {
// holder.iv_dynamic_pic.setBackgroundColor(ranColor[i]);
// }
// Random random = new Random();
// int r = random.nextInt(256);
// int g = random.nextInt(256);
// int b = random.nextInt(256);
// textView.setTextColor(Color.rgb(r,g,b));
/**
* 获取十六进制的颜色代码.例如 "#5A6677"
* 分别取R、G、B的随机值,然后加起来即可
*
* @return String
*/
public static String getRandColor() {
String R, G, B;
Random random = new Random();
R = Integer.toHexString(random.nextInt(256)).toUpperCase();
G = Integer.toHexString(random.nextInt(256)).toUpperCase();
B = Integer.toHexString(random.nextInt(256)).toUpperCase();
R = R.length() == 1 ? "0" + R : R;
G = G.length() == 1 ? "0" + G : G;
B = B.length() == 1 ? "0" + B : B;
return R + G + B;
}
/**
* 获取十六进制的颜色代码.例如 "#6E36B4" , For HTML ,
*
* @return String
*/
public static String getRandColorCode() {
String r, g, b;
Random random = new Random();
r = Integer.toHexString(random.nextInt(256)).toUpperCase();
g = Integer.toHexString(random.nextInt(256)).toUpperCase();
b = Integer.toHexString(random.nextInt(256)).toUpperCase();
r = r.length() == 1 ? "0" + r : r;
g = g.length() == 1 ? "0" + g : g;
b = b.length() == 1 ? "0" + b : b;
return r + g + b;
}
private static final int[] prefix = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// /**
// * 随机产生最大为18位的long型数据(long型数据的最大值是9223372036854775807,共有19位) * * @param digit * 用户指定随机数据的位数
// */
// public static long randomLong(int digit) {
// if (digit >= 19 || digit <= 0)
// throw new IllegalArgumentException("digit should between 1 and 18(1<=digit<=18)");
// String s = RandomUtil.randomNumeric(digit - 1);
// return Long.parseLong(getPrefix() + s);
// }
private static int getDigit(int max) {
return rd.nextInt(max + 1);
}
/**
* 保证第一位不是零 * * @return
*/
private static String getPrefix() {
return prefix[rd.nextInt(9)] + "";
}
}
如有错误之处,请多多指教