Android plug-in development, first entry

write in front

This article is original, please indicate the address in the form of a link for reprint: http://kymjs.com/code/2014/09/15/02
I haven’t written a blog for a long time, this time I’m going to write about my research results in the past few days - Android Plug-in development framework CJFrameForAndroid.

It's been a long time since I wrote a blog. This time I'm going to write about my research results in the past few days - CJFrameForAndroid , an Android plug-in development framework .

Background change

First, you need to know what plug-in development is. Take the most common QQ as an example, there is a management in the third interface dynamic. After clicking on it, you can choose a lot of planting functions. Here, Tencent only puts some web applications, so if you want to add a plane game in the future, How to do? Let the user reinstall it, this is the problem solved by plug-in development.

Summarize plug-in development in one sentence: You can basically understand that an apk can be run without installation. It's just that this operation has many restrictions, so it is called a plug-in, otherwise it is called a virus. In fact, Taobao, Baidu, Tencent, etc. have mature dynamic loading frameworks, including apkplug, but they are not open source.

Let me talk about the difficulties of this technology in my opinion: 1. An uninstalled apk cannot be run under normal conditions; 2. The resources of this apk cannot be referenced; 3. Even if the interface of this apk is loaded, it cannot communicate with the user. interact.

At first, I checked the information. The first point is easy to solve. There is a dexClassLoad class loader in Android. Everyone should understand that it is to load a class to run through reflection. Second, there are two methods on the Internet: you can put the resources of the plug-in on the sd card and read it in the form of a stream, but some people object that there will be problems with reading with a stream, and the wildcard is too poor; one is better The solution is to copy the resources in the apk into the current app, and then it can be loaded. This method is good, but users will copy a copy of the plug-in every time they download it. Over time, the space requirements are too high, and there is no way to solve the third point. And the third point, there is a project called AndroidDynamicLoader on github, which uses Fragment as the form of plug-in. Due to the particularity of Fragment (it can handle logical interaction and has the same life cycle as Activity). However, Fragment is too restrictive and too fragmented to make it too complicated to use. Until I found an official 360 blog, the blog gave an idea: to dynamically change the environment where an apk is located through the Application class designed in the proxy/delegation mode to achieve the purpose of dynamic loading. With this idea in mind, I wanted to design an application class myself, but the technology was limited and it was too complicated, so combining the idea of ​​AndroidDynamicLoader and the idea of ​​360, I designed an Activity to proxy the plug-in Activity, so there was CJFrameForAndroid.

###Principle description

首先解释几个名词: APP项目:指要调用插件apk的那个已经安装到用户手机上的应用。
插件项目:指没有被安装且希望借助已经安装到手机上的项目运行的apk。 
插件化:Activity继承自CJActivity,且与APP项目jar包冲突已经解决的插件项目称为已经被插件化。 
Activity事务:在CJFrameForAndroid中,一个Activity的生命周期以及交互事件统称为Activity的事务。 
托管所:指插件中的一个委派/代理Activity,通过这个Activity去处理插件中Activity的全部事务,从而表现为就像插件中的Activity在运行一样。

CJFrameForAndroid的实现原理是通过类加载器,动态加载存在于SD卡上的apk包中的Activity。通过使用一个托管所,插件Activity全部事务(包括声明周期与交互事件)将交由托管所来处理,间接实现插件的运行。
一句话描述:CJFrameForAndroid中的托管所,复制了插件中的Activity,来替代插件中的Activity与用户交互。
看到这里你应该就明白了,整个框架最核心的部分就是这个托管所。这里给出CJFrameForAndroid中这个托管所的细节代码:

  /**
   * 通过反射,获取到插件的资源访问器
   */
  protected void initResources() {
      try {
          AssetManager assetManager = AssetManager.class.newInstance();
          Method addAssetPath = assetManager.getClass().getMethod(
                  "addAssetPath", String.class);
          addAssetPath.invoke(assetManager, mDexPath);
          mAssetManager = assetManager;
      } catch (Exception e) {
          e.printStackTrace();
      }
      Resources superRes = super.getResources();
      mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),
              superRes.getConfiguration());
      mTheme = mResources.newTheme();
      mTheme.setTo(super.getTheme());
  }
   
  /**
   * 启动插件的Activity
   */
  protected void launchPluginActivity() {
      PackageInfo packageInfo = CJTool.getAppInfo(this, mDexPath);
      if ((packageInfo.activities != null)
              && (packageInfo.activities.length > 0)) {
          String activityName = packageInfo.activities[mAtyIndex].name;
          mClass = activityName;
          launchPluginActivity(mClass);
      }
  }
   
  /**
   * 启动指定的Activity
   * 
   * @param className
   *            要启动的Activity完整类名
   */
  protected void launchPluginActivity(final String className) {
      try {
          Class<?> atyClass = getClassLoader().loadClass(className);
          Constructor<?> atyConstructor = atyClass
                  .getConstructor(new Class[] {});
          Object instance = atyConstructor.newInstance(new Object[] {});
          setRemoteActivity(instance);
          mPluginAty.setProxy(this, mDexPath);
          Bundle bundle = new Bundle();
          bundle.putInt(CJConfig.FROM, CJConfig.FROM_PROXY_APP);
          mPluginAty.onCreate(bundle);
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
   
  /**
   * 保留一份插件Activity对象
   */
  protected void setRemoteActivity(Object activity) {
      if (activity instanceof I_CJActivity) {
          mPluginAty = (I_CJActivity) activity;
      } else {
          throw new ClassCastException(
                  "plugin activity must implements I_CJActivity");
      }
  }

本框架目前仅仅是一个开发阶段,仅仅是实现了插件Activity的运行(原理上来说,动态注册的广播也可以运行),而Service、contentProvider都没办法使用,这些都仍在研究中。 在未来的某一天,也许会将这个CJFrameForAndroid插件框架与KJFrameForAndroid快捷开发框架合并,组成一个更完善应用开发框架,对自己说:加油!

●目前仅支持Activity和Fragment,其他特殊组件暂未测试。
●APP项目和插件项目中,都需要使用到CJFrameForAndroid的jar包。
●在项目中必须加入托管所声明。
●在开发插件的时候,必须继承CJActivity;
●在插件的Activity中,一切使用this的部分必须使用that来替代;
●在插件Activity跳转时,推荐使用CJActivityUtils类来辅助跳转;
●在插件和APP两个工程中不能引用相同的jar包;

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326692342&siteId=291194637