編輯:關於Android編程
1.頭文件中存放的是對某個庫中所定義的函數、宏(define)、類型、全局變量等進行聲明,它類似於一份倉庫清單。若用戶程序中需要使用某個庫中的函數,則只需要將該庫所對應的頭文件include到程序中即可。
2.頭文件中定義的是庫中所有函數的函數原型。而函數的具體實現則是在庫文件中。
3.在連接器連接程序時,會依據用戶程序中導入的頭文件,將對應的庫函數導入到程序中。頭文件以.h為後綴名。
頭文件是給編譯器用的,庫文件是給連接器用的
1.動態庫:在編譯用戶程序時不會將用戶程序內使用的庫函數連接到用戶程序的目標代碼中,只有在運行時,且用戶程序執行到相關函數時才會調用該函數庫裡的相應函數,因此動態函數庫所產生的可執行文件比較小。
2.靜態庫:在編譯用戶程序時會將其內使用的庫函數連接到目標代碼中,程序運行時不再需要靜態庫。使用靜態庫生成可執行文件比較大。
為什麼要進行交互?
首先,java語言提供的類庫無法滿足要求,且在數學運算,實時渲染的游戲上,音視頻處理等方面上與c/c++相比效率稍低。然後,java語言無法直接操作硬件,c/c++代碼不僅能操作硬件而且還能發揮硬件最佳性能。接著,使用java調用本地的c/c++代碼所寫的庫,省去了重復開發的麻煩,並且可以利用很多開源的庫提高程序效率。
Java調用C/C++大概有這樣幾個步驟
編寫帶有native方法的Java類, 使用javac工具編譯Java類 使用javah來生成與native方法對應的頭文件 實現相應的頭文件, 並編譯為動態鏈接庫我們對這個還是很清楚的,看代碼:
c代碼:
// // Created by Administrator on 2016/8/1. // #include "JNIUtils.h" #include#include #include #include /** * 把一個jstring轉換成一個c語言的char* 類型. */ char* _JString2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String"); jstring strencode = (*env)->NewStringUTF(env,"GB2312"); jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env, barr); jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE); if(alen > 0) { rtn = (char*)malloc(alen+1); //"\0" memcpy(rtn, ba, alen); rtn[alen]=0; } (*env)->ReleaseByteArrayElements(env, barr, ba,0); return rtn; } JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intMethod (JNIEnv *env, jclass jobj,jint num){ return num*num; } JNIEXPORT jboolean JNICALL Java_com_losileeya_jnimaster_JNIUtils_booleanMethod (JNIEnv * env, jclass jobj,jboolean boolean){ return !boolean; } JNIEXPORT jstring JNICALL Java_com_losileeya_jnimaster_JNIUtils_stringMethod (JNIEnv * env, jclass jobj,jstring jstr){ //jstring jstr-->char* char* fromJava = _JString2CStr(env,jstr); char* fromC = "add I am from C!! "; //字符串的拼接函數,會把拼接後的結果放在第一個參數裡面 strcat(fromJava,fromC); return (*env)->NewStringUTF(env,fromJava); } JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intArrayMethod (JNIEnv * env, jclass jobj,jintArray array){ int i, sum = 0; jsize len = (*env)->GetArrayLength(env, array); jint *body = (*env)->GetIntArrayElements(env, array, 0); for (i = 0; i < len; ++i) { sum += body[i]; } (*env)->ReleaseIntArrayElements(env, array, body, 0); return sum; }
c++代碼:
/ // Created by Administrator on 2016/8/1. // #include "JNIUtils.h" #include#include #include #include /** * 把一個jstring轉換成一個c++語言的char* 類型. */ char* _JString2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass( "java/lang/String"); jstring strencode = env->NewStringUTF("GB2312"); jmethodID mid = env->GetMethodID( clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); // String .getByte("GB2312"); jsize alen = env->GetArrayLength( barr); jbyte* ba = env->GetByteArrayElements( barr, JNI_FALSE); if(alen > 0) { rtn = (char*)malloc(alen+1); //"\0" memcpy(rtn, ba, alen); rtn[alen]=0; } env->ReleaseByteArrayElements(barr, ba,0); return rtn; } JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intMethod(JNIEnv * env, jclass jobj,jint num){ return num *num; } JNIEXPORT jboolean JNICALL Java_com_losileeya_jnimaster_JNIUtils_booleanMethod(JNIEnv * env, jclass jobj,jboolean boolean){ return !boolean; } JNIEXPORT jstring JNICALL Java_com_losileeya_jnimaster_JNIUtils_stringMethod (JNIEnv *env , jclass jobj, jstring jstr){ //jstring jstr-->char* char* fromJava = _JString2CStr(env,jstr); char* fromC = "add I am from C!! "; //字符串的拼接函數,會把拼接後的結果放在第一個參數裡面 strcat(fromJava,fromC); return env->NewStringUTF(fromJava); } JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intArrayMethod(JNIEnv * env, jclass jobj,jintArray array){ int sum = 0; jsize len = env->GetArrayLength(array); jint *arr = env->GetIntArrayElements(array, 0); for(int i = 0;i ReleaseIntArrayElements(array, arr,0); return sum; }
從上面Native函數的命名上我們可以了解到JNI函數的命名規則: Java代碼中的函數聲明需要添加native 關鍵 字;Native的對應函數名要以“Java”開頭,後面依次跟上Java的“package名”、“class名”、“函數名”,中間以下劃線“” 分割,在package名中的“.”也要改為“_”。此外,關於函數的參數和返回值也有相應的規則。對於Java中的基本類型如int 、double 、char 等,在Native端都有相對應的類型來表示,如jint 、jdouble 、jchar 等;其他的對象類型則統統由jobject 來表示(String 是個例外,由於其使用廣泛,故在Native代碼中有jstring 這個類型來表示,正如在上例中返回值String 對應到Native代碼中的返回值jstring )。而對於Java中的數組,在Native中由jarray 對應,具體到基本類型和一般對象類型的數組則有jintArray 等和jobjectArray 分別對應(String 數組在這裡沒有例外,同樣用jobjectArray 表示)。還有一點需要注意的是,在JNI的Native函數中,其前兩個參數JNIEnv 和jobject* 是必需的——前者是一個JNIEnv 結構體的指針,這個結構體中定義了很多JNI的接口函數指針,使開發者可以使用JNI所定義的接口功能;後者指代的是調用這個JNI函數的Java對象,有點類似於C++中的this 指針。在上述兩個參數之後,還需要根據Java端的函數聲明依次對應添加參數。在上例中,Java中聲明的JNI函數沒有參數,則Native的對應函數只有類型為JNIEnv 和jobject* 的兩個參數。
效果圖:
一般來說,要在Native代碼中訪問Java對象,有如下幾個步驟:
得到該Java對象的類定義。JNI定義了jclass 這個類型來表示Java的類的定義,並提供了FindClass接口,根據類的完整的包路徑即可得到其jclass 。 根據jclass 創建相應的對象實體,即jobject 。在Java中,創建一個新對象只需要使用new 關鍵字即可,但在Native代碼中創建一個對象則需要兩步:首先通過JNI接口GetMethodID得到該類的構造函數,然後利用NewObject接口構造出該類的一個實例對象。 訪問jobject 中的成員變量或方法。訪問對象的方法是先得到方法的Method ID,然後使用CallMethod 接口調用,這裡Type對應相應方法的返回值——返回值為基本類型的都有相對應的接口,如CallIntMethod;其他的返回值(包括String) 則為CallObjectMethod。可以看出,創建對象實質上是調用對象的一個特殊方法,即構造函數。訪問成員變量的步驟一樣:首先 GetFieldID得到成員變量的ID,然後Get/SetField讀/寫變量值。尋找class對象, 並實例化
JVM在Java中都是自己啟動的, 在C/C++中只能自己來啟動了, 啟動完之後的事情就和在Java中一樣了, 不過要使用C/C++的語法.
獲取class對象比較簡單, FindClass(env, className).
cls = (*env)->FindClass(env, "xxxx");
在Java中的類名格式是java.lang.String, 但是className的格式有點不同, 不是使用’.’作為分割, 而是’/’, 即java/lang/String.
我們知道Java中構造函數有兩種, 一種是默認的沒有參數的, 一種是自定義的帶有參數的. 對應的在C/C++中, 有兩種調用構造函數的方法.
調用默認構造函數
// 調用默認構造函數 obj = (*env)->AllocObjdect(env, cls);
構造函數也是方法, 類似調用方法的方式.
// 調用指定的構造函數, 構造函數的名字叫做mid = (*env)->GetMethodID(env, cls, " ", "()V"); obj = (*env)->NewObject(env, cls, mid);
調用方法和修改屬性
關於方法和屬性是有兩個ID與之對應, 這兩個ID用來標識方法和屬性.
jmethodID mid; jfieldID fid;
方法分為靜態和非靜態的, 所以對應的有
mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;"); mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");
上面兩個方法是同名的, 都叫sayHello, 但是簽名不同, 所以可以區分兩個方法.
JNI的函數都是有一定規律的, Static就表示是靜態, 沒有表示非靜態.
方法的調用如下
jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg); jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);
我們可以看到靜態方法是只需要class對象, 不需要實例的, 而非靜態方法需要使用我們之前實例化的對象.
屬性也有靜態和非靜態, 示例中只有非靜態的.
獲取屬性ID
fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
改屬性的值
(*env)->SetObjectField(env, obj, fid, arg); // 修改屬性
關於jstring的說明
java的String都是使用了unicode, 是雙字節的字符, 而C/C++中使用的單字節的字符。
從C轉換為java的字符, 使用NewStringUTF方法
jstring arg = (*env)->NewStringUTF(env, name);
從java轉換為C的字符, 使用GetStringUTFChars
const char* str = (*env)->GetStringUTFChars(env, result, 0);
下面我們來看代碼:
c代碼:
/* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_helloFromJava * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1helloFromJava (JNIEnv *env, jobject jobj){ jclass jclazz=(*env)->FindClass(env,"com/losileeya/jnimaster/JNIUtils"); jmethodID jmethodid=(*env)->GetMethodID(env,jclazz,"helloFromJava","()V"); jobject jobjs=(*env)->AllocObject(env,jclazz); (*env)->CallVoidMethod(env,jobjs,jmethodid); } /* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_add * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1add (JNIEnv *env, jobject jobj){ //1.得到類對應的字節碼 //全類名,把.改成/ //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = (*env)->FindClass(env, "com/losileeya/jnimaster/JNIUtils"); //2.得到要調用的方法名 //第三個參數:方法名 //第四個但是:方法簽名 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "add", "(II)I"); //3.得到要調用的方法對應的類的實例 // jobject (*AllocObject)(JNIEnv*, jclass); jobject jobjs = (*env)->AllocObject(env, jclazz); //4.調用方法 // jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); int reuslt = (*env)->CallIntMethod(env,jobjs,jmethodid,99,1); } /* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_printString * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1printString (JNIEnv *env, jobject jobj){ //1.得到類對應的字節碼 //全類名,把.改成/ //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = (*env)->FindClass(env, "com/losileeya/jnimaster/JNIUtils"); //2.得到要調用的方法名 //第三個參數:方法名 //第四個但是:方法簽名 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V"); //3.得到要調用的方法對應的類的實例 // jobject (*AllocObject)(JNIEnv*, jclass); jobject jobjs = (*env)->AllocObject(env, jclazz); //4.調用方法 // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); jstring text = (*env)->NewStringUTF(env,"I am from C!!"); (*env)->CallVoidMethod(env, jobjs, jmethodid,text); //成功調用了Java中JNI裡面的printString(String s); } /* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1sayHello (JNIEnv * env, jobject jobj){ //1.得到字節碼 jclass jclazz = (*env)->FindClass(env,"com/losileeya/jnimaster/JNIUtils"); //2.得到方法 jmethodID jmethodid = (*env)->GetStaticMethodID(env,jclazz,"sayHello","(Ljava/lang/String;)V"); //3.調用 //void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); jstring text = (*env)->NewStringUTF(env,"I am from C!! I am static method !!!"); (*env)->CallStaticVoidMethod(env,jclazz,jmethodid,text);//成功調用了Java中JNI類的靜態方法sayHello(String text) }
c++代碼:
/* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_helloFromJava * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1helloFromJava(JNIEnv*env,jobject jobj){ jclass jclazz = env->FindClass("com/losileeya/jnimaster/JNIUtils"); jmethodID jmethodid = env->GetMethodID(jclazz, "helloFromJava", "()V"); jobject jobjs = env->AllocObject(jclazz); env->CallVoidMethod(jobjs, jmethodid); } /* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_add * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1add(JNIEnv*env,jobject jobj){ //1.得到類對應的字節碼 //全類名,把.改成/ //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = env->FindClass( "com/losileeya/jnimaster/JNIUtils"); //2.得到要調用的方法名 //第三個參數:方法名 //第四個但是:方法簽名 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID jmethodid = env->GetMethodID(jclazz, "add", "(II)I"); //3.得到要調用的方法對應的類的實例 // jobject (*AllocObject)(JNIEnv*, jclass); jobject jobjs = env->AllocObject(jclazz); //4.調用方法 // jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); int reusle = env->CallIntMethod(jobjs,jmethodid,99,1); } /* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_printString * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1printString(JNIEnv*env,jobject jobj){ //1.得到類對應的字節碼 //全類名,把.改成/ //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = env->FindClass( "com/losileeya/jnimaster/JNIUtils"); //2.得到要調用的方法名 //第三個參數:方法名 //第四個但是:方法簽名 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID jmethodid = env->GetMethodID( jclazz, "printString", "(Ljava/lang/String;)V"); //3.得到要調用的方法對應的類的實例 // jobject (*AllocObject)(JNIEnv*, jclass); jobject jobjs = env->AllocObject(jclazz); //4.調用方法 // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); jstring text = env->NewStringUTF("I am from C!!"); env->CallVoidMethod( jobjs, jmethodid,text); //成功調用了Java中JNI裡面的printString(String s); } /* * Class: com_losileeya_jnimaster_JNIUtils * Method: ccallJava_sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1sayHello(JNIEnv*env,jobject jobj){ //1.得到字節碼 jclass jclazz = env->FindClass("com/losileeya/jnimaster/JNIUtils"); //2.得到方法 jmethodID jmethodid = env->GetStaticMethodID(jclazz,"sayHello","(Ljava/lang/String;)V"); //3.調用 //void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); jstring text = env->NewStringUTF("I am from C!! I am static method !!!"); env->CallStaticVoidMethod(jclazz,jmethodid,text);//成功調用了Java中JNI類的靜態方法sayHello(String text) }
可以看到,上述代碼和前面講到的步驟完全相符。這裡提一下編程時要注意的要點:1、FindClass要寫明Java類的完整包路徑,並將 “.”以“/”替換;2、GetMethodID的第三個參數是方法名(對於構造函數一律用“”表示),第四個參數是方法的“簽 名”,需要用一個字符串序列表示方法的參數(依聲明順序)和返回值信息。由於篇幅所限,這裡不再具體說明如何根據方法的聲明構造相應的“簽名”,請參考 JNI的相關文檔。
關於上面談到的步驟再補充說明一下:在JNI規范中,如上這種使用NewObject創建的對象實例被稱為“Local Reference”,它僅在創建它的Native代碼作用域內有效,因此應避免在作用域外使用該實例及任何指向它的指針。如果希望創建的對象實例在作用 域外也能使用,則需要使用NewGlobalRef接口將其提升為“Global Reference”——需要注意的是,當Global Reference不再使用後,需要顯式的釋放,以便通知JVM進行垃圾收集。
順便看下截圖:
在Android使用Jni時,為了能夠使UI線程即主線程與工作線程分開,經常要創建工作線程,然後在工作線程中調用C/C++函數.為了在C/C++ 函數中更新Android的UI,又時常使用回調。jni更新ui的話,我們就要注重jobject的使用了。
看代碼:(使用)
static { System.loadLibrary("CCallJavaForUI"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void CCallJavaForUI(View view){ this.callShowToast(); } public void showToast(){ //this - Activity的實例 //startActitity();--> //new MainActivity(); System.out.println("showToast()----------"); Toast.makeText(this, "showToast()---------", Toast.LENGTH_LONG).show(); } /** * 調用MainActivity中的showToast()方法 */ public native void callShowToast();
c代碼 :
// // Created by Administrator on 2016/8/6. // #include "JNIUtils.h" #include#include /** * 調用java 中MainActivity中的showToast()方法 * jobject jobj:誰調用就是誰的實例,當前是JNI.this--->MainActivity.this */ JNIEXPORT void JNICALL Java_com_losileeya_jniupdateui_MainActivity_callShowToast (JNIEnv * env, jobject jobj){ //1.得到字節碼 jclass jclazz = (*env)->FindClass(env,"com/losileeya/jniupdateui/MainActivity"); //2.得到方法 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID jmethodid = (*env)->GetMethodID(env,jclazz,"showToast","()V"); //3.得到對象 // jobject jobjs = (*env)->AllocObject(env,jclazz); //4.調用方法 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env,jobj,jmethodid);//成功調用了中MainActivity中的showToast()方法 };
c++代碼:
// // Created by Administrator on 2016/8/6. // #include "JNIUtils.h" #include#include /** * 調用java 中MainActivity中的showToast()方法 * jobject jobj:誰調用就是誰的實例,當前是JNI.this--->MainActivity.this */ JNIEXPORT void JNICALL Java_com_losileeya_jniupdateui_MainActivity_callShowToast (JNIEnv * env, jobject jobj){ //1.得到字節碼 jclass jclazz = (*env)->FindClass(env,"com/losileeya/jniupdateui/MainActivity"); //2.得到方法 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID jmethodid = (*env)->GetMethodID(env,jclazz,"showToast","()V"); //3.得到對象 // jobject jobjs = (*env)->AllocObject(env,jclazz); //4.調用方法 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env,jobj,jmethodid);//成功調用了中MainActivity中的showToast()方法 };
效果圖:
Java調用C和C++函數時的JNI使用區別:
注意:jni.h頭文件中對於.c & .cpp采用不同的定義
在C的定義中,env是一個兩級指針,而在C++的定義中,env是個一級指針
C形式需要對env指針進行雙重deferencing,而且須將env作為第一個參數傳給jni函數
jclass (JNICALL *GetObjectClass) (JNIEnv *env, jobject obj); jclass GetObjectClass(jobject obj) { return functions->GetObjectClass(this,obj); }
對於*.c
1.jclass test_class = (*env)->GetObjectClass(env, obj);
2.jfieldID id_num = (*env)->GetFieldID(env, test_class, “num”, “I”);
對於 *.cpp
1.jclass test_class = env->GetObjectClass(obj);
2.jfieldID id_num = env->GetFieldID(test_class, “num”, “I”);
在 C 中,
JNI 函數調用由“(*env)->”作前綴,目的是為了取出函數指針所引用的值。
在 C++ 中,
JNIEnv 類擁有處理函數指針查找的內聯成員函數。
下面將說明這個細微的差異,其中,這兩行代碼訪問同一函數,但每種語言都有各自的語法。
C 語法:jsize len = (*env)->GetArrayLength(env,array);
C++ 語法:jsize len =env->GetArrayLength(array);
1、jni 可以調用本地C函數。 2、jni 調用C++庫時,首先要將C++庫提供的功能封裝成純C格式的函數接口,然後jni裡面調用這些C接口。 總結,沒什麼區別。一個是 jni調用c。另一個是jni調用c,c調用c++。
JNI使用c和cpp的基本使用和了解就講的差不多了,更多的學習可以去看jni的使用安全手冊。
一加手機3手機是發布不久的全新旗艦手機,一加手機3延續上一代高性價比特點,非常受歡迎。那麼一加手機3怎麼截屏呢?下文下載吧小編給大家介紹一下一加手機3截圖方
MSM8909+Android5.1.1之bootloader---修改UART0時鐘頻率導致無法下載的問題解決 用高通的QFIL下載程序,正常下載界面後顯示如
相信很多開發者會把圖片存放到七牛上,我的web站點也是吧圖片存儲到七牛上,對於以圖片為主的站點,這樣可以節省很大帶寬。將圖片上傳到七牛服務器的重點就是獲得上傳憑證uplo
本文實例講述了Android編程根據系列圖片繪制動畫的方法。分享給大家供大家參考,具體如下:一、采用系統提供的Animation類,用自帶的方法其中的animation.