前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android插件化学习之路(九)之DynamicLoadApk 源码解析(下)

Android插件化学习之路(九)之DynamicLoadApk 源码解析(下)

作者头像
老马的编程之旅
发布2022-06-22 10:19:46
2460
发布2022-06-22 10:19:46
举报
文章被收录于专栏:深入理解Android

承接上一篇,继续分析DynamicLoadApk 源码。 4.2 DLPluginPackage 插件信息对应的实体类,主要属性如下:

代码语言:javascript
复制
public String packageName;
public String defaultActivity;
public DexClassLoader classLoader;
public AssetManager assetManager;
public Resources resources;
public PackageInfo packageInfo;

packageName为插件的包名; defaultActivity为插件的 Launcher Main Activity; classLoader为加载插件的 ClassLoader; assetManager为加载插件资源的 AssetManager; resources利用assetManager中已经加载的资源创建的Resources,代理组件中会从这个Resources中读取资源。 packageInfo被PackageManager解析后的插件信息。 这些信息都会在DLPluginManager#loadApk(…)时初始化。

4.3 DLAttachable.java/DLServiceAttachable.java DLServiceAttachable 与 DLAttachable 类似,下面先分析 DLAttachable.java。 DLAttachable 是一个接口,主要作用是以统一所有不同类型的代理 Activity,如DLProxyActivity、DLProxyFragmentActivity,方便作为同一接口统一处理。 DLProxyActivity和DLProxyFragmentActivity都实现了这个类。

DLAttachable 目前只有一个接口

代码语言:javascript
复制
public interface DLAttachable {
    /**
     * when the proxy impl ( {@see DLProxyImpl#launchTargetActivity()} ) launch
     * the plugin activity , dl will call this method to attach the proxy activity
     * and pluginManager to the plugin activity. the proxy activity will load
     * the plugin's resource, so the proxy activity is a resource delegate for
     * plugin activity.
     * 
     * @param proxyActivity a instance of DLPlugin, {@see DLBasePluginActivity}
     *            and {@see DLBasePluginFragmentActivity}
     * @param pluginManager DLPluginManager instance, manager the plugins
     */
    public void attach(DLPlugin proxyActivity, DLPluginManager pluginManager);
}

抽象函数,表示将插件Activity和代理Activity绑定在一起,其中的proxyActivity参数就是指插件Activity。 同样 DLServiceAttachable 类似,作用是统一所有不同类型的代理 Service,实现插件Service和代理Service的绑定。虽然目前只有DLProxyService。

代码语言:javascript
复制
public interface DLServiceAttachable {

    public void attach(DLServicePlugin remoteService, DLPluginManager pluginManager);
}

4.4 DLPlugin.java/DLServicePlugin.java

DLPlugin 与 DLServicePlugin 类似,下面先分析 DLPlugin.java。 DLPlugin 是一个接口,包含Activity生命周期、触摸、菜单等抽象函数 DLBase*Activity 都实现了这个类,这样插件的 Activity 间接实现了此类。 主要作用是统一所有不同类型的插件 Activity,如Activity、FragmentActivity,方便作为同一接口统一处理,所以这个类叫DLPluginActivity更合适。

代码语言:javascript
复制
public interface DLPlugin {

    public void onCreate(Bundle savedInstanceState);
    public void onStart();
    public void onRestart();
    public void onActivityResult(int requestCode, int resultCode, Intent data);
    public void onResume();
    public void onPause();
    public void onStop();
    public void onDestroy();
    public void attach(Activity proxyActivity, DLPluginPackage pluginPackage);
    public void onSaveInstanceState(Bundle outState);
    public void onNewIntent(Intent intent);
    public void onRestoreInstanceState(Bundle savedInstanceState);
    public boolean onTouchEvent(MotionEvent event);
    public boolean onKeyUp(int keyCode, KeyEvent event);
    public void onWindowAttributesChanged(LayoutParams params);
    public void onWindowFocusChanged(boolean hasFocus);
    public void onBackPressed();
    public boolean onCreateOptionsMenu(Menu menu);
    public boolean onOptionsItemSelected(MenuItem item);
}

同样 DLServicePlugin 主要作用是统一所有不同类型的插件 Service,方便作为统一接口统一处理,目前包含Service生命周期等抽象函数。

代码语言:javascript
复制
public interface DLServicePlugin {

    public void onCreate(); 

    public void onStart(Intent intent, int startId); 

    public int onStartCommand(Intent intent, int flags, int startId);

    public void onDestroy();

    public void onConfigurationChanged(Configuration newConfig); 

    public void onLowMemory();

    public void onTrimMemory(int level);

    public IBinder onBind(Intent intent);

    public boolean onUnbind(Intent intent);

    public void onRebind(Intent intent);

    public void onTaskRemoved(Intent rootIntent); 

    public void attach(Service proxyService, DLPluginPackage pluginPackage);
}

4.5 DLProxyActivity.java/DLProxyFragmentActivity.java 代理 Activity,他们是在宿主 Manifest 中注册的组件,也是启动插件 Activity 时,真正被启动的 Activity,他们的内部会完成插件 Activity 的初始化和启动。 这两个类大同小异,所以这里只分析DLProxyActivity。 首先来看下它的成员变量。 (1). DLPlugin mRemoteActivity 表示真正需要启动的插件Activity。这个属性名应该叫做pluginActivity更合适。 上面我们已经介绍了,DLPlugin是所有插件Activity都间接实现了的接口。 接下来在代理Activity的生命周期、触摸、菜单等函数中我们都会同时调用 mRemoteActivity 的相关函数,模拟插件Activity的相关功能。

代码语言:javascript
复制
public class DLProxyActivity extends Activity implements DLAttachable {

    protected DLPlugin mRemoteActivity;
    private DLProxyImpl impl = new DLProxyImpl(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        impl.onCreate(getIntent());
    }

    @Override
    public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager) {
        mRemoteActivity = remoteActivity;
    }

    @Override
    public AssetManager getAssets() {
        return impl.getAssets() == null ? super.getAssets() : impl.getAssets();
    }

    @Override
    public Resources getResources() {
        return impl.getResources() == null ? super.getResources() : impl.getResources();
    }

    @Override
    public Theme getTheme() {
        return impl.getTheme() == null ? super.getTheme() : impl.getTheme();
    }

    @Override
    public ClassLoader getClassLoader() {
        return impl.getClassLoader();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        mRemoteActivity.onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onStart() {
        mRemoteActivity.onStart();
        super.onStart();
    }

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

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

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

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

    @Override
    protected void onDestroy() {
        mRemoteActivity.onDestroy();
        super.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mRemoteActivity.onSaveInstanceState(outState);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        mRemoteActivity.onRestoreInstanceState(savedInstanceState);
        super.onRestoreInstanceState(savedInstanceState);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        mRemoteActivity.onNewIntent(intent);
        super.onNewIntent(intent);
    }

    @Override
    public void onBackPressed() {
        mRemoteActivity.onBackPressed();
        super.onBackPressed();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        return mRemoteActivity.onTouchEvent(event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        super.onKeyUp(keyCode, event);
        return mRemoteActivity.onKeyUp(keyCode, event);
    }

    @Override
    public void onWindowAttributesChanged(LayoutParams params) {
        mRemoteActivity.onWindowAttributesChanged(params);
        super.onWindowAttributesChanged(params);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        mRemoteActivity.onWindowFocusChanged(hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        mRemoteActivity.onCreateOptionsMenu(menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        mRemoteActivity.onOptionsItemSelected(item);
        return super.onOptionsItemSelected(item);
    }

    @Override
    public ComponentName startService(Intent service) {
        return super.startService(service);
    }

}

(2). DLProxyImpl impl 主要封装了插件Activity的公用逻辑,如初始化插件 Activity 并和代理 Activity 绑定、获取资源等。

DLProxyImpl 主要封装了插件Activity的公用逻辑,如初始化插件 Activity 并和代理 Activity 绑定、获取资源等,相当于把DLProxyActivity和DLProxyFragmentActivity的公共实现部分提出出来,核心逻辑位于下面介绍的 onCreate() 函数。 主要函数:

1) DLProxyImpl(Activity activity) 构造函数,参数为代理 Activity。

代码语言:javascript
复制
public DLProxyImpl(Activity activity) {
    mProxyActivity = activity;
}

2) public void onCreate(Intent intent) onCreate 函数,会在代理 Activity onCreate 函数中被调用,流程图如下:

代码语言:javascript
复制
public void onCreate(Intent intent) {

    // set the extra's class loader
    //设置intent的ClassLoader
    intent.setExtrasClassLoader(DLConfigs.sPluginClassloader);
    //获取PackageName
    mPackageName = intent.getStringExtra(DLConstants.EXTRA_PACKAGE);
    //获得待启动Activity的ClassName
    mClass = intent.getStringExtra(DLConstants.EXTRA_CLASS);
    Log.d(TAG, "mClass=" + mClass + " mPackageName=" + mPackageName);

    mPluginManager = DLPluginManager.getInstance(mProxyActivity);
    //获得插件信息PluginPackage
    mPluginPackage = mPluginManager.getPackage(mPackageName);
    //从PluginPackage中获取AssetManager
    mAssetManager = mPluginPackage.assetManager;
    //从PluginPackage中获取Resources
    mResources = mPluginPackage.resources;

    initializeActivityInfo();
    handleActivityInfo();
    launchTargetActivity();
}

3) protected void launchTargetActivity() 加载待启动插件 Activity 完成初始化流程,并通过DLPlugin和DLAttachable接口的 attach 函数实现和代理 Activity 的双向绑定。流程图见上图虚线框部分。

代码语言:javascript
复制
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
protected void launchTargetActivity() {
    try {
        Class<?> localClass = getClassLoader().loadClass(mClass);
        Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});
        Object instance = localConstructor.newInstance(new Object[] {});
        mPluginActivity = (DLPlugin) instance;
        ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager);
        Log.d(TAG, "instance = " + instance);
        // attach the proxy activity and plugin package to the mPluginActivity
        mPluginActivity.attach(mProxyActivity, mPluginPackage);

        Bundle bundle = new Bundle();
        bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL);
        mPluginActivity.onCreate(bundle);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

4) private void initializeActivityInfo() 获得待启动插件的 ActivityInfo,其中对插件Activity的主题做了一些处理

代码语言:javascript
复制
private void initializeActivityInfo() {
    PackageInfo packageInfo = mPluginPackage.packageInfo;
    if ((packageInfo.activities != null) && (packageInfo.activities.length > 0)) {
        if (mClass == null) {
            mClass = packageInfo.activities[0].name;
        }

        //Finals 修复主题BUG
        int defaultTheme = packageInfo.applicationInfo.theme;
        for (ActivityInfo a : packageInfo.activities) {
            if (a.name.equals(mClass)) {
                mActivityInfo = a;
                // Finals ADD 修复主题没有配置的时候插件异常
                if (mActivityInfo.theme == 0) {
                    if (defaultTheme != 0) {
                        mActivityInfo.theme = defaultTheme;
                    } else {
                        if (Build.VERSION.SDK_INT >= 14) {
                            mActivityInfo.theme = android.R.style.Theme_DeviceDefault;
                        } else {
                            mActivityInfo.theme = android.R.style.Theme;
                        }
                    }
                }
            }
        }

    }
}

5) private void handleActivityInfo() 设置代理 Activity 的主题等信息。 其他的 get* 函数都是获取一些插件相关信息,会被代理 Activity 调用。 同样 DLServiceProxyImpl 主要封装了插件Service的公用逻辑,如初始化插件 Service 并和代理 Activity 绑定。

代码语言:javascript
复制
private void handleActivityInfo() {
    Log.d(TAG, "handleActivityInfo, theme=" + mActivityInfo.theme);
    if (mActivityInfo.theme > 0) {
        mProxyActivity.setTheme(mActivityInfo.theme);
    }
    Theme superTheme = mProxyActivity.getTheme();
    mTheme = mResources.newTheme();
    mTheme.setTo(superTheme);
    // Finals适配三星以及部分加载XML出现异常BUG
    try {
        mTheme.applyStyle(mActivityInfo.theme, true);
    } catch (Exception e) {
        e.printStackTrace();
    }

    // TODO: handle mActivityInfo.launchMode here in the future.
}

4.7 DLBasePluginActivity.java/DLBasePluginFragmentActivity.java 插件 Activity 基类,插件中的Activity都要继承 DLBasePluginActivity/DLBasePluginFragmentActivity 之一(目前尚不支持 ActionBarActivity)。

主要作用是根据是否被代理,确定一些函数直接走父类逻辑还是代理 Activity 或是空逻辑。

DLBasePluginActivity继承自Activity,同时实现了DLPlugin接口。这两个类大同小异,所以这里只分析DLBasePluginActivity。 主要变量:

代码语言:javascript
复制
/**
 * 代理activity,可以当作Context来使用,会根据需要来决定是否指向this
 */
protected Activity mProxyActivity;

/**
 * 等同于mProxyActivity,可以当作Context来使用,会根据需要来决定是否指向this<br/>
 * 可以当作this来使用
 */
protected Activity that;
protected DLPluginManager mPluginManager;
protected DLPluginPackage mPluginPackage;

mProxyActivity为代理 Activity,通过attach(…)函数绑定。 that与mProxyActivity等同,只是为了和this指针区分,表示真实的Context,这里真实指的是被代理情况下为代理 Activity,未被代理情况下等同于 this。

Attach方法,设置代理的Activity

代码语言:javascript
复制
@Override
public void attach(Activity proxyActivity, DLPluginPackage pluginPackage) {
    Log.d(TAG, "attach: proxyActivity= " + proxyActivity);
    mProxyActivity = (Activity) proxyActivity;
    that = mProxyActivity;
    mPluginPackage = pluginPackage;
}

根据是否被代理,确定一些函数直接走父类逻辑还是代理 Activity 或是空逻辑。(此处贴部分代码)

代码语言:javascript
复制
/**
 * @param dlIntent
 * @return may be {@link #START_RESULT_SUCCESS},
 *         {@link #START_RESULT_NO_PKG}, {@link #START_RESULT_NO_CLASS},
 *         {@link #START_RESULT_TYPE_ERROR}
 */
public int startPluginActivityForResult(DLIntent dlIntent, int requestCode) {
    if (mFrom == DLConstants.FROM_EXTERNAL) {
        if (dlIntent.getPluginPackage() == null) {
            dlIntent.setPluginPackage(mPluginPackage.packageName);
        }
    }
    return mPluginManager.startPluginActivityForResult(that, dlIntent, requestCode);
}

public int startPluginService(DLIntent dlIntent) {
    if (mFrom == DLConstants.FROM_EXTERNAL) {
        if (dlIntent.getPluginPackage() == null) {
            dlIntent.setPluginPackage(mPluginPackage.packageName);
        }
    }
    return mPluginManager.startPluginService(that, dlIntent);
}

4.8 DLBasePluginService.java 插件 Service 基类,插件中的 Service 要继承这个基类,主要作用是根据是否被代理,确定一些函数直接走父类逻辑还是代理 Service 或是空逻辑。 PS:截止目前这个类还是不完善的,至少和DLBasePluginActivity对比,还不支持非代理的情况(贴部分代码)

代码语言:javascript
复制
@Override
public void attach(Service proxyService, DLPluginPackage pluginPackage) {
    // TODO Auto-generated method stub
    LOG.d(TAG, TAG + " attach");
    mProxyService = proxyService;
    mPluginPackage = pluginPackage;
    that = mProxyService;
    mFrom = DLConstants.FROM_EXTERNAL;
}

protected boolean isInternalCall() {
    return mFrom == DLConstants.FROM_INTERNAL;
}

@Override
public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    LOG.d(TAG, TAG + " onBind");
    return null;
}

@Override
public void onCreate() {
    // TODO Auto-generated method stub
    LOG.d(TAG, TAG + " onCreate");
}

4.9 DLIntent.java 继承自 Intent,封装了待启动组件的 PackageName 和 ClassName。

代码语言:javascript
复制
public class DLIntent extends Intent {



    private String mPluginPackage;
    private String mPluginClass;

    public DLIntent() {
        super();
    }

    public DLIntent(String pluginPackage) {
        super();
        this.mPluginPackage = pluginPackage;
    }

    public DLIntent(String pluginPackage, String pluginClass) {
        super();
        this.mPluginPackage = pluginPackage;
        this.mPluginClass = pluginClass;
    }

    public DLIntent(String pluginPackage, Class<?> clazz) {
        super();
        this.mPluginPackage = pluginPackage;
        this.mPluginClass = clazz.getName();
    }

    public String getPluginPackage() {
        return mPluginPackage;
    }

    public void setPluginPackage(String pluginPackage) {
        this.mPluginPackage = pluginPackage;
    }

    public String getPluginClass() {
        return mPluginClass;
    }

    public void setPluginClass(String pluginClass) {
        this.mPluginClass = pluginClass;
    }

    public void setPluginClass(Class<?> clazz) {
        this.mPluginClass = clazz.getName();
    }

    @Override
    public Intent putExtra(String name, Parcelable value) {
        setupExtraClassLoader(value);
        return super.putExtra(name, value);
    }

    @Override
    public Intent putExtra(String name, Serializable value) {
        setupExtraClassLoader(value);
        return super.putExtra(name, value);
    }

    private void setupExtraClassLoader(Object value) {
        ClassLoader pluginLoader = value.getClass().getClassLoader();
        DLConfigs.sPluginClassloader = pluginLoader;
        setExtrasClassLoader(pluginLoader);
    }

}

4.10 SoLibManager.java 调用SoLibManager拷贝 so 库到 Native Library 目录。 主要函数: (1) copyPluginSoLib(Context context, String dexPath, String nativeLibDir) 函数中以ZipFile形式加载插件,循环读取其中的文件,如果为.so结尾文件、符合当前平台 CPU 类型且尚未拷贝过最新版,则新建Runnable拷贝 so 文件。

代码语言:javascript
复制
/**
 * copy so lib to specify directory(/data/data/host_pack_name/pluginlib)
 *
 * @param dexPath      plugin path
 * @param nativeLibDir nativeLibDir
 */
public void copyPluginSoLib(Context context, String dexPath, String nativeLibDir) {
    String cpuName = getCpuName();
    String cpuArchitect = getCpuArch(cpuName);

    sNativeLibDir = nativeLibDir;
    Log.d(TAG, "cpuArchitect: " + cpuArchitect);
    long start = System.currentTimeMillis();
    try {
        ZipFile zipFile = new ZipFile(dexPath);
        Enumeration<? extends ZipEntry> entries = zipFile.entries();
        while (entries.hasMoreElements()) {
            ZipEntry zipEntry = (ZipEntry) entries.nextElement();
            if (zipEntry.isDirectory()) {
                continue;
            }
            String zipEntryName = zipEntry.getName();
            if (zipEntryName.endsWith(".so") && zipEntryName.contains(cpuArchitect)) {
                final long lastModify = zipEntry.getTime();
                if (lastModify == DLConfigs.getSoLastModifiedTime(context, zipEntryName)) {
                    // exist and no change
                    Log.d(TAG, "skip copying, the so lib is exist and not change: " + zipEntryName);
                    continue;
                }
                mSoExecutor.execute(new CopySoTask(context, zipFile, zipEntry, lastModify));
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    long end = System.currentTimeMillis();
    Log.d(TAG, "### copy so time : " + (end - start) + " ms");
}

获取CPU类型

代码语言:javascript
复制
/**
 * get cpu name, according cpu type parse relevant so lib
 *
 * @return ARM、ARMV7、X86、MIPS
 */
private String getCpuName() {
    try {
        FileReader fr = new FileReader("/proc/cpuinfo");
        BufferedReader br = new BufferedReader(fr);
        String text = br.readLine();
        br.close();
        String[] array = text.split(":\\s+", 2);
        if (array.length >= 2) {
            return array[1];
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

获取CPU架构

代码语言:javascript
复制
@SuppressLint("DefaultLocale")
private String getCpuArch(String cpuName) {
    String cpuArchitect = DLConstants.CPU_ARMEABI;
    if (cpuName.toLowerCase().contains("arm")) {
        cpuArchitect = DLConstants.CPU_ARMEABI;
    } else if (cpuName.toLowerCase().contains("x86")) {
        cpuArchitect = DLConstants.CPU_X86;
    } else if (cpuName.toLowerCase().contains("mips")) {
        cpuArchitect = DLConstants.CPU_MIPS;
    }

    return cpuArchitect;
}

4.11 DLUtils.java 这个类中大都是无用或是不该放在这里的函数,也许是大版本升级及维护人过多后对工具函数的维护不够所致。

至此,DynamicLoadApk 源码已解析完毕。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档