抖音作为日活量过亿的产品,在今年年初,也终于上线了抖音开放平台,目前支持app接入登录与分享。app通过抖音授权获取用户信息;分享视频并进行处理,提高app在抖音的曝光度。
刚好项目有个新需求,需要集成抖音登录与分享。抖音开发者文档
本以为不就是接个SDK嘛,湿湿水啦!
但是bug总与我相伴!且听我慢慢道来。
1、在项目根目录build.gradle里添加maven:
allprojects {
repositories {
……
maven { url 'https://dl.bintray.com/aweme-open-sdk-team/public' }
}
}
2、接着在app目录的build.gradle添加依赖:
dependencies {
implementation 'com.bytedance.ies.ugc.aweme:open-sdk:0.0.1.3'
}
别忘了在manifest添加权限并处理,最后同步项目
1、在Application中,onCreate方法下初始化TikTokOpenApiFactory
String clientkey = "xxx"; // 到开发者网站申请并替换
TikTokOpenApiFactory.init(new TikTokOpenConfig(clientkey));
2、分享视频
//视频文件路径
private ArrayList<String> mUri = new ArrayList<>();
mUri.add("xxx.video-path");
// 初始化api,需要传入targetApp
TiktokOpenApi tiktokOpenApi =
TikTokOpenApiFactory.create(this, TikTokConstants.TARGET_APP.AWEME);
// 初始化Share
Share.Request request = new Share.Request();
TikTokVideoObject videoObject = new TikTokVideoObject();
videoObject.mVideoPaths = mUri;
TikTokMediaContent content = new TikTokMediaContent();
content.mMediaObject = videoObject;
request.mMediaContent = content;
//配置完成后,调用share方法唤起抖音app
tiktokOpenApi.share(request);
3、接收回调
两种方式接收回调信息:
(1)在包名下创建.tiktokapi.TikTokEntryActivity,初始化TTOpenApi,
实现 TikTokApiEventHandler接口,在onResp方法中回调结果。(和微信回调类似)
(2)自定义接收回调全路径,比如需要回调的activity路径为:
com.douyin.share.TikTokEntryActivity
则在前面传分享参数时指定回调全路径:
request.callerLocalEntry = com.douyin.share.TikTokEntryActivity
回调activity实现TikTokApiEventHandler接口,并在manifest中注册activity,添加android:exported=“true”,然后在onResp方法接收回调:
public void onResp(BaseResp resp) {
if (resp.getType() == TikTokConstants.ModeType.SHARE_CONTENT_TO_TT_RESP) {
switch (resp.errorCode){
case TikTokConstants.BaseErrorCode.OK:
Toast.makeText(this, "分享成功",Toast.LENGTH_SHORT).show();
break;
case TikTokConstants.BaseErrorCode.ERROR_UNKNOW:
Toast.makeText(this, "未知错误",Toast.LENGTH_SHORT).show();
break;
case TikTokConstants.BaseErrorCode.ERROR_CANCEL:
Toast.makeText(this, "取消分享",Toast.LENGTH_SHORT).show();
break;
}
}
finish();
}
OK!至此我们就可以接收分享回调了!心情别提有多愉快!!
来看看效果:
what!我取消分享后,虽然弹出了Toast,但是并没有回到我们的app,而是留在抖音。这就坑了!微信取消分享后都可以回到app,但是抖音就不行。估计是我哪里配置不对,去扒官方文档,在FQA里发现了这样一段话:
就是说,抖音在回调activity的时候,这个类是通过singleInstance启动的,如果想回到应用,也有办法,注意画红线的部分,“启动自己app的类跳回自己app的栈”,我将信将疑的照做了,在onResp方法启动MainActivity
public void onResp(BaseResp resp) {
if (resp.getType() == TikTokConstants.ModeType.SHARE_CONTENT_TO_TT_RESP) {
……
//添加这行代码
startActivity(new Intent(this, MainActivity.class));
}
finish();
}
我们再来跑一遍:
没毛病,好像是成功了!?嗯?有点怪,页面启动分享的按钮怎么不见了!
我继续按返回键操作:
what!按下返回键后,又回到了抖音主界面,再接着按返回键,才回到我们的app(有启动分享的按钮)。
这就坑了,文档不是说启动自己app的类就能回到自己app的栈吗?我好奇的打开了手机任务栈,发现:
坑!怎么我自己app的界面跑到抖音的进程里了?
我们来捋一捋这些场景,并结合官方文档来分析。文档描述,tiktok回调这个类是通过singleInstance的,即抖音回调activity的时候,是通过singleInstance启动TikTokEntryActivity的,这个时候的TikTokEntryActivity运行在独立的任务栈里,属于前台任务;当调用到TikTokEntryActivity的onResp方法,执行到startActivity(new Intent(this, MainActivity.class)),启动MainActivity并finish自己,这个时候抖音的任务栈属于前台任务栈,那么自然而然的,新启动的MainActivity就依附在抖音的任务栈上。接着再按返回键,退出了抖音的任务栈,最后才回到自己app的任务栈。
OK!知道了原因,对应的解决方案也就有了。在TikTokEntryActivity调用finish后,我们只需要将MainActivity拉回自己app的任务栈,那么不就可以搞定了吗?
想法是好,但是还会有两个问题:
(1)如何将MainActivity拉回自己app的任务栈?
(2)接收回调后,不一定是跳转到MainActivity,而是怎么回到启动分享的那个activity?
第一个问题好解决,把MainActivity的启动模式设置为singleTask就行了;
第二个问题,怎么回到启动分享的那个activity?比如一个实际的场景,A是根activity,A可以启动B或者启动C,然后B和C都可以唤起抖音分享,那么分享后回到哪个activity得知道,也就是需要知道栈顶activity。就跟唤起微信,取消分享的场景一样(不知道抖音为什么不做成和微信一样的处理方式,还需要开发者额外处理)。
把问题1和2结合起来就是:
接收回调后,如何跳转到启动分享的那个activity?(即自己app栈的栈顶activity)。
具体怎么做?搜索了一番,发现shareSDK是支持抖音分享的,可以扒它们抖音SDK模块的jar包看看是怎么处理的?
下载shareSDK官方demo,用Android Studio打开后,在依赖包目录下找到了关于抖音模块的代码
DouYinHandlerActivity,没错,就是这个类,其中有一个方法引起了我的注意:
很明显,shareSDK的处理是在onDestroy方法,创建Intent,并通过隐式启动来跳转到自己app的任务栈。action的值表示需要在manifest对应的activity去添加,flags被混淆成了数字,反编译后发现是FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP,对应
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP
找到办法后我欣喜若狂,赶紧参照该方法处理。发现可以跳转到自己app,但是有时候会失效!后来还下载了shareSDK的apk测试,也发现偶尔会有失效的问题。
看来这个办法不够完美,只能继续寻找方案。
……
然后,在抖音开放平台官网发现,轻颜相机是目前市面上集成了抖音SDK的app,我赶紧下载测试:
可以看到,轻颜相机是可以回到自己app的。那么轻颜相机是怎么做的呢?扒了下源码,最终靠懵 + 猜,发现了如下的方法:
public int c(d paramd) {
……
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(this.mActivity.getPackageName());
stringBuilder.append(".wttsharesdk.WttShareActivity");
String str = stringBuilder.toString();
ComponentName componentName =
new ComponentName(this.mActivity.getPackageName(), str);
Intent intent = new Intent();
intent.setComponent(componentName);
this.mActivity.startActivity(intent);
……
}
逻辑是使用StringBuilder拼接activity路径,然后通过ComponentName隐式启动startActivity,回到自己的app。
至此,我们来整理下思路,首先我们可以获取到自己app栈顶activity(这个activity必须是singleTask模式),拿到ComponentName,再通过隐式Intent跳转,不就能回到自己app任务栈了吗?
开搞,代码实现:
……
private String getTopClassName(){
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfos = manager.getRunningTasks(1);
if (taskInfos != null && taskInfos.size() != 0){
ComponentName componentName = taskInfos.get(0).topActivity;
//拿到栈顶的activity className
return componentName.getClassName();
}
return "";
}
private void openComponent(){
String className = getTopClassName();
if (!className.isEmpty()){
ComponentName componentName =
new ComponentName(getPackageName(),className);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
}
}
……
在onResp()里调用openComponent()
public void onResp(BaseResp resp) {
if (resp.getType() == TikTokConstants.ModeType.SHARE_CONTENT_TO_TT_RESP) {
……
openComponent();
}
finish();
}
注意栈顶activity需要设置launchMode=“singleTask”,至于为什么,稍后解释
我们来看看修改后的效果:
可以看到,分享后按返回键正常回到了自己的app,继续返回,退出app任务栈;接着回到抖音的任务栈,再返回,最后回到桌面,具体流程如下图:
大概分三步:
(1)app栈顶activityB唤起抖音分享,启动模式为singleTask;
(2)分享结束后,抖音启动TikTokEntryActivity,启动模式为singleInstance,回调onResp;
(3)TikTokEntryActivity调用finish,并通过Component启动栈顶的activityB,由于activityB已存在实例,系统重用该实例并调到栈顶,将任务栈拉回前台。
至此,整体看下来,主要还是对activity启动模式的理解程度,以及反编译的一些相关知识,虽然平时接触不多,但是当真正遇到问题的时候,我们有了思路,才会体现出自己肚子里有多少墨水,从容应对。