Android学习之插件化

一、插件化的来由

随着项目的增大,很容易出现65536/64k的问题,同时为了让多个APP可以并发的开发,插件化就应用而生。

将整个app拆分成很多模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk,最终打包的时候将宿主apk和插件apk联合打包。

插件化开发总的来说有以下几点好处:

1、宿主和插件分开编译
2、并发开发
3、动态更新插件
4、按需下载模块
5、方法数或变量数爆棚,突破65536的限制

二、类加载器

Android系统中有两个类加载器分别为PathClassLoader和DexclassLoader。

PathClassLoader:只能加载系统中已经安装过的apk

DexClassLoader:可以加载jar/apk/dex,可以从SD卡中加载未安装的apk

DexClassLoader (String dexPath, String dexOutputDir, String libPath, ClassLoader parent)

dexPath:目标所在的apk或者jar文件的路径,装载器将从路径中寻找指定的目标类。
dexOutputDir:dex文件存放的路径,需要内部路径,比如context.getFilesDir().getAbsolutePath()
libPath:动态库路径(将被添加到app动态库搜索路径列表中) 
parent:该装载器的父装载器,一般为当前执行类的装载器 this.getClass().getClassLoader()

三、动态加载资源

由于插件apk没有安装,所以在调用插件的上下文、资源的时候都是没法调用的。

资源的加载是通过AssetManager内的一个方法addAssetPath(String path)。该方法是hide的,不能直接调用。那么对于资源加载就可以使用反射的方式来获取。

1、重写getResources()方法,会回调多次

2、使用反射调用addAssetPath方法

3、解决插件资源与宿主资源的处突

如果使用到的资源,插件和宿主都同时存在,则使用插件的资源;
如果使用到的资源只有插件有,则使用插件的;
如果使用到的资源只有宿主有的,则使用宿主的。

public void setResources(Context context, String path) {
        AssetManager assetManager = null;
        try {
            assetManager = AssetManager.class.newInstance();
            Method assetMethod = AssetManager.class.getMethod("addAssetPath",String.class);
            assetMethod.invoke(assetManager,path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        resources =  new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());
    }

四、实现一个简单插件化案例

1、创建一个library项目 ,包含一个接口

    public interface AppInterface {
        void setContentView(int layoutResID);

        <T extends View> T findViewById(int id);

        void onCreate(Bundle savedInstanceState);

        void onStart();

        void onResume();

        void onDestroy();

        void onPause();

        void onSaveInstanceState(Bundle outState);

        /**
         * 需要宿主app注入给插件app上下文
        */
        void attach(Activity activity);
    }

2、创建一个插件项目,引用library项目

新建一个BaseActivity基类(实现AppInterface接口),该项目中所有的Activity都要继承该基类

public class BaseActivity extends AppCompatActivity implements AppInterface {

        private Activity that;

        @Override
        public void attach( Activity activity) {
            //上下文注入进来了
            this.that = activity;
        }

        public void onCreate(@Nullable Bundle savedInstanceState) {
            if (that == null) {
                super.onCreate(savedInstanceState);
            } else {
            }
        }
        @Override
        public void setContentView(int layoutResID) {
            if (that == null) {
                super.setContentView(layoutResID);
            }else{
                that.setContentView(layoutResID);
            }

        }

        @Override
        public <T extends View> T findViewById(int id) {
            if (that == null) {
                return super.findViewById(id);
            }else{
                return that.findViewById(id);
            }

        }

		//重写startActivity实现页面间的跳转
		@Override
        public void startActivity(Intent intent) {
            if(that != null) {
                //ProxyActivity --->className
                Intent m = new Intent();
			    //传入要跳转的activity的类名,以便使用反射来获取相应的类
                m.putExtra("className", intent.getComponent().getClassName());
                that.startActivity(m);
            }else {
                super.startActivity(intent);
            }
        }
		//重写startService实现activity调用service
		@Override
        public ComponentName startService(Intent service) {
            if(that != null) {
                Intent m = new Intent();
                m.putExtra("serviceName", service.getComponent().getClassName());
                return that.startService(m);
            }
            return super.startService(service);
        }
		
		@Override
        public void onSaveInstanceState(Bundle outState) {

        }

        @Override
        public ClassLoader getClassLoader() {
            if (that == null) {
                return super.getClassLoader();
            } else {
                return that.getClassLoader();
            }
        }
		
        @Override
        public LayoutInflater getLayoutInflater() {
            if (that == null) {
                return super.getLayoutInflater();
            } else {
                return that.getLayoutInflater();
            }
        }


        @Override
        public WindowManager getWindowManager() {
            if (that == null) {
                return super.getWindowManager();
            } else {
                return that.getWindowManager();
            }
        }

        @Override
        public Window getWindow() {
            if (that == null) {
                return super.getWindow();
            } else {
                return that.getWindow();
            }
        }

        public void onStart() {
            if (that == null) {
                super.onStart();
            } else {
            }
        }

        public void onDestroy() {
            if (that == null) {
                super.onDestroy();
            } else {
            }
        }

        public void onPause() {
            if (that == null) {
                super.onPause();
            } else {
            }
        }

        public void onResume() {
            if (that == null) {
                super.onResume();
            } else {
            }
        }
    }

创建两个个Activity用于测试跳转,继承该基类,用于测试

	public class MainActivity extends BaseActivity {

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ImageView img = (ImageView) findViewById(R.id.img);
            img.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
				    //由于插件apk没有被安装,所以需要使用代理类的上下文,即that
                    //Toast.makeText(that, "点击啦", Toast.LENGTH_SHORT).show();
					startActivity(new Intent(that,TestActivity.class));
                }
            });
        }
    }
	
	public class TestActivity extends BaseActivity{

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
        }
    }

3、创建一个主项目,引用library项目

由于由于插件apk没有被安装,没有办法在清单文件中注册,所以使用代理Acitivity的方式来完成插件apk的调用。代理Acitivity需要继承Activity而不能是AppCompatActivity

<1> 创建一个加载插件apk的类,用于获取apk的信息

	public class PluginManager {
        //-------1:构建单例类start--------
        private static PluginManager instance = new PluginManager();

        public static PluginManager getInstance() {
            return instance;
        }

        private Context context;

        //插件apk的入口类名
        private String entryName;

        //-------3:构造classLoader start-------------
        private DexClassLoader dexClassLoader;

        //-------4:构造resources start--------
        private Resources resources;

        public void setContext(Context context) {
            this.context = context.getApplicationContext();
        }

        public void loadPath(String path) {
            setEntryName(path);
            setClassLoader(path);
            setResources(path);
        }

        //-------2:获取插件app入口activity name start--------
        private void setEntryName(String path) {
            //得到packageManager来获取包信息
            PackageManager packageManager = context.getPackageManager();
            //参数一是apk的路径,参数二是希望得到的内容
            PackageInfo packageInfo = null;
            try {
                packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //得到插件app的入口activity名称
            entryName = packageInfo.activities[0].name;
        }

        public String getEntryName() {
            return entryName;
        }


        private void setClassLoader(String path) {
            //dex的缓存路径,必须是内部存储路径
            File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);

            dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsoluteFile().getAbsolutePath(), null, context.getClassLoader());
        }

        public DexClassLoader getDexClassLoader() {
            return dexClassLoader;
        }

        public Resources getResources() {
            return resources;
        }

        public void setResources(String path) {
            //由于构建resources必须要传入AssetManager,这里先构建一个AssetManager
            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();
            }
        }
    }

<2> 创建代理Activity,并在清单文件中注册该Activity

	public class ProxyActivity extends Activity {
        /**
         * 要跳转的activity的name
        */
        private String className = "";
        private AppInterface appInterface = null;

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            /**
             * step1:得到插件app的activity的className
            */
            className = getIntent().getStringExtra("className");
            /**
             * step2:通过反射拿到class,
             * 但不能用以下方式
             * classLoader.loadClass(className)
             * Class.forName(className)
             * 因为插件app没有被安装!
             * 这里我们调用我们重写过多classLoader
            */
            try {
                Class activityClass = getClassLoader().loadClass(className);
                Constructor constructor = activityClass.getConstructor();
                Object instance = constructor.newInstance();
                //将获取的实例转为AppInterface
                appInterface = (AppInterface) instance;
                appInterface.attach(this);
                Bundle bundle = new Bundle();
                appInterface.onCreate(bundle);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void startActivity(Intent intent) {
            String className1 = intent.getStringExtra("className");
            Intent intent1 = new Intent(this, ProxyActivity.class);
            intent1.putExtra("className", className1);
            super.startActivity(intent1);
        }

		/**
		 * 插件apk中调用startService时,相当于调用传入的Activity context.startService方法,这
		 * 里重写这个方法,相当于绑定一个ProxyService代理插件中的Service,在ProxyService的生命
		 * 周期方法中调用被代理对象的生命周期方法。
		 * @param service
		 * @return
		 */
		@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);
		}

		/**
		 * 插件apk中调用bindService时,相当于调用传入的Activity context.bindService方法,这
		 * 里重写这个方法,相当于绑定一个ProxyService代理插件中的Service,在ProxyService的生命
		 * 周期方法中调用被代理对象的生命周期方法。
		 * @param service
		 * @param conn
		 * @param flags
		 * @return
		 */
		@Override
		public boolean bindService(Intent service, ServiceConnection conn, int flags) {
			String serviceName = service.getStringExtra("serviceName");
			Intent intent1 = new Intent(this, ProxyService.class);
			intent1.putExtra("serviceName", serviceName);
			return super.bindService(intent1, conn, flags);
		}
		
		
        @Override
        protected void onStart() {
            super.onStart();
            appInterface.onStart();
        }


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

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

        @Override
        public ClassLoader getClassLoader() {
            //不用系统的ClassLoader,用dexClassLoader加载
            return PluginManager.getInstance().getDexClassLoader();
        }

        @Override
        public Resources getResources() {
            //不用系统的resources,自己实现一个resources
            return PluginManager.getInstance().getResources();
        }
    }

在ProxyActivity的所有生命周期方法中都必须要调用被代理类对应的生命周期方法,这样就实现了对被代理类生命周期的控制。

<3> 创建测试类

	public class MainActivity extends AppCompatActivity {

		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main);
			TextView textView = (TextView) findViewById(R.id.click);
			textView.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View view) {
					click();
				}
			});
			PluginManager.getInstance().setContext(this);
		}
		
		private void click() {
			load();
			/**
			 * 点击跳往插件app的activity,一律跳转到PRoxyActivity
			 */
			Intent intent = new Intent(this, ProxyActivity.class);
			intent.putExtra("className", PluginManager.getInstance().getEntryName());
			startActivity(intent);

		}

		//加载插件apk
		private void load() {
			/**
			 * 事先放置到SD卡根目录的plugin.apk
			 * 现实场景中是有服务端下发
			 */
			File file = new File(Environment.getExternalStorageDirectory().getPath(), "myplugin-debug.apk");

			PluginManager.getInstance().loadPath(file.getAbsoluteFile().getPath());
		}
		
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值