動態加載apk文件並調用其代碼

轉載:文中思路來自鏈接:http://blog.csdn.net/jiangwei0910410003/article/details/17679823
一、編寫動態功能類

編寫一個apk,用於導出方法,供其他apk調用

public class Dynamic implements IDynamic{
    private Activity mActivity;  

    public Dynamic() {
        // TODO Auto-generated constructor stub
    }
    @Override  
    public void init(Activity activity) {  
        mActivity = activity;  
        Log.i("TestDynamic", "In Dynamic::Init");
    }  

    @Override  
    public void showBanner() {  
        Toast.makeText(mActivity, "我是ShowBannber方法", 1500).show();  
    }  

    @Override  
    public void showDialog() {  
        Toast.makeText(mActivity, "我是ShowDialog方法", 1500).show();  
    }  

    @Override  
    public void showFullScreen() {  
        Toast.makeText(mActivity, "我是ShowFullScreen方法", 1500).show();  
    }  

    @Override  
    public void showAppWall() {  
        Toast.makeText(mActivity, "我是ShowAppWall方法", 1500).show();  
    }  

    @Override  
    public void destory() {  
    }  

}

IDynamic是一個接口,用於外界可以使用接口進行調用

public interface IDynamic {
    /**初始化方法*/  
    public void init(Activity activity);  
    /**自定義方法*/  
    public void showBanner();  
    public void showDialog();  
    public void showFullScreen();  
    public void showAppWall();  
    /**銷燬方法*/  
    public void destory();
}

此外,在manifest.xml中,需要聲明一個Action,用於其他apk能通過Intent檢索到此apk

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="com.example.dynamiclib"/>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

,名字無所謂,只要其他apk通過Intent能檢索到此apk就可以

二、調用動態apk

根據目標apk包名,獲取其包路徑、catche路徑等信息,創建DexClassLoader對象,並通過反射機制來調用動態apk中的功能函數代碼

public class MainActivity extends Activity implements OnClickListener{

    private Button btn_show_banner;
    private Button btn_show_dialog;
    private Button btn_show_fullscreen;
    private Button btn_show_appwall;
    private final static String TAG = "TestDynamic";
    private IDynamic libInterface = null;
    private Method method_init = null;
    private Method method_showBanner = null;
    private Method method_showDialog = null;
    private Method method_showFullScreen = null;
    private Method method_showAppWall = null;
    private Object object = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_show_banner = (Button) findViewById(R.id.button1);
        btn_show_dialog = (Button) findViewById(R.id.button2);
        btn_show_fullscreen = (Button) findViewById(R.id.button3);
        btn_show_appwall = (Button) findViewById(R.id.button4);

        /**
         * 調用靜態apk時,需要有一個解壓路徑
         * **/
        File dexOutputDir = this.getDir("dex", 0);
        /**
         * 獲取apk存放的路徑,此apk未安裝,在磁盤上,也可能是網絡臨時拉下來的
         * 這裏測試,放在SD卡根目錄
         * 主要是用於DexClassLoader的第一個參數使用
         * **/
        String dexPath = Environment.getExternalStorageDirectory().toString() 
                + File.separator 
                + "DynamicLib.apk";
        Log.i(TAG, "ApkPath = "+dexPath);
        Log.i(TAG, "dexOutPath = "+dexOutputDir.getAbsolutePath());


        /**
         * 與被調用的apk里名稱約定要一致
         * 用來找到指定apk,系統中多個時,下面get要注意取哪一個
         * **/
        Intent intent = new Intent("com.example.dynamiclib");
        /**
         * 獲取包管理器
         * **/
        PackageManager pm = getPackageManager();
        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
        /**
         * 獲得指定Activity信息
         * 如果有兩個名字都是“com.example.dynamiclib”的Intent,
         * 則可能需要get(1),因爲它會返回兩個activity
         * **/
        ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
        /**
         * 獲得apk的目錄或者jar的目錄    
         * **/
        String apkPath = activityInfo.applicationInfo.sourceDir;    
        /**
         * native代碼的目錄    
         * **/
        String libPath = activityInfo.applicationInfo.nativeLibraryDir;  
        /**
         * 第一種方法
         * 創建DexClassLoader
         * 把apk加載到虛擬機中
         * 注意:這裏的路徑可以使用兩種
         *      即可以是dexPath(磁盤上的靜態文件)
         *      也可以是通過pm獲取的已經安裝的apk的路徑
         * **/

        DexClassLoader dcl = new DexClassLoader(dexPath, 
                dexOutputDir.getAbsolutePath(), 
                null, 
                this.getClass().getClassLoader());

        /**
         * 第二種方法
         * 使用PathClassLoader
         * 這裏只能找到已經安裝的apk
         * 並且路徑,必須通過Intent、PM來聯合獲取,否則不能成功
         * 此外,Intent裏的名字,如果系統裏存在兩個名字相同的apk,name可能還需要試試第二個
         * 因爲不一定哪個在前,哪個在後
         * **/
        //PathClassLoader dcl = new PathClassLoader(apkPath, libPath, this.getClass().getClassLoader());

        Log.i(TAG, "DexClassLoader ="+dcl);
        try {
            Log.i(TAG, "before dcl.loadClass");
            /**
             * 加載目標class,名稱爲目標類名
             * **/
            Class<?> orgClass = dcl.loadClass("com.example.dynamiclib.Dynamic");
            Log.i(TAG, "orgClass load is " + orgClass.getClassLoader().toString());
            object = orgClass.newInstance();
            Class[] param = new Class[1];
            param[0] = Activity.class;
            method_init = orgClass.getMethod("init", param);
            method_showBanner = orgClass.getMethod("showBanner");
            method_showDialog = orgClass.getMethod("showDialog");
            method_showFullScreen = orgClass.getMethod("showFullScreen");
            method_showAppWall = orgClass.getMethod("showAppWall");
            if (method_init != null) {
                method_init.invoke(object, this);
            }
            Log.i(TAG, "libInterFace is "+libInterface);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            Log.i(TAG, "Not Found Class");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        btn_show_banner.setOnClickListener(this);
        btn_show_dialog.setOnClickListener(this);
        btn_show_fullscreen.setOnClickListener(this);
        btn_show_appwall.setOnClickListener(this);

    }
    void ShowTips(String tips)
    {
        Toast.makeText(this, tips, Toast.LENGTH_LONG).show();
    }
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.button1:
            if (method_showBanner != null) {
                try {
                    method_showBanner.invoke(object);
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            else {
                ShowTips("類加載失敗");
            }
            break;
        case R.id.button2:
            if (method_showDialog != null) {
                try {
                    method_showDialog.invoke(object);
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            else {
                ShowTips("類加載失敗");
            }
            break;
        case R.id.button3:
            if (method_showFullScreen != null) {
                try {
                    method_showFullScreen.invoke(object);
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            else {
                ShowTips("類加載失敗");
            }
            break;
        case R.id.button4:
            if (method_showAppWall != null) {
                try {
                    method_showAppWall.invoke(object);
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            else {
                ShowTips("類加載失敗");
            }
            break;
        default:
            break;
        }
    }
}
  1. 這裏是此處通過反射機制來調用代碼,還沒有完成通過接口來調用的功能,待繼續調試
  2. 這裏是從已經安裝的apk獲取路徑等信息,如果是目錄下靜態的apk(並沒有安裝到手機上),直接把構建DexClassLoader的第一個參數改爲靜態apk的絕對路徑,其他不需要變化,即可正常運行;
  3. 不過如果使用PathClassLoader,來加載,必須通過intent查找目標apk,同時目標apk必須已經安裝,因爲它只能加載固定目錄下的apk,不能隨意路徑的apk
  4. 使用了IDynamic後,需要把對應的jar包引入到工程裏的lib中,通過“Build Path”->”Configure Build Path”裏進行添加;只把jar拷貝到這個目錄是沒有用的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章