Delegate text transformation to “plugin” Android a

2019-02-19 01:52发布

问题:

Context

Our app shows an HTML flashcard to the user.
We have added several layers of "filters" to satisfy different groups of users:

  • To satisfy chess enthusiasts, we convert any {FEN:rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2} block to an HTML table representing a chess board with pieces at the right position
  • To satisfy Chinese language learners, we convert to <ruby>字<rt>zì</rt></ruby>
  • ...

Original HTML → Chess transformation → Chinese transformation → ... → Final HTML to display

Problem

The number of filters is growing, leading to problems:

  • Slower rendition
  • Heavier download
  • Bigger source code to maintain
  • More bugs/crashes
  • Maintenance burden

Question

So, we would like to make these separately installable apps.
For instance, a chess+Chinese enthusiast would install 3 apps:

  • TheApp
  • TheApp Chess plugin
  • TheApp Chinese plugin

TheApp would automatically discover what plugins are installed, and call them in turn (order does not matter).

I was thinking of using an intent THEAPPTRANSFORM, but how can I receive the list of apps that have an <intent-filter> for THEAPPTRANSFORM, and call them all in turn?

Speed is a major requirement. I have read that Intents are 10+ times slower than direct calls... would Parcelable help here?

If impossible, is there any other solution?

回答1:

To know apps which have a broadcast reciever with THEAPPTRANSFORM as filter, you can use below code

PackageManager pm = getPackageManager();
    Intent intent = new Intent("THEAPPTRANSFORM");
    List<ResolveInfo> info = pm.queryBroadcastReceivers(intent, 0);
    for (ResolveInfo resolveInfo : info) {

        Log.e("apps", "packages = " + resolveInfo.activityInfo.packageName);
    }


回答2:

You need a dynamically installed plugin which is not part of your application. I think you have a few options for this.

Solution 1: Scripting

Ship a scripting language interpreter with your app. (eg. ruby - http://ruboto.org/). Create an interface for executing these scripts. Make a central database of such scripts, or load them from external storage. Now, you can execute these scripts and get the required result.

Solution 2: AIDL

Use a remote service in the plugin apps. Provide an AIDL for third parties to develop apps with a remote service with that AIDL. Such services should also conform to an intent filter defined by you. Now you can use packagemanager to find such services, select one and connect to it. Now you can call all the AIDL methods. This will be inter process communication using binder, for your application this will be a synchronous call. (see this SO question for details - Access remote service in different application)

Downside to this approach is that all these services need to be running when your app is running, so you have to handle the starting/stopping of these services. It will also affect the power consumption if the services are running in the background.

Solution 3: Broadcast/receiver

Third party installed apps that have a broadcast receiver with an intent filter for custom intent defined by you. Also, your app needs to have a broadcast receiver with a custom intent that the plugins can call with the result. Now, say you want to call a third party plugin for some transformation, you have to do this:

Use packagemanager to find all the third party apps conforming to your custom intent. Send a broadcast with extradata about transformation. Handle the transformation in the broadcast receiver of the plugin app. Once transformation is done, send a broadcast with result to the original app.

This option is entirely asynchronous, and it may take any amount of time to execute with no guaranties.