首页 文章 精选 留言 我的

精选列表

搜索[webpack入门],共10000篇文章
优秀的个人博客,低调大师

ios入门之消息推送详解

前言 在去年的苹果大会上,苹果带来的iOS 10 系统中将之前繁杂的推送通知统一成UserNotifications.framework 来集中管理和使用通知功能,还增加一些实用的功能——撤回单条通知、更新已展示通知、中途修改通知内容、在通知中显示多媒体资源、自定义UI等功能。那么在ios10之前,ios的消息推送是怎么分类的呢? ios 10之前 在ios之前,iOS推送分为Local Notifications(本地推送) 和 Remote Notifications(远程推送)。 本地推送 不需要服务器支持(无需联网)就能发出的推送通知,app本地创建通知,加入到系统的Schedule里,如果触发器条件达成时会推送相应的消息内容,如常见的定时任务闹钟等。 使用上也是非常简单。 /* @property(nonatomic,copy) NSDate *fireDate; @property(nonatomic,copy) NSTimeZone *timeZone; 时区 @property(nonatomic) NSCalendarUnit repeatInterval; 重复间隔(枚举) @property(nonatomic,copy) NSCalendar *repeatCalendar; 重复日期(NSCalendar) @property(nonatomic,copy) CLRegion *region 设置区域(设置当进入某一个区域时,发出一个通知) @property(nonatomic,assign) BOOL regionTriggersOnce YES,只会在第一次进入某一个区域时发出通知.NO,每次进入该区域都会发通知 @property(nonatomic,copy) NSString *alertBody; @property(nonatomic) BOOL hasAction; 是否隐藏锁屏界面设置的alertAction @property(nonatomic,copy) NSString *alertAction; 设置锁屏界面一个文字 @property(nonatomic,copy) NSString *alertLaunchImage; 启动图片 @property(nonatomic,copy) NSString *alertTitle @property(nonatomic,copy) NSString *soundName; @property(nonatomic) NSInteger applicationIconBadgeNumber; @property(nonatomic,copy) NSDictionary *userInfo; // 设置通知的额外的数据 */ - (IBAction)addLocalNote:(id)sender { // 创建一个本地通知 UILocalNotification *localNote = [[UILocalNotification alloc] init]; // 设置本地通知的一些属性(通知发出的时间/通知的内容) // 设置通知发出的时间 localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0]; //设置通知的内容 localNote.alertBody = @"吃饭了吗?"; //设置锁屏界面的文字 localNote.alertAction = @"查看具体的消息"; //设置锁屏界面alertAction是否有效 localNote.hasAction = YES; //设置通过点击通知打开APP的时候的启动图片(无论字符串设置成什么内容,都是显示应用程序的启动图片) localNote.alertLaunchImage = @"111"; //设置通知中心通知的标题 localNote.alertTitle = @"222222222222"; //设置音效 localNote.soundName = @"buyao.wav"; //设置应用程序图标右上角的数字 localNote.applicationIconBadgeNumber = 1; //设置通知之后的属性 localNote.userInfo = @{@"name" : @"张三", @"toName" : @"李四"}; //调度通知 [[UIApplication sharedApplication] scheduleLocalNotification:localNote]; } 当用户点击本地推送通知的时候,会自动打开app,这里有2种情况:app在后台运行,或者被系统进程杀死,对于这两种情况,我们怎么处理呢? app后台运行 这时候我们只需要调用下AppDelegate方法即可。代码实现 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { // 跳转逻辑 if (application.applicationState == UIApplicationStateActive) return; if (application.applicationState == UIApplicationStateInactive) { // 当应用在后台收到本地通知时执行的跳转代码 [self jumpToSession]; } NSLog(@"local notifacation %@", notification); } - (void)jumpToSession { UILabel *redView = [[UILabel alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(0, 100, 300, 400); redView.numberOfLines = 0; // redView.text = [NSString stringWithFormat:@"%@", launchOptions]; [self.window.rootViewController.view addSubview:redView]; } app被杀死 对于app被杀死的情况,要先启动app,启动完毕会调用AppDelegate方法。需要特别注意的是:在iOS8.0以后本地通知有了一些变化,如果要使用本地通知,需要得到用户的许可。部分代码实现: #define IS_iOS8 ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* UIUserNotificationTypeNone = 0, 不发出通知 UIUserNotificationTypeBadge = 1 << 0, 改变应用程序图标右上角的数字 UIUserNotificationTypeSound = 1 << 1, 播放音效 UIUserNotificationTypeAlert = 1 << 2, 是否运行显示横幅 */ [application setApplicationIconBadgeNumber:0]; if (IS_iOS8) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil]; [application registerUserNotificationSettings:settings]; } // 如果是正常启动应用程序,那么launchOptions参数是null,其他方式需要对launchOptions设置 if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) { // 当被杀死状态收到本地通知时执行的跳转代码 // [self jumpToSession]; UILabel *redView = [[UILabel alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(0, 100, 300, 400); redView.numberOfLines = 0; redView.text = [NSString stringWithFormat:@"%@", launchOptions]; [self.window.rootViewController.view addSubview:redView]; } return YES; } 远程推送 远程推送指从远程服务器推送给客户端的通知(需要联网),远程推送服务一般采用苹果的APNS (Apple Push Notification Service)。 要实现远程推送,一般会涉及到三个阶段: APNS Pusher应用程序把要发送的消息、目的iPhone的标识打包,发给APNS。 APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone。 iPhone把发来的消息传递给相应的应用程序, 并且按照设定弹出Push通知。 基本配置 条件:新建一个对应你bundle的push 证书,打开Push Notifications 开关(XCode7不打开也可以正常使用,XCode8以后必须打开)。 代码实现:注册接受APNs通知。 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { // 1.注册UserNotification,以获取推送通知的权限 UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil]; [application registerUserNotificationSettings:settings]; // 2.注册远程推送 [application registerForRemoteNotifications]; } else { [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeNewsstandContentAvailability | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound]; } return YES; } 调用AppDelegate方法,获取到用户的deviceToken。 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // <32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea> // <32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea> NSLog(@"%@", deviceToken.description); } 推送通知,和本地通知一样有两种状况。 // 当接受到远程退职时会执行该方法(当进入前台或者应用程序在前台) - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"%@", userInfo); UIView *redView = [[UIView alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(100, 100, 100, 100); [self.window.rootViewController.view addSubview:redView]; } 苹果建议使用方法 /* 1.开启后台模式 2.调用completionHandler,告诉系统你现在是否有新的数据更新 3.userInfo添加一个字段:"content-available" : "1" : 只要添加了该字段,接受到通知都会在后台运行 */ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"%@", userInfo); UIView *redView = [[UIView alloc] init]; redView.backgroundColor = [UIColor redColor]; redView.frame = CGRectMake(100, 100, 100, 100); [self.window.rootViewController.view addSubview:redView]; completionHandler(UIBackgroundFetchResultNewData); } UserNotitfication iOS10 中统一了本地推送和远程推送的 API,在 UserNotifications.framework 来统一处理与推送相关任务,并增加了图片、音频、视频,自定义通知 UI 等新特性。 通知界面 多媒体 在此次版本中,iOS10 不仅新增消息的3dtouch等,还对图片、音频、视频等多媒体做了改进和优化。 类型 限制大小 图片 10M 音频 5M 视频 50M 多媒体推送代码: if #available(iOS 10.0, *) { let content = UNMutableNotificationContent() content.title = "iOS10 推送测试" content.body = "附件" content.userInfo = ["icon":"1","mutable-content":1] content.categoryIdentifier = "InputSomething" let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false) let requestIdentifier = "imageLocal" if let imageURL = Bundle.main.url(forResource: "avatar@2x", withExtension: "png"), let attachment = try? UNNotificationAttachment(identifier: "imageAttachment", url: imageURL, options: nil) { content.attachments = [attachment] } let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger) UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in if (error != nil) { print("error: \(error.debugDescription)") } }) } 通常在做多媒体自定义推送的时候,一般会用到UNNotificationServiceExtension应用扩展,通过在 payload 中增加 mutable-content 字段来触发扩展。 { "aps":{ "alert":"IOS10 推送测试", "sound":"default", "badge":1, "mutable-content":1, "category":"InputSomething" }, "image":"https://ws1.sinaimg.cn/mw690/934b5ef8gw1fapg2ssteej20oz0oz420.jpg" } 当推送达到 app 时,会启动扩展并回调 didReceive 方法。在该方法里面可以对推送的 UNMutableNotificationContent 做出相应的修改。在 didReceive 回调方法中的 request 包含了推送的具体信息,可以通过其 userInfo 属性来解析出多媒体的 url。 let imageURL = Bundle.main.url(forResource: "lufei", withExtension: "jpg") 值得注意的是这里 Bundle 指的是扩展的沙盒,不是 app 的沙盒,所以资源的路径要正确。 而读取远程资源比读取本地资源一般要多一步保存操作。 private func downloadAndSave(url: URL, handler: @escaping (_ localURL: URL?) -> Void) { let task = URLSession.shared.dataTask(with: url, completionHandler: { data, res, error in var localURL: URL? = 下载完之后保存到本地并返回本地的 url handler(localURL) }) task.resume() } 得到本地的 url 之后操作就一样了,都是通过 url 来生成一个 UNNotificationAttachment 对象。一切都操作完之后将这个 UNMutableNotificationContent 对象返还 contentHandler(bestAttemptContent)。 自定义界面 除了上述功能外,苹果还新增了自定义界面,这个绝对是大招。 其中上面的黄色区域可以理解成一个 ViewController 操作,下面绿色部分就是 Title 之类的显示内容。这部分是可以隐藏的。在扩展的目录下的 info.plist 编辑一些界面相关的东西。 说明: UNNotificationExtensionCategory 触发 Extension 的 category 这里需要在注册才能有效的触发 字符串类型 UNNotificationExtensionInitialContentSizeRatio 上图黄色区域的长宽比,float 类型 UNNotificationExtensionDefaultContentHidden 默认内容是否隐藏,Bool 类型 参考:ios 10.0之UserNotitfication

优秀的个人博客,低调大师

docker入门安装与简单使用

快速了解 (一) Docker 是什么 官方给出的定义是:Docker是一个集开发,打包,运行应用于一体的开放式平台。Docker可以用来快速交付应用。使用Docker,你可以将应用程序从你的基础设施中分离出来,并将基础设施当做一个管理平台。Docker可以加快打包时间,加快测试,加快发布,缩短开发及运行代码之间的周期。Docker通过结合内核容器化特点和工作流,并使之工具化来实现这一切,帮助管理和发布你的应用。 (二) Docker平台 Docker的生态环境正逐渐壮大起来,Docker的核心,提供了一种运行大部分应用程序的方式,并安全地隔离在容器中。隔离性和安全性允许你在主机上同时运行多个容器。容器天生的轻量级特性,使其在运行时无需加载额外的虚拟机管理器,这意味着你可以从硬件中获得更多东西。 容器周边是一个工具化的平台,可以从以下几个方面帮助你: 将你的应用(或支持的组件)置于Docker容器中 分发和打包这些容器,以便将来用于开发和测试 将这些应用部署至生产环境,无论是数据中心还是公有云环境。 (三) Docker 引擎 Docker Engine是一个C/S架构的应用程序,主要有以下组件: 服务端守护进程 REST API接口,程序用来与守护进程进行交互 CLI(command line interface)命令行接口客户端 Docker Engine Components 注:Docker 引擎 组成(图片来源于docs.docker.com) CLI使用脚本或直接使用CLI命令,利用Docker的REST API来控制Docker守护进程或与之进行交互。其他许多Docker应用程序使用底层API和CLI。 守护进程创建并管理Docker对象,Docker对象包括镜像,容器,网络,数据卷等等。 (四) Docker可以干什么 快速交付应用 便捷的部署和调整 高密集度,高负载 (五) Docker 重要概念 镜像(images) Docker镜像是一个只读的模板。包含了容器运行时所需要的文件系统和一些参数。镜像是无状态的,也不会改变。镜像是用来创建容器的。你可以使用docker pull命令获取一个别人已创建好的镜像,或者使用dockerbuild来构建一个自己的镜像。 容器(containers) Docker容器就像是一个文件夹,容器中包含了应用运行所需的一切。每个容器都是一个隔离的和安全的应用平台。容器是镜像的一个实例,它是有状态的,而且随时会改变,容器一般是短暂的。 启动一个容器的过程如下: 检查指定的镜像是否存在 如果不存在,则去Docker Hub下载 加载镜像并运行 Docker注册中心(Docker Registries): Docker注册中心是托管Docker镜像的地方,有公共的和私有的,你可以往这些注册中心上传或下载镜像。 Docker Hub是Docker官方的公共镜像仓库,这里汇集了非常多的常用镜像,我们可以直接从这里下载使用,也可以自己制作自己的镜像,并分享给别人。 docker ubuntu14.04安装 编者按:docker的发展非常迅速,apt源的更新往往比较滞后。所以docker官网推荐的安装方式都是下载docker安装脚本安装。 依赖关系: Ubuntu 14.04版本无需安装额外的依赖包,可以直接安装。 安装步骤: 使用管理员帐号登录ubuntu 14.04系统,保证该管理有root权限,或者可以执行sudo命令。 检查curl包有没有安装。 $ which curl 如果curl没有安装的话,更新apt源之后,安装curl包。 $ sudo apt-get update $ sudo apt-get install curl 获得最新的docker安装包。 $ curl -sSL https://get.docker.com/ | sh shell会提示你输入sudo的密码,然后开始执行安装过程。 确认Docker是否安装成功。 $ sudo docker run hello-world 这个命令会下载一个测试用的镜像并启动一个容器运行它。 这时候本底没有镜像 docker会去Docker注册中心下载 再 执行 docker run hello-world 运行镜像 注意:docker要调用底层库需要 sudo权限 可以执行 sudo -i后 就切换到root了 docker run hello-world 接着简单描述了一下产生该条信息所经历的一系列步骤: 1 Docker客户端连接到守护进程 2 Docker守护进程从Docker Hub拉取了hello-world镜像 3 Docker守护进程从该镜像创建了一个容器,来执行了命令并输出了我们当前看的这条消息 4 Docker守护进程将容器中的标准输出转发到你的终端。 另一种校验方式 sudo service docker status 运行结果: $ sudo service docker status docker start/running, process 10692 国情需要 1、配置Daocloud,去官网申请一个账户 2、配置 Docker 加速器 将 –registry-mirror 加入到你的 Docker 配置文件 /etc/default/docker 中 3、重启docker sudo service docker restart docker简单几个命令 启动docker服务 $ sudo service docker start 或者 $ sudo systemctl start docker 设置开机启动 $ sudo systemctl enable docker 添加一个docker组(避免每次执行docker命令都带sudo) 注:1.12版本的docker 安装完后已经创建好了docker分组,检查是否已存在:cat /etc/group |grep docker,如果你的没有,则手动添加一下: $ sudo groupadd docker 将当前用户加入docker组 $ sudo usermod -aG docker $USER 编辑/etc/default/grub,修改GRUB_CMDLINE_LINUX的值如下: GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1" 保存并退出。重启系统 检查版本 $ docker -v Docker version 1.12.0, build 8eab29e 升级 后续如果有新的版本更新,直接使用apt-get即可: $ sudo apt-get upgrade docker-engine 卸载 在卸载或删除Docker数据时,一定要检查确保需要的数据已做好备份! 卸载Docker: $ sudo apt-get purge docker-engine $ sudo apt-get autoremove --purge docker-engine 上述命令只是卸载了docker程序,并不会删除数据(镜像,容器,数据卷或你在主机上的配置文件等),如果要将这些也全部删除,执行: $ sudo rm -rf /var/lib/docker 然后手动删除你创建的配置文件即可。 注: 如果你有创建docker组,并且该组也不再需要,则可以将该组删除: $ sudo groupdel docker 可以看一下目前所有的容器: docker ps -a 查看正在运行的容器: docker ps 查看最后创建的一个容器。 docker ps -l 查看本地镜像 docker images 在线查找镜像 如果你不知道都有哪些可用的镜像,在线查找一下 直接使用docker search + [你想查找的镜像关键字],如: $ docker search tomcat 查看 tomcat的镜像 拉取镜像 使用docker pull [镜像名称[:版本]]可以拉取一个镜像: 注: 完整的镜像名称由[repository]/name:[tag]构成,[repository]为仓库,如果省略,则默认为library,即从Docker Hub拉取;如果省略tag,则默认拉取latest版本,name为镜像名称,不能省略。 pull tomcat:7 创建容器 docker run -d --name tomcat001 -p 8081:8080 tomcat:8 其中,-d 指定容器在后台运行;–name用于指定容器的名称;-p 8081:8080 用于将容器内的8080端口映射到主机的8081端口,你也可以换成其他本机端口.如果需要,你可以创建多个容器使用,需要注意的是,本机映射的端口不能重复,name也不能重复。 容器日志 docker logs -f tomcat001 停止容器 docker stop tomcat001 启动容器 docker start tomcat001 docker run 该命令包括了 docker create 和 docker start 删除容器 删除一个容器前,需要先停止容器,否则,需要使用 -f选项强制删除(不推荐)。 另外,如果容器包含数据卷的话,可以使用-v选项连同关联的数据卷一块删除,删除容器时,默认不删除数据卷,因为数据卷通常会包含我们的数据。 docker rm tomcat001 删除镜像 docker rmi tomcat:8 //删除一个镜像前,需要先停止使所有用它的容器,否则会报错,或者使用 -f选项强制删除。 简单实践-跑一个tomcat 1、拉取tomcat的镜像 docker pull tomcat:8 2、查看一下镜像是否拉取成功 docker images 3、创建并且启动容器 docker run -d --name t8 -p 8081:8080 tomcat:8 //最简单的使用数据卷的方式就是在创建一个容器时 增加-v选项 4、查看容器日志 docker logs t8 5、浏览器访问tomcat8 http://宿主机ip:映射端口/ 6、关闭容器 docker stop t8 7、启动容器 docker start t8 8、查看启动了的容器 docker ps 9、创建目录 //创建目录 sudo mkdir -p /opt/data/myapp/ROOT sudo chown -R $USER:root /opt/data/myapp // $USER 当前登录用户名 //在/opt/data/myapp/ROOT下创建一个WEB-INF目录 mkdir /opt/data/myapp/ROOT/WEB-INF 10、新建一个web.xml //新建一个web.xml vi /opt/data/myapp/ROOT/WEB-INF/web.xml <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app> 11、新建一个jsp页面 新建一个jsp页面 vi /opt/data/myapp/ROOT/index.jsp <!DOCTYPE html> <html lang="en"> </body> Hello, this is my first docker app! </body> </html> 12、将我们的应用挂载到容器中 docker run -d --name myapp -v /opt/data/myapp:/usr/local/tomcat/webapps -p 8888:8080 tomcat:8 13浏览器访问 http://宿主机ip:8888/ 打造自己的开发环境镜像 1 、新建一个文件夹,并命名为baseos mkdir -p /opt/data/baseos 该目录作为我们构建镜像的上下文,构建该镜像的所有内容都会放到该目录下。 2、创建Dockerfile文件 touch Dockerfile //该命令会创建一个空白文本文档 3、编辑Dockerfile vim Dockerfile 写入 # Base os image FROM ubuntu:16.04 MAINTAINER your_name <your_email_address> LABEL Description="This image is the base os images." Version="1.0" # reconfig timezone RUN echo "Asia/Beijing" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata 说明 以#开头的行为注释; 第二行FROM关键字告诉docker我们的新镜像基于哪个基础镜像; 第三行MAINTAINER关键字是维护者信息,可以修改为你的名字和邮箱地址; 第四行LABEL Description是对镜像的一个简单说明,Version指明该镜像的版本号,方便维护; 第六行RUN关键字用于执行一条命令。这里主要是修改系统的时区从标准UTC改为亚洲/Beijing。 4、下载jdk–》移动到Dockerfile所在目录 5、修改Dockerfile文件 # Base os image FROM ubuntu:16.04 MAINTAINER your_name <your_email_address> LABEL Description="This image is the base os images." Version="1.0" # reconfig timezone RUN echo "Asia/Beijing" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata ADD jdk-7u80-linux-x64.tar.gz /usr/lib/jvm RUN update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.7.0_80/bin/java 300 && update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.7.0_80/bin/javac 300 ENV JAVA_HOME /usr/lib/jvm/jre 6构建镜像、执行docker build -t cjn/baseos:1.0 .命令构建,注意不要忘了最后的那个点. 说明: -t, –tag=”” Repository name (and optionally a tag) to be applied to the resulting image in case of success 7、测试结果

优秀的个人博客,低调大师

Macaca-Java版入门指南

导语 接触Macaca已经有一段时间,从开始的对于UI自动化的调研开始,将Macaca与Appium,Robotium等自动化方案进行了多方对比,最终Macaca脱颖而出成为团队的敲定方案,随后经历了Macaca从JS版本到Java版本的迭代,踩了很多坑,积累了很多经验教训,在这里总结沉淀一下,以方便后来人少走弯路,加快脚步。 一、认识Macaca Macaca是一套基于WebDriver标准协议开发的开源自动化解决方案,旨在解决跨平台,跨终端的UI自动化测试的短板问题,减少繁杂、重复的人工工作,降低自动化测试的上手门坎。终端上,Macaca同时支持Native(iOS&&Android)、Hybrid、Mobile Web、以及PC端的自动化测试,用例编写上,Macaca采取CS架构,可以支持任意编程语言的封装,目前已

优秀的个人博客,低调大师

Android进阶之NDK开发入门

最近一直在练车,晒的是好黑好黑啊,好久没写博客了,趁着中午有时间,赶紧写一篇,写完就得骑着我的宝马电瓶车去驾校学车,累死哦。 为了满足as和ec的用户,我两种IDE都讲,好久没水了,装装X。 首先,得做好开发准备,下载好NDK包,as用户呢,可以在setting–>Android SDK里面找到,如下图: 可以看看自己的NDK是否安装了,如果没安装就勾选,并点击Apply就会自动下载了,下载的安装包会在sdk目录下看到,是一个叫ndk-bundle的文件夹,然后我们将这个路径设置到环境变量中,可以用ndk-build来测试一下是否已添加进环境变量中,ec的用户可以直接打开sdk manager去下载,生成的文件和上述情况一样。 好了,准备工作做好了。接下来就是开发了。。。。 Android Studio 用户: 首先来看看目录结构: 在app目录下新建了存储.c和mk文件,这些文件是为了生成so文件准备的,java目录下是调用的支持类JniTools.java。 好了,接下来分步骤讲解吧。跟着一步一步的就能实现啦! 第一步,新建一个调用类JniTools package jni; /** * Created by wangqi on 2016/8/8. */ public class JniTools { public native int add(int a,int b); } 类中的本地方法是没有方法体的,并且用native修饰方法。 第二步,生成.h文件 首先打开Terminal,cd到jni目录的上一级,然后输入”包名.类名”的全路径,不然会找不到该类,要么就是so文件调用不了,输入javah jni.JniToos,接着就会在与jni同级目录中找到一个jni_JniTools.h的文件,这个文件名就是以全路径来命名的,你的调用类也必须这个全路径名,不然会报错,如图: 然后,我们来打开.h文件来看看: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class jni_JniTools */ #ifndef _Included_jni_JniTools #define _Included_jni_JniTools #ifdef __cplusplus extern "C" { #endif /* * Class: jni_JniTools * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_jni_JniTools_add (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif 可以看到我们熟知的方法add,前面加了好多不熟知的方法修饰,先不管他,这个方法似乎没有实现方法体,我们来实现它。 /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class jni_JniTools */ #ifndef _Included_jni_JniTools #define _Included_jni_JniTools #ifdef __cplusplus extern "C" { #endif /* * Class: jni_JniTools * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_jni_JniTools_add (JNIEnv *evn, jclass clazz, jint a, jint b) { return a + b; } #ifdef __cplusplus } #endif #endif 注意方法内部的参数默认是没有对象的,还有就是jobject最好改成jclass,有人在博客中提到这也会成为调用不了的一个因素,好了,方法体实现了,接着,将这个.h文件,我们强制命名为.c文件,然后在app的目录下新建一个jni目录,将这个.c文件复制到app目录下的jni文件下。 第三步,实现mk文件 接着,我们在app目录下的jni目录下新建一个mk文件 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := JniTools LOCAL_SRC_FILES := jni_JniTools.c include $(BUILD_SHARED_LIBRARY) 文件内容我们先关注LOCAL_MODULE ,这个后天跟着的是你加载的库名称,LOCAL_SRC_FILES 是你.c文件的文件名。 第四步,ndk-build命令生成so文件 我们cd到app目录下的jni目录下,然后输入命令ndk-build,你会看到: E:\AS_workspace\ViewTools\app\jni>ndk-build [arm64-v8a] Install : libJniTools.so => libs/arm64-v8a/libJniTools.so [x86_64] Install : libJniTools.so => libs/x86_64/libJniTools.so [mips64] Install : libJniTools.so => libs/mips64/libJniTools.so [armeabi-v7a] Install : libJniTools.so => libs/armeabi-v7a/libJniTools.so [armeabi] Install : libJniTools.so => libs/armeabi/libJniTools.so [x86] Install : libJniTools.so => libs/x86/libJniTools.so [mips] Install : libJniTools.so => libs/mips/libJniTools.so 这就是生成成功了,接着你就会看到app目录下多了一个libs文件和obj文件,打开libs文件,你会看到各种适应机型的so文件。 第五步,调用so里面的实现方法 public class MainActivity extends AppCompatActivity { static { System.loadLibrary("JniTools"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); JniTools jniTools = new JniTools(); Log.i("jni", jniTools.add(10, 20) + "--------------------"); } 首先得先加载类库,这个库名称就是mk文件里面LOCAL_MODULE后面所对应的名称,接着,我们调用jniTools里面的add方法,这个方法会自动去寻找so文件内实现的方法体,完成调用。 成功运行截图 eclipse用户: 实现步骤和上面一模一样,我先展示一下结构图。 首先在包下面新建一个实现类,然后cd到src目录下,敲命令javah “包名.类名”生成的.h文件打开实现里面的方法,和上面一样,接着在JNI项目文件夹下新建一个jni目录,将.h文件放进去,并改成.c文件,然后新建mk文件,和上面一样,然后cd到jni目录下,敲入ndk-build,会在JNI目录下多出一个libs文件夹和一个obj文件夹,接着我们就在MainActivity中去调用。 package com.example.jni; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { static { System.loadLibrary("jnitest"); } private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.add); jnitest jnitest = new jnitest(); int a = jnitest.add(10, 20); tv.setText(a + ""); } } 成功图样 好了,结束了,要是实现中有不对的地方,和我说说,我来看看。 对了,as有好几种项目视图,我一般用Android和Project这两种,有些刚用as的用户,可能因为不适应,新建libs文件夹后倒入jar文件,gradle后发现调用不了里面的方法,我感觉可能是因为你libs文件夹可能放错位置了,记住,无论是eclipse还是Android Studio,libs文件夹一定是与src文件夹是在同一级目录,发现不是的话那就是新建错了。 好了,不说了,要去学车了,争取当个老司机。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册