플러그식 플러그인화 실현 (1)

13405 단어
플러그인화 (1)
앞말
  • 정의: 전체 app를 로 분리한다. 이 모듈은 를 포함하고 각 모듈은 하나apk (모듈화된 각 모듈은 lib 파일), 최종 포장 시 apk apk를 분리하여 포장한다.apk
  • 플러그인 이점:
  • 숙주와 플러그인을 분리하여 컴파일하여 서로 영향을 주지 않는다.
  • 병행 개발, 숙주와 플러그인 분리 동시 개발;
  • 동적 업데이트 플러그인;
  • 모듈식 플러그 인 필요 시 다운로드
  • 문제 해결65535;

  • 플러그인화의 난이도
  • 플러그인 는 관리하기 어렵다. 플러그인이 설치되지 않았기 때문에 상하문이 없기 때문에 생명주기와 관련된 모든 방법은 직접 호출할 수 없다.
  • android 9.0@hide의 주석이 있어서 이 방법은 개발자에게 보이지 않는다.
  • 호환성 문제;
  • 숙주와 플러그인 간의 통신 문제이기 때문에 숙주와 플러그인이 모두 이 기준을 따르도록 표준을 제정하여 통신 문제와 플러그인의 생명주기 관리 문제를 해결해야 한다.


  • 2. 사고방식 분석 실현
    library를 만들어서 apk apk를 모두 이것library에 의존하게 하고 apk apk에서 모두 이 기준을 실현해야 하며 apk의 생명주기가 관리된다.
        public interface PayInterfaceActivity {
        /**
         *         activity context     
         *        
         */        
        void attach(Activity proxyActivity);
    
        /**
         *     
         */
        void onCreate(Bundle savedInstanceState);
    
        void onStart();
    
        void onResume();
    
        void onPause();
    
        void onStop();
            
        void onRestart();    
    
        void onDestroy();
    
        void onSaveInstanceState(Bundle outState);
    
        boolean onTouchEvent(MotionEvent event);
    
        void onBackPressed();
    }
    

    숙주 apk 코드 구현
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
        private Button mBtn_load;
        private Button mBtn_jump;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            PluginManager.getInstance().setContext(this);
            mBtn_load = findViewById(R.id.btn_load);
            mBtn_jump = findViewById(R.id.btn_jump);
            setListener();
        }
    
        private void setListener() {
            mBtn_load.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //    ,           
                    loadPlugin();
                }
            });
            mBtn_jump.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //   activity      activity
                    //           activity      ProxyActivity,proxyActivity       activity
                    Intent intent = new Intent(MainActivity.this, ProxyActivity.class);
                    //activities[0]  launcher activity
                    intent.putExtra("className", PluginManager.getInstance().getPackageInfo().activities[0].name);
                    startActivity(intent);
                }
            });
        }
    
    
        private void loadPlugin() {
            //             app  ,       
            File pluginDir = getDir("plugin", Context.MODE_PRIVATE);
            String name = "pluginb.apk";
            String filePath = new File(pluginDir, name).getAbsolutePath();
            File file = new File(filePath);
            if (file.exists()) {
                file.delete();
            }
            InputStream is = null;
            OutputStream os = null;
            try {
                File file1 = new File(Environment.getExternalStorageDirectory(), name);
                Log.i(TAG, "load: file1  = " + file1.getAbsolutePath());
                is = new FileInputStream(file1);
                os = new FileOutputStream(file);
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                if (new File(filePath).exists()) {
                    Toast.makeText(this, "    ", Toast.LENGTH_SHORT).show();
                }
    
                //      ,    
                PluginManager.getInstance().loadPath(this);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                    if (os != null) {
                        os.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    플러그인 관리 클래스
    /**
     * description:     ,             ,
     */
    public class PluginManager {
    
        private Context mContext;
    
        private PluginManager() {
        }
    
        private static final PluginManager instance = new PluginManager();
    
        public static PluginManager getInstance() {
            return instance;
        }
    
    
        private PackageInfo mPackageInfo;
        private DexClassLoader mDexClassLoader;
        private Resources mResources;
    
    
        //    
        public void loadPath(Context context) {
            //     activity
            //         
            String pluginbPath = getPluginPath(context);
    
            //    apk     activity
            PackageManager manager = context.getPackageManager();
            mPackageInfo = manager.getPackageArchiveInfo(pluginbPath, PackageManager.GET_ACTIVITIES);
    
            File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
            /**
             *    DexClassLoader mResources  
             * dexPath:  apk     
             * optimizedDirectory:    ,          
             * librarySearchPath:    ,    
             * ClassLoader
             */
            mDexClassLoader = new DexClassLoader(pluginbPath, dexOutFile.getAbsolutePath(), null, context.getClassLoader());
            try {
                //       @hide   ,        
                AssetManager assetManager = AssetManager.class.newInstance();
                Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
                addAssetPath.invoke(assetManager, pluginbPath);
                mResources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        private String getPluginPath(Context context) {
            File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
            String name = "pluginb.apk";
            return new File(pluginDir, name).getAbsolutePath();
        }
    
        public DexClassLoader getDexClassLoader() {
            return mDexClassLoader;
        }
    
        public Resources getResources() {
            return mResources;
        }
    
        public PackageInfo getPackageInfo() {
            return mPackageInfo;
        }
    
        public void setContext(Context context) {
            mContext = context;
        }
    }
    

    숙주에서 플러그인의activity를 불러옵니다
    /**
     * description:ProxyActivity            ,    activity   ,
     *     activity,        ,xxxx.class,  assert,    resource
     *               ,    intent   class   
     */
    public class ProxyActivity extends Activity {
        //         activity    
        private String className;
        private static final String TAG = "ProxyActivity";
        private PayInterfaceActivity mPayInterfaceActivity;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            className = getIntent().getStringExtra("className");
            Log.i(TAG, "onCreate: className = " + className);
            //     class  (why?),           Class  ,    app    (            ?),
            //  classLoader   class  
            try {
                //className--->Class--  -->className   activity  --->  activity onCreate()    activity   
                Class> activityClass = getClassLoader().loadClass(className);
                //   activity  ,    apk  ,          mainActivity
                Constructor> constructor = activityClass.getConstructor();
                //   instance     activity,     activity    ,    onCreate()  
                //          onCreate()  ,           
                Object instance = constructor.newInstance();
                //  instance    apk MainActivity,   apk Activity   PayInterfaceActivity  ,      
                // TODO: 2018/4/27 java.lang.ClassCastException: com.wvbx.alipayplugin.MainActivity cannot be cast to com.wvbx.paystandard.PayInterfaceActivity
                mPayInterfaceActivity = (PayInterfaceActivity) instance;
                mPayInterfaceActivity.attach(this);
                //    activity    activity    ,       bundle  
                Bundle bundle = new Bundle();
                mPayInterfaceActivity.onCreate(bundle);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
        @Override
        public void startActivity(Intent intent) {
            //       activity         activity     
            String className = intent.getStringExtra("className");
            Intent intent1 = new Intent(this, ProxyActivity.class);
            intent1.putExtra("className", className);
            super.startActivity(intent1);
        }
    
        //  ProxyActivity        activity,
        //      ProxyActivity           activity     
        //     PayInterfaceActivity  ,          
        @Override
        protected void onStart() {
            super.onStart();
            mPayInterfaceActivity.onStart();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mPayInterfaceActivity.onPause();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mPayInterfaceActivity.onDestroy();
        }
    
        //         classLoader,    
        @Override
        public ClassLoader getClassLoader() {
            return PluginManager.getInstance().getDexClassLoader();
        }
    
        @Override
        public Resources getResources() {
            return PluginManager.getInstance().getResources();
        }
    }
    

    플러그인 activity의 기본 클래스
    /**
     * description:
     *             apk    ,       ,                        ,
     *               activity     ,                           
     */
    public class BaseActivity extends Activity implements PayInterfaceActivity {
    
        //       activity
        protected Activity that;
    
        @Override
        public void attach(Activity proxyActivity) {
            this.that = proxyActivity;
        }
    
    
        @Override
        public  T findViewById(int id) {
            if (that != null) {
                return that.findViewById(id);
            } else {
                return super.findViewById(id);
            }
        }
    
        @Override
        public void setContentView(int layoutResID) {
            if (that != null) {
                that.setContentView(layoutResID);
            } else {
                super.setContentView(layoutResID);
            }
        }
    
        @Override
        public Intent getIntent() {
            if (that != null) {
                return that.getIntent();
            } else {
                return super.getIntent();
            }
        }
    
        @Override
        public ClassLoader getClassLoader() {
            if (that != null) {
                return that.getClassLoader();
            } else {
                return super.getClassLoader();
            }
        }
    
        @NonNull
        @Override
        public LayoutInflater getLayoutInflater() {
            if (that != null) {
                return that.getLayoutInflater();
            } else {
                return super.getLayoutInflater();
            }
        }
    
        @Override
        public ApplicationInfo getApplicationInfo() {
            if (that != null) {
                return that.getApplicationInfo();
            } else {
                return super.getApplicationInfo();
            }
        }
    
        @Override
        public Window getWindow() {
            if (that != null) {
                return that.getWindow();
            } else {
                return super.getWindow();
            }
        }
    
    
        @Override
        public void startActivity(Intent intent) {
            //     activity  ,      activity   ,  proxyActivity1    proxyActivity2
            //proxyActivity-->className
            Intent i = new Intent();
            //  activity   standard  
            i.putExtra("className",intent.getComponent().getClassName());
            //   that proxyActivity
            that.startActivity(i);
        }
    
        @Override
        public void setContentView(View view) {
            if (that != null) {
                that.setContentView(view);
            } else {
                super.setContentView(view);
            }
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
    
        }
    
        @Override
        public void onStart() {
    
        }
    
        @Override
        public void onResume() {
    
        }
    
        @Override
        public void onPause() {
    
        }
    
        @Override
        public void onRestart() {
    
        }
    
        @Override
        public void onStop() {
    
        }
    
        @Override
        public void onDestroy() {
    
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
    
        }
    }
    

    계승BaseActivity의 자activity
    public class MainActivity extends BaseActivity {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //     xml  onclick  ,       ,
            findViewById(R.id.main).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //     MainActivity.this,       
                    Toast.makeText(that, "    ", Toast.LENGTH_SHORT).show();
                    that.startActivity(new Intent(that, SecondActivity.class));
                }
            });
        }
        
    }
    

    좋은 웹페이지 즐겨찾기