您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“Android服務(wù)器怎么實(shí)現(xiàn)so文件調(diào)用”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Android服務(wù)器怎么實(shí)現(xiàn)so文件調(diào)用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。
native 層的開發(fā)就是通指的 JNI/NDK 開發(fā),通過(guò) JNI 可以實(shí)現(xiàn) java 層和 native 層(主要是 C/C++ )的相互調(diào)用,native 層經(jīng)編譯后產(chǎn)生 so 動(dòng)態(tài)鏈接庫(kù),so 文件具有可移植性廣,執(zhí)行效率高,保密性強(qiáng)等優(yōu)點(diǎn)。
那么問(wèn)題來(lái)了,如何調(diào)用 so 文件顯得異常重要,當(dāng)然你也可以直接分析 so 文件的偽代碼,利用強(qiáng)悍的編程功底直接模擬關(guān)鍵操作,但是我想對(duì)于普通人來(lái)說(shuō)頭發(fā)還是比較重要的。
當(dāng)前調(diào)用 so 文件的主流操作應(yīng)該是:
1,基于 Unicorn 的各種實(shí)現(xiàn)(還在學(xué)習(xí)中,暫且不表)
2,Android 服務(wù)器的搭建,在 App 內(nèi)起 http 服務(wù)完成調(diào)用 so 的需求(當(dāng)然前提是過(guò)了 so 的效驗(yàn)等操作)
至于為什么選用 AndServer,好吧,不為什么,只是因?yàn)樗阉鞯搅怂?
為什么結(jié)合 Service,在學(xué)習(xí) Android 開發(fā)的時(shí)候了解到了 Service 的生命周期,個(gè)人理解用 Service 去創(chuàng)建 Http 服務(wù)比較好。
當(dāng)然也有 Application 的簡(jiǎn)單使用,因?yàn)樵谡江h(huán)境中,大多數(shù) so 文件的邏輯中都有 context 的一些包名了,簽名了的效驗(yàn)等,自定義 Application 的話獲取 context 傳參就好了。
這是我編譯好的一個(gè) so 文件,就是根據(jù)入?yún)⒆鱿潞?jiǎn)單的字符串拼接(以下是 native 層編譯前的 c 代碼)
extern "C"JNIEXPORT jstring JNICALLJava_com_fw_myapplication_ndktest_NdkTest_stringFromUTF(JNIEnv *env, jobject instance, jstring str_) { jclass String_clazz = env->FindClass("java/lang/String"); jmethodID concat_methodID = env->GetMethodID(String_clazz, "concat", "(Ljava/lang/String;)Ljava/lang/String;"); jstring str = env->NewStringUTF(" from so --[NightTeam夜幕]"); jobject str1 = env->CallObjectMethod(str_, concat_methodID, str); const char *chars = env->GetStringUTFChars((jstring)str1, 0); return env->NewStringUTF(chars);}
這部分代碼還是有必要貼一下的,簡(jiǎn)單的靜態(tài)注冊(cè)使用了反射的思想,反射在逆向中至關(guān)重要
接下來(lái)是 java 代碼,定義了 native 函數(shù)
package com.fw.myapplication.ndktest;public class NdkTest { public static native String stringFromUTF(String str); static { System.loadLibrary("yemu"); }}
如果到這里有點(diǎn)懵逼的同學(xué)可能需要去補(bǔ)下 Android 開發(fā)基礎(chǔ)了
先說(shuō)下我的環(huán)境,因?yàn)檫@個(gè)環(huán)境影響太大了
1,AndroidStudio 3.4
2,手機(jī) Android 6 架構(gòu) armeabi-v7a
打開 AndroidStudio 新建 project
cdn.nlark.com/yuque/0/2020/webp/97322/1607479407035-f0bfc349-9792-4b75-be4b-0b2c52f2cabc.webp?x-oss-process=image/watermark,type_d3F5LW1pY3JvaGVp,size_10,text_dGl0b2RhdGEuY29t,color_FFFFFF,shadow_50,t_80,g_se,x_10,y_10">
在 module 的 build 中加這么一句,然后 sync
把編譯好的 so 文件復(fù)制到 libs 文件夾下(和剛才的 jniLibs.srcDirs 對(duì)應(yīng))
把 so 對(duì)應(yīng)的 java 代碼也 copy 過(guò)來(lái),注意包名類名的一致性
打開 activity_main.xml 文件為 TextView 添加 id
打開 MainActiviy.java 開始編碼
這兩行的意思就是,先從布局中找到對(duì)應(yīng) id 的 TextView,然后為其設(shè)置 Text(調(diào)用 native 函數(shù)的返回值)
下面測(cè)試一下咱們的 so 調(diào)用情況
可以看到咱們的 so 文件調(diào)用成功(這里咱們的 so 沒(méi)有效驗(yàn),只是測(cè)試 app 是否可以正常調(diào)用)
AndServer 官方文檔:https://yanzhenjie.com/AndServer/
打開官方文檔,看看人家的入門介紹,新建 java 文件
如圖經(jīng)典 MVC 的 C 就寫好了,定義了一個(gè) nightteam_sign 接口,請(qǐng)求方式為 get,請(qǐng)求參數(shù)為 sign,調(diào)用 native 函數(shù),然后返回 json,但是這里我想利用 Application 獲取下 context 對(duì)象,取下包名,接下來(lái)自定義 Applictaion
package com.nightteam.httpso;import android.app.Application;public class MyApp extends Application { private static MyApp myApp; public static MyApp getInstance() { return myApp; } @Override public void onCreate() { super.onCreate(); myApp = this; }}
然后在 manifest 文件中指定要啟動(dòng)的 Application
然后修改 MyController.java 的代碼
接下來(lái)把官方文檔-服務(wù)器的代碼 copy 下來(lái)
導(dǎo)入一些包,修改部分代碼如下
新版本的 AndServer.serverBuilder 已經(jīng)需要傳遞 context 了,這里把網(wǎng)絡(luò)地址和端口號(hào)也修改為從構(gòu)造參數(shù)中獲取,到這里 AndServer 的東西基本完了,實(shí)際上咱們就搭建一個(gè)調(diào) so 的接口,并沒(méi)有過(guò)多的業(yè)務(wù)邏輯,所以代碼就是使用的最簡(jiǎn)單的
咱們這里用按鈕的點(diǎn)擊事件啟動(dòng) Service,故在 activity_main.xml 中添加一個(gè) button 并指定點(diǎn)擊事件
接下來(lái)編寫自定義 Service 代碼
package com.nightteam.httpso.Service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;import com.nightteam.httpso.ServerManager;import java.net.InetAddress;import java.net.UnknownHostException;public class MyService extends Service { private static final String TAG = "NigthTeam"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate: MyService"); new Thread() { @Override public void run() { super.run(); InetAddress inetAddress = null; try { inetAddress = InetAddress.getByName("0.0.0.0"); Log.d(TAG, "onCreate: " + inetAddress.getHostAddress()); ServerManager serverManager = new ServerManager(getApplicationContext(), inetAddress, 8005); serverManager.startServer(); } catch (UnknownHostException e) { e.printStackTrace(); } } }.start(); } @Override public IBinder onBind(Intent intent) { return null; }}
打上了幾個(gè) log,在子線程中啟動(dòng) AndServer 的服務(wù)(何時(shí)使用 UI 線程和子線程是 Android 基礎(chǔ),這里就不贅述了)
注意一下,這里從 0.0.0.0 獲取 inetAddress,可不要寫錯(cuò)了,localhost 和 0.0.0.0 的區(qū)別請(qǐng)移步搜索引擎
然后就是向 ServerManager 的構(gòu)造函數(shù)傳遞 context,inetAddress,port 用來(lái) new 對(duì)象,隨后開啟服務(wù)
最后注意檢查下 manifest 文件中 Service 的聲明
回到我們的 MainActivity.java 的 operate( button 的點(diǎn)擊事件)編寫啟動(dòng) Service 代碼
public void operate(View view) { switch (view.getId()){ case R.id.id_bt_index: //啟動(dòng)服務(wù):創(chuàng)建-->啟動(dòng)-->銷毀 //如果服務(wù)已經(jīng)創(chuàng)建了,后續(xù)重復(fù)啟動(dòng),操作的都是同一個(gè)服務(wù),不會(huì)再重新創(chuàng)建了,除非你先銷毀它 Intent it1 = new Intent(this, MyService.class); Log.d(TAG, "operate: button"); startService(it1); ((Button) view).setText("服務(wù)已開啟"); break; } }
到這里我們的服務(wù)基本搭建好了,但是為了方便起見,我想把咱們的本機(jī) ip 顯示在 App 上,這樣我們就不用去設(shè)置再查看了
我在網(wǎng)上找到了一個(gè)獲取 ip 地址的一個(gè)工具類,源碼如下:
package com.nightteam.httpso;import java.net.InetAddress;import java.net.NetworkInterface;import java.net.SocketException;import java.util.Enumeration;import java.util.regex.Pattern;public class NetUtils { private static final Pattern IPV4_PATTERN = Pattern.compile("^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"); private static boolean isIPv4Address(String input) { return IPV4_PATTERN.matcher(input).matches(); } //獲取本機(jī)IP地址 public static InetAddress getLocalIPAddress() { Enumeration<NetworkInterface> enumeration = null; try { enumeration = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } if (enumeration != null) { while (enumeration.hasMoreElements()) { NetworkInterface nif = enumeration.nextElement(); Enumeration<InetAddress> inetAddresses = nif.getInetAddresses(); if (inetAddresses != null) while (inetAddresses.hasMoreElements()) { InetAddress inetAddress = inetAddresses.nextElement(); if (!inetAddress.isLoopbackAddress() && isIPv4Address(inetAddress.getHostAddress())) { return inetAddress; } } } } return null; }}
把工具類 copy 到我們的 Android 項(xiàng)目中,繼續(xù)在 MainActivity.java 中編碼
獲取了一下本機(jī)地址和 Android SDK 版本( Android 8 之后啟動(dòng) Service 方式不一樣)
最后一步就是為 app 申請(qǐng)網(wǎng)絡(luò)權(quán)限了
隨后連接我們的手機(jī),運(yùn)行項(xiàng)目,測(cè)試一下,點(diǎn)擊開啟服務(wù)
看下 AndroidStudio 日志
好像一切正常,在瀏覽器訪問(wèn)下試試( ip 就是 App 中顯示的 ip 地址)
如圖正常訪問(wèn)到了我們想要的內(nèi)容
回過(guò)頭來(lái)說(shuō)下 Service,打開我們手機(jī)的設(shè)置,找到應(yīng)用程序管理-運(yùn)行中的服務(wù)(手機(jī)不同,方式不同)
可以看到我們的程序,運(yùn)行了一個(gè)服務(wù),這個(gè)服務(wù)就是咱們編碼的 MyService
接下來(lái)殺掉該 App進(jìn)程,再次查看運(yùn)行中的服務(wù)
讀到這里,這篇“Android服務(wù)器怎么實(shí)現(xiàn)so文件調(diào)用”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。