Android 应用插件

最近为了解一个盒子上app在我们平台上无法工作的问题,去浅尝了反编译apk并在smali中注入代码,推荐一个叫Android Killer的IDE工具,几乎集成了所有反编译和打包的功能。

同时也了解该app使用的插件加载,之所以会使用到插件来完成该功能,因为该app涉及到底层的audio实现,audio在framework会在各家平台有不同定制,利用插件就能保证对主程序来说平台都一样,而插件就根据各个平台的不同去做不同实现。

下面简要介绍一下该app的插件实现方法:

1、插件也是以apk的形式存在的

先来看看插件的Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="1" android:versionName="1.0" package="com.kelvinwu.earlycmccplugin" platformBuildVersionCode="19" platformBuildVersionName="4.4.2-1456859"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <meta-data android:name="IPlugin" android:value="com.kelvinwu.cmccplugin.EarlyAppEvent" />
        <meta-data android:name="IMicrophone" android:value="com.kelvinwu.cmccplugin.MicrophoneController" />
        <meta-data android:name="IKaraoke" android:value="com.kelvinwu.cmccplugin.EarlyISingEvent" />
        <meta-data android:name="IAudioTrack" android:value="com.kelvinwu.cmccplugin.AudioTracker" />
        <meta-data android:name="IAudioTrackProfile" android:value="com.kelvinwu.cmccplugin.AudioTrackProfile" />
        <meta-data android:name="HardWareLevel" android:value="2" />
        <meta-data android:name="Manufacturer" android:value="kelvinwu" />
        <service android:name="com.kelvinwu.cmccplugin.PluginService">
            <intent-filter>
                <action android:name="com.cmcc.karaoke.plugin" />
            </intent-filter>
        </service>
    </application>
</manifest>

可以看到application下有两类tag:meta-data和service

meta-data:标签中name是接口的名字,value是实现该接口的具体实现类的名字,主程序中可以根据需要实现的接口去拿实现类的对象。

service:定义了一个有很特殊的名字的action,这个的作用是针对已经安装好的插件,主app能够通过pm去查询这样的一个service,用于辨别哪些package是插件。

这里的插件有两种存在方式:1、被安装(通过特殊的service去查询即可) 2、未安装(apk包中asset自带)

app会优先去查找系统中已经安装过的,如果找不到会用apk包中带的插件包。

2、如何使用插件中的类

app中提供了一个插件工具类,主要功能就是根据我们需要的接口类,根据插件创建出具体的实现类。

InterfaceClass I = function(InterfaceClass.class);

对外就这样封装使用很简单方便,下面看看代码实现,因为是反编译的就不是很好看,基本原理很简单。

final class d
  implements b
{
  private static final String b = MainApplication.b().getApplicationInfo().dataDir;
  private static final String c = b + "/plugin/asset.apk";
  private static final String d = b + "/plugin/asset.cfg";
  private static final String e = b + "/plugin/libs";
  private static final String f = b + "/plugin/opt";
  HashMap<String, ClassLoader> a = new HashMap();
  private ApplicationInfo g = null;
  
/*
返回一个可用的插件的application
*/
  private ApplicationInfo a()
  {
    Object localObject2;
    StringBuilder localStringBuilder;
    if (this.g == null)
    {
//优先查找已经安装的package
      localObject1 = new ArrayList();
      localObject2 = com.iflytek.app.b.b().getPackageManager().queryIntentServices(new Intent("com.cmcc.karaoke.plugin"), 128).iterator();
      while (((Iterator)localObject2).hasNext()) {
        ((ArrayList)localObject1).add(((ResolveInfo)((Iterator)localObject2).next()).serviceInfo.applicationInfo);
      }
//从已经安装的package中找到第一个能满足需求的package的applicationinfo
      this.g = a((List)localObject1);
      localObject2 = com.iflytek.log.b.a(this);
      localStringBuilder = new StringBuilder("verify installed packages pass = ");
      if (this.g == null)
      {
        localObject1 = "null";
        ((com.iflytek.log.b)localObject2).c((String)localObject1);
      }
    }
    else if (this.g == null)
    {
//其次查找asset中自带的插件
      this.g = b();
      localObject2 = com.iflytek.log.b.a(this);
      localStringBuilder = new StringBuilder("verify assets packages pass = ");
      if (this.g != null) {
        break label233;
      }
    }
    label233:
    for (Object localObject1 = "null";; localObject1 = this.g.packageName)
    {
      ((com.iflytek.log.b)localObject2).c((String)localObject1);
      if (this.g == null)
      {
        this.g = d("empty.apk");
        com.iflytek.log.b.a(this).c("use empty package = " + this.g.packageName);
      }
      return this.g;
      localObject1 = this.g.packageName;
      break;
    }
  }
  
/*
参数1:apk绝对路径
参数2:native lib库路径
*/
  private static ApplicationInfo a(String paramString1, String paramString2)
  {
    PackageInfo localPackageInfo = MainApplication.b().getPackageManager().getPackageArchiveInfo(paramString1, 128);
    if (localPackageInfo != null)
    {
      localPackageInfo.applicationInfo.sourceDir = paramString1;
      localPackageInfo.applicationInfo.nativeLibraryDir = paramString2;
      return localPackageInfo.applicationInfo;
    }
    return null;
  }
  
/*
选出符合要求的application
*/
  private ApplicationInfo a(List<ApplicationInfo> paramList)
  {
    paramList = paramList.iterator();
    while (paramList.hasNext())
    {
      ApplicationInfo localApplicationInfo = (ApplicationInfo)paramList.next();
      if (a(localApplicationInfo)) {
        return localApplicationInfo;
      }
    }
    return null;
  }
  
  private Object a(ApplicationInfo paramApplicationInfo, String paramString)
  {
    try
    {
      String str1 = paramApplicationInfo.packageName;
      String str2 = paramApplicationInfo.sourceDir;
      String str3 = paramApplicationInfo.nativeLibraryDir;
      ClassLoader localClassLoader = (ClassLoader)this.a.get(str1);
      paramApplicationInfo = localClassLoader;
      if (localClassLoader == null)
      {
        paramApplicationInfo = new File(f);
        if (!paramApplicationInfo.exists()) {
          paramApplicationInfo.mkdirs();
        }
        paramApplicationInfo = new DexClassLoader(str2, f, str3, MainApplication.b().getClassLoader());
        this.a.put(str1, paramApplicationInfo);
      }
      paramApplicationInfo = Class.forName(paramString, true, paramApplicationInfo).newInstance();
      return paramApplicationInfo;
    }
    catch (Exception paramApplicationInfo)
    {
      paramApplicationInfo.printStackTrace();
    }
    return null;
  }
  
  private <T> T a(ApplicationInfo paramApplicationInfo, String paramString, Class<T> paramClass)
  {
    paramApplicationInfo = a(paramApplicationInfo, paramString);
    if ((paramApplicationInfo != null) && (paramClass.isInstance(paramApplicationInfo))) {
      return paramApplicationInfo;
    }
    throw new InstantiationError("pluginClassName:" + paramString + "Instance fail");
  }
/*
判断系统是否满足底层需求,判断的方法存在于插件当中
*/  
  private boolean a(ApplicationInfo paramApplicationInfo)
  {
    String str = paramApplicationInfo.metaData.getString("IPlugin");
    if (str == null) {
      return false;
    }
    paramApplicationInfo = (IPlugin)a(paramApplicationInfo, str, IPlugin.class);
    if (paramApplicationInfo == null) {
      return false;
    }
    return paramApplicationInfo.isSupport(MainApplication.b());
  }

/*
参数1:插件包
参数2:配置里写入的package名
*/  
  private static boolean a(File paramFile, String paramString)
  {
    try
    {
      boolean bool = Arrays.equals(com.iflytek.utils.hash.b.a(MainApplication.b().getAssets().open(paramString)), com.iflytek.utils.hash.b.a(paramFile));
      return bool;
    }
    catch (Exception paramFile) {}
    return false;
  }
  
  private ApplicationInfo b()
  {
    int i = 0;
    Object localObject1 = new File(c);
    Object localObject2 = new File(d);
    if ((((File)localObject1).exists()) && (((File)localObject2).exists()))
    {
      localObject2 = e.b((File)localObject2);
      if (TextUtils.isEmpty((CharSequence)localObject2)) {}
    }
    for (boolean bool = a((File)localObject1, "plugin/" + (String)localObject2); bool; bool = false)
    {
      localObject1 = a(c, e);
      if ((localObject1 == null) || (!a((ApplicationInfo)localObject1))) {
        break;
      }
      return localObject1;
    }
    for (;;)
    {
      try
      {
        localObject1 = new File(d);
        e.a((File)localObject1);
        localObject2 = MainApplication.b().getAssets().list("plugin");
        int j = localObject2.length;
        if (i < j)
        {
          String str = localObject2[i];
          if ((!str.endsWith(".apk")) || (str.equals("empty.apk"))) {
            break label217;
          }
          ApplicationInfo localApplicationInfo = d(str);
          if ((localApplicationInfo == null) || (!a(localApplicationInfo))) {
            break label217;
          }
          e.a((File)localObject1, Arrays.asList(new String[] { str }));
          return localApplicationInfo;
        }
      }
      catch (IOException localIOException)
      {
        localIOException.printStackTrace();
      }
      return null;
      label217:
      i += 1;
    }
  }
  
  private static ApplicationInfo d(String paramString)
  {
    String str1 = c;
    String str2 = e;
    File localFile = new File(f);
    e.a(new File(str2));
    e.a(localFile);
    localFile.mkdirs();
    com.iflytek.aichang.util.b.a(MainApplication.b(), "plugin/" + paramString, str1);
    a.a(str1, str2);
    return a(str1, str2);
  }
  
/*
这个是核心的接口,构造需求的接口类
*/
  public final <T> T a(Class<T> paramClass)
  {
    ApplicationInfo localApplicationInfo = a();
    return a(localApplicationInfo, localApplicationInfo.metaData.getString(paramClass.getSimpleName()), paramClass);
  }
  
/*
对外获取manifest中对应的meta-deta
*/
  public final String a(String paramString)
  {
    return a().metaData.getString(paramString);
  }
  
  public final int b(String paramString)
  {
    return a().metaData.getInt(paramString);
  }
  
  public final boolean c(String paramString)
  {
    return a().metaData.getBoolean(paramString);
  }
}

中间都是一些跟package和application相关的API,这里就不多说了

插件的两个基本核心就是,插件apk的设计和插件的工具类的实现。

    原文作者:Kelvin wu
    原文地址: https://zhuanlan.zhihu.com/p/21429908
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞