從SD上加載jar並運行


轉載自:http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html


1、實現jar包

JarDemo工程

package com.test.inteface;
public interface ILoader {
	public String sayHi();
}

package com.test.jardemo;

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.KeyEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.FrameLayout.LayoutParams;

import com.test.inteface.ILoader;
public class JarLoader implements ILoader {
	@Override
	public String sayHi() {
		return "Hello world!";
	}
}  
</pre><pre name="code" class="java">1.1導出jar包(路徑爲:E:\software\android\SDK\build-tools\19.0.3\JarDemo.jar。<span style="font-family: Arial, Helvetica, sans-serif;">ILoader所在文件可以不在包內</span>)
1.2將導出的jar轉換爲dex格式。使用腳本進行轉換。轉文本爲Jar2Dex.bat
   內容爲:
<span style="white-space:pre">	</span><pre name="code" class="java">@echo off
E: cd
cd E:\software\android\SDK\build-tools\19.0.3
dx --dex --output=JarDemoDex.jar JarDemo.jar


轉換好後得到JarDemoDex.jar文件放在SD卡上的123/目錄下。


2.從sdk上引用jar包

建立工程loadjar


接口的包名、類名應同上面一致

package com.test.inteface;
public interface ILoader {
	public String sayHi();
}

package com.test.jardemo;

import java.io.File;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;

import com.test.inteface.ILoader;

import dalvik.system.DexClassLoader;

public class MainActivity extends Activity  {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		loadJar();
//		new JarLoader().sayHi();
		
	}	
	/** 
	 *  
	 * @Title: loadJar  
	 * @Description: 項目工程中必須定義接口, 而被引入的第三方jar包實現這些接口,然後進行動態加載 。 
	 *          相當於第三方按照接口協議來開發, 使得第三方應用可以以插件的形式動態加載到應用平臺中。 
	 * @return void     
	 * @throws 
	 */  

	private ILoader mLib = null;
	private void loadJar() {
		final File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().toString()
				+ File.separator + "123" + File.separator + "JarDemoDex.jar");
	    File dexOutputDir = getApplicationContext().getDir("dex", 0);
		DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(), 
				dexOutputDir.getAbsolutePath()	,null,getClassLoader());
		Class libProviderClazz = null;
		try {
			
			libProviderClazz = cl.loadClass("com.test.jardemo.JarLoader");
			mLib = (ILoader) libProviderClazz.newInstance();//獲取到這個類
			Toast.makeText(MainActivity.this, mLib.sayHi(), Toast.LENGTH_SHORT).show();
		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}
	
}

運行程序,觀察結果。

不過,一個同事說加載APK效果更好。有空試試。

參考自:http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html

Android動態加載jar/dex


前言

   在目前的軟硬件環境下,Native App與Web App在用戶體驗上有着明顯的優勢,但在實際項目中有些會因爲業務的頻繁變更而頻繁的升級客戶端,造成較差的用戶體驗,而這也恰恰是Web App的優勢。本文對網上Android動態加載jar的資料進行梳理和實踐在這裏與大家一起分享,試圖改善頻繁升級這一弊病。

 

聲明

  歡迎轉載,但請保留文章原始出處:) 

    博客園:http://www.cnblogs.com

    農民伯伯: http://over140.cnblogs.com  

    Android中文翻譯組:http://androidbox.sinaapp.com/

 

正文

  一、 基本概念和注意點

    1.1  首先需要了解一點:在Android中可以動態加載,但無法像Java中那樣方便動態加載jar

      原因:Android的虛擬機(Dalvik VM)是不認識Java打出jar的byte code,需要通過dx工具來優化轉換成Dalvik byte code纔行。這一點在咱們Android項目打包的apk中可以看出:引入其他Jar的內容都被打包進了classes.dex。

      所以這條路不通,請大家注意。

 

    1.2  當前哪些API可用於動態加載

      1.2.1  DexClassLoader

        這個可以加載jar/apk/dex,也可以從SD卡中加載,也是本文的重點。

      1.2.3  PathClassLoader  

        只能加載已經安裝到Android系統中的apk文件。

 

  二、 準備

    本文主要參考"四、參考文章"中第一篇文章,補充細節和實踐過程。

    2.1  下載開源項目

      http://code.google.com/p/goodev-demo

      將項目導入工程,工程報錯的話應該是少了gen文件夾,手動添加即可。注意這個例子是從網上下載優化好的jar(已經優化成dex然後再打包成的jar)到本地文件系統,然後再從本地文件系統加載並調用的。本文則直接改成從SD卡加載。

 

  三、實踐 

    3.1  編寫接口和實現

      3.1.1  接口IDynamic

package com.dynamic;

public interface IDynamic {
    public String helloWorld();
}

       3.1.2  實現類DynamicTest

複製代碼
package com.dynamic;

public class DynamicTest implements IDynamic {

    @Override
    public String helloWorld() {
        return "Hello World!";
    }
}
複製代碼

 

    3.2  打包並轉成dex

      3.2.1  選中工程,常規流程導出即可,如圖:

      注意:在實踐中發現,自己新建一個Java工程然後導出jar是無法使用的,這一點大家可以根據文章一來了解相關原因,也是本文的重點之一。這裏打包導出爲dynamic.jar

      (後期修復:打包請不要把接口文件打進來,參見文章末尾後續維護!)

      3.2.2  將打包好的jar拷貝到SDK安裝目錄android-sdk-windows\platform-tools下,DOS進入這個目錄,執行命名:

dx --dex --output=test.jar dynamic.jar

 

    3.3  修改調用例子

      修改MainActivity,如下:

複製代碼
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mToastButton = (Button) findViewById(R.id.toast_button);
        
        // Before the secondary dex file can be processed by the DexClassLoader,
        
// it has to be first copied from asset resource to a storage location.
//        final File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),SECONDARY_DEX_NAME);
//        if (!dexInternalStoragePath.exists()) {
//            mProgressDialog = ProgressDialog.show(this,
//                    getResources().getString(R.string.diag_title), 
//                    getResources().getString(R.string.diag_message), true, false);
//            // Perform the file copying in an AsyncTask.
//            // 從網絡下載需要的dex文件
//            (new PrepareDexTask()).execute(dexInternalStoragePath);
//        } else {
//            mToastButton.setEnabled(true);
//        }
        
        mToastButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                // Internal storage where the DexClassLoader writes the optimized dex file to.
                
//final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
                final File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().toString()
                    + File.separator + "test.jar");
                // Initialize the class loader with the secondary dex file.
//                DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
//                        optimizedDexOutputPath.getAbsolutePath(),
//                        null,
//                        getClassLoader());
                DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
                    Environment.getExternalStorageDirectory().toString(), null, getClassLoader());
                Class libProviderClazz = null;
                
                try {
                    // Load the library class from the class loader.
                    
// 載入從網絡上下載的類
//                    libProviderClazz = cl.loadClass("com.example.dex.lib.LibraryProvider");
                    libProviderClazz = cl.loadClass("com.dynamic.DynamicTest");
                    
                    // Cast the return object to the library interface so that the
                    
// caller can directly invoke methods in the interface.
                    
// Alternatively, the caller can invoke methods through reflection,
                    
// which is more verbose and slow.
                    
//LibraryInterface lib = (LibraryInterface) libProviderClazz.newInstance();
                    IDynamic lib = (IDynamic)libProviderClazz.newInstance();
                    
                    // Display the toast!
                    
//lib.showAwesomeToast(view.getContext(), "hello 世界!");
                    Toast.makeText(MainActivity.this, lib.helloWorld(), Toast.LENGTH_SHORT).show();
                } catch (Exception exception) {
                    // Handle exception gracefully here.
                    exception.printStackTrace();
                }
            }
        });
    }
複製代碼

 

    3.4  執行結果

     

 

  四、參考文章

    [推薦]在Android中動態載入自定義類

    Android app中加載jar插件

    關於Android的ClassLoader探索

    Android App 如何動態加載類

 

  五、補充

    大家可以看看DexClassLoader的API文檔,裏面不提倡從SD卡加載,不安全。此外,我也正在組織翻譯組儘快把這個命名空間下的幾個類都翻譯出來,以供大家參考。

    工程下載:這裏,Dex文件下載:這裏。大家可以直接把Dex文件拷貝到SD卡,然後運行例子。

 

  六、後期維護

    6.1  2011-12-1  修復本文錯誤

      感謝網友ppp250和liuzhaocn的反饋,基本按照評論2來修改:

      6.1.1  不需要在本工程裏面導出jar,自己新建一個Java工程然後導出來也行。

      6.1.2  導出jar時不能帶接口文件,否則會報以下錯:

         java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

      6.1.3  將jar優化時應該重新成jar(jar->dex->jar),如果如下命令:

      dx --dex --output=test.jar dynamic.jar

 

    6.2  2012-3-29  本文升級版:

      Android應用開發提高系列(4)——Android動態加載(上)——加載未安裝APK中的類

      請大家參照最新的文章來做動態加載!

 

結束 

  除了翻譯組的工作和自己本職的工作以外,很難抽時間出來分享一些開發心得,但正所謂擠擠總是有的,歡迎交流!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章