美文网首页
插件化方式(插桩式)

插件化方式(插桩式)

作者: 爬行的蚂蚁2725 | 来源:发表于2019-02-20 11:41 被阅读0次

    组件化与插件化的区别:

    组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Module),开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件合并统一成一个apk,这就是组件化开发。

    插件化开发和组件化开发略有不用,插件化开发时将整个app拆分成很多模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk(组件化的每个模块是个lib),最终打包的时候将宿主apk和插件apk分开打包。

    插件化的几种方式:插桩式、hook、按需加载。
    1.插桩式

    大概步骤:
    1.1 设计接纳标准
    1.2 开发插件时遵循这个标准
    1.3 在宿主的清单文件用一个空的activity 插桩
    1.4 反射获得 插件的启动activity
    1.5 加载
    具体步骤:
    设计插件的标准接口
    创建一个lib module,创建一个接口,主要用来传递给插件Context和管理插件的生命周期。

    activity接口
    public interface PayInterfaceActivity {
    //传递Context
        public void attach(Activity proxyActivity);
    
        /**
         * 生命周期
         * @param savedInstanceState
         */
        public void onCreate(Bundle savedInstanceState);
        public void onStart();
        public void onResume();
        public void onPause();
        public void onStop();
        public void onDestroy();
        public void onSaveInstanceState(Bundle outState);
        public boolean onTouchEvent(MotionEvent event);
        public void onBackPressed();
    }
    
    service接口
    public interface PayInterfaceService {
    
        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);
        //传递Context
        public void attach(Service proxyService);
    }
    

    插件中
    创建一个baseActivity 和baseServie 实现定一个的接口,接收context,并重写跟context有关的方法,插件中的Activity 和service都继承各自的基类。

    public class BaseActivity  extends Activity implements PayInterfaceActivity {
    
    
        protected  Activity that;
        @Override
        public void attach(Activity proxyActivity) {
            this.that = proxyActivity;
        }
    
    
        @Override
        public void setContentView(View view) {
            if (that != null) {
                that.setContentView(view);
            }else {
                super.setContentView(view);
            }
        }
    
        @Override
        public void setContentView(int layoutResID) {
            that.setContentView(layoutResID);
        }
    
        @Override
        public ComponentName startService(Intent service) {
            Intent m = new Intent();
            m.putExtra("serviceName", service.getComponent().getClassName());
            return that.startService(m);
        }
    
        @Override
        public View findViewById(int id) {
            return that.findViewById(id);
        }
    
        @Override
        public Intent getIntent() {
            if(that!=null){
                return that.getIntent();
            }
            return super.getIntent();
        }
        @Override
        public ClassLoader getClassLoader() {
            return that.getClassLoader();
        }
    @Override
        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    
            return that.registerReceiver(receiver, filter);
        }
    
        @Override
        public void sendBroadcast(Intent intent) {
            that.sendBroadcast(intent);
        }
        @Override
        public void startActivity(Intent intent) {
    //        ProxyActivity --->className
            Intent m = new Intent();
            m.putExtra("className", intent.getComponent().getClassName());
            that.startActivity(m);
        }
        @NonNull
        @Override
        public LayoutInflater getLayoutInflater() {
            return that.getLayoutInflater();
        }
        @Override
        public ApplicationInfo getApplicationInfo() {
            return that.getApplicationInfo();
        }
        @Override
        public Window getWindow() {
            return that.getWindow();
        }
        @Override
        public WindowManager getWindowManager() {
            return that.getWindowManager();
        }
        @Override
        public void onCreate(Bundle savedInstanceState) {
        }
        @Override
        public void onStart() {
        }
        @Override
        public void onResume() {
        }
        @Override
        public void onPause() {
        }
        @Override
        public void onStop() {
        }
        @Override
        public void onDestroy() {
        }
        @Override
        public void onSaveInstanceState(Bundle outState) {
        }
    }
    
    public class BaseService extends Service  implements PayInterfaceService {
        private static final String TAG = "david";
        private Service that;
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void attach(Service proxyService) {
            this.that = proxyService;
        }
    
    
        @Override
        public void onCreate() {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onCreate");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onStartCommand");
            return Service.START_STICKY;
        }
    
        @Override
        public void onDestroy() {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onDestroy");
        }
    
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onConfigurationChanged");
        }
    
        @Override
        public void onLowMemory() {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onLowMemory");
        }
    
        @Override
        public void onTrimMemory(int level) {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onTrimMemory");
    
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onUnbind");
            return false;
        }
    
        @Override
        public void onRebind(Intent intent) {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onRebind");
        }
    
        @Override
        public void onTaskRemoved(Intent rootIntent) {
            // TODO Auto-generated method stub
            Log.d(TAG, TAG + " onTaskRemoved");
        }
    }
    

    宿主中
    在清单文件AndroidManifest.xml中 插桩activity 和service及broadcastReveiver

     <activity android:name=".ProxyActivity"/>
     <service android:name=".ProxyService"/>
    <receiver android:name=".ProxyBroadCast" />
    

    创建一个PluginManager的单例类 管理插件方法,获取插件的packageinfo,resources,dexClassLoader等,获取Resources时要通过反射调用AssetManager中的addAssetPath方法将插件的路径传给assetmanager

    public class PluginManager {
        private PackageInfo packageInfo;
        private Resources resources;
        private Context context;
        private DexClassLoader dexClassLoader;
        private static final PluginManager ourInstance = new PluginManager();
    
        public static PluginManager getInstance() {
            return ourInstance;
        }
    
        private PluginManager() {
        }
    
        public void setContext(Context context) {
            this.context = context;
        }
    
        public PackageInfo getPackageInfo() {
            return packageInfo;
        }
    
        public void loadPath(Context context) {
    
            File filesDir = context.getDir("plugin", Context.MODE_PRIVATE);
            String name = "pluginb.apk";
            String path = new File(filesDir, name).getAbsolutePath();
    
            PackageManager packageManager=context.getPackageManager();
            packageInfo=packageManager.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES);
    
    
    //        activity 名字
            File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
            dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsolutePath()
                    , null, context.getClassLoader());
    //        resource
    
            try {
                AssetManager assetManager = AssetManager.class.newInstance();
    
                Method addAssetPath=AssetManager.class.getMethod("addAssetPath", String.class);
                addAssetPath.invoke(assetManager, path);
                resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
            } catch ( Exception e) {
                e.printStackTrace();
            }
    //解析app中清单文件中的broadcastReceiver
    parseReceivers(context, path);
          }
    private void parseReceivers(Context context, String path) {
    
    //        Package对象
    //        PackageParser pp = new PackageParser();
    
    //        PackageParser.Package  pkg = pp.parsePackage(scanFile, parseFlags);
            try {
                Class   packageParserClass = Class.forName("android.content.pm.PackageParser");
                Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
                Object packageParser = packageParserClass.newInstance();
               Object packageObj=  parsePackageMethod.invoke(packageParser, new File(path), PackageManager.GET_ACTIVITIES);
    
                Field receiverField=packageObj.getClass().getDeclaredField("receivers");
    //拿到receivers  广播集合    app存在多个广播   集合  List<Activity>  name  ————》 ActivityInfo   className
                List receivers = (List) receiverField.get(packageObj);
    
                Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");
                Field intentsField = componentClass.getDeclaredField("intents");
    
    
                // 调用generateActivityInfo 方法, 把PackageParser.Activity 转换成
                Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity");
    //            generateActivityInfo方法
                Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
               Object defaltUserState= packageUserStateClass.newInstance();
                Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo",
                        packageParser$ActivityClass, int.class, packageUserStateClass, int.class);
                Class<?> userHandler = Class.forName("android.os.UserHandle");
                Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId");
                int userId = (int) getCallingUserIdMethod.invoke(null);
    
                for (Object activity : receivers) {
                   ActivityInfo info= (ActivityInfo) generateReceiverInfo.invoke(packageParser,  activity,0, defaltUserState, userId);
                   BroadcastReceiver broadcastReceiver= (BroadcastReceiver) dexClassLoader.loadClass(info.name).newInstance();
                    List<? extends IntentFilter> intents= (List<? extends IntentFilter>) intentsField.get(activity);
                    for (IntentFilter intentFilter : intents) {
                        //动态注册广播
                        context.registerReceiver(broadcastReceiver, intentFilter);
                    }
                }
                //generateActivityInfo
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
        }
    
        public Resources getResources() {
            return resources;
        }
    
        public DexClassLoader getDexClassLoader() {
            return dexClassLoader;
        }
    }
    

    在application 中提前加载插件,并将插件复制到app的私有目录,

    private void loadPlugin() {
            File filesDir = this.getDir("plugin", Context.MODE_PRIVATE);
            String name = "pluginb.apk";
            String filePath = new File(filesDir, name).getAbsolutePath();
            File file = new File(filePath);
            if (file.exists()) {
                file.delete();
            }
            InputStream is = null;
            FileOutputStream os = null;
            try {
                Log.i(TAG, "加载插件 " + new File(Environment.getExternalStorageDirectory(), name).getAbsolutePath());
                is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), name));
                os = new FileOutputStream(filePath);
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                File f = new File(filePath);
                if (f.exists()) {
                    Toast.makeText(this, "dex overwrite", Toast.LENGTH_SHORT).show();
                }
                PluginManager.getInstance().loadPath(this);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                try {
                    os.close();
                    is.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    

    从宿主跳转到插件的activity时,先跳转到插桩的activity中并携带上实际要调转的插件的activity类

    Intent intent = new Intent(this, ProxyActivity.class);
            intent.putExtra("className", PluginManager.getInstance().getPackageInfo().activities[0].name);
            startActivity(intent);
    

    在宿主中发送广播

    public void sendBroadCast(View view) {
            Toast.makeText(getApplicationContext(), "我是宿主  插件插件!收到请回答!!  1", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent();
            intent.setAction("xx.xxxx");
            sendBroadcast(intent);
        }
    

    在插桩的activity中 解析实际要调转的activity并反射获取该类对象强转为定义的接口,并将activity的生命周期在对应的方法中调用传递给插件。

    public class ProxyActivity  extends Activity {
    //    需要加载插件的  类名
        private String className;
        PayInterfaceActivity payInterfaceActivity;
        // com.dongnao.alvin.taopiaopiao.MainActivity
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState  ) {
            super.onCreate(savedInstanceState );
            className = getIntent().getStringExtra("className");
    //        class
            try {
                Class activityClass = getClassLoader().loadClass(className);
                Constructor constructor = activityClass.getConstructor(new Class[]{});
                Object instance= constructor.newInstance(new Object[]{});
    //          转为定义的接口
                payInterfaceActivity = (PayInterfaceActivity) instance;
                //传递给插件Context
                payInterfaceActivity.attach(this);
                Bundle bundle = new Bundle();
                payInterfaceActivity.onCreate(bundle);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void startActivity(Intent intent) {
              //插件中实际要跳转的activity,
            String className1=intent.getStringExtra("className");
             //跳转到自己
            Intent intent1 = new Intent(this, ProxyActivity.class);
            intent1.putExtra("className", className1);
            super.startActivity(intent1);
        }
    
        @Override
        public ComponentName startService(Intent service) {
            String serviceName = service.getStringExtra("serviceName");
            Intent intent1 = new Intent(this, ProxyService.class);
            intent1.putExtra("serviceName", serviceName);
            return super.startService(intent1);
        }
    
        @Override
        public ClassLoader getClassLoader() {
            return PluginManager.getInstance().getDexClassLoader();
        }
    
        @Override
        public Resources getResources() {
            return PluginManager.getInstance().getResources();
        }
    
    
        @Override
        protected void onStart() {
            super.onStart();
            payInterfaceActivity.onStart();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            payInterfaceActivity.onDestroy();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            payInterfaceActivity.onPause();
        }
    }
    
    public class ProxyBroadCast extends BroadcastReceiver {
        private String className;
        PayInterfaceBroadcast payInterfaceBroadcast;
        public ProxyBroadCast(String className,Context context) {
            this.className = className;
            try {
                Class loadClass = PluginManager.getInstance().getDexClassLoader().loadClass(className);
                Constructor<?> localConstructor =loadClass.getConstructor(new Class[] {});
                Object instance = localConstructor.newInstance(new Object[] {});
                payInterfaceBroadcast = (PayInterfaceBroadcast) instance;
                payInterfaceBroadcast.attach(context);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    //class------>  object--->payintenfaceBroadCast
        @Override
        public void onReceive(Context context, Intent intent) {
            payInterfaceBroadcast.onReceive(context, intent);
        }
    }
    

    插件中广播的两种接收方式
    动态 要实现定义的接口,不需要在清单文件注册

    public class MyReceiver  extends BroadcastReceiver  implements PayInterfaceBroadcast{
    
    
        @Override
        public void attach(Context context) {
            Toast.makeText(context, "-----绑定上下文成功---->", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "-----插件收到广播--->", Toast.LENGTH_SHORT).show();
            Toast.makeText(context, "-----插件收到广播1--->", Toast.LENGTH_SHORT).show();
            Toast.makeText(context, "-----插件收到广播2--->", Toast.LENGTH_SHORT).show();
            Toast.makeText(context, "-----插件收到广播3--->", Toast.LENGTH_SHORT).show();
        }
    }
    

    静态 不用实现定义的接口了,要在清单文件注册

    public class StaticReceiver  extends BroadcastReceiver {
        static final String ACTION = "com.xx.receivebrod.Receive1.PLUGIN_ACTION";
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "我是插件   收到宿主的消息  静态注册的广播  收到宿主的消息----->", Toast.LENGTH_SHORT).show();
            context.sendBroadcast(new Intent(ACTION));
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Toast.makeText(context, "休眠之后---->", Toast.LENGTH_SHORT).show();
        }
    }
    

    相关文章

      网友评论

          本文标题:插件化方式(插桩式)

          本文链接:https://www.haomeiwen.com/subject/myjcyqtx.html