共计 5441 个字符,预计需要花费 14 分钟才能阅读完成。
so 文件调用
随着 Android 挪动平安的高速倒退,不论是为了执行效率还是程序的安全性等,要害代码下沉 native 层已成为基本操作。
native 层的开发就是通指的 JNI/NDK 开发,通过 JNI 能够实现 java 层和 native 层(次要是 C/C++)的互相调用,native 层经编译后产生 so 动态链接库,so 文件具备可移植性广,执行效率高,保密性强等长处。
那么问题来了,如何调用 so 文件显得异样重要,当然你也能够间接剖析 so 文件的伪代码,利用强悍的编程功底间接模仿要害操作,然而我想对于普通人来说头发还是比拟重要的。
以后调用 so 文件的支流操作应该是:
1,基于 Unicorn 的各种实现(还在学习中,暂且不表)
2,Android 服务器的搭建,在 App 内起 http 服务实现调用 so 的需要(当然前提是过了 so 的效验等操作)
至于为什么选用 AndServer,好吧,不为什么,只是因为搜寻到了它
为什么联合 Service,在学习 Android 开发的时候理解到了 Service 的生命周期,集体了解用 Service 去创立 Http 服务比拟好。
当然也有 Application 的简略应用,因为在正式环境中,大多数 so 文件的逻辑中都有 context 的一些包名了,签名了的效验等,自定义 Application 的话获取 context 传参就好了。
libyemu.so 简介
这是我编译好的一个 so 文件,就是依据入参做下简略的字符串拼接(以下是 native 层编译前的 c 代码)
extern "C"
JNIEXPORT jstring JNICALL
Java_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);
}
这部分代码还是有必要贴一下的,简略的动态注册应用了反射的思维,反射在逆向中至关重要
接下来是 java 代码,定义了 native 函数
package com.fw.myapplication.ndktest;
public class NdkTest {public static native String stringFromUTF(String str);
static {System.loadLibrary("yemu");
}
}
如果到这里有点懵逼的同学可能须要去补下 Android 开发根底了
Android 我的项目测试 so
先说下我的环境,因为这个环境影响太大了
1,AndroidStudio 3.4
2,手机 Android 6 架构 armeabi-v7a
关上 AndroidStudio 新建 project
在 module 的 build 中加这么一句,而后 sync
把编译好的 so 文件复制到 libs 文件夹下(和方才的 jniLibs.srcDirs 对应)
把 so 对应的 java 代码也 copy 过去,留神包名类名的一致性
关上 activity_main.xml 文件为 TextView 增加 id
关上 MainActiviy.java 开始编码
这两行的意思就是,先从布局中找到对应 id 的 TextView,而后为其设置 Text(调用 native 函数的返回值)
上面测试一下咱们的 so 调用状况
能够看到咱们的 so 文件调用胜利(这里咱们的 so 没有效验,只是测试 app 是否能够失常调用)
AndServer 代码编写
AndServer 官网文档:https://yanzhenjie.com/AndServer/
关上官网文档,看看人家的入门介绍,新建 java 文件
如图经典 MVC 的 C 就写好了,定义了一个 nightteam_sign 接口,申请形式为 get,申请参数为 sign,调用 native 函数,而后返回 json,然而这里我想利用 Application 获取下 context 对象,取下包名,接下来自定义 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 文件中指定要启动的 Application
而后批改 MyController.java 的代码
接下来把官网文档 - 服务器的代码 copy 下来
导入一些包,批改局部代码如下
新版本的 AndServer.serverBuilder 曾经须要传递 context 了,这里把网络地址和端口号也批改为从结构参数中获取,到这里 AndServer 的货色根本完了,实际上咱们就搭建一个调 so 的接口,并没有过多的业务逻辑,所以代码就是应用的最简略的
Service 代码编写
咱们这里用按钮的点击事件启动 Service,故在 activity_main.xml 中增加一个 button 并指定点击事件
接下来编写自定义 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;}
}
打上了几个 log,在子线程中启动 AndServer 的服务(何时应用 UI 线程和子线程是 Android 根底,这里就不赘述了)
留神一下,这里从 0.0.0.0 获取 inetAddress,可不要写错了,localhost 和 0.0.0.0 的区别请移步搜索引擎
而后就是向 ServerManager 的构造函数传递 context,inetAddress,port 用来 new 对象,随后开启服务
最初留神查看下 manifest 文件中 Service 的申明
开启 Service,并获取本机 ip
回到咱们的 MainActivity.java 的 operate(button 的点击事件)编写启动 Service 代码
public void operate(View view) {switch (view.getId()){
case R.id.id_bt_index:
// 启动服务: 创立 --> 启动 --> 销毁
// 如果服务曾经创立了,后续反复启动,操作的都是同一个服务,不会再从新创立了,除非你先销毁它
Intent it1 = new Intent(this, MyService.class);
Log.d(TAG, "operate: button");
startService(it1);
((Button) view).setText("服务已开启");
break;
}
}
到这里咱们的服务根本搭建好了,然而为了不便起见,我想把咱们的本机 ip 显示在 App 上,这样咱们就不必去设置再查看了
我在网上找到了一个获取 ip 地址的一个工具类,源码如下:
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();}
// 获取本机 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 我的项目中,持续在 MainActivity.java 中编码
获取了一下本机地址和 Android SDK 版本(Android 8 之后启动 Service 形式不一样)
申请权限,启动 App
最初一步就是为 app 申请网络权限了
随后连贯咱们的手机,运行我的项目,测试一下,点击开启服务
看下 AndroidStudio 日志
如同一切正常,在浏览器拜访下试试(ip 就是 App 中显示的 ip 地址)
如图失常拜访到了咱们想要的内容
回过头来说下 Service,关上咱们手机的设置,找到应用程序治理 - 运行中的服务(手机不同,形式不同)
能够看到咱们的程序,运行了一个服务,这个服务就是咱们编码的 MyService
接下来杀掉该 App 过程,再次查看运行中的服务
我这里在权限治理设置了主动运行,能够放弃服务的运行。(这个中央还是依据零碎有大小差别)
至此应用 App 起 http 服务调 so 就实现了
TiToData:业余的短视频、直播数据接口服务平台。
更多信息请分割:TiToData
笼罩支流平台:抖音,快手,小红书,TikTok,YouTube