欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android 占位式插件开发

程序员文章站 2022-03-10 23:03:21
开头2张tu1.占位式插件化,宿主启动插件activity流程插件开发三大要素:宿主 标准,插件包第一步创建宿主工程布局文件

开头2张tu

android 占位式插件开发android 占位式插件开发

1.占位式插件化,宿主启动插件activity流程

插件开发三大要素:宿主 标准,插件包

第一步创建宿主工程 

android 占位式插件开发

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_one"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="loadPligin"
        android:text="加载插件" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="startPluginActivity"
        android:text="启动插件activity" />

</LinearLayout>

2.制定标准

android 占位式插件开发

创建插件工程(一定是工程不是library):

android 占位式插件开发准备工作做完了,之后会很长废话也比较多,我尽量简洁多贴图少说废话

首先我们要把标准添加到宿主工程和插件工程里面

android 占位式插件开发

插件工程同上不在贴图,在宿主模块里面定义一个插件管理类

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

    private static PluginManager pluginManager;
    private Resources resources;

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

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

    public DexClassLoader getDexClassLoader() {
        return dexClassLoader;
    }

    public Resources getResources() {
        return resources;
    }

    /**
     * 加载插件activity
     */
    private void loadPlugin() {
        try {
            File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
            if (!file.exists()) {
                Log.e(TAG, "文件不存在");
                return;
            }
            //获取到文件的位置
            String pluginPath = file.getAbsolutePath();

            //加载插件里面的class dexClassLoader 需要一个缓存目录 /data/data/包名/pDir
            File fileDir = context.getDir("pDir", Context.MODE_PRIVATE);
            dexClassLoader = new DexClassLoader(pluginPath, fileDir.getAbsolutePath(), null, context.getClassLoader());

            //加载插件里面的布局
            AssetManager assetManager = AssetManager.class.newInstance();
            //把插件包的路径添加进去
            Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class);
            //执行此方法,参数为插件包的路径
            addAssetPathMethod.invoke(assetManager, pluginPath);

            //宿主的配置
            Resources r = context.getResources();
            //使用宿主的resource加载插件里面的资源resource
            resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration());
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

管理类准备好了现在要准备标准了,在standard里面定义ActivityInterface(我太懒了所以标准接口里面就写了我用到的几个方法,可以多写一些)

public interface ActivityInterface {
    /**
     * 把宿主的(app)的环境给插件
     * @param appActivity
     */
    void  insertAppContext(Activity appActivity);
    void onCreate(Bundle savedInstanceState) ;

    void onStart() ;

    void onResume() ;

    void onDestroy() ;
}

在插件模块里面定义基础类 在PluginActivity中用的所有有关activity的方法都要在基础类中重写(看不懂继续往下|)

public class BaseActivity extends AppCompatActivity implements ActivityInterface {
    //宿主的环境
    public Activity appActivity;

    @Override
    public void insertAppContext(Activity appActivity) {
        this.appActivity = appActivity ;
    }

    /**
     * 添加注释因为要实现的是接口里面的方法而不是重写AppCompatActivity的
     * @param savedInstanceState
     */
    @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 onDestroy() {

    }

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

    @Override
    public View findViewById(int id) {
        return appActivity.findViewById(id);
    }

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

PluginActivity继承基础类

public class PluginActivity extends BaseActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_plugin);
        Toast.makeText(appActivity,"我是插件",Toast.LENGTH_LONG).show();
    }
}

这里一定要是全路径

android 占位式插件开发

因为插件apk 没有组件环境所以当宿主activity调用插件的activity时需要在宿主工程里面定义一个代理activity作为媒介

/**
 * 代理activity 占位插件里面的activity
 **/
public class ProxyActivity  extends ComponentActivity {

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

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

    /**
     * 代理类里面会加载插件正在的类
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String className = getIntent().getStringExtra("className");
        try{
            Class  mPluginActivityClass = getClassLoader().loadClass(className);
            //实例化插件包里面的Activity
            Constructor constructor = mPluginActivityClass.getConstructor(new Class[]{});
            Object mPluginActivity = constructor.newInstance(new Object[]{});
            ActivityInterface activityInterface = (ActivityInterface) mPluginActivity;

            //注入
            activityInterface.insertAppContext(this);

            activityInterface.onCreate(savedInstanceState);

        }catch (Exception e){

        }
    }
}

在宿主的mainActivity中加载插件

public class MainActivity extends AppCompatActivity {

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

    public void loadPligin(View view) {
        PluginManager.getInstance(this).loadPlugin();
    }

    /**
     * 9.0以上需要动态获取权限
     *
     * @param view
     */
    public void startPluginActivity(View view) {
        int hasWriteStoragePermission = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (hasWriteStoragePermission == PackageManager.PERMISSION_GRANTED) {
            instant();
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);

        }
    }

    private void instant() {
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
        String path = file.getAbsolutePath();
        //插件包里面的activity
        PackageManager packageManager = getPackageManager();
        PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
        ActivityInfo activityInfo = packageInfo.activities[0];
        //占位 代理activtiy
        Intent intent = new Intent(this, ProxyActivity.class);
        intent.putExtra("className", activityInfo.name);
        startActivity(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //用户同意,执行操作
                instant();
            } else {
                //用户不同意,向用户展示该权限作用
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    new AlertDialog.Builder(this)
                            .setMessage(R.string.storage_permissions_remind)
                            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    ActivityCompat.requestPermissions(MainActivity.this,
                                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                                }
                            })
                            .setNegativeButton("Cancel", null)
                            .create()
                            .show();
                }
            }
        }
    }
}

插件工程new出apk

android 占位式插件开发

将生成的apk(我这边改了名字)放入手机内存卡

android 占位式插件开发

插件里面的activity跳转

在plugin 添加testActivity

android 占位式插件开发

proxyActivty中添加

@Override
public void startActivity(Intent intent) {
    String className = intent.getStringExtra("className");
    //要给TestActivity进栈
    Intent proxyIntent = new Intent(this, ProxyActivity.class);
    proxyIntent.putExtra("className", className);
    super.startActivity(proxyIntent);
}

pluginActivty 正常跳转

 findViewById(R.id.btn_start_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(appActivity,TestActivity.class);
                startActivity(intent);
            }
        });

因为插件和宿主都有修改所以要重新打包上传。

本文地址:https://blog.csdn.net/hellenicguo/article/details/107149525