導航:首頁 > 操作系統 > android回調函數jni

android回調函數jni

發布時間:2024-06-08 22:02:29

A. android 直播怎麼實現的

流到伺服器上,在客戶端播放camera採集的數據。採用SurfaceView+ SurfaceTexture來顯示camera數據, SurfaceTexture在接收圖像流之後,可以進行二次處理,比如(加字體水印), 處理完畢後再送給另一個SurfaceView用於顯示.
先說說,android端採集攝像頭數據,這個網上有很多的資料,可以網路下。這里可以在回調函數OnPreviewFrame(byte[] data,Camera cma)中獲取攝像頭數據data.
關鍵是如何把data傳輸到伺服器,要實現這個目標,我使用ffmpeg 來解決這個為題。在android 工程創建jni文件夾,使用jni調用。native函數如下:
// 在OnPreviewFrame中調用
public native void push(byte[] data);
// 可以在OnSurfcaeChanged()中調用. 在jni中初始化輸出路徑.
public native int initPush(String outputurl,int width,int height);

B. 濡備綍鍦╝ndroid鐨刯ni綰跨▼涓瀹炵幇鍥炶皟

JNI鍥炶皟鏄鎸囧湪c/c++浠g爜涓璋冪敤java鍑芥暟錛屽綋鍦╟/c++鐨勭嚎紼嬩腑鎵ц屽洖璋冨嚱鏁版椂錛屼細瀵艱嚧鍥炶皟澶辮觸銆

鍏朵腑涓縐嶅湪Android緋葷粺鐨勮В鍐蟲柟妗堟槸錛

鎶奵/c++涓鎵鏈夌嚎紼嬬殑鍒涘緩錛岀敱pthread_create鍑芥暟鏇挎崲涓虹敱Java灞傜殑鍒涘緩綰跨▼鐨勫嚱鏁癆ndroidRuntime::createJavaThread銆

鍋囪炬湁c++鍑芥暟:

[cpp] view plain
void *thread_entry(void *args)
{
while(1)
{
printf("thread running...\n");
sleep(1);
}

}

void init()
{
pthread_t thread;
pthread_create(&thread,NULL,thread_entry,(void *)NULL);
}

init()鍑芥暟鍒涘緩涓涓綰跨▼錛岄渶瑕佸湪璇ョ嚎紼嬩腑璋冪敤java綾籘est鐨勫洖璋冨嚱鏁癛eceive:

[cpp] view plain
public void Receive(char buffer[],int length){
String msg = new String(buffer);
msg = "received from jni callback:" + msg;
Log.d("Test", msg);
}

棣栧厛鍦╟++涓瀹氫箟鍥炶皟鍑芥暟鎸囬拡錛

[cpp] view plain
//test.h
#include <pthread.h>
//function type for receiving data from native
typedef void (*ReceiveCallback)(unsigned char *buf, int len);

/** Callback for creating a thread that can call into the Java framework code.
* This must be used to create any threads that report events up to the framework.
*/
typedef pthread_t (* CreateThreadCallback)(const char* name, void (*start)(void *), void* arg);

typedef struct{
ReceiveCallback recv_cb;
CreateThreadCallback create_thread_cb;
}Callback;

鍐嶄慨鏀筩++涓鐨剗nit鍜宼hread_entry鍑芥暟錛

[cpp] view plain
//test.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
#include "test.h"

void *thread_entry(void *args)
{
char *str = "i'm happy now";
Callback cb = NULL;
int len;
if(args != NULL){
cb = (Callback *)args;
}

len = strlen(str);
while(1)
{
printf("thread running...\n");
//invoke callback method to java
if(cb != NULL && cb->recv_cb != NULL){
cb->recv_cb((unsigned char*)str, len);
}
sleep(1);
}

}

void init(Callback *cb)
{
pthread_t thread;
//pthread_create(&thread,NULL,thread_entry,(void *)NULL);
if(cb != NULL && cb->create_thread_cb != NULL)
{
cb->create_thread_cb("thread",thread_entry,(void *)cb);
}
}

鐒跺悗鍦╦ni涓瀹炵幇鍥炶皟鍑芥暟錛屼互鍙婂叾浠栧疄鐜幫細

[cpp] view plain
//jni_test.c
#include <stdlib.h>
#include <malloc.h>
#include <jni.h>
#include <JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"

#include "test.h"
#define RADIO_PROVIDER_CLASS_NAME "com/tonny/Test"

using namespace android;

static jobject mCallbacksObj = NULL;
static jmethodID method_receive;

static void (JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
LOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
}
}

static void receive_callback(unsigned char *buf, int len)
{
int i;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jcharArray array = env->NewCharArray(len);
jchar *pArray ;

if(array == NULL){
LOGE("receive_callback: NewCharArray error.");
return;
}

pArray = (jchar*)calloc(len, sizeof(jchar));
if(pArray == NULL){
LOGE("receive_callback: calloc error.");
return;
}

// buffer to jchar array
for(i = 0; i < len; i++)
{
*(pArray + i) = *(buf + i);
}
// buffer to jcharArray
env->SetCharArrayRegion(array,0,len,pArray);
//invoke java callback method
env->CallVoidMethod(mCallbacksObj, method_receive,array,len);
//release resource
env->DeleteLocalRef(array);
free(pArray);
pArray = NULL;

(env, __FUNCTION__);
}

static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
{
return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
}

static Callback mCallbacks = {
receive_callback,
create_thread_callback
};

static void jni_class_init_native
(JNIEnv* env, jclass clazz)
{
method_receive = env->GetMethodID(clazz, "Receive", "([CI)V");
}

static int jni_init
(JNIEnv *env, jobject obj)
{

if (!mCallbacksObj)
mCallbacksObj = env->NewGlobalRef(obj);

return init(&mCallbacks);
}

static const JNINativeMethod gMethods[] = {
{ "class_init_native", "()V", (void *)jni_class_init_native },
{ "native_init", "()I", (void *)jni_init },
};

static int registerMethods(JNIEnv* env) {

const char* const kClassName = RADIO_PROVIDER_CLASS_NAME;
jclass clazz;
/* look up the class */
clazz = env->FindClass(kClassName);
if (clazz == NULL) {
LOGE("Can't find class %s/n", kClassName);
return -1;
}
/* register all the methods */
if (env->RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK)
{
LOGE("Failed registering methods for %s/n", kClassName);
return -1;
}
/* fill out the rest of the ID cache */
return 0;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
LOGI("Radio JNI_OnLoad");
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed/n");
goto fail;
}

if(env == NULL){
goto fail;
}
if (registerMethods(env) != 0) {
LOGE("ERROR: PlatformLibrary native registration failed/n");
goto fail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
fail:
return result;
}

jni鐨凙ndroid.mk鏂囦歡涓鍏變韓搴撹劇疆涓猴細

[cpp] view plain
LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper

鏈鍚庡啀瀹炵幇Java涓鐨凾est綾伙細

[java] view plain
//com.tonny.Test.java

public class Test {

static{
try {
System.loadLibrary("test");
class_init_native();

} catch(UnsatisfiedLinkError ule){
System.err.println("WARNING: Could not load library libtest.so!");
}

}

public int initialize() {
return native_radio_init();
}

public void Receive(char buffer[],int length){
String msg = new String(buffer);
msg = "received from jni callback" + msg;
Log.d("Test", msg);
}

protected static native void class_init_native();

protected native int native_init();

}

C. 如何在Android下使用JNI

1.引言
我們知道,Android系統的底層庫由c/c++編寫,上層Android應用程序通過Java虛擬機調用底層介面,銜接底層c/c++庫與Java應用程序間的介面正是JNI(JavaNative Interface)。本文描述了如何在ubuntu下配置AndroidJNI的開發環境,以及如何編寫一個簡單的c函數庫和JNI介面,並通過編寫Java程序調用這些介面,最終運行在模擬器上的過程。

2.環境配置

2.1.安裝jdk1.6
(1)從jdk官方網站下載jdk-6u29-linux-i586.bin文件。
(2)執行jdk安裝文件
[html] view plainprint?
01.$chmod a+x jdk-6u29-linux-i586.bin
02.$jdk-6u29-linux-i586.bin
$chmod a+x jdk-6u29-linux-i586.bin
$jdk-6u29-linux-i586.bin
(3)配置jdk環境變數

[html] view plainprint?
01.$sudo vim /etc/profile
02.#JAVAEVIRENMENT
03.exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29
04.exportJRE_HOME=$JAVA_HOME/jre
05.exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
06.exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
$sudo vim /etc/profile
#JAVAEVIRENMENT
exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29
exportJRE_HOME=$JAVA_HOME/jre
exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
保存後退出編輯,並重啟系統。

(4)驗證安裝

[html] view plainprint?
01.$java -version
02.javaversion "1.6.0_29"
03.Java(TM)SE Runtime Environment (build 1.6.0_29-b11)
04.JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)
05.$javah
06.用法:javah[選項]<類>
07.其中[選項]包括:
08.-help輸出此幫助消息並退出
09.-classpath<路徑>用於裝入類的路徑
10.-bootclasspath<路徑>用於裝入引導類的路徑
11.-d<目錄>輸出目錄
12.-o<文件>輸出文件(只能使用-d或-o中的一個)
13.-jni生成JNI樣式的頭文件(默認)
14.-version輸出版本信息
15.-verbose啟用詳細輸出
16.-force始終寫入輸出文件
17.使用全限定名稱指定<類>(例
18.如,java.lang.Object)。
$java -version
javaversion "1.6.0_29"
Java(TM)SE Runtime Environment (build 1.6.0_29-b11)
JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)
$javah
用法:javah[選項]<類>
其中[選項]包括:
-help輸出此幫助消息並退出
-classpath<路徑>用於裝入類的路徑
-bootclasspath<路徑>用於裝入引導類的路徑
-d<目錄>輸出目錄
-o<文件>輸出文件(只能使用-d或-o中的一個)
-jni生成JNI樣式的頭文件(默認)
-version輸出版本信息
-verbose啟用詳細輸出
-force始終寫入輸出文件
使用全限定名稱指定<類>(例
如,java.lang.Object)。2.2.安裝android應用程序開發環境
ubuntu下安裝android應用程序開發環境與windows類似,依次安裝好以下軟體即可:
(1)Eclipse
(2)ADT
(3)AndroidSDK
與windows下安裝唯一不同的一點是,下載這些軟體的時候要下載Linux版本的安裝包。
安裝好以上android應用程序的開發環境後,還可以選擇是否需要配置emulator和adb工具的環境變數,以方便在進行JNI開發的時候使用。配置步驟如下:
把emulator所在目錄android-sdk-linux/tools以及adb所在目錄android-sdk-linux/platform-tools添加到環境變數中,android-sdk-linux指androidsdk安裝包android-sdk_rxx-linux的解壓目錄。
[plain] view plainprint?
01.$sudo vim /etc/profile
02.exportPATH=~/software/android/android-sdk-linux/tools:$PATH
03. exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH
$sudo vim /etc/profile
exportPATH=~/software/android/android-sdk-linux/tools:$PATH
exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH
編輯完畢後退出,並重啟生效。

2.3.安裝NDK
NDK是由android提供的編譯android本地代碼的一個工具。
(1)從androidndk官網http://developer.android.com/sdk/ndk/index.html下載ndk,目前最新版本為android-ndk-r6b-linux-x86.tar.bz2.
(2)解壓ndk到工作目錄:
[plain] view plainprint?
01.$tar -xvf android-ndk-r6b-linux-x86.tar.bz2
02.$sudo mv android-ndk-r6b /usr/local/ndk
$tar -xvf android-ndk-r6b-linux-x86.tar.bz2
$sudo mv android-ndk-r6b /usr/local/ndk
(3)設置ndk環境變數

[plain] view plainprint?
01.$sudo vim /etc/profile
02.exportPATH=/usr/local/ndk:$PATH
$sudo vim /etc/profile
exportPATH=/usr/local/ndk:$PATH

編輯完畢後保存退出,並重啟生效

(4)驗證安裝

[plain] view plainprint?
01.$ cd/usr/local/ndk/samples/hello-jni/
02.$ ndk-build
03.Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
04.Gdbsetup : libs/armeabi/gdb.setup
05.Install : libhello-jni.so => libs/armeabi/libhello-jni.so
$ cd/usr/local/ndk/samples/hello-jni/
$ ndk-build
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup : libs/armeabi/gdb.setup
Install : libhello-jni.so => libs/armeabi/libhello-jni.so

3.JNI實現
我們需要定義一個符合JNI介面規范的c/c++介面,這個介面不用太復雜,例如輸出一個字元串。接下來,則需要把c/c++介面的代碼文件編譯成共享庫(動態庫).so文件,並放到模擬器的相關目錄下。最後,啟動Java應用程序,就可以看到最終效果了。

3.1.編寫Java應用程序代碼
(1)啟動Eclipse,新建android工程

Project:JNITest

Package:org.tonny.jni

Activity:JNITest

(2)編輯資源文件

編輯res/values/strings.xml文件如下:編輯res/layout/main.xml文件
我們在主界面上添加了一個EditText控制項和一個Button控制項。

(3)編輯JNITest.java文件


static表示在系統第一次載入類的時候,先執行這一段代碼,在這里表示載入動態庫libJNITest.so文件。

再看這一段:

[java] view plainprint?
01.privatenativeString GetReply();
privatenativeString GetReply();
native表示這個方法由本地代碼定義,需要通過jni介面調用本地c/c++代碼。

[java] view plainprint?
01.publicvoidonClick(View arg0) {
02.edtName.setText(reply);
03.}
publicvoidonClick(View arg0) {
edtName.setText(reply);
}

這段代碼表示點擊按鈕後,把native方法的返回的字元串顯示到EditText控制項。

(4)編譯工程,生成.class文件。

3.2.用javah工具生成符合JNI規范的c語言頭文件

在終端中,進入android工程所在的bin目錄

[plain] view plainprint?
01.$cd ~/project/Android/JNITest/bin
$cd ~/project/Android/JNITest/bin
我們用ls命令查看,可以看到bin目錄下有個classes目錄,其目錄結構為classes/org/tonny/jni,即classes的子目錄結構是android工程的包名org.tonny.jni。請注意,下面我們准備執行javah命令的時候,必須進入到org/tonny/jni的上級目錄,即classes目錄,否則javah會提示找不到相關的java類。

下面繼續:

[plain] view plainprint?
01.$cd classes
02.$javah org.tonny.jni.JNITest
03.$ls
04.org org_tonny_jni_JNITest.h
$cd classes
$javah org.tonny.jni.JNITest
$ls
org org_tonny_jni_JNITest.h

執行javahorg.tonny.jni.JNITest命令,在classes目錄下會生成org_tonny_jni_JNITest.h頭文件。如果不進入到classes目錄下的話,也可以這樣:

[plain] view plainprint?
01.$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest
$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest
-classpath 參數表示裝載類的目錄。

3.3.編寫c/c++代碼
生成org_tonny_jni_JNITest.h頭文件後,我們就可以編寫相應的函數代碼了。下面在android工程目錄下新建jni目錄,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h頭文件拷貝到jni目錄下,並在jni目錄下新建org_tonny_jni_JNITest.c文件,編輯代碼如下:

[cpp] view plainprint?
01.#include<jni.h>
02.#include<string.h>
03.#include"org_tonny_jni_JNITest.h"
04.
05.
06.JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply
07.(JNIEnv *env, jobject obj){
08.return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");
09.}
#include<jni.h>
#include<string.h>
#include"org_tonny_jni_JNITest.h"

JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply
(JNIEnv *env, jobject obj){
return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");
}

我們可以看到,該函數的實現相當簡單,返回一個字元串為:"Hello,JNITest"

3.4.編寫Android.mk文件
在~/project/Android/JNITest/jni目錄下新建Android.mk文件,android可以根據這個文件的編譯參數編譯模塊。編輯Android.mk文件如下:

[plain] view plainprint?
01.LOCAL_PATH:= $(call my-dir)
02.include$(CLEAR_VARS)
03.LOCAL_MODULE := libJNITest
04.LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c
05.include$(BUILD_SHARED_LIBRARY)
LOCAL_PATH:= $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := libJNITest
LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c
include$(BUILD_SHARED_LIBRARY)

LOCAL_MODULE表示編譯的動態庫名稱

LOCAL_SRC_FILES 表示源代碼文件

3.5.用ndk工具編譯並生成.so文件
進入到JNITest的工程目錄,執行ndk-build命令即可生成libJNITest.so文件。

[plain] view plainprint?
01.$cd ~/project/Android/JNITest/
02.$ndk-build
03.Invalidattribute name:
04.package
05.Install : libJNITest.so => libs/armeabi/libJNITest.so
$cd ~/project/Android/JNITest/
$ndk-build
Invalidattribute name:
package
Install : libJNITest.so => libs/armeabi/libJNITest.so
可以看到,在工程目錄的libs/armeabi目錄下生成了libJNITest.so文件。

3.6.在模擬器上運行
(1)首先,我們把android模擬器啟動起來。進入到emulator所在目錄,執行emulator命令:

[plain] view plainprint?
01.$cd ~/software/android/android-sdk-linux/tools
02.$./emulator @AVD-2.3.3-V10 -partition-size 512
$cd ~/software/android/android-sdk-linux/tools
$./emulator @AVD-2.3.3-V10 -partition-size 512
AVD-2.3.3-V10表示你的模擬器名稱,與在Eclipse->AVDManager下的AVDName對應,-partition-size表示模擬器的存儲設備容量。

(2)接下來,我們需要把libJNITest.so文件拷貝到模擬器的/system/lib目錄下,執行以下命令:

[plain] view plainprint?
01.$cd ~/project/Android/JNITest/libs/armeabi/
02.$adb remount
03.$adb push libJNITest.so /system/lib
04.80 KB/s (10084 bytes in 0.121s)
$cd ~/project/Android/JNITest/libs/armeabi/
$adb remount
$adb push libJNITest.so /system/lib
80 KB/s (10084 bytes in 0.121s)

當在終端上看到有80 KB/s (10084 bytes in 0.121s)傳輸速度等信息的時候,說明拷貝成功。

(3)在終端上執行JNITest程序,這個我們可以在Eclipse下,右鍵點擊JNITest工程,RunAs->Android Application,即可在模擬器上啟動程序

D. 如何在android的jni線程中實現回調

您好,很高興能幫助您
如果是C/C++回調,你只要參考linux的線程指南,在線程函數中傳入回調函數地址就行了。如果是要回調到Java層,稍微復雜點。
首先,你需要在onload的時候,找到回調函數所在的類,用全局變數保存:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
LOGE("JNI_OnLoad start");
jint version;
g_vm = vm; // 全局變數保存
JNIEnv *env;
jobject cls;
version = vm->GetEnv((void **)&env, JNI_VERSION_1_2);

if (env)
{
g_clazz = env->FindClass(CLASS_CustomSurfaceView); // 全局變數保存
}

LOGE("JNI_OnLoad finish g_clazz = 0x%x", g_clazz);
return JNI_VERSION_1_2;
}

在JNI啟動線程的時候,需要把線程掛到JVM上,不然不能訪問Java。你有了g_vm, g_clazz, 以及env,就可以做回調操作了。
// 線程函數

void *threadFunc(void *data)
{
JNIEnv *env = MNull;
int ret = g_vm->AttachCurrentThread( (JNIEnv **) &env, MNull); // 掛到JVM
if (ret < 0)
{
LOGE("fail to attach");
return;
}
// TODO: 在這里做你的回調操作

g_vm->DetachCurrentThread(); // 從JVM卸載
return;
}

你的採納是我前進的動力,
記得好評和採納,答題不易,互相幫助,

E. android 為什麼要使用jni

android的jni可以使用c/c++來開發,相比java而言,運行的效率提高了很多,特別是在做一些圖像演算法,或者游戲邏輯的時候,使用jni將大大的提高效率。比如某個游戲要採用opengl,同樣載入一個由1000個多邊形組成的3d模型,jni要比java運算快好幾倍,這樣就保證了游戲運行的fps不會太低。
另外一個好處就是內存管理上面,java的內存管理全部由虛擬機來管理,C++由程序員來管理,利用率上面就好多了。
等等其他優點。
既然這么多的優點,為什麼一個android程序不採用純c來開發呢?因為是android的 UI framework採用java,所以,在UI上面還是採用java來開發。

F. android ndk到底是什麼

NDK是一系列工具的集合。它提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk(AndroidPackage的縮寫,Android安裝包)。這些工具對開發者的幫助是巨大的。它集成了交叉編譯器,並提供了相應的mk文件隔離CPU、平台、ABI等差異,開發人員只需要簡單修改mk文件(指出「哪些文件需要編譯」、「編譯特性要求」等),就可以創建出so。它可以自動地將so和Java應用一起打包,極大地減輕了開發人員的打包工作。
NDK提供了一份穩定、功能有限的API頭文件聲明,Google明確聲明該API是穩定的,在後續所有版本中都穩定支持當前發布的API。從該版本的NDK中看出,這些API支持的功能非常有限,包含有:C標准庫(libc)、標准數學庫(libm)、壓縮庫(libz)、Log庫(liblog)。

G. Android開發 如何使用JNI代碼訪問 android上sdcard的文件

那是必須的,當然可以。java底層也是和c打交道的啊

閱讀全文

與android回調函數jni相關的資料

熱點內容
沒有伺服器怎麼發現其他節點 瀏覽:335
文明傳奇怎麼開伺服器 瀏覽:56
javalistint 瀏覽:675
程序員到公司當領導 瀏覽:225
用演算法控制玩家的行為 瀏覽:482
androidsdk17下載 瀏覽:792
怎麼給單獨表格添加密碼 瀏覽:12
下載壓縮密碼 瀏覽:259
android系統上編程 瀏覽:470
單片機模擬i2c從機 瀏覽:238
教育年報系統伺服器如何開啟 瀏覽:842
對稱密鑰加密後的長度 瀏覽:294
微製造編程軟體下載 瀏覽:107
旋住宿酒店用哪個App最好 瀏覽:60
三菱編程中怎麼創建子程序 瀏覽:201
在單片機溫度輸入採集信號有 瀏覽:686
電腦雲伺服器同步 瀏覽:420
方舟生存進化手游版怎麼轉伺服器 瀏覽:91
哪個app可以聽小說 瀏覽:162
網路發送數據如何加密 瀏覽:203