最近有个需求是在push信息内制定某模块(Activty)名称,端上拿到该名称,在用户点击通知的时候自动打开对应Activity页面。
老逻辑是这样实现的:
每次新增一个功能模块的时候,都会在代码中使用switch语句进行选择判断:
switch( nameFromPush){
case "name1":
startActivity(this,ActivityName1.class);
break;
case "name2":
startActivity(this,ActivityName2.class);
break;
...
}
这么做有一个问题,需要修改代码。每次都要修改。
既然startActivity每次都要调用,且跳转的页面唯一取决于targetActivity的名称,那么,能否有一种方式能直接将push中的信息字段应用于startActivity()的第二个参数,从而废弃掉这种通过代码映射的方式呢。答案是肯定的,我们的思路即为下发targetActivity的类名,参数就可以直接使用。
但随之而来的又一个问题在于,类名其实是需要使用完全限定名的,也就需要推送下发全称,比如
startActivity(this,"com.ghostinmatrix.package1.TargetActivity.class");
,那么一旦这个模块修改了package(在类名本身不变的情况下),下发的push就不会生效了,因为app已经找不到这个类了。综上,push下发完全限定名是非常不合理的,只能下发类名。这就需要你在拿到push下发的“TargetActivity”,在app内生成它的完全限定名。我们可以使用如下方式:
DexFile df = new DexFile(DuApp.getAppContext().getPackageCodePath());
Enumeration<String> enumeration = df.entries();
while (enumeration.hasMoreElements()) {
String classname = enumeration.nextElement();
if (classname.contains("com.baidu.lbs")) {
String[] name = classname.split("\\.");
hashMap.put(name[name.length - 1], classname);
}
}
这里要介绍一下DexFile类,你只需要知道,通过ApplicationContext.getPackageCodePath能够访问到这个App内所有加载的类。而DexFile就是这样一个加载的入口。
/**
* Return the full path to this context's primary Android package.
* The Android package is a ZIP file which contains application's
* primary code and assets.
*
* <p>Note: this is not generally useful for applications, since they should
* not be directly accessing the file system.
*
* @return String Path to the code and assets.
*/
public abstract String getPackageCodePath();
注释大意为:返回这个context所对应的主包名,它是一个包含着app代码和资源的zip包。
因此通过这个路径可以获得所有主包名下的所有类的全限定名。
PS:对于Gradle 2.0.0以上的闪电编译(Instant Run)来说,存在一个问题,即通过上述方式不能获得apk中所有编译的类,而是只能找到其运行时和instant run所用到的.dex文件,(如com.tools.android.fd.runtime, com.tools.android.fd.common and com.tools.android.tools.ir.api)。其余的编译为.dex文件的类会被压缩到叫做instant-run.zip的压缩文件中放在同级目录下。不过不用担心,instant run只会用于Debug模式下的构建,而不会对Release模式有影响。
回到正题,DexFile能够得到所有类的完全限定名,也就一定会包含push下发的类名,后面的就不用多说了吧。找到对应的全限定名,作为第二个参数,startActivity。
网友评论