技术标签: android
1.先去穿山甲官网,注册登录账号
2.侧边栏->广告变现->流量->应用->新建应用
3.侧边栏->广告变现->流量->代码位->新建代码位
4. 侧边栏->接入中心->SDK下载与接入文档
下载SDK,可以一边看文档一边写代码,一边参考SDK代码
打开页面如下:在工程配置那按照步骤集成SDK
在激励视频广告那按照步骤集成激励视频广告
1.从下载的SDK中复制open_ad_sdk.arr到 libs目录下
2. 在app的 build.gradle 中添加如下代码:
android {
repositories {
flatDir {
dirs 'libs'
}
}
}
dependencies {
implementation(name: 'open_ad_sdk', ext: 'aar')
}
3.添加权限
<!--穿山甲-->
<!-- 必要权限 -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- 可选权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.VIBRATE" /> <!-- suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission
android:name="android.permission.WAKE_LOCK"
tools:node="remove" />
<!-- 可选,穿山甲提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,添加位置权限或参数将帮助投放定位广告 -->
<!-- 请注意:无论通过何种方式提供给穿山甲用户地理位置,均需向用户声明地理位置权限将应用于穿山甲广告投放,穿山甲不强制获取地理位置信息 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- demo场景用到的权限,不是必须的 -->
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" /> <!-- 建议添加“query_all_package”权限,穿山甲将通过此权限在Android R系统上判定广告对应的应用是否在用户的app上安装,避免投放错误的广告,以此提高用户的广告体验。若添加此权限,需要在您的用户隐私文档中声明! -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
注意,我在这儿加入了读取权限:
android:requestLegacyExternalStorage="true"
4.provider配置,在application标签里进行配置
<!--穿山甲SDK-->
<provider
android:name="com.bytedance.sdk.openadsdk.TTFileProvider"
android:authorities="${applicationId}.TTFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<!--穿山甲SDK-->
<provider
android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"
android:authorities="${applicationId}.TTMultiProvider"
android:exported="false" />
5.在xml文件夹下新建fie_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="tt_external_root" path="." />
<external-path name="tt_external_download" path="Download" />
<external-files-path name="tt_external_files_download" path="Download" />
<files-path name="tt_internal_file_download" path="Download" />
<cache-path name="tt_internal_cache_download" path="Download" />
</paths>
至此,SDK配置完成
1.目录结构
2.RewardVideoActivity:(其实参照SDK文件和说明文档一点一点来就行了,我直接在这个Activity里进行了初始化和视频加载,SDK中是分别编写的)
注意:mHorizontalCodeId 和 mVerticalCodeId 改为自己新建项目的代码位(不过我在项目中没用到mHorizontalCodeId,给注释了,主要关注 mVerticalCodeId 这个就行)
package com.example.testdemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.annotation.Nullable;
import com.bytedance.sdk.openadsdk.AdSlot;
import com.bytedance.sdk.openadsdk.TTAdConfig;
import com.bytedance.sdk.openadsdk.TTAdConstant;
import com.bytedance.sdk.openadsdk.TTAdNative;
import com.bytedance.sdk.openadsdk.TTAdSdk;
import com.bytedance.sdk.openadsdk.TTAppDownloadListener;
import com.bytedance.sdk.openadsdk.TTRewardVideoAd;
import com.example.testdemo.utils.RewardAdvancedInfo;
import com.example.testdemo.utils.RewardBundleModel;
import com.example.testdemo.utils.TToast;
import static com.bytedance.sdk.openadsdk.TTAdLoadType.PRELOAD;
//穿山甲
public class RewardVideoActivity extends Activity {
private static final String TAG = "OneActivity";
private Button mLoadAd;
private Button mLoadAdVertical;
private Button mShowAd;
private TTAdNative mTTAdNative;
private String mHorizontalCodeId;
private String mVerticalCodeId;
private TTRewardVideoAd mttRewardVideoAd;
private boolean mIsLoaded = false;
// 是否开放进阶奖励功能
private final boolean isEnableAdvancedReward = false;
private RewardAdvancedInfo mRewardAdvancedInfo;
private int mNowPlayAgainCount = 0;
private int mNextPlayAgainCount = 0;
private boolean mHasShowDownloadActive = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_reward_video);
// 返回按钮
// findViewById(R.id.btn_arv_back).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// finish();
// }
// });
// mLoadAd = (Button) findViewById(R.id.btn_reward_load);
mLoadAdVertical = (Button) findViewById(R.id.btn_reward_load_vertical);
mShowAd = (Button) findViewById(R.id.btn_reward_show);
getExtraInfo();
initClickEvent();
// 1.初始化穿山甲SDK
TTAdConfig();
// 2.手动授权,我直接写Manitest里了,但是现在好像都需要手动授权了,这里我没写
// 3.创建TTAdNative对象,用于调用广告请求接口
mTTAdNative = TTAdSdk.getAdManager().createAdNative(this);
}
private void getExtraInfo() {
Intent intent = getIntent();
if (intent == null) {
return;
}
// mHorizontalCodeId = intent.getStringExtra("horizontal_rit");
// mVerticalCodeId = intent.getStringExtra("vertical_rit");
// mHorizontalCodeId = "950516680";
mVerticalCodeId = "950513057";
}
private void initClickEvent() {
// mLoadAd.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
//
// loadAd(mHorizontalCodeId);
// }
// });
mLoadAdVertical.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loadAd(mVerticalCodeId);
}
});
mShowAd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//当mIsLoaded标识为true 代表广告视频本地加载完整 可直接开启一个主线程处理showRewardVideoAd
if (mttRewardVideoAd != null && mIsLoaded) {
//step6:在获取到广告后展示,强烈建议在onRewardVideoCached回调后,展示广告,提升播放体验
//该方法直接展示广告
// mttRewardVideoAd.showRewardVideoAd(RewardVideoActivity.this);
//展示广告,并传入广告展示的场景
mttRewardVideoAd.showRewardVideoAd(RewardVideoActivity.this, TTAdConstant.RitScenes.CUSTOMIZE_SCENES, "scenes_test");
mttRewardVideoAd = null;
} else {
TToast.show(RewardVideoActivity.this, "请先加载广告");
}
}
});
}
public void loadAd(final String codeId) {
// 4。 创建广告请求参数AdSlot,具体参数含义参考文档
AdSlot adSlot = new AdSlot.Builder()
.setCodeId(codeId)
// .setRewardName("饲料") //奖励的名称 选填 -已废弃
// .setRewardAmount(300) //奖励的数量 选填 -已废弃
//模板广告需要设置期望个性化模板广告的大小,单位dp,激励视频场景,只要设置的值大于0即
// 且仅是模板渲染的代码位ID使用,非模板渲染代码位切勿使用
// .setExpressViewAcceptedSize(500,500) /
// .setUserID("tag123")//tag_id
// .setMediaExtra("media_extra") //附加参数
.setOrientation(TTAdConstant.VERTICAL) //必填参数,期望视频的播放方向:TTAdConstant.HORIZONTAL 或 TTAdConstant.VERTICAL
.setAdLoadType(PRELOAD)//推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略
.build();
// 5. 请求广告 -异步加载广告
mTTAdNative.loadRewardVideoAd(adSlot, new TTAdNative.RewardVideoAdListener() {
@Override
public void onError(int code, String message) {
Log.e(TAG, "Callback --> onError: " + code + ", " + String.valueOf(message));
TToast.show(RewardVideoActivity.this, message);
}
//视频广告加载后,视频资源缓存到本地的回调,在此回调后,播放本地视频,流畅不阻塞。
@Override
public void onRewardVideoCached() {
Log.e(TAG, "Callback --> onRewardVideoCached");
mIsLoaded = true;
TToast.show(RewardVideoActivity.this, "Callback --> rewardVideoAd video cached");
}
@Override
public void onRewardVideoCached(TTRewardVideoAd ad) {
Log.e(TAG, "Callback --> onRewardVideoCached");
mIsLoaded = true;
TToast.show(RewardVideoActivity.this, "Callback --> rewardVideoAd video cached");
// ad.showRewardVideoAd(RewardVideoActivity.this, TTAdConstant.RitScenes.CUSTOMIZE_SCENES, "scenes_test");
//当mIsLoaded标识为true 代表广告视频本地加载完整 可直接开启一个主线程处理showRewardVideoAd
}
// 广告加载完成的回调
//视频广告的素材加载完毕,比如视频url等,在此回调后,可以播放在线视频,网络不好可能出现加载缓冲,影响体验。
@Override
public void onRewardVideoAdLoad(TTRewardVideoAd ad) {
Log.e(TAG, "Callback --> onRewardVideoAdLoad");
TToast.show(RewardVideoActivity.this, "rewardVideoAd loaded 广告类型:" + getAdType(ad.getRewardVideoAdType()));
mttRewardVideoAd = ad;
// 6. 广告交互监听器
mttRewardVideoAd.setRewardAdInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() {
//视频广告展示回调
@Override
public void onAdShow() {
Log.d(TAG, "Callback --> rewardVideoAd show");
TToast.show(RewardVideoActivity.this, "rewardVideoAd show");
}
//广告的下载bar点击回调
@Override
public void onAdVideoBarClick() {
Log.d(TAG, "Callback --> rewardVideoAd bar click");
TToast.show(RewardVideoActivity.this, "rewardVideoAd bar click");
}
//视频广告关闭回调
@Override
public void onAdClose() {
Log.d(TAG, "Callback --> rewardVideoAd close");
TToast.show(RewardVideoActivity.this, "rewardVideoAd close");
if (isEnableAdvancedReward && mRewardAdvancedInfo != null) {
Log.d(TAG, "本次奖励共发放:" + mRewardAdvancedInfo.getRewardAdvancedAmount());
}
}
//视频播放完成回调
@Override
public void onVideoComplete() {
Log.d(TAG, "Callback --> rewardVideoAd complete");
TToast.show(RewardVideoActivity.this, "rewardVideoAd complete");
}
//视频广告播放错误回调
@Override
public void onVideoError() {
Log.e(TAG, "Callback --> rewardVideoAd error");
TToast.show(RewardVideoActivity.this, "rewardVideoAd error");
}
//视频播放完成后,奖励验证回调,rewardVerify:是否有效,rewardAmount:奖励梳理,rewardName:奖励名称,code:错误码,msg:错误信息
@Override
public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName,int errorCode, String errorMsg) {
String logString = "verify:" + rewardVerify + " amount:" + rewardAmount +
" name:" + rewardName + " errorCode:" + errorCode + " errorMsg:" + errorMsg;
Log.e(TAG, "Callback --> " + logString);
TToast.show(RewardVideoActivity.this, logString);
}
@Override
public void onRewardArrived(boolean isRewardValid, int rewardType, Bundle extraInfo) {
RewardBundleModel rewardBundleModel = new RewardBundleModel(extraInfo);
Log.e(TAG, "Callback --> rewardVideoAd has onRewardArrived " +
"\n奖励是否有效:" + isRewardValid +
"\n奖励类型:" + rewardType +
"\n奖励名称:" + rewardBundleModel.getRewardName() +
"\n奖励数量:" + rewardBundleModel.getRewardAmount() +
"\n建议奖励百分比:" + rewardBundleModel.getRewardPropose());
if (!isRewardValid) {
Log.d(TAG, "发送奖励失败 code:" + rewardBundleModel.getServerErrorCode() +
"\n msg:" + rewardBundleModel.getServerErrorMsg());
return;
}
if (!isEnableAdvancedReward) {
// 未使用进阶奖励功能
if (rewardType == TTRewardVideoAd.REWARD_TYPE_DEFAULT) {
Log.d(TAG, "普通奖励发放,name:" + rewardBundleModel.getRewardName() +
"\namount:" + rewardBundleModel.getRewardAmount());
}
} else {
// 使用了进阶奖励功能
if (mRewardAdvancedInfo != null) {
mRewardAdvancedInfo.proxyRewardModel(rewardBundleModel, false);
}
}
}
//视频广告跳过回调
@Override
public void onSkippedVideo() {
Log.e(TAG, "Callback --> rewardVideoAd has onSkippedVideo");
TToast.show(RewardVideoActivity.this, "rewardVideoAd has onSkippedVideo");
}
});
/其实到这儿就没了,但是为了严谨,增加了再看一个的回调 和 下载的回调///
mttRewardVideoAd.setRewardPlayAgainInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() {
@Override
public void onAdShow() {
mNowPlayAgainCount = mNextPlayAgainCount;
Log.d(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 rewardPlayAgain show");
TToast.show(RewardVideoActivity.this, "rewardVideoAd show");
}
@Override
public void onAdVideoBarClick() {
Log.d(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 rewardPlayAgain bar click");
TToast.show(RewardVideoActivity.this, "rewardVideoAd bar click");
}
@Override
public void onAdClose() {
// 再看广告不会调到这个回调
}
//视频播放完成回调
@Override
public void onVideoComplete() {
Log.d(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 rewardPlayAgain complete");
TToast.show(RewardVideoActivity.this, "rewardVideoAd complete");
}
@Override
public void onVideoError() {
Log.e(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 rewardPlayAgain error");
TToast.show(RewardVideoActivity.this, "rewardVideoAd error");
}
//视频播放完成后,奖励验证回调,rewardVerify:是否有效,rewardAmount:奖励梳理,rewardName:奖励名称
@Override
public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int errorCode, String errorMsg) {
String logString = "rewardPlayAgain verify:" + rewardVerify + " amount:" + rewardAmount +
" name:" + rewardName + " errorCode:" + errorCode + " errorMsg:" + errorMsg;
Log.e(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 " + logString);
TToast.show(RewardVideoActivity.this, logString);
}
@Override
public void onRewardArrived(boolean isRewardValid, int rewardType, Bundle extraInfo) {
RewardBundleModel rewardBundleModel = new RewardBundleModel(extraInfo);
Log.e(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 rewardPlayAgain has onRewardArrived " +
"\n奖励是否有效:" + isRewardValid +
"\n奖励类型:" + rewardType +
"\n奖励名称:" + rewardBundleModel.getRewardName() +
"\n奖励数量:" + rewardBundleModel.getRewardAmount() +
"\n建议奖励百分比:" + rewardBundleModel.getRewardPropose());
if (!isEnableAdvancedReward) {
// 再看一个未使用进阶奖励功能
if (rewardType == TTRewardVideoAd.REWARD_TYPE_DEFAULT) {
Log.d(TAG, "再看一个普通奖励发放,name:" + rewardBundleModel.getRewardName() +
"\namount:" + rewardBundleModel.getRewardAmount());
}
} else {
// 再看一个使用了进阶奖励功能
if (mRewardAdvancedInfo != null) {
mRewardAdvancedInfo.proxyRewardModel(rewardBundleModel, true);
}
}
}
@Override
public void onSkippedVideo() {
Log.e(TAG, "Callback --> 第 " + mNowPlayAgainCount + " 次再看 rewardPlayAgain has onSkippedVideo");
TToast.show(RewardVideoActivity.this, "rewardVideoAd has onSkippedVideo");
}
});
mttRewardVideoAd.setRewardPlayAgainController(new TTRewardVideoAd.RewardAdPlayAgainController() {
@Override
public void getPlayAgainCondition(int nextPlayAgainCount, Callback callback) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_PLAY_AGAIN_ALLOW, true);
bundle.putString(KEY_PLAY_AGAIN_REWARD_NAME, "饲料");
bundle.putString(KEY_PLAY_AGAIN_REWARD_AMOUNT, nextPlayAgainCount + "g");
mNextPlayAgainCount = nextPlayAgainCount;
callback.onConditionReturn(bundle);
}
});
mttRewardVideoAd.setDownloadListener(new TTAppDownloadListener() {
@Override
public void onIdle() {
mHasShowDownloadActive = false;
}
@Override
public void onDownloadActive(long totalBytes, long currBytes, String fileName, String appName) {
Log.d("DML", "onDownloadActive==totalBytes=" + totalBytes + ",currBytes=" + currBytes + ",fileName=" + fileName + ",appName=" + appName);
if (!mHasShowDownloadActive) {
mHasShowDownloadActive = true;
}
}
@Override
public void onDownloadPaused(long totalBytes, long currBytes, String fileName, String appName) {
Log.d("DML", "onDownloadPaused===totalBytes=" + totalBytes + ",currBytes=" + currBytes + ",fileName=" + fileName + ",appName=" + appName);
}
@Override
public void onDownloadFailed(long totalBytes, long currBytes, String fileName, String appName) {
Log.d("DML", "onDownloadFailed==totalBytes=" + totalBytes + ",currBytes=" + currBytes + ",fileName=" + fileName + ",appName=" + appName);
}
@Override
public void onDownloadFinished(long totalBytes, String fileName, String appName) {
Log.d("DML", "onDownloadFinished==totalBytes=" + totalBytes + ",fileName=" + fileName + ",appName=" + appName);
}
@Override
public void onInstalled(String fileName, String appName) {
Log.d("DML", "onInstalled==" + ",fileName=" + fileName + ",appName=" + appName);
}
});
}
});
}
private String getAdType(int type) {
switch (type) {
case TTAdConstant.AD_TYPE_COMMON_VIDEO:
return "普通激励视频,type=" + type;
case TTAdConstant.AD_TYPE_PLAYABLE_VIDEO:
return "Playable激励视频,type=" + type;
case TTAdConstant.AD_TYPE_PLAYABLE:
return "纯Playable,type=" + type;
case TTAdConstant.AD_TYPE_LIVE:
return "直播流,type=" + type;
}
return "未知类型+type=" + type;
}
/*初始化穿山甲SDK*/
private void TTAdConfig(){
TTAdSdk.getAdManager().requestPermissionIfNecessary(this);
TTAdConfig config=new TTAdConfig.Builder()
.appId("5352720")
.useTextureView(true) //使用TextureView控件播放视频,默认为SurfaceView,当有SurfaceView冲突的场景,可以使用TextureView
.appName("TestDemo")
.allowShowNotify(true) //是否允许sdk展示通知栏提示
.debug(true) //测试阶段打开,可以通过日志排查问题,上线时去除该调用
.directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI, TTAdConstant.NETWORK_STATE_3G) //允许直接下载的网络状态集合
.supportMultiProcess(false)//是否支持多进程
.needClearTaskReset()
// .injectionAuth(TTLiveTokenHelper.getInstance().useHostAuth() ? new TTInjectionAuthImpl() : null)
.build();
TTAdSdk.init(this, config, new TTAdSdk.InitCallback() {
@Override
public void success() {
Log.i(TAG, "success: " + TTAdSdk.isInitSuccess());
Log.i(TAG, "success: 初始化穿山甲成功");
}
@Override
public void fail(int code, String msg) {
Log.i(TAG, "fail: code = " + code + " msg = " + msg);
}
});
}
}
3.activity_reward_video.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="HardcodedText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- <Button-->
<!-- android:id="@+id/btn_arv_back"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:drawablePadding="2dp"-->
<!-- android:gravity="left|center_vertical"-->
<!-- android:paddingLeft="13dp"-->
<!-- android:text="Back"-->
<!-- android:textAllCaps="false"-->
<!-- android:textSize="15sp" />-->
<!-- <Button-->
<!-- android:id="@+id/btn_reward_load"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_marginTop="10dp"-->
<!-- android:layout_height="52dp"-->
<!-- android:layout_marginStart="8dp"-->
<!-- android:layout_marginLeft="8dp"-->
<!-- android:layout_marginEnd="8dp"-->
<!-- android:layout_marginRight="8dp"-->
<!-- android:gravity="center"-->
<!-- android:text="加载横版激励视频广告"-->
<!-- android:textColor="@android:color/black"-->
<!-- android:textSize="14sp"/>-->
<Button
android:id="@+id/btn_reward_load_vertical"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:gravity="center"
android:text="加载竖版激励视频广告"
android:textColor="@android:color/black"
android:textSize="14sp"/>
<Button
android:id="@+id/btn_reward_show"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:gravity="center"
android:text="展示激励视频广告"
android:textColor="@android:color/black"
android:textSize="14sp"/>
</LinearLayout>
4.记得在Mainifest.xml中注册该Activity
<activity android:name=".RewardVideoActivity"/>
compileSdkVersion 29
buildToolsVersion "29.0.3" //这个我之前写的33.0.3就报错了
2. Android项目中出现了这个构建错误:unexpected element <queries> found in <manifest>解决方案
具体可以看我转载的这篇文章
我的错误:之前我的版本号为3.5.2改为3.5.4就好了
classpath 'com.android.tools.build:gradle:3.5.4'
3. 视频停留在暂停页面不播放,然后直接跳转播放结束
原因:缺少安全验证
在xml中新建随便一个xml文件,我新建的security.xml,具体内容为:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
<domain-config>
<domain includeSubdomains="true">i.snssdk.com</domain>
<domain includeSubdomains="true">is.snssdk.com</domain>
<domain includeSubdomains="true">pangolin.snssdk.com</domain>
<domain includeSubdomains="true">extlog.snssdk.com</domain>
<domain includeSubdomains="true">sf3-ttcdn-tos.pstatp.com</domain>
<domain includeSubdomains="true">bds.snssdk.com</domain>
<domain includeSubdomains="true">dig.bdurl.net</domain>
</domain-config>
</network-security-config>
然后在Manifest.xml中引入该文件:
android:networkSecurityConfig="@xml/security"
再运行视频就能正常播放了
4.java.lang.SecurityException: getUniqueDeviceId: The user 10397 does not meet the requirements to acc…
targetSdkVersion版本太高了,原来我是29,改为28了
为啥每次播的视频一样,官方给出了解释:
参考:
文章浏览阅读74次。数组1、定长数组和变长数组package cn.gec.scala import scala.collection.mutable.ArrayBuffer object ArrayDemo { def main(args: Array[String]) { //初始化一个长度为8的定长数组,其所有元素均为0 val arr1 = n..._scala array[long] wordcount
文章浏览阅读2.1k次。灰度重心法是根据每行光条纹横截面内的灰度分布特征逐行进行处理,通过在行坐标的方向上,逐行计算提取光条纹区域的灰度重心点,并将该点用来代表该截面的光条纹中心点位置,最后将所有中心点拟合形成光条纹中心线。灰度重心法计算光条纹中心点的公式(光条纹第v列的灰度重心坐标):图像包含U行、V列的图像中坐标(u, v)处的像素灰度值为I(u,v),其中u=1,2,3,…,U; v=1,2,3…,V。灰度重心法提取光条纹中心线时运算速度快,实时性好。但是易受图像中的噪点干扰,导致中心线坐标偏移。#include._灰度中心法
文章浏览阅读2k次。又到了毕业季,一般学校都会对毕业生的论文进行查重检测,看看大家的重复率。这个对大家的毕业是十分重要的,如果查重不过,可能会被延期毕业。所以很多学生也会自己事先查重,然后自己多次修改,达到学校的要求。可是很多学生不知道如何选择检测系统,下面我就给大家介绍一下主流的几种系统: 1.万方检测,特点是检测结果粗放,不是很令人满意,但是价格足够便宜,非常适合大家前期修改论文。 _千万级和亿级查重哪个好
文章浏览阅读75次。该协议实际上适用于PLC编程端口以及 FX-232AW 模块的通信。通讯格式:命令 命令码 目标设备DEVICE READ CMD "0" X,Y,M,S,T,C,DDEVICE WRITE CMD "1" X,Y,M,S,T,C,DFORCE ON CMD " 7" X,Y,M,S,T,CFORCE OFF CMD "8" X,Y,M,S,T,C传输格式: R..._fxplc一次性最多读写多少字节
文章浏览阅读1.8k次,点赞4次,收藏9次。1. DOM(Document Object Model)2018年通用版本是 DOM 3DOM的作用:对Html文档进行增删改查DOM文档树:(Object =&amp;amp;amp;gt;)祖先 Node =&amp;amp;amp;gt;Document创建Html标签;Text 创建文本;Element 创建其他元素标签;Comment 创建注释…2. Node 接口2. 1 属性childNodes //获取的..._dom和虚拟dom
文章浏览阅读3.6k次,点赞4次,收藏10次。注:本文源码解析基于Kafka2.1.0版本我们知道,Kafka中的每个Topic一般会分配N个Partition,那么生产者(Producer)在将消息记录(ProducerRecord)发送到某个Topic对应的Partition时采用何种策略呢?Kafka中采用了分区器(Partitioner)来为我们进行分区路由的操作。本文将详细讨论Kafka给我们提供的分区器实现DefaultPa..._kafka partitioner
文章浏览阅读102次。自2017年5月份经历勒索软件WannaCry的大规模爆发后,思科Talos团队在6月27日发现了最新的勒索软件变种,暂命名为Nyetya。目前已经在多个国家发现了这个勒索软件的感染事件,思科Talos团队正在积极分析并不断更新最新的防护信息。原文链接:http://blog.talosintelligence.com/2017/06/worldwid...
文章浏览阅读2.4w次,点赞142次,收藏1k次。初学spring的时候使用注解总觉得使用注解很神奇,加一个注解就能实现想要的功能,很好奇,也想自己根据需要写一些自己实现的自定义注解。问题来了,自定义注解到底是什么?肯定会有人和我一样有这个疑惑,我根据自己的理解总结一下。看完下面的几个使用自定义注解的实战demo,小伙伴大概就懂怎么用了。其实注解一点也不神奇,注解就是一种标志,单独使用注解,就相当于在类、方法、参数和包上加上一个装饰,什么功能也没有,仅仅是一个标志,然后这个标志可以加上一些自己定义的参数。_springboot自定义注解加载字段上怎么使用
文章浏览阅读4k次。C3P0 APPARENT DEADLOCK 问题_apparent deadlock creating emergency threads for unassigned pending task
文章浏览阅读2.1k次。一、POM简介pom作为项目对象模型,通过使用pom.xml来实现管理maven项目,主要描述了项目的如下部分:配置文件、开发者需要遵循的规则、缺陷管理系统、组织和licenses、项目的url、项目的依赖性和其它所有的项目相关因素。二、POM的语法规则一份比较全的pom.xml文件可以参考如下,通过它我们来对pom.xml中的语法规则进行说明。<project> <..._pom.xml语法
文章浏览阅读9.8k次,点赞2次,收藏6次。查看消费组./kafka-consumer-groups.sh --bootstrap-server ip:port--list//查看组中消费者状态./kafka-consuemr-groups.sh --bootstrap-server ip:port--group 消费组名称--describe_kafka查看消费者ip
文章浏览阅读1.8k次。参考文章:https://blog.csdn.net/scholar_man/article/details/78784163打印机使用的是斑马打印机条码样式工具类ZplPrinterimport org.springframework.context.EnvironmentAware;import org.springframework.context.annotation.Configuration;import org.springframework.core.env.Environme