在系统层实现三方应用去除广告页
这个功能只是我的一个小练习(如何去除的思路来自千里马feamework的教程,自己在发散思维后进行实现的),在实际开发中可以借鉴去解决一些其他的问题,但是关于三方应用去除开屏广告这个操作会有其他各式各样的影响因素
思路:
将应用的包名、广告页的类名、主界面的类名都dump出来然后写死到Array.xml里面,在framework/base/core下的Activity.java的startActivity方法里面去实现
大致流程:这里新写一个方法,将数据从xml文件取出(使用TypedArray暂存)并处理后存入ConcurrentHashMap(线程安全)「当前只存了包名和主界面类名,后面还需要使用广告页类名去判断是否弹广告」,当拿到系统资源且传入的intent不为空时,开始遍历packageName并判断传入的intent的包名是否存在于map中,若存在,返回主界面包名类名,若不存在,返回空
在startActivity中,判断intent和上面的方法都不为空的时候,使用intent.setComponent方法修改ComponentName。
之后startActivity带着修改后的intent继续运行。
在xml中写入数据
这里我是在framework/base/core/res/res/values/arrays.xml中新增数据
<string-array name="package_name">
<item>com.android.contacts/com.android.contacts.activities.PeopleActivity/com.android.contacts.activities.ContactEditorActivity</item>
<item>com.android.settings/com.android.settings.Settings/com.android.phone.MobileNetworkSettings</item>
<item>a1/b/c</item>
<item>a2/b/c</item>
<item>a3/b/c</item>
<item>a4/b/c</item>
<item>a5/b/c</item>
<item>a6/b/c</item>
<item>a7/b/c</item>
<item>a8/b/c</item>
<item>a9/b/c</item>
<item>a10/b/c</item>
<item>a11/b/c</item>
<item>a12/b/c</item>
<item>a13/b/c</item>
<item>a14/b/c</item>
<item>a15/b/c</item>
<item>a16/b/c</item>
</string-array>
注:记得在symbols.xml里面定义一下
在framework/base/core下的Activity.java中新增如下方法
这里主要是去获取xml文件中存储的内容并且判断传入的intent的包名是否是xml文件中所拥有的,如果有,则输出我们需要的包名类名,如果没有则为null
/**
* 获取包名类名数据并进行分组
*/
private String removeInfomercial(Intent intent){
//存储包名以及修改后的类名
ConcurrentHashMap<String, String> packageName = new ConcurrentHashMap<String,String>();
//获取系统资源是否存在
int resId = Resources.getSystem().getIdentifier("package_name","array","android");
//log确认是否获取到资源
Log.i("test3", String.valueOf(resId));
if (resId != 0){
//将arrays.xml中package_name中的包名类名数据存入typedArray中
TypedArray overalls = Resources.getSystem().obtainTypedArray(resId);
//遍历typedArray并拆分拿到的字符串后存入map中
for (int i = 0;i<overalls.length();i++){
String[] packageNames = overalls.getString(i).split("/");
packageName.put(packageNames[0],packageNames[2]);
}
//回收 TypedArray 避免内存泄漏
overalls.recycle();
//log确认内容填充完毕
Log.i("test3",packageName.toString());
}else {
//处理未找到资源情况
Log.i("test3","未获取数据");
}
//当拿到系统资源且传入的intent不为空时,开始遍历packageName并判断传入的intent的包名是否存在于map中
if (intent != null && resId != 0){
for (String name:packageName.keySet()){
Log.i("test3","map中的key值"+name);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.DONUT) {
Log.i("test3","map中的key值与拿到的包名对比结果"+name.equals(intent.getComponent().getPackageName())+"key值"+name+"包名"+intent.getComponent().getPackageName().toString());
//注意:获取包名时:通过intent.getComponent().getPackageName()而不是intent.getPackage()
//前者是一个标准且有效的方式来获取显式 Intent 中目标组件的包名,而后者则不是一个标准方法,可能不存在
if (name.equals(intent.getComponent().getPackageName())){
//若存在则返回对应的包名类名
Log.i("test3","输出的包名为"+packageName.get(name));
return packageName.get(name);
}
}
}
}
Log.i("test3","intent输出内容"+intent.getComponent().getPackageName().toString());
//若不存在则返回null
return null;
}
这里是具体实现的地方,这块也比较简单,自己看
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
//判空后修改需要打开的界面
if (removeInfomercial(intent) != null && intent.getComponent() != null){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.DONUT) {
//创建新的ComponentName并填充传入的数据
ComponentName destCom = new ComponentName(intent.getComponent().getPackageName(),removeInfomercial(intent));
//其他界面缺少android.intent.category.LAUNCHER和android.intent.category.LAUNCHER导致无法启动
//或者在AndroidManifest.xml文件中的exported属性被设置为false。当exported属性为false时,表示该Activity只能被本应用调用
// Log.i("test3","修改后的内容:"+removeInfomercial(intent));
//修改打开的界面
intent.setComponent(destCom);
// 设置Intent的动作为MAIN
intent.setAction(Intent.ACTION_MAIN);
// 添加LAUNCHER类别
intent.addCategory(Intent.CATEGORY_LAUNCHER);
Log.i("test3","传入的数据"+destCom.toString());
// Log.i("test3","修改内容"+intent.setComponent(destCom).toString());
}
}else {
Log.i("test3","未进行修改");
}
//获取打开界面的action
Log.i("test3"," action为: "+intent.toString());
}
遇到的问题:
- 如何获取数据
- 在symbols.xml内没有去声明导致拿不到数据
- 仿照Activity .java里面使用TypedArray拿到数据后再遍历然后去处理
- 在removeInfomercial类中进行map中的数据与应用传到系统层的数据进行判断时出现空指针
- 获取包名时:通过intent.getComponent().getPackageName()而不是intent.getPackage()
- 前者是一个标准且有效的方式来获取显式 Intent 中目标组件的包名,而后者则不是一个标准方法,可能不存在
- 由于是在模拟器上进行测试,选择的应用目标界面缺少android.intent.action.MAIN和android.intent.category.LAUNCHER两个参数导致无法启动
- 强行添加Action和Category后还是有问题
- 当前来看是因为exported属性为false时,表示该Activity只能被本应用调用
log打印的intent内容示例
02-07 16:58:11.819 2275 2275 I test3 : action为: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.settings/.Settings (has extras) }
02-07 16:58:17.737 2902 2902 I test3 : action为: Intent { act=android.intent.action.MAIN cmp=com.android.phone/.MobileNetworkSettings }
02-07 16:55:39.557 2275 2275 I test3 : action为: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.contacts/.activities.PeopleActivity bnds=[96,634][192,730] (has extras) }
02-07 16:55:41.066 2413 2413 I test3 : action为: Intent { act=android.intent.action.INSERT dat=content://com.android.contacts/contacts pkg=com.android.contacts (has extras) }
逻辑存在的缺陷
- 只要有应用调用startActivity就会走一遍这个逻辑,代码中存在两个遍历,在数据量大的情况下会导致各种性能问题
- 应用的开屏广告过程中部分应用会进行一些资源的加载,在资源没有加载完就进入主界面可能会导致闪屏甚至如果是关键资源未加载可能会导致应用crash
344

被折叠的 条评论
为什么被折叠?



