日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java类加载器 架构 设计_类加载器(DexClassLoader)与插件化(动态加载)

發布時間:2024/10/5 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java类加载器 架构 设计_类加载器(DexClassLoader)与插件化(动态加载) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

類加載器與插件化解析

2.1 類裝載器 DexClassLoader

首先,我們需要了解關于java代碼本地import的一些知識:

import中所引用的類有兩個特點:

1.必須存在于本地,當程序運行時需要該類時,內部類裝載器會自動裝載該類,這對程序員來講是透明的,即程序員感知不到該過程

2.編譯時必須在現場,否則編譯過程會因為找不到引用文件而不能正常編譯。

使用ClassLoader的必要說明,多用于動態加載一些自定義的類。

一般情況下,應用程序不需要創建一個全新的ClassLoader,而是使用當前環境中已經存在的ClassLoader。因為Java的Runtime環境在初始化時,其內部會創建一個ClassLoader對象用于加載Runtime所需的各種Java類。

每個ClassLoader必須有一個父ClassLoader,在裝載Class文件時,子ClassLoader會先請求其父ClassLoader加載該Class文件,只有當其父ClassLoader找不到該Class時,子ClassLoader才會繼續裝載該類,這是一種安全機制。

Android使用的是DexClassLoader。

下面簡單介紹下DexClassLoader的使用方法

假設這里有兩個APK,第一個叫做Host,第二個叫做Plugin,其中Plugin中定義了一個PluginClass,該類中定義了一個函數,functionl(),代碼如下:

public class PluginClass {

public PluginClass(){

Log.i( "Plugin" , "PluginClass client initialized" ) ;

}

public int functionl( int a, int b ) {

return a + b;

}

}

我們剩下要做的就是在這個Host APK中去掉用我們PluginClass的functionl()方法。

這里用注釋解釋下代碼

public void useDexClassLoader(){

//確定目標class所在的位置

Intent intent = new Intent("com.haiii.android.plugin.client",null);

//通過PackageManager獲取信息。

PackageManager pm = getPackageManager();

final List plugins = pm.queryIntentActivities(intent,0);

ResolveInfo info = plugins.get(0);

ActivityInfo ainfo = info.activityInfo;

//當存在多個dexPath路徑時,需要此分隔符

String div = System.getProperty("npath.separator");

//報名

String packageName = ainfo.packageName;

//目標全路徑

String dexPath = ainfo.applicationInfo.sourceDir;

//本地解析輸出路徑

String dexOutputDir = getApplicationInfo().dataDir;

//目標lib(C/C++)文件存放路徑

String libPath = ainfo.applicationInfo.nativeLibraryDir;

//創建我們自己的ClassLoader

DexClassLoader cl = new DexClassLoader(dexPath,dexOutputDir,libPath,this.getClass().getClassLoader());

try {

//通過反射機制去調用方法

Class> clazz = cl.loadClass(packageName+".PluginClass");

Object obj = clazz.newInstance();

Class[] params = new Class[2];

params[0] = Integer.TYPE;

params[1] = Integer.TYPE;

Method action = clazz.getMethod("functionl",params);

Integer ret = (Integer)action.invoke(obj,12,34);

Log.i("Host","returnvalueis"+ret);

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (NoSuchMethodException e) {

e.printStackTrace();

} catch (IllegalArgumentException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}catch(Exception e){

Log.e("Host", "Something wrong has happened!");

}

}

上面的注釋應該挺明確的,這里主要對DexClassLoader的初始化做下說明:

dexPath,指目標類所在的APK或Jar文件的路徑。類裝載器將從該路徑中尋找指定的目標類,該路徑必須是APk或Jar的全路徑,比如/data/app/com.haiii.android.plugin.apk。如果要包含多個路徑,路徑之間必須用特定分隔符進行分割,即上面的System.getProperty(“path.sepator”)。

dexOutputDir,由于dex文件被包含在APk或者Jar中,因此在裝載目標類之前需要從APk或jar中解壓出dex文件,該參數就是指定解壓出的dex文件存放路徑。在Android系統中,一個應用程序一般對應一個Linux用戶id,應用程序僅對屬于自己的數據目錄路徑有寫的權限,因此,該參數可以使用該程序的數據路徑。

libPath,指目標類中所使用的C/C++庫存放路徑。

parent,指該裝載器的父裝載器,一般為當前執行類的裝載器。

通過類裝載器裝載class之后會返回一個class對象,但這個class對象,我們本地是沒有對其定義的,所以我們無法使用它的方法,而且此時也沒有生成我們需要的PluginClass對象,只是裝載了PluginClass的代碼而已。所以這里就需要用到反射去創建對象并調用相應的方法。

2.2 基于類裝載器設計的”插件“架構

目前市場上也有不少關于插件化的東西,這里將簡單介紹下關于使用類裝載器實現插件化的思想與方法。

上面的關于DexClassLoader的介紹,我們發現,整個調用都還算簡單,但到了最后使用反射這一塊太過于繁瑣,既然我們知道我們要調用的類和方法,那么為什么不考慮下,使用接口機制呢??相信看這篇文章的人都有一定的Android基礎,那么應該都了解service遠程aidl機制里,有一個asInterface的方式,我們可以模仿下。

首先定義一個接口,這個接口僅僅定義函數的輸入和輸出,不定義具體實現。這個接口要同時參與Host與Plugin兩個項目(APK)的編譯。

//接口定義如下

public interface Comm{

public int functionl( int a, int b ) ;

}

然后修改下PluginClass的代碼

public class PluginClass implements Comm{

public PluginClass(){

Log.i( "Plugin" , "PluginClass client initialized" ) ;

}

public int functionl( int a, int b ) {

return a + b;

}

}

然后在Host中,我們對于Class對象newInstance()之后返回的對象就可以強制轉換為Comm接口對象了,修改下代碼:

try{

Class> clazz = cl.loadClass(packageName + ".PluginClass");

Comm comm = (Comm)clazz.newInstance();

Integer ret = comm.functionl(12,34);

Log.i("Host","return value is " + ret);

}

這段代碼執行結果與上一段代碼的結果是相同的。

我們來看下,當前市場中關于插件的描述:

1.一種邏輯概念,不是技術標準;

2.插件不能獨立運行,必須運行與一個宿主程序中,由宿主程序去調用插件程序。

3.宿主程序中可以管理不同的插件,包括數量,禁用或使用,主題

設置等。多個插件,應該能做到切換插件。

4.宿主程序需要保證向下兼容,新版本應該能運行老版本的插件。

注意點:

1.接口一般定義在Host中,如本例的Comm.java。

2.Plugin項目中需要使用Comm是,必須通過一個外部的jar包,這個jar包必須是以Library方式添加到Plugin的build

path中,不能以外部jar包的方式添加,因為我們只需要在Host中存在我們定義的接口,而不想在Plugin中也編譯進去,如果都編譯進去,就會產生包名相同但驗證碼不同的文件,導致”Class

ref in pre-verified class resolved to unexpected implementation”.

總結

以上是生活随笔為你收集整理的java类加载器 架构 设计_类加载器(DexClassLoader)与插件化(动态加载)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。