Androidプラグイン化シリーズ第(五)編---Activityのプラグイン化案(エージェントモード)

6959 ワード

この文章ではActivityのプラグイン化案を紹介しますが、Activityのプラグイン化案は今日紹介したものだけではありません.本文を読む前に、まず私の前の2編のブログを見て、もし前の2編が真剣に見たことがあるならば、本文を読むのはせいぜい10分で終わります!
  • Androidプラグイン化シリーズ第(一)編---Hook技術のActivityの起動過程ブロック
  • Androidプラグイン化シリーズ第(二)編---ダイナミックロード技術のapkスキンケア
  • Androidプラグイン化シリーズ第(四)編---プラグインロードメカニズムの2つの案
  • 上記の記事を見て、プラグインのActivityクラスをロードする方法を知りました.Activityのプラグイン化をするには、いくつかの問題を解決する必要があります.
  • 1、プラグインのすべてのActivityはホストに登録されていません.どのようにAMSをだましてリストファイルが存在しないActivityを起動しますか.
  • 2、Activityクラスをロードした後、プラグインのActivityにライフサイクルを持たせる方法.
  • 3、プラグインapkで使用された各種リソース、どのように動的にリソースをロードするか.

  • 問題1については、このシリーズの第1編ですでに述べているように、まずホストにProxyActivityを宣言し、次にProxyActivityを起動します.すなわち、ProxyActivity+プラグインに登録されていないActivity=標準のActivity
    問題2については、実は本シリーズの第1編でも体現されており、次の編でもっとはっきり書くつもりです.
    質問3については、本シリーズの第2編でも書いたように、プラグインのリソースをロードする方法を肌を変える例で説明します.
    このブログでは、Activityクラスをロードした後、プラグインのActivityにライフサイクルを持たせる方法について主に2つ目の質問をしています.
    ここではActivityエージェントモードを使用します.古いやり方で、宿主APKにProxyActivity(エージェントActivity)を登録し、ピットとして使用します.プラグインAPKのいずれかのActivityを開くたびに、ホストでProxyActivityを起動し、ProxyActivityのライフサイクルメソッドでプラグインのActivityインスタンスのライフサイクルメソッドを呼び出し、プラグインAPKのビジネスロジックを実行します.だから考えが来た.第1に、ProxyActivityには、プラグインで現在呼び出す必要があるActivityのライフサイクルメソッドを記録するActivityインスタンスを保存する必要があります.第二に、ProxyActivityはどのようにプラグインapkのActivityのすべてのライフサイクルを呼び出す方法で、反射を使用しますか?それとも他の方法ですか.
    このActivityのプラグイン化の考え方は、最初はdynamic-load-apkから来ており、dynamic-load-apkはプラグインapkのActivityのすべてのライフサイクルを呼び出す方法で、反射が効率に影響を与えるため、最後にインタフェースの方式に変更されるため、最初は反射の方式を使用していた.まず、反射の方法を見てみましょう.エージェントactivityでは、最初のステップですべてのライフサイクルを反射する方法を取得します.
        protected void instantiateLifecircleMethods(Class> localClass) {
            String[] methodNames = new String[] {
                    "onRestart",
                    "onStart",
                    "onResume",
                    "onPause",
                    "onStop",
                    "onDestory"
            };
            for (String methodName : methodNames) {
                Method method = null;
                try {
                    method = localClass.getDeclaredMethod(methodName, new Class[] { });
                    method.setAccessible(true);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
                //mActivityLifecircleMethods map 
                mActivityLifecircleMethods.put(methodName, method);
            }
    
            Method onCreate = null;
            try {
                onCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });
                onCreate.setAccessible(true);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            mActivityLifecircleMethods.put("onCreate", onCreate);
    
            Method onActivityResult = null;
            try {
                onActivityResult = localClass.getDeclaredMethod("onActivityResult",
                        new Class[] { int.class, int.class, Intent.class });
                onActivityResult.setAccessible(true);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            mActivityLifecircleMethods.put("onActivityResult", onActivityResult);
        }
    

    ステップ2、すべてのActivityのライフサイクルを反射する方法
        @Override
        protected void onResume() {
            super.onResume();
            Method onResume = mActivityLifecircleMethods.get("onResume");
            if (onResume != null) {
                try {
                    onResume.invoke(mRemoteActivity, new Object[] { });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
        @Override
        protected void onPause() {
            Method onPause = mActivityLifecircleMethods.get("onPause");
            if (onPause != null) {
                try {
                    onPause.invoke(mRemoteActivity, new Object[] { });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            super.onPause();
        }
    
       protected void setRemoteActivity(Object activity) {
            try {
                // Activity 
                mRemoteActivity = (Activity) activity;
            } catch (ClassCastException e) {
                e.printStackTrace();
            }
        }
    

    これは反射の方式で、後でdynamic-load-apkは最適化を経て、インタフェースの方式に変えて、activityのライフサイクルの方法を1つのインタフェースをカプセル化して、エージェントactivityの中でこのインタフェースを実現して、それからエージェントactivityを通じてプラグインactivityの実現のライフサイクルの方法を呼び出します.
    public interface DLPlugin {
    
        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 onCreate(Bundle savedInstanceState);
        public void setProxy(Activity proxyActivity, String dexPath);
        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);
    }
    
        @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();
        }
    
    

    これがdynamic-load-apkのActivityプラグイン化スキームです.このスキームを使用するには、プラグインを開発するには一定の仕様に従う必要があります.dynamic-load-apkの要件は次のとおりです.
  • 1、thisを使用しないでください(インタフェースを除く):thisは現在のオブジェクト、すなわちapkのactivityを指していますが、activityは通常の意味でのactivityではないため、thisは意味がありませんが、contextではなくインタフェースを表しています.例えばactivityが実現し、インタフェースが実現されている場合、thisは引き続き有効です.
  • 2、thatを使用:thisが使用できない以上、thatを使用します.thatはapkのactivityのベースクラスBase Activityのメンバーで、apkのインストールが実行されている間にthisを指し、インストールされていない間にホストプログラムのエージェントactivity、anyway、that is better than thisを指します.

  • -3、activityのメンバーメソッド呼び出し問題:原則としてthatでメンバーメソッドを呼び出す必要があるが、ほとんどの一般的なapiが書き換えられているため、一部のapiに対してthatで呼び出す必要がある.また、apkのインストール後も正常に動作します.
  • 4、新しいactivityを起動する制約:外部activityの起動は制限されず、apk内部のactivityの起動には制限があり、まずapk中のactivityが登録されていないため暗黙的な呼び出しはサポートされず、次にBaseActivityで定義された新しい方法startActivity ByProxyとstartActivity ForResultByProxy、そしてLaunchModeはサポートされていない.
  • 5、現在はService、BroadcastReceiverなどの登録が必要なコンポーネントはサポートされていませんが、ブロードキャストはコード動的登録が可能です.

  • 制限が非常に大きいことがわかり、今では誰もこのような方法でプラグイン化の開発をしていないと推定されていますが、2014年の頃、dynamic-load-apkという思想は非常に先進的で、後のプラグイン化案に貴重な経験を提供したと信じています.次のブログではHook方式で、業界が提唱しているActivityプラグイン化案を実現することを説明します.
    Please accept mybest wishes for your happiness and success !
    参照先:https://github.com/singwhatiwanna/dynamic-load-apk