前言:前段时间做在OTT盒子项目,被安排实现以下开机广告功能,就像市面上多数智能电视一样。
开始做时遇到2个问题,第一开机时是没有网络的,怎么实时播放广告呢。第二开机过程中怎么启动播放器进行广告播放呢。
针对这2个问题,我下面详细描述下我做的处理。
1、广告视频的下载以及与服务器的交互。
由于OTT在开机过程中是没有网络的所以我们必须有一个核心service在第一次开机后通过接收BOOT_COMPLETED以及CONNECTIVITY_CHANGE广播进行自启动来和服务器进行交互,从而将所需要的广告视频资源下载到本地,然后将每次的播放行为上报给服务器方便记录。
public class AdvertiseService extends Service implements Handler.Callback {
private static final int MSG_GETLIST = 1;
private static final int MSG_UPLOADLOG = 2;
private static final int MSG_ADMASTER = 3;
public static long GETLIST_INTERVAL = 30 * 60 * 1000;
public static long ADMASTER_INTERVAL = 1 * 60 * 1000;
private Handler mHandler = new Handler(this);
public AdvertiseService() {
}
@Override
public void onCreate() {
super.onCreate();
Constants.setDir(getApplicationContext());
mHandler.sendEmptyMessageDelayed(MSG_UPLOADLOG, 2 * 60 * 1000);
mHandler.sendEmptyMessageDelayed(MSG_GETLIST, 3 * 60 * 1000);
if (Utils.isNetworkConnected(this) && mNeedUploadLog) {
AdmasterSdk.init(this, Utils.getAdMasterUrl(this));
AdmasterSdk.setLogState(true);
AdmasterSdk.onExpose(Utils.getAdMasterUrl(this));
} else {
mHandler.sendEmptyMessageDelayed(MSG_ADMASTER, 500);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Utils.log(LOGD, "onStartCommand --> enter, intent: " + intent + ", flags: " + flags + ", startId: " + startId + "!");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public boolean handleMessage(Message msg) {
Utils.log(LOGW, "handleMessage, msg = " + msg.what);
switch (msg.what) {
case MSG_GETLIST:
Utils.getListService(this);
mHandler.sendEmptyMessageDelayed(MSG_GETLIST, GETLIST_INTERVAL);
break;
case MSG_UPLOADLOG:
Utils.getIpService(this);
Utils.getLocationService(this);
break;
case MSG_ADMASTER:
if (Utils.isNetworkConnected(this) && mNeedUploadLog) {
AdmasterSdk.init(this, Utils.getAdMasterUrl(this));
AdmasterSdk.setLogState(true);
AdmasterSdk.onExpose(Utils.getAdMasterUrl(this));
} else {
mHandler.sendEmptyMessageDelayed(MSG_ADMASTER, ADMASTER_INTERVAL);
}
break;
}
return false;
}
}
service还是比较简单的主要是做一些和服务器的交互,获取广告位id,获取相关公网ip等主要信息,从而将盒子的广告信息注册到服务器将最新的广告视频带下来。通过服务器返回的result中带的下载链接,将广告下载下来。
2、广告顺利下载下来了那怎么让系统在launcher起来时自动播放呢?
通过查阅android系统源码,发现启动应用是在ActivityManagerService.java中。通过修改getHomeIntent()方法将启动的应用改成自己的MeidaPlayerActiviy就能在开机完成后不是启动laucnher而是我们的广告播放器。具体代码如下
Intent getHomeIntent() {
if (SystemProperties.get("persist.sys.ad.exist").equals("1")) {
Intent skyIntent = SkyBootIntentOem.getInstance().getHomeIntent();
if (skyIntent != null) {
return skyIntent;
}
}
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
在SkyBootIntentOem类中定义要启动的intent。
public Intent getHomeIntent(){
if (!isFirst) {
return null;
}
Intent intent = new Intent("com.phicomm.phitvadvertisement.AdvertiseMainActivity");
isFirst = false;
return intent;
}
这样理论上就能在开机时顺利优先启动我们所自定义的MeidaPlayerActiviy来播放广告了。但是尝试了多次后发现虽然代码调用了相关的intent但是应用却没有起来。在查阅了诸多资料后发现原来是MeidaPlayerActiviy这个appliction有一个关键属性没有加。。。android:directBootAware="true"就是这个属性。
由于activitymanagerservice启动应用时系统还没有完全起来,所以要优先启动应用就必须加上这个属性。
有了这个属性后开机广告应用顺利完成,剩下的就是bug的修复了。
之后我会将完整代码提交到github上,有兴趣的小伙伴可以看下。