Android HookActivity一行代码实现开屏广告
2018-05-09 本文已影响1356人
酸菜xwdz
需求背景
多个产品线都需要实现开屏广告,我们产品广告都是接的我们自家广告SDK,而SplashActivity
只是几行代码请求我们广告,广告SDK会把View封装好返回来,SplashActivity
要做的事情只是获取响应结果,并且show
出来.
如何实现一行代码实现开屏广告
- 通过Hook Instrumentation监听到App主界面启动并且回调出来,然后跳转到内置的一个activity里面实现开屏广告广告结束再finish。
- 如何鉴别是否是主LAUNCHER(App第一个启动界面)
关于如何判断APP的第一个启动界面请看AndroidManifest.xml一段代码
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
intent-filter
标签里面两个字段分别表示如下意思
-
android.intent.action.MAIN
决定应用程序最先启动的Activity -
android.intent.category.LAUNCHER
表示activity应该被列入系统的启动器(launcher)(允许用户启动它)。Launcher是安卓系统中的桌面启动器,是桌面UI的统称。
也就是说根据如上标签可以判断是否是App第一个入口Activity代码如下:
if (Intent.ACTION_MAIN.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
}
如何Hook Instrumentation实现监听Activity生命周期
我们在Activity调用的startActivity(intent)
方法最后都会调用到Instrumentation的execStartActivity
方法,而最后activity的创建会被回调到Instrumentation
的callActivityOnCreate
方法。强烈推荐Activity的启动过程
也就是说只需要我们通过反射替换Instrumentation
然后在callActivityOnCreate
方法里面回调出来即可。反射基础用法
代码如下:
实现Instrumentation实现一个代理类:
/**
* @author 黄兴伟 (xwd9989@gamil.com)
* @since 2018/5/9
*/
public class ProxyInstrumentation extends Instrumentation {
private OnActivityCreateListener mOnActivityCreateListener;
public ProxyInstrumentation() {
Log.e("TAG", "ProxyInstrumentation created");
}
public void setActivityCreateListener(OnActivityCreateListener onActivityCreateListener) {
this.mOnActivityCreateListener = onActivityCreateListener;
}
@Override
public void callActivityOnCreate(Activity activity, Bundle icicle) {
super.callActivityOnCreate(activity, icicle);
final Intent intent = activity.getIntent();
if (Intent.ACTION_MAIN.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
//判断是否是住入口 回调
if (mOnActivityCreateListener != null) {
mOnActivityCreateListener.onHookActivityCreated(activity, icicle);
}
}
}
/*回调接口*/
public interface OnActivityCreateListener {
void onHookActivityCreated(Activity activity, Bundle icicle);
}
}
反射获取Instrumentation,将代理ProxyInstrumentation注入到ActivityThread中
代码如下:
/**
* @author 黄兴伟 (xwd9989@gamil.com)
* @since 2018/5/9
*/
public class HookActivityHelper {
private static final HookActivityHelper INSTANCE = new HookActivityHelper();
private ProxyInstrumentation mProxyInstrumentation;
public static HookActivityHelper get() {
return INSTANCE;
}
public void open() {
mProxyInstrumentation.setActivityCreateListener(new ProxyInstrumentation.OnActivityCreateListener() {
@Override
public void onHookActivityCreated(Activity activity, Bundle icicle) {
Intent intent = new Intent(activity, WelcomeActivity.class);
activity.startActivity(intent);
}
});
}
private HookActivityHelper() {
try {
Class<?> clazz = Class.forName("android.app.ActivityThread");
Method currentActivityThread = clazz.getDeclaredMethod("currentActivityThread");
Object object = currentActivityThread.invoke(null);
Field field = clazz.getDeclaredField("mInstrumentation");
field.setAccessible(true);
mProxyInstrumentation = new ProxyInstrumentation();
field.set(object, mProxyInstrumentation);
} catch (Exception e) {
e.printStackTrace();
}
}
}
新建内置WelcomeActivity
/**
* 再这个内置的Activity实现开屏广告UI或请求广告SDK即可
* @author 黄兴伟 (xwd9989@gamil.com)
* @since 2018/5/9
*/
public class WelcomeActivity extends AppCompatActivity {
private TextView mTextView;
private CountDownTimer mCountDownTimer = new CountDownTimer(6000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
mTextView.setText("Hook成功,欢迎来到酸菜个人站点 huangxingwei.cn 6秒后返回MainActivity = " + millisUntilFinished / 1000);
}
@Override
public void onFinish() {
finish();
Log.e("TAG", "WelcomeActivity finished");
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("TAG", "WelcomeActivity created success");
setContentView(R.layout.welcome_layout);
mTextView = findViewById(R.id.text);
mCountDownTimer.start();
}
}
AndroidManifest.xml声明如下
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xwdz.simple">
<application
android:name=".TestApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".hook.WelcomeActivity"/>
</application>
</manifest>
最后再程序的Application编写一行代码即可实现开屏广告。
public class TestApp extends Application {
@Override
public void onCreate() {
super.onCreate();
HookActivityHelper.get().open();
}
}
Log如下
05-09 17:27:40.832 3997-3997/? E/TAG: ProxyInstrumentation created
05-09 17:27:40.853 3997-3997/? E/TAG: MainActivity created success
05-09 17:27:41.072 3997-3997/? E/TAG: WelcomeActivity created success
05-09 17:27:51.091 3997-3997/com.xwdz.simple E/TAG: WelcomeActivity finished
测试效果如下
hook6.gif
总结
完整代码参照simple-code,其实HookActivity还可以做更多的事,比如插件Activity等,希望能给大家提供到思路,感谢阅读。