Android插件化探索(一)占位式(插桩式)插件化详解

·  阅读 2526

前言

由于近期项目中要用到插件,所以特地去翻找资料学习了一番,现在在这里分享我所学到的东西给大家,有什么错误的希望能给我指出来,文章有点长,希望大家能认真读完。

近些年来,插件化可谓是特别的火热,就拿支付宝美团等软件来说,都是使用这个技术来支撑他们的产品。但是什么是插件化呢,插件化到底有什么好处呢?

插件化也就是运行的APP(宿主APP)去加载插件APP(没有安装的APP),这就是所谓的插件化开发。

插件化到底运行在什么场景下呢?其实插件化使用的场景有很多,这里就比如下图的支付宝或者美团等APP,点击某个相应的item,就会跳转到相应的页面当中,其实这个页面是插件apk中的页面,但是它到底怎么做到的呢?怎么做到不安装apk而加载插件中的页面呢?

下面我们就来探索探索不用安装插件apk是怎么去加载里面的Activity、Service、BroadCastReceiver等这些组件的。本篇文章所提的是占位式(插桩式)插件化。

由于插件apk是没有安装的,也就是插件apk没有组件的一些环境,比如context上下文对象之类的,如果要用到这些环境就必须依赖宿主的环境运行。所以我们就要宿主跟插件之间定义一个标准。用来传递宿主中的环境给插件。

加载插件中的Activity

第一步:

首先我们先定义一个标准,让插件实现我们的标准来传递宿主APP的环境,下图是定义Activity类的标准,下面我们从加载插件中的Activity开始讲起。

首先我们要在插件中实现刚才我们定义的标准,由于插件都需要宿主APP的环境,所以我们就定义一个基类来实现该标准,然后让我们的插件的Activity来继承该基类,该Activity就具有了宿主的环境了。如图所示。

第二步: build工程,得到插件apk,命名为 plugin.apk 并把它放到我们的sd目录下。让宿主APP来加载插件。 首先我们要加载插件apk中的类,就需要用到DexClassLoader这个类,下面是用该类来加载插件apk的方法。

 File file = new File(path);
 File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
 //加载插件的class
 dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
复制代码

参数说明:

path:插件所存放的目录(plugin.apk存放的目录)

pluginDir.getAbsolutePath():插件apk解析后dex文件所存放的路径

null:该参数是so库存放的路径,由于插件里没有so库,所以为null。

context.getClassLoader():类加载器

获取到了DexClassLoader的对象,我们就可以拿到插件中的类了,接下来我们要获取插件apk中的资源对象,也就是 Resources对象。由于插件apk没有宿主的环境,也就无法使用 context.getResources()的方式来得到一个Resources对象,我们只能new 一个对象出来。

//Resources的构造方法
@Deprecated
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
    this(null);
    mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
复制代码

从上面代码中可以看出,我们可以看出创建一个 Resources对象需要传递一个 AssetManagerDisplayMetricsConfiguration对象,而这些对象我们怎么可以获取到呢?其实后两个参数很容易获取到。后面的两个参数我们可以直接用宿主的Resources对象里面的属性就可以了,如下

 Resources appResources = context.getResources();
 DisplayMetrics displayMetrics = appResources.getDisplayMetrics();
 Configuration configuration = appResources.getConfiguration();
复制代码

现在我们比较头疼的问题是如何获取AssetManager这个类的对象?其实我们可以利用反射的方式来获取一个AssetManager的对象,如下

AssetManager pluginAssetManager = AssetManager.class.newInstance();
复制代码

阅读源码发现,AssetManager类里面要更改资源路径就必须要调用 addAssetPath 方法。从这里得出我们可以反射拿到这个方法,然后传入插件apk的资源路径即可。

AssetManager pluginAssetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(pluginAssetManager, path);
复制代码

通过上述代码可以获取到我们的插件apk的 AssetManager对象了,拥有该对象,我们就可以获取到 Resources对象了,总体代码如下:

 //加载插件的资源文件
 //1、获取插件的AssetManager
 AssetManager pluginAssetManager = AssetManager.class.newInstance();
 Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
 addAssetPath.setAccessible(true);
 addAssetPath.invoke(pluginAssetManager, path);
 //2、获取宿主的Resources
 Resources appResources = context.getResources();
 //实例化插件的Resources
 pluginResource = new Resources(pluginAssetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
复制代码

拿到了DexClassLoader对象和Resources对象我们就可以加载插件apk中的activity等组件了。要跳转到插件中的activity,我们首先要获取到插件找那个的Activity,通过 PackageManager.getPackageArchiveInfo()我们可以得到一个 PackageInfo对象,而这个对象里面就有我们需要的Activity数组,如下图:

获取插件Activity数组我们就可以拿到Activity对象,并且可以进行跳转了:

//获取插件包的Activity
PackageManager packageManager = getPackageManager();
PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
//获取在manifest文件中注册的第一个activity
ActivityInfo activity = packageArchiveInfo.activities[0];
Intent intent = new Intent(this, ProxyActivity.class);
intent.putExtra("className", activity.name);
复制代码

通过以上代码显示,我们先是跳转到了一个ProxyActivity,然后把插件的Activity类名传递过去,为什么不直接跳转到插件的Activity呢?原因是因为插件的Activity没有在我们的宿主的manifest文件中进行注册,如果直接跳转就会发生崩溃,所以我们这里先跳转到一个代理的Activity,因为该Activity是宿主里面的并且在manifest文件中进行注册了,所以我们用它来进行模拟Activity入栈出栈的操作,当我们的代理Activity回调onCreate() 的时候,我们可以获取到传递过来的className来反射得到我们的插件中的Activity对象,由于我们插件中的Activity是实现了上面所述的标准,我们可以通过标准来调用插件Activity中的生命周期。

  @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //真正的加载插件里面的Activity
        String className = getIntent().getStringExtra("className");
        try {
            Class<?> pluginActivity1Clazz = getClassLoader().loadClass(className);
            Constructor<?> constructor = pluginActivity1Clazz.getConstructor(new Class[]{});
            pluginActivity1 = (IActivityInterface) constructor.newInstance(new Object[]{});
            pluginActivity1.insertAppContext(this);
            Bundle bundle = new Bundle();
            bundle.putString("value", "我是宿主传递过来的字符串");
            pluginActivity1.onCreate(bundle);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
复制代码

当执行完上述代码后我们就可以加载出插件的Activity了。执行效果如下:

插件内跳转Activity

跳转成功后,我们就要实现插件中的Activity跳转到插件中的另一个Activity了。大家都知道跳转Activity的话我们都要执行startActivity() 方法,而查看源码而知,startActivity() 里调用了 this.startActivity(intent, null);这里的 this表示着上下文对象,但是我们的插件APP没有上下文对象的环境,如果执行了 startActivity() 方法肯定会发生崩溃的,那么怎样才能避免崩溃,并且成功跳转呢。其实非常简单,我们只要使用宿主APP的环境就可以来实现该功能了,只不过是稍微有些麻烦。我们需要重写 startActivity() 方法,并且传递我们需要跳转的Activity的全类名给宿主,且跳转的方法交给我们的宿主来进行就可以了,然后我们重写宿主APP的代理ActivitystartActivity() 方法用来接收插件APP传递过来的全类名,最后执行宿主APP的代理ActivitystartActivity() 方法即可。如下:

插件中的startActivity

另外提示一点,我们插件的Activity 所有使用的都是宿主的环境,比如 setContentView()findViewById() 等方法,运行后效果如下:

至此,我们动态加载插件APP的Activity已经讲完了,至于动态加载Service与动态加载广播的方法跟加载Activity的方法类似,这里不做讲解。下面讲解的是静态注册插件中的广播。

注册插件内的静态广播

要加载插件中的静态广播,我们先来提问一个问题。我们APP中的静态广播到底是什么时候被注册的呢? 其实在手机开机的时候,手机里面所有的已经安装APP,系统会再次进行安装一遍,这也就能说明Android手机开机的时候为什么会那么慢。等到安装完成后,会马上扫描 /data/app/ 目录,然后逐个去解析该目录下的所有 apk里面的 Manifest.xml 文件, 如果里面有静态广播后,就会自动注册。

我们现在来研究系统是如何去解析 apk文件里面的组件信息的。我们知道解析apk 文件会用到 PackageManagerService 这个类,所以我们来查看这个类的源码。

跟踪源码发现,执行到 packageParser.parsePackage() 这个方法的时候会返回一个 PackageParser.Package 对象,我们进去查看一下 PackageParser 这个类的源码,发现 Package 是其里面的静态内部类。

从这个内部类里我们找到了我们想要的东西,就是解析 Manifest.xml 文件后的数据类。我们可以通过反射来获取这些属性的值。那操作的步骤是怎样的呢?

第一步:

我们应该先模拟 PMS 解析安装包的形式获取到这些信息。阅读源码发现,PackageParser.class 里的 Package parsePackage(File packageFile, int flags) 正好返回一个 Package 对象,所以我们需要执行这个方法来获取它。

以下是代码的实现:

Class<?> mPackageParserClass = Class.forName("android.content.pm.PackageParser");
Object mPackageParser = mPackageParserClass.newInstance();
Method parsePackageMethod = mPackageParserClass.getMethod("parsePackage", File.class, int.class);
Object mPackage = parsePackageMethod.invoke(mPackageParser, file, PackageManager.GET_ACTIVITIES);

//2、获取Package类下的   public final ArrayList<Activity> receivers = new ArrayList<Activity>(0); 广播集合
Field mReceiversField = mPackage.getClass().getDeclaredField("receivers");
//本质上是 ArrayList<Activity> receivers
ArrayList<Object> receivers = (ArrayList<Object>) mReceiversField.get(mPackage);
复制代码

第二步:

在第一步我们获取到了 receivers 这个广播集合,有了这个集合我们就可以拿到里面的广播来进行注册了。我们先来看看先跟到这个 List<Activity> 尖括号里面的对象,发现这个Activity 不是我们四大组件的Activity,它纯粹是为了封装这些数据的Java bean,所以千万不要把它搞混了。

我们可以发现里面有个ActivityInfo 的属性。我们继续跟这个类和他的父类的源码,结果发现,name 这个属性刚好就是 android:name="com.xxx.xxx" 对应里面的包名跟类名,我们需要获取到这个值来 new 一个广播对象出来。

我们现在想办法得到上述 Activity 里面的 info这个属性这样我们就可以获得 name的值了。继续阅读 PackageParser 的源码,我们发现

通过这个方法我们可以得到一个 ActivityInfo 对象,所以我们就来执行这个方法。执行该方法我们需要传递四个参数,第一个Activity类型我们已经有了,第二个参数我们可以直接传个0,第三个我们可以反射来获取一个类的实例。如

 Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
 Object mPackageUserState = mPackageUserStateClass.newInstance();
复制代码

现在就剩下最后一个参数了,就是我们的userId,到底怎么获取这个参数值呢?其实我们可以从 android.os.UserHandle 这个类入手,分析该类得出的结果

通过图中的方法可以获取一个userId;所以我们就以反射来获取该userId; 详细代码如下:

//先获取到 ActivityInfo类
Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object mPackageUserState = mPackageUserStateClass.newInstance();

Method generateActivityInfoMethod = mPackageParserClass.getMethod("generateActivityInfo", mActivity.getClass(),
int.class, mPackageUserStateClass, int.class);
//获取userId
Class<?> mUserHandleClass = Class.forName("android.os.UserHandle");
//public static @UserIdInt int getCallingUserId()
int userId = (int) mUserHandleClass.getMethod("getCallingUserId").invoke(null);

//执行此方法 由于是静态方法 所以不用传对象
ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, mPackageUserState, userId);
复制代码

以上我们就可以获取到一个ActivityInfo 对象了。

第三步: 我们知道注册广播的时候还要添加一个IntentFliter,但是这个应该从哪里取到呢? 其实我们刚才分析的 Activity 那个类说起 ,它继承与 Component 在跟进这个类去看,发现里面有个 intents 的集合,查看泛型继承的 IntentInfo,就是我们想要的 IntentFliter,所以我们要得到这个集合,然后进行遍历就可以得到 IntentFliter,然后我们就可以注册广播了。

所以注册广播的代码如下:

//3、遍历所有的静态广播
//Activity 该Activity 不是四大组件里面的activity,而是一个Java bean对象,用来封装清单文件中的activity和receiver
for (Object mActivity : receivers) {
    //4、获取该广播的全类名 即 <receiver android:name=".PluginStaticReceiver"> android:name属性后面的值
    //  /**
    //     * Public name of this item. From the "android:name" attribute.
    //     */
    //    public String name;
    
    // public static final ActivityInfo generateActivityInfo(Activity a, int flags,
    //            PackageUserState state, int userId)
    //先获取到 ActivityInfo类
    Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
    Object mPackageUserState = mPackageUserStateClass.newInstance();
    
    Method generateActivityInfoMethod = mPackageParserClass.getMethod("generateActivityInfo", mActivity.getClass(),
    int.class, mPackageUserStateClass, int.class);
    //获取userId
    Class<?> mUserHandleClass = Class.forName("android.os.UserHandle");
    //public static @UserIdInt int getCallingUserId()
    int userId = (int) mUserHandleClass.getMethod("getCallingUserId").invoke(null);
    
    //执行此方法 由于是静态方法 所以不用传对象
    ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, mPackageUserState, userId);
    String receiverClassName = activityInfo.name;
    Class<?> receiverClass = getClassLoader().loadClass(receiverClassName);
    BroadcastReceiver receiver = (BroadcastReceiver) receiverClass.newInstance();
    
    //5、获取 intent-filter  public final ArrayList<II> intents;这个是intent-filter的集合
    //静态内部类反射要用 $+类名
    //getField(String name)只能获取public的字段,包括父类的;
    //而getDeclaredField(String name)只能获取自己声明的各种字段,包括public,protected,private。
    Class<?> mComponentClass = Class.forName("android.content.pm.PackageParser$Component");
    Field intentsField = mActivity.getClass().getField("intents");
    ArrayList<IntentFilter> intents = (ArrayList<IntentFilter>) intentsField.get(mActivity);
    for (IntentFilter intentFilter : intents) {
        //6、注册广播
        context.registerReceiver(receiver, intentFilter);
    }
}
复制代码

到这里我们已经完成了对插件中的静态广播进行注册。运行效果如下:

至此,我们的占位式插件化已经讲解完毕了,这里贴一下部分类的完整代码

MainActivity.class

public class MainActivity extends AppCompatActivity {

    private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "plugin.apk";
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 666) {
                isLoadSuccess = true;
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                }
                Toast.makeText(MainActivity.this, "加载插件成功!", Toast.LENGTH_SHORT).show();
            } else if (msg.what == 0) {
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                }
                Toast.makeText(MainActivity.this, "加载插件失败,请检查插件是否存在!", Toast.LENGTH_SHORT).show();
            }
        }
    };
    //是否加载完成
    private boolean isLoadSuccess = false;

    private ProgressDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    public void loadPlugin(View view) {
        //判断是否已经赋予权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, 666);
            } else {
                showProgress();
                PluginManager.getInstance(this).loadPlugin(handler, path);
            }
        } else {
            showProgress();
            PluginManager.getInstance(this).loadPlugin(handler, path);
        }

    }


    public void jumpPluginActivity(View view) {
        if (isLoadSuccess) {
            //获取插件包的Activity
            PackageManager packageManager = getPackageManager();
            PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
            //获取在manifest文件中注册的第一个activity
            ActivityInfo activity = packageArchiveInfo.activities[0];
            Intent intent = new Intent(this, ProxyActivity.class);
            intent.putExtra("className", activity.name);
            startActivity(intent);
        } else {
            Toast.makeText(this, "请先加载插件!", Toast.LENGTH_SHORT).show();
        }

    }

    private void showProgress() {
        if (dialog == null) {
            dialog = new ProgressDialog(this);
        }
        dialog.setTitle("加载插件中,请稍后。。。");
        dialog.setCancelable(false);
        dialog.show();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 666) {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                showProgress();
                PluginManager.getInstance(this).loadPlugin(handler, path);
            } else {
                Toast.makeText(MainActivity.this, "请打开读取SD卡权限", Toast.LENGTH_SHORT).show();
            }
        }
    }

    public void parsePlugin(View view) {
        PluginManager.getInstance(this).parsePlugin(path);
    }

    public void sendStaticReceiver(View view) {
        Intent intent = new Intent("plugin_static_receiver");
        intent.putExtra("str", "我是从宿主中发来的字符");
        sendBroadcast(intent);
    }
}
复制代码

PluginManager.class

public class PluginManager {
    private static final String TAG = PluginManager.class.getSimpleName();
    private static PluginManager instance;
    private Context context;
    private DexClassLoader dexClassLoader;
    private Resources pluginResource;

    private PluginManager(Context context) {
        this.context = context;
    }

    public static PluginManager getInstance(Context context) {
        if (instance == null) {
            synchronized (PluginManager.class) {
                if (instance == null) {
                    instance = new PluginManager(context);
                }
            }
        }
        return instance;
    }

    public void loadPlugin(final Handler handler, final String path) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    File file = new File(path);
                    if (!file.exists()) {
                        Log.e(TAG, "插件不存在");
                        return;
                    }
                    File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
                    //加载插件的class
                    dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
                    //加载插件的资源文件
                    //1、获取插件的AssetManager
                    AssetManager pluginAssetManager = AssetManager.class.newInstance();
                    Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
                    addAssetPath.setAccessible(true);
                    addAssetPath.invoke(pluginAssetManager, path);
                    //2、获取宿主的Resources
                    Resources appResources = context.getResources();
                    //实例化插件的Resources
                    pluginResource = new Resources(pluginAssetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
                    if (dexClassLoader != null && pluginResource != null) {
                        handler.sendEmptyMessage(666);
                    } else {
                        handler.sendEmptyMessage(0);
                    }
                }catch (Exception e) {
                    e.printStackTrace();
                    handler.sendEmptyMessage(0);
                }
            }
        }).start();

    }

    @SuppressLint("PrivateApi")
    public void parsePlugin(String pluginPath) {
        try {
            File file = new File(pluginPath);
            if (!file.exists()) {
                Log.e(TAG, "插件不存在");
                return;
            }
            //1、解析插件包  public Package parsePackage(File packageFile, int flags)
            Class<?> mPackageParserClass = Class.forName("android.content.pm.PackageParser");
            Object mPackageParser = mPackageParserClass.newInstance();
            Method parsePackageMethod = mPackageParserClass.getMethod("parsePackage", File.class, int.class);
            Object mPackage = parsePackageMethod.invoke(mPackageParser, file, PackageManager.GET_ACTIVITIES);

            //2、获取Package类下的   public final ArrayList<Activity> receivers = new ArrayList<Activity>(0); 广播集合
            Field mReceiversField = mPackage.getClass().getDeclaredField("receivers");
            ArrayList<Object> receivers = (ArrayList<Object>) mReceiversField.get(mPackage);

            //3、遍历所有的静态广播
            //Activity 该Activity 不是四大组件里面的activity,而是一个Java bean对象,用来封装清单文件中的activity和receiver
            for (Object mActivity : receivers) {
                //4、获取该广播的全类名 即 <receiver android:name=".PluginStaticReceiver"> android:name属性后面的值
                //  /**
                //     * Public name of this item. From the "android:name" attribute.
                //     */
                //    public String name;

                // public static final ActivityInfo generateActivityInfo(Activity a, int flags,
                //            PackageUserState state, int userId)
                //先获取到 ActivityInfo类
                Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
                Object mPackageUserState = mPackageUserStateClass.newInstance();

                Method generateActivityInfoMethod = mPackageParserClass.getMethod("generateActivityInfo", mActivity.getClass(),
                        int.class, mPackageUserStateClass, int.class);
                //获取userId
                Class<?> mUserHandleClass = Class.forName("android.os.UserHandle");
                //public static @UserIdInt int getCallingUserId()
                int userId = (int) mUserHandleClass.getMethod("getCallingUserId").invoke(null);

                //执行此方法 由于是静态方法 所以不用传对象
                ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, mPackageUserState, userId);
                String receiverClassName = activityInfo.name;
                Class<?> receiverClass = getClassLoader().loadClass(receiverClassName);
                BroadcastReceiver receiver = (BroadcastReceiver) receiverClass.newInstance();

                //5、获取 intent-filter  public final ArrayList<II> intents;这个是intent-filter的集合
                //静态内部类反射要用 $+类名
                //getField(String name)只能获取public的字段,包括父类的;
                //而getDeclaredField(String name)只能获取自己声明的各种字段,包括public,protected,private。
                Class<?> mComponentClass = Class.forName("android.content.pm.PackageParser$Component");
                Field intentsField = mActivity.getClass().getField("intents");
                ArrayList<IntentFilter> intents = (ArrayList<IntentFilter>) intentsField.get(mActivity);
                for (IntentFilter intentFilter : intents) {
                    //6、注册广播
                    context.registerReceiver(receiver, intentFilter);
                }

            }

        }catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Resources getResource() {
        return pluginResource;
    }

    public DexClassLoader getClassLoader() {
        return dexClassLoader;
    }
}
复制代码

ProxyActivity.class

public class ProxyActivity extends Activity {

    private IActivityInterface pluginActivity1;
    private boolean isRegister;
    private ProxyReceiver proxyReceiver;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //真正的加载插件里面的Activity
        String className = getIntent().getStringExtra("className");
        try {
            Class<?> pluginActivity1Clazz = getClassLoader().loadClass(className);
            Constructor<?> constructor = pluginActivity1Clazz.getConstructor(new Class[]{});
            pluginActivity1 = (IActivityInterface) constructor.newInstance(new Object[]{});
            pluginActivity1.insertAppContext(this);
            Bundle bundle = new Bundle();
            bundle.putString("value", "我是宿主传递过来的字符串");
            pluginActivity1.onCreate(bundle);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void onStart() {
        super.onStart();
        pluginActivity1.onStart();

    }

    @Override
    protected void onResume() {
        super.onResume();
        pluginActivity1.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        pluginActivity1.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        pluginActivity1.onStop();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        pluginActivity1.onRestart();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        pluginActivity1.onDestroy();
        if (isRegister) {
            unregisterReceiver(proxyReceiver);
        }
    }

    @Override
    public Resources getResources() {
        return PluginManager.getInstance(this).getResource();
    }

    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance(this).getClassLoader();
    }

    @Override
    public void startActivity(Intent intent) {
        String className = intent.getStringExtra("className");
        //自己跳自己
        Intent newIntent = new Intent(this, this.getClass());
        newIntent.putExtra("className", className);
        super.startActivity(newIntent);
    }

    @Override
    public ComponentName startService(Intent service) {
        String className = service.getStringExtra("className");
        ProxyService.pluginServiceClassName = className;
        //自己跳自己
        Intent newService = new Intent(this, ProxyService.class);
        newService.putExtra("className", className);
        return super.startService(newService);
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        proxyReceiver = new ProxyReceiver(receiver.getClass().getName());
        isRegister = true;
        return super.registerReceiver(proxyReceiver, filter);
    }

}

复制代码

插件中的BaseActivity.class

public abstract class BaseActivity extends Activity implements IActivityInterface {
    protected Activity appActivity;
    @Override
    public void insertAppContext(Activity appActivity) {
        this.appActivity = appActivity;
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onCreate(Bundle savedInstanceState) { }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStart() { }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onResume() { }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onPause() { }
    @SuppressLint("MissingSuperCall")
    @Override
    public void onStop() { }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onRestart() { }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onDestroy() { }

    @Override
    public void setContentView(int resId) {
        appActivity.setContentView(resId);
    }

    @Override
    public <T extends View> T findViewById(int id) {
        return appActivity.findViewById(id);
    }

    @Override
    public void startActivity(Intent intent) {
        Intent newIntent = new Intent();
        newIntent.putExtra("className", intent.getComponent().getClassName());
        appActivity.startActivity(newIntent);
    }

    @Override
    public ComponentName startService(Intent service) {
        Intent newService = new Intent();
        newService.putExtra("className", service.getComponent().getClassName());
        return appActivity.startService(newService);
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return appActivity.registerReceiver(receiver, filter);
    }

    @Override
    public void sendBroadcast(Intent intent) {
        appActivity.sendBroadcast(intent);
    }
}

复制代码

Plugin1Activity.class

public class Plugin1Activity extends BaseActivity {
    private static final String TAG = Plugin1Activity.class.getSimpleName();
    private boolean isRegister;

    @SuppressLint("MissingSuperCall")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreateActivity");
        setContentView(R.layout.activity_plugin1);
        TextView textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.btn_jump);
        String value = savedInstanceState.getString("value");
        textView.setText(value != null ? value : "");
        Toast.makeText(appActivity, value, Toast.LENGTH_SHORT).show();
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(appActivity, Plugin2Activity.class);
                startActivity(intent);
            }
        });
        findViewById(R.id.btn_start_plugin_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(new Intent(appActivity, PluginService.class));
            }
        });
        findViewById(R.id.btn_register_plugin_receiver).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isRegister) {
                    IntentFilter filter = new IntentFilter();
                    filter.addAction("yuongzw");
                    registerReceiver(new PluginReceiver(), filter);
                    isRegister = true;
                }
            }
        });
        findViewById(R.id.btn_send_plugin_receiver).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("yuongzw");
                intent.putExtra("str", "我是从插件发送的广播");
                sendBroadcast(intent);
            }
        });
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStartActivity");
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onResume() {
        Log.d(TAG, "onResumeActivity");
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onPause() {
        Log.d(TAG, "onPauseActivity");
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStop() {
        Log.d(TAG, "onStopActivity");
    }
    @SuppressLint("MissingSuperCall")
    @Override
    public void onRestart() {
        Log.d(TAG, "onRestartActivity");
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroyActivity");
    }
}

复制代码

PluginStaticReceiver.class

public class PluginStaticReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("plugin_static_receiver".equals(intent.getAction())) {
            Toast.makeText(context, "我是插件的静态广播,我收到了:" + intent.getStringExtra("str"), Toast.LENGTH_SHORT).show();
        }
    }
}
复制代码

下面附上Github的项目地址,给需要的小伙伴进行参考:

项目地址:PluginLoadDemo

分类:
Android
标签:
分类:
Android
标签: