前提要求: Android SDK v6.2.85及以上版本,仅支持国内SDK。
支持广告平台: 目前仅支持Topon ADX平台
断点续安装方式简介:用户在点击 app 类的广告后,会下载广告中的 apk 。由于当前安卓的安装链路过长,会存在一些已下载但未安装的 apk。通过断点续安装提示的接口,开发者可以拿到有效的 apk 做提示,由用户选择是否继续安装,从而提升 app 的安装率。
激活弹窗提示简介: 用户在和广告交互的过程中,会存在下载了应用并未安装或者安装了应用并未打开的情况,这时可以通过弹窗提示用户,最大化广告收益。该功能是断点续安装提示功能的升级,开发者不需要处理应用解析和弹窗的具体逻辑,只要调用接口就可以实现在指定时机弹窗提醒用户完成安装或激活的操作。
适用场景: 断点续安装方式和激活弹窗提示建议在用户退出APP时、或浏览使用APP中,不影响用户体验的情况下提示。
用于获取ATApkManager对象,需要在初始化SDK后调用。
| 方法名 | 方法介绍 |
|---|---|
| getATApkManager(Context context) | context 是指上下文环境,这里必须传入开发者的 Activity,必须要在初始化SDK后才能调用此方法,否则会返回null。 |
ATApk数据的管理类,用于获取apk数据,然后进行安装。
| 方法名 | 方法介绍 |
|---|---|
| showOpenOrInstallAppDialog(ATApkDialogClickListener dialogClickListener) | 通过该接口可以检查通过广告sdk下载完成未安装或已安装未激活的应用,通过弹窗的方式提示用户安装或打开激活。 重复调用该接口会按时间循序循环返回未安装或已安装未激活的应用。 |
| setApkListener(ATApkListener apkListener) | 设置加载 ATApk 的回调接口 |
| loadApk() | 加载 ATApk 数据,每调用一次只能获取一个 ATApk。重复调用该接口会按时间循序循环返回未安装的应用。 |
| startInstall(ATApk apk) | 传入 loadApk() 中拿到的 ATApk 数据,调用系统安装器进行安装。 |
激活弹窗提示:调用showOpenOrInstallAppDialog方法传入的listener,开发者需要实现对应的接口。
| 方法名 | 方法介绍 |
|---|---|
| onShowResult(int resultType) | 该方法回调时,返回的参数值有: ATApkDialogClickListener.NO_DLG:没有可以安装或激活的应用 ATApkDialogClickListener.INSTALL_APP_DLG:弹窗提示用户安装应用 ATApkDialogClickListener.OPEN_APP_DLG:弹窗提示用户打开应用激活 |
| onButtonClick(int clickType) | 该方法回调时,返回的参数值有: ATApkDialogClickListener.CONFIRM_BUTTON:弹窗后,用户点击了确认 ATApkDialogClickListener.CANCEL_BUTTON:弹窗后,用户点击了取消 |
断点续安装:需要在调用loadApk方法获取 GDTApk 数据之前,调用setApkListener方法传入该listener,监听ATApk数据的获取。
| 方法名 | 方法介绍 |
|---|---|
| onApkLoad(ATApk apk) | 该方法回调时,会返回ATApk 数据 |
| onError(String code, String msg, String networkFirmId) | 该方法回调时,说明获取ATApk数据的一些错误信息提示。 code: 错误码。60001,说明当前暂无待安装的APK。60002,说明获取安装列表时发生异常。 msg: 错误信息。 networkFirmId:广告平台id。 |
断点续安装:ATApkListener接口返回的数据类 ATApk。
| 方法名 | 方法介绍 |
|---|---|
| getPackageName() | 获取 app 广告的包名 |
| getTitle() | 获取 app 广告的标题 |
| getDesc() | 获取 app 广告的描述信息 |
| getAppName() | 获取 app 广告的名字 |
| getLogoUrl() | 获取 app 广告的图标地址 |
/**
* 当调用系统安装器 {@link com.anythink.china.api.ATApkManager#startInstall(com.anythink.china.api.ATApk)}后,
* 界面会跳转到安装器的界面上,这个时候我们没有提供 apk 文件是否安装完成或者失败的回调。
*
* 所以,开发者可以利用 activity 或者 fragment 的生命周期回调,结合自己的业务逻辑,去做出正确的展示。
*
* 比如,在 activity 中,从当前 app 跳转到系统安装器界面,会回调 onPause() -> onStop();
* 从系统安装器界面返回到 app 中的时候,会回调 onRestart() -> onStart() -> onResume()。
*
* 当界面从系统安装器返回后,可以通过判断 apk 是否已经安装 {@link #isAPKInstalled(android.content.Context, java.lang.String)} 来更新视图
*/
public class DownloadApkManagerActivity extends Activity implements View.OnClickListener {
private static final String TAG = DownloadApkManagerActivity.class.getSimpleName();
private ATApkManager mApkManager;
private ATApk mApk;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download_apk_manager);
mApkManager = ATChinaSDKHandler.getATApkManager(this);
initListener();
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
if (mApk != null && isAPKInstalled(this, mApk.getPackageName())) {
// 更新你的视图
Log.i(TAG, "apk 文件已经安装,可以更新你的视图");
}
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public void onBackPressed() {
showOpenOrInstallAppDialog(true);
}
private void initListener() {
findViewById(R.id.bt_show_apk_dialog).setOnClickListener(this);
findViewById(R.id.bt_load_apk).setOnClickListener(this);
findViewById(R.id.bt_install_apk).setOnClickListener(this);
if (mApkManager != null) {
mApkManager.setApkListener(new ATApkListener() {
@Override
public void onApkLoad(ATApk apk) {
if (apk != null) {
DownloadApkManagerActivity.this.mApk = apk;
Log.d(TAG, "onApkLoad() >>> title: " + apk.getTitle() + " desc: " + apk.getDesc() + " appName: " + apk.getAppName() +
" packageName: " + apk.getPackageName() + " logoUrl: " + apk.getLogoUrl());
} else {
Log.d(TAG, "apk is null.");
}
}
@Override
public void onError(String code, String msg, String networkFirmId) {
Log.e(TAG, "onError() >>> code: " + code + " msg: " + msg + " networkFirmId: " + networkFirmId);
}
});
}
}
@Override
public void onClick(View v) {
if (mApkManager == null) {
return;
}
int viewId = v.getId();
if (viewId == R.id.bt_show_apk_dialog) {
//点击按钮进行激活弹窗提示
showOpenOrInstallAppDialog(false);
} else if (viewId == R.id.bt_load_apk) {
mApkManager.loadApk();
} else {
if (mApk != null) {
mApkManager.startInstall(mApk);
} else {
Log.e(TAG, "apk is null, can not start install.");
}
}
}
private void showOpenOrInstallAppDialog(boolean isFromBackPress) {
if (mApkManager != null) {
mApkManager.showOpenOrInstallAppDialog(new ATApkDialogClickListener() {
@Override
public void onShowResult(int resultType) {
Log.d(TAG, "onShowResult() >>> resultType: " + resultType);
if (resultType == ATApkDialogClickListener.NO_DLG) {
if (isFromBackPress) {
finish();
} else {
Toast.makeText(DownloadApkManagerActivity.this, "没有可以安装或激活的应用", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onButtonClick(int clickType) {
Log.d(TAG, "onButtonClick() >>> clickType: " + clickType);
if (isFromBackPress) {
finish();
};
}
});
}
}
/**
* 该方法用于判断某个 apk 是否已经安装
* @param context
* @param pkgName
* @return
*/
private boolean isAPKInstalled(Context context, String pkgName) {
if (TextUtils.isEmpty(pkgName)) {
return false;
}
PackageManager pm = context.getPackageManager();
try {
PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_GIDS);
return packageInfo != null;
} catch (Throwable e) {
return false;
}
}
}