兩者區(qū)別:
站在用戶的角度思考問題,與客戶深入溝通,找到新賓網(wǎng)站設(shè)計(jì)與新賓網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、國(guó)際域名空間、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋新賓地區(qū)。
一,靜態(tài)庫(kù)的使用需要:
1
包含一個(gè)對(duì)應(yīng)的頭文件告知編譯器lib文件里面的具體內(nèi)容
2
設(shè)置lib文件允許編譯器去查找已經(jīng)編譯好的二進(jìn)制代碼
二,動(dòng)態(tài)庫(kù)的使用:
程序運(yùn)行時(shí)需要加載動(dòng)態(tài)庫(kù),對(duì)動(dòng)態(tài)庫(kù)有依賴性,需要手動(dòng)加入動(dòng)態(tài)庫(kù)
三,依賴性:
靜態(tài)鏈接表示靜態(tài)性,在編譯鏈接之后,
lib庫(kù)中需要的資源已經(jīng)在可執(zhí)行程序中了,
也就是靜態(tài)存在,沒有依賴性了
動(dòng)態(tài),就是實(shí)時(shí)性,在運(yùn)行的時(shí)候載入需要的資源,那么必須在運(yùn)行的時(shí)候提供
需要的
動(dòng)態(tài)庫(kù),有依賴性,
運(yùn)行時(shí)候沒有找到庫(kù)就不能運(yùn)行了
四,區(qū)別:
簡(jiǎn)單講,靜態(tài)庫(kù)就是直接將需要的代碼連接進(jìn)可執(zhí)行程序;動(dòng)態(tài)庫(kù)就是在需要調(diào)用其中的函數(shù)時(shí),根據(jù)函數(shù)映射表找到該函數(shù)然后調(diào)入堆棧執(zhí)行。
做成靜態(tài)庫(kù)可執(zhí)行文件本身比較大,但不必附帶動(dòng)態(tài)庫(kù)
做成動(dòng)態(tài)庫(kù)可執(zhí)行文件本身比較小,但需要附帶動(dòng)態(tài)庫(kù)
五:
首先糾正所謂“靜態(tài)連接就是把需要的庫(kù)函數(shù)放進(jìn)你的exe之中”的說法。在真實(shí)世界中,有三個(gè)概念:Use
static
libary,
static
linked
DLL,
dynamic
linked
DLL.
多數(shù)人混淆了static
libary
和
static
linked
DLL的概念,當(dāng)然他們有似是而非的“相似之處”,比如都用到.lib,下面具體說明。
使用靜態(tài)庫(kù)(Use
static
libary)是把.lib和其他.obj一起build在目標(biāo)文件中,目標(biāo)文件可以是.exe,也可以是.dll或.oxc等。一般情況下,可以根本就沒有“對(duì)應(yīng)的”.dll
文件,如C
Run
Time(CRT)庫(kù)。一個(gè)例子就是,寫一個(gè)main(){},build出來并不是只有幾個(gè)字節(jié),當(dāng)然有人會(huì)說那還有exe文件頭呢?是,即使加上文件頭的尺寸,build出的執(zhí)行文件仍然“莫名的大”。實(shí)際上那多出來的部分就是CRT靜態(tài)庫(kù)。姑且可以把靜態(tài)庫(kù).lib理解成外部程序的obj文件比較合理,它包含了函數(shù)的實(shí)現(xiàn)。
之前的分析android和標(biāo)準(zhǔn)linux在編譯器的差別,那么是標(biāo)準(zhǔn)編譯器編譯的bin文件是否可以在andorid上運(yùn)行的。
下面一個(gè)android bin為例,通過readelf -l命令,
再看標(biāo)準(zhǔn)的linux加載器
通過INTERP 段的區(qū)別,android使用linker程序,而標(biāo)準(zhǔn)Linux使用的是ld-linux.so這個(gè)so,內(nèi)核分析它的.interp段,也就是linker或者ld-linux.so,將動(dòng)態(tài)連接器映射到進(jìn)程的地址空間,然后將控制權(quán)交給動(dòng)態(tài)鏈接。
將arm-linux-gnueabi下的ld-linux.so.3和libc.so.6 copy到android平臺(tái)的/lib下
root@sp9820e_2h10:/lib # ls
ld-linux.so.3
libc.so.6
運(yùn)行helloworld,添加/lib到LD_LIBRARY_PATH,運(yùn)行成功
此處需要注意DexClassLoader的四個(gè)參數(shù):
參數(shù)1 dexPath:待加載的dex文件路徑,如果是外存路徑,一定要加上讀外存文件的權(quán)限(uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/ ),否則會(huì)報(bào)與上面一樣的錯(cuò)誤,這點(diǎn)參考文章2中說這個(gè)權(quán)限可有可無是錯(cuò)誤的。(更正下:Android4.4 KitKat及以后的版本需要此權(quán)限,之前的版本不需要權(quán)限)
在開發(fā)Android App的過程當(dāng)中,可能希望實(shí)現(xiàn)插件式軟件架構(gòu),將一部分代碼以另外一個(gè)APK的形式單獨(dú)發(fā)布,而在主程序中加載并執(zhí)行這個(gè)APK中的代碼。
實(shí)現(xiàn)這個(gè)任務(wù)的一般方法是:
復(fù)制代碼
// 加載類cls
Context pluginContext = mainContext.createPackageContext(PLUGIN_PKG, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
ClassLoader loader = pluginContext.getClassLoader();
Class? cls = loader.loadClass(CLASS_NAME);
// 通過反射技術(shù),調(diào)用cls中的方法,下面是一個(gè)示例,實(shí)際代碼因情況而定
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("someMethod");
method.invoke(obj);
復(fù)制代碼
但是,這個(gè)方法在Android 4.1及之后的系統(tǒng)中存在一些問題:對(duì)于收費(fèi)應(yīng)用,Google Play會(huì)將其安裝在一個(gè)加密目錄之下(具體就是/data/app-asec),而不是一個(gè)普通目錄之下(具體就是/data/app);安裝在加密目錄中的應(yīng)用,我們是無法使用上述方法來加載并執(zhí)行代碼的;而實(shí)際情況是,我們經(jīng)常就是依靠插件應(yīng)用來收費(fèi)的。
解決上述問題的一個(gè)方案是:將插件的二進(jìn)制代碼拷貝到SD卡中,主程序從SD卡中加載并執(zhí)行其代碼。
實(shí)現(xiàn)這個(gè)任務(wù)的具體方法是:
復(fù)制代碼
Class? cls = null;
try {
// 嘗試第一種方法
cls = loadClass1(mainContext, pkg, entryCls);
} catch (Exception e) {
// 嘗試第二種方法
cls = loadClass2(mainContext, pkg, entryCls);
}
// 示例代碼
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("someMethod");
method.invoke(obj);
// 第一種加載方法
private Class? loadClass1(Context mainContext, String pkg, String entryCls) throws Exception {
Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
ClassLoader loader = pluginContext.getClassLoader();
return loader.loadClass(entryCls);
}
// 第二種加載方法
private Class? loadClass2(Context mainContext, String pkg, String entryCls) throws Exception {
Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
String path = generatePluginDexPath(mainContext, pkg);
ensureFileExist(pluginContext, pkg, path);
// cacheDir必須是主程序的私有目錄,否則DexClassLoader可能會(huì)拒絕加載
String cacheDir = mainContext.getApplicationInfo().dataDir;
ClassLoader parentLoader = pluginContext.getClassLoader();
DexClassLoader loader = new DexClassLoader(path, cacheDir, null, parentLoader);
return loader.loadClass(entryCls);
}
// 獲取程序版本號(hào)
private int getVersionCode(Context context, String pkg) {
PackageInfo info = null;
int versionCode = 0;
try {
info = context.getPackageManager().getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);
versionCode = info.versionCode;
} catch (Exception e) {}
return versionCode;
}
// 獲取插件二進(jìn)制代碼的存儲(chǔ)位置,注意做好版本控制;路徑必須是以.dex結(jié)束,否則加載會(huì)出問題
private String generatePluginDexPath(Context context, String pkg) {
int version = getVersionCode(context, pkg);
String path = getMyAppPath() + ".classes/" + pkg + version + ".dex";
return path;
}
// 主程序在SD卡上的數(shù)據(jù)目錄
private String getMyAppPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyApp/";
}
// 拷貝插件的二進(jìn)制代碼到SD卡
private void ensureFileExist(Context pluginContext, String pkg, String path) throws Exception {
File file = new File(path);
if(file.exists()) return;
file.getParentFile().mkdirs();
Resources res = pluginContext.getResources();
int id = res.getIdentifier("classes", "raw", pkg);
InputStream in = res.openRawResource(id);
FileOutputStream out = new FileOutputStream(file);
try {
byte[] buffer = new byte[1024 * 1024];
int n = 0;
while((n = in.read(buffer)) 0) {
out.write(buffer, 0, n);
} out.flush();
} catch (IOException e) {
in.close();
out.close();
}
}
復(fù)制代碼
插件工程這邊也需要做相應(yīng)的修改:
1.編譯插件工程;
2.將bin目錄之下的classes.dex拷貝到/res/raw目錄之下;
3.重新編譯插件工程;
4.發(fā)布插件APK。
android 如何動(dòng)態(tài)的加載類----app插件技術(shù)
轉(zhuǎn)自:
?
前言:
?
? ? ? 在目前的軟硬件環(huán)境下,Native App與Web App在用戶體驗(yàn)上有著明顯的優(yōu)勢(shì),但在實(shí)際項(xiàng)目中有些會(huì)因?yàn)闃I(yè)務(wù)的頻繁變更而頻繁的升級(jí)客戶端,造成較差的用戶體驗(yàn),而這也恰恰是Web App的優(yōu)勢(shì)?,F(xiàn)如今很多項(xiàng)目要求需要采用類似于微信或Q游這樣的插件化開發(fā)模式越來越多,本文就是闡述android的動(dòng)態(tài)加載技術(shù)來滿足插件化開發(fā)模式的文章。
?
1.基本概念
1.1??在Android中可以動(dòng)態(tài)加載,但無法像Java中那樣方便動(dòng)態(tài)加載jar。
Android的虛擬機(jī)(DalvikVM)是不認(rèn)識(shí)Java打出jar的byte code,需要通過dx工具來優(yōu)化轉(zhuǎn)換成Dalvikbyte code才行。這一點(diǎn)在咱們Android項(xiàng)目打包的apk中可以看出:引入其他Jar的內(nèi)容都被打包進(jìn)了classes.dex。即android要加載的java類必須dex格式的代碼文件.
1.2??在Android中可以加載基于NDK的so庫(kù)。
NDK的執(zhí)行效率很高,加密性很好,但同時(shí)開發(fā)入門難度大,一般用于加解密、數(shù)學(xué)運(yùn)算等場(chǎng)合。so的加載很簡(jiǎn)單,如果APK發(fā)布時(shí)已經(jīng)攜帶了so文件,只需要在加載時(shí)調(diào)用System.loadLibrary(libName)方法即可。由于軟件的安裝目錄中存放so的目錄是沒有寫權(quán)限的,開發(fā)者不能更改該目錄的內(nèi)容,所以如果要?jiǎng)討B(tài)加載存放在其他地方的so文件,用System.load(pathName)方法即可。
1.3??在Android中支持動(dòng)態(tài)加載dex文件的兩種方式:
DexClassLoader:這個(gè)可以加載jar/apk/dex,也可以從SD卡中加載,也是本文的重點(diǎn)
PathClassLoader:只能加載已經(jīng)安裝到Android系統(tǒng)中的apk文件。也就是 /data/app 目錄下的 apk 文件。其它位置的文件加載的時(shí)候都會(huì)出現(xiàn) ClassNotFoundException.因?yàn)?PathClassLoader 會(huì)去讀取 /data/dalvik-cache 目錄下的經(jīng)過 Dalvik 優(yōu)化過的 dex 文件,這個(gè)目錄的 dex 文件是在安裝 apk 包的時(shí)候由 Dalvik 生成的。
?
2.注意
2.1 采用不用安裝的插件開發(fā)模式,只能夠使用?DexClassLoader進(jìn)行加載.不過動(dòng)態(tài)加載是有一些限制的,比如插件(子apk)包中的Activity、Service類是不能動(dòng)態(tài)加載的,因?yàn)槿鄙俾暶?;即使你在Manifest文件中進(jìn)行了聲明,系統(tǒng)默認(rèn)也是到安裝apk所在的路徑中去尋找類,所以你會(huì)遇到一個(gè)ClassNotFound的異常。插件里你可以用主apk中先前放入的layout、strings等資源。但是插件中自帶的界面只能用純代碼進(jìn)行編寫,插件中是不能加載插件(子apk)中的xml作為layout等資源使用的。所以在開發(fā)上一些特效會(huì)比較困難些,建議預(yù)先植入主apk中。
2.2?大家可以看看DexClassLoader的API文檔,里面不提倡從SD卡加載,不安全
3.如何制作插件
3.1 把工程導(dǎo)出為jar包
3.2 執(zhí)行SDK安裝目錄android-sdk-windows\platform-tools下的dx命令,把jar包轉(zhuǎn)換為dex格式
dx?--dex?--output=dex名 jar包名
4.如何做到啟動(dòng)未安裝的apk中的activity?
采用反射機(jī)制,把主apk中的activity的context傳遞到插件的activity中,然后采用反射進(jìn)行回調(diào)插件activity的方法。不足之出就是,插件中的activity并不是真正的activity,它只是運(yùn)行在主activity中。比如:點(diǎn)擊返回直接退出當(dāng)前activity而不是回到主activity。實(shí)例如下:
?
這是調(diào)用的Activity:
?
[java]?view plaincopy ? ?
package?com.beyondsoft.activity;??
??
import?java.lang.reflect.Constructor;??
import?java.lang.reflect.InvocationTargetException;??
import?java.lang.reflect.Method;??
??
import?dalvik.system.DexClassLoader;??
import?android.app.Activity;??
import?android.content.pm.PackageInfo;??
import?android.os.Bundle;??
import?android.util.Log;??
??
public?class?PlugActivity?extends?Activity?{??
??
????private?Class?mActivityClass;??
????private?Object?mActivityInstance;??
????Class?localClass;??
????private?Object?instance;??
??
????@Override??
????protected?void?onCreate(Bundle?savedInstanceState)?{??
????????super.onCreate(savedInstanceState);??
??
????????Bundle?paramBundle?=?new?Bundle();??
????????paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY",?true);??
????????paramBundle.putString("str",?"PlugActivity");??
????????String?dexpath?=?"/sdcard/FragmentProject.apk";??
????????String?dexoutputpath?=?"/mnt/sdcard/";??
????????LoadAPK(paramBundle,?dexpath,?dexoutputpath);??
????}??
??
????@Override??
????protected?void?onStart()?{??
????????super.onStart();??
????????Method?start;??
????????try?{??
????????????start?=?localClass.getMethod("onStart");??
????????????????start.invoke(instance);??
????????}?catch?(Exception?e)?{??
????????????//?TODO?Auto-generated?catch?block??
????????????e.printStackTrace();??
????????}??
????}??
??
????@Override??
????protected?void?onResume()?{??
????????//?TODO?Auto-generated?method?stub??
????????super.onResume();??
????????Method?resume;??
????????try?{??
????????????resume?=?localClass.getMethod("onResume");??
????????????resume.invoke(instance);??
????????}?catch?(Exception?e)?{??
????????????//?TODO?Auto-generated?catch?block??
????????????e.printStackTrace();??
????????}??
????}??
??
????@Override??
????protected?void?onPause()?{??
????????super.onPause();??
????????Method?pause;??
????????try?{??
????????????pause?=?localClass.getMethod("onPause");??
????????????pause.invoke(instance);??
????????}?catch?(Exception?e)?{??
????????????e.printStackTrace();??
????????}??
????}??
??
????@Override??
????protected?void?onStop()?{??
????????super.onStop();??
????????try?{??
????????????Method?stop?=?localClass.getMethod("onStop");??
????????????stop.invoke(instance);??
????????}?catch?(Exception?e)?{??
????????????e.printStackTrace();??
????????}??
????}??
??
????@Override??
????protected?void?onDestroy()?{??
????????//?TODO?Auto-generated?method?stub??
????????super.onDestroy();??
????????try?{??
????????????Method?des?=?localClass.getMethod("onDestroy");??
????????????des.invoke(instance);??
????????}?catch?(Exception?e)?{??
????????????//?TODO?Auto-generated?catch?block??
????????????e.printStackTrace();??
????????}??
????}??