文章采集api(抖音爬虫教程,AndServer+Service打造Android服务器实现so文件调用)
优采云 发布时间: 2021-10-04 14:14文章采集api(抖音爬虫教程,AndServer+Service打造Android服务器实现so文件调用)
抖音爬虫教程,AndServer+Service搭建Android服务器实现so文件调用so文件调用
随着Android手机安全的飞速发展,无论是为了执行效率还是程序安全,关键代码下沉到native层已经成为一项基本操作。
native层的开发参考JNI/NDK开发。通过JNI,Java层和native层(主要是C/C++)可以相互调用。本机层编译生成so动态链接库,so文件具有可移植性广、执行效率高、保密性强等优点。
那么问题来了,如何调用so文件是极其重要的。当然你也可以直接分析so文件的伪代码,用强大的编程技巧直接模拟按键操作,不过我觉得对于普通人来说头发还是比较重要的。
目前主流的调用so文件的操作应该是: 1.基于Unicorn的各种实现(还在学习中,暂不列出) 2. Android服务器搭建,在App中启动http服务完成调用so的需求(当然前提是经过so等验证)
至于为什么选择AndServer,嗯,不是为什么,只是因为我发现了它为什么要和Service结合,我在学习Android开发的时候就了解了Service的生命周期,个人理解还是用Service来做比较好创建 Http 服务。
当然也有Application的简单使用,因为在正式环境下,so文件的大部分逻辑都有上下文的一些包名,签名验证等,如果自定义Application,就可以得到传递参数的上下文。
短视频直播数据采集接口SDK,请点击查看接口文档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新建项目
在模块的build中加入这句话,然后同步
将编译好的so文件复制到libs文件夹(对应刚才的jniLibs.srcDirs)
复制so对应的java代码,注意包名和类名的一致性
打开activity_main.xml文件,给TextView添加一个id
打开 MainActiviy.java 开始编码
这两行的意思是,先从layout中找到id对应的TextView,然后为其设置Text(调用原生函数的返回值)。让我们测试一下我们的通话情况。
可以看到我们的so文件调用成功了(这里我们的so无效,测试一下app是否可以正常调用)
AndServer代码编写
AndServer官方文档:/AndServer/ 打开官方文档,看看别人的介绍,新建一个java文件
如图,写了经典的MVC C,定义了一个nightteam_sign接口,请求方法为get,请求参数为sign,调用native函数,然后返回json,但是这里我想用Application获取上下文对象并去掉包名,然后自定义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;
}
}
然后在清单文件中指定要启动的应用程序
然后修改MyController.java的代码
接下来复制官方文档服务器的代码导入一些包,修改部分代码如下
AndServer.serverBuilder 的新版本已经需要传递上下文。这里也修改了网络地址和端口号,从构造参数中获取。至此,AndServer 的东西基本完成了。其实我们会搭建一个界面来调整so。业务逻辑比较多,所以代码最简单好用
服务代码编写
这里我们用按钮的点击事件启动Service,所以在activity_main.xml中添加一个按钮并指定点击事件
接下来编写自定义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;
}
}
我打印了几条日志,在子线程中启动了AndServer服务(什么时候使用UI线程和子线程是Android的基础,这里不再赘述)。注意,这里从 0.0.0.0 获取inetAddress,别搞错了,localhost和0.0.0.的区别0,请移至搜索引擎,然后将context、inetAddress、port传递给ServerManager的构造函数用于新建对象,然后启动服务最后注意查看manifest文件中Service的声明
打开Service,获取本地ip
回到我们MainActivity.java的操作(按钮点击事件)编写启动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;
}
}
至此,我们的服务基本设置好了,但是为了方便,我想在App上显示我们本地的IP,这样我们就不用再设置和检查了。我在网上找到了一个获取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 enumeration = null;
try {
enumeration = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
NetworkInterface nif = enumeration.nextElement();
Enumeration inetAddresses = nif.getInetAddresses();
if (inetAddresses != null)
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (!inetAddress.isLoopbackAddress() && isIPv4Address(inetAddress.getHostAddress())) {
return inetAddress;
}
}
}
}
return null;
}
}
将工具类复制到我们的 Android 项目中,并在 MainActivity.java 中继续编码
获取本地地址和Android SDK版本(Android 8之后启动Service的方式不同)
申请权限,启动App
最后一步是为应用申请网络权限
然后连接我们的手机,运行项目,测试一下,点击启动服务
查看AndroidStudio日志
好像一切正常,在浏览器访问下试试(ip是App中显示的ip地址)
如图,我们访问到了我们想要的内容。回去再说Service,打开我们手机的设置,找到应用管理-运行服务(不同的手机,不同的方法)
可以看到我们的程序运行了一个服务,这个服务就是我们编码的MyService
接下来,杀死App进程并再次检查正在运行的服务
这里我在权限管理中设置了自动运行,可以保持服务运行。(这个地方还是根据系统大小不同) 到此为止,可以使用App启动http服务并调整so。