Android Things APP版本更新的解决方案

Android Things中如果使用传统的Intent来安装APK,你将永远安装不上

常见的应用内APK安装方法有以下两种方法

1.Intent安装APK的方法如下:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + “.fileProvider”, new File(path + “app-debug.apk”));
intent.setDataAndType(contentUri, “application/vnd.android.package-archive”);
startActivity(intent);

同时添加权限:

<uses-permission android:name=”android.permission.REQUEST_INSTALL_PACKAGES”/>
2.通过install命令来安装

命令如下:

pm install -i 当前包名 –user 0 XX.apk
运行方法:

Runtime.getRuntime().exec(“”)
注意:以上两种方法在android 手机中没有问题,但截止目前在Android Things 1.0.1中是行不通的

为了能在Android Things系统中更新APP,故经过不断的探索发现,*终采用插件化的方案来解决此问题

%title插图%num
经过评估测试,*终发现360出品的RePlugin框架满足我们的需求

https://github.com/Qihoo360/RePlugin

在RePlugin壳子中,必须申请完所有权限,因为壳子是不会更新的。我整理了所有的权限,在下面列出来

<!–Android Things所有权限–>
<uses-permission android:name=”com.google.android.things.permission.MANAGE_BLUETOOTH” />
<uses-permission android:name=”com.google.android.things.permission.PERFORM_UPDATE_NOW” />
<uses-permission android:name=”com.google.android.things.permission.USE_PERIPHERAL_IO” />
<uses-permission android:name=”com.google.android.things.permission.SET_TIME” />
<uses-permission android:name=”com.google.android.things.permission.REBOOT” />

<!–Android所有权限–>
<uses-permission android:name=”andriod.permission.ACCESS_CHECKIN_PROPERTIES”></uses-permission>
<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION” />
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”/>
<uses-permission android:name=”android.permission.ACCESS_LOCATION_EXTRA_COMMANDS”/>
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE”/>
<uses-permission android:name=”android.permission.ACCESS_NOTIFICATION_POLICY”/>
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE”/>
<uses-permission android:name=”android.permission.ADD_VOICEMAIL”/>
<uses-permission android:name=”android.permission.BLUETOOTH”/>
<uses-permission android:name=”android.permission.BLUETOOTH_ADMIN”/>
<uses-permission android:name=”android.permission.BODY_SENSORS”/>
<uses-permission android:name=”android.permission.BROADCAST_STICKY”/>
<uses-permission android:name=”android.permission.CALL_PHONE”/>
<uses-permission android:name=”android.permission.CAMERA”/>
<uses-permission android:name=”android.permission.CHANGE_NETWORK_STATE”/>
<uses-permission android:name=”android.permission.CHANGE_WIFI_MULTICAST_STATE”/>
<uses-permission android:name=”android.permission.CHANGE_WIFI_STATE”/>
<uses-permission android:name=”android.permission.DISABLE_KEYGUARD”></uses-permission>
<uses-permission android:name=”android.permission.EXPAND_STATUS_BAR”></uses-permission>
<uses-permission android:name=”android.permission.GET_ACCOUNTS”/>
<uses-permission android:name=”android.permission.GET_PACKAGE_SIZE”/>
<uses-permission android:name=”android.permission.GET_TASKS”/>
<uses-permission android:name=”android.permission.INSTALL_SHORTCUT”/>
<uses-permission android:name=”android.permission.INTERNET”/>
<uses-permission android:name=”android.permission.KILL_BACKGROUND_PROCESSES”/>
<uses-permission android:name=”android.permission.MODIFY_AUDIO_SETTINGS”/>
<uses-permission android:name=”android.permission.NFC”></uses-permission>
<uses-permission android:name=”android.permission.PERSISTENT_ACTIVITY”></uses-permission>
<uses-permission android:name=”android.permission.PROCESS_OUTGOING_CALLS”></uses-permission>
<uses-permission android:name=”android.permission.READ_CALENDAR”/>
<uses-permission android:name=”android.permission.READ_CALL_LOG”/>
<uses-permission android:name=”android.permission.READ_CONTACTS”/>
<uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE”/>
<uses-permission android:name=”android.permission.READ_PHONE_STATE”/>
<uses-permission android:name=”android.permission.READ_SMS”/>
<uses-permission android:name=”android.permission.READ_SYNC_SETTINGS”/>
<uses-permission android:name=”android.permission.READ_SYNC_STATS”/>
<uses-permission android:name=”android.permission.READ_VOICEMAIL”/>
<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”/>
<uses-permission android:name=”android.permission.RECEIVE_MMS”/>
<uses-permission android:name=”android.permission.RECEIVE_SMS”/>
<uses-permission android:name=”android.permission.RECEIVE_WAP_PUSH”/>
<uses-permission android:name=”android.permission.RECORD_AUDIO”/>
<uses-permission android:name=”android.permission.REORDER_TASKS”/>
<uses-permission android:name=”android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS”/>
<uses-permission android:name=”android.permission.REQUEST_INSTALL_PACKAGES”/>
<uses-permission android:name=”android.permission.RESTART_PACKAGES”/>
<uses-permission android:name=”android.permission.SEND_SMS”/>
<uses-permission android:name=”android.permission.SET_ALARM”/>
<uses-permission android:name=”android.permission.SET_WALLPAPER”/>
<uses-permission android:name=”android.permission.SET_WALLPAPER_HINTS”/>
<uses-permission android:name=”android.permission.SYSTEM_ALERT_WINDOW”/>
<uses-permission android:name=”android.permission.TRANSMIT_IR”/>
<uses-permission android:name=”android.permission.UNINSTALL_SHORTCUT”/>
<uses-permission android:name=”android.permission.USE_FINGERPRINT”/>
<uses-permission android:name=”android.permission.USE_SIP”/>
<uses-permission android:name=”android.permission.VIBRATE”/>
<uses-permission android:name=”android.permission.WAKE_LOCK”/>
<uses-permission android:name=”android.permission.WRITE_CALENDAR”/>
<uses-permission android:name=”android.permission.WRITE_CALL_LOG”/>
<uses-permission android:name=”android.permission.WRITE_CONTACTS”/>
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>
<uses-permission android:name=”android.permission.WRITE_SYNC_SETTINGS”/>
<uses-permission android:name=”android.permission.WRITE_VOICEMAIL”/>

<!–所有系统权限–>
<uses-permission android:name=”android.permission.ACCOUNT_MANAGER”/>
<uses-permission android:name=”android.permission.BATTERY_STATS”/>
<uses-permission android:name=”android.permission.BIND_ACCESSIBILITY_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_CARRIER_MESSAGING_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_CARRIER_SERVICES”/>
<uses-permission android:name=”android.permission.BIND_CHOOSER_TARGET_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_DEVICE_ADMIN”/>
<uses-permission android:name=”android.permission.BIND_CONDITION_PROVIDER_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_DREAM_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_INCALL_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_INPUT_METHOD”/>
<uses-permission android:name=”android.permission.BIND_MIDI_DEVICE_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_NFC_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_NOTIFICATION_LISTENER_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_PRINT_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_QUICK_SETTINGS_TILE”/>
<uses-permission android:name=”android.permission.BIND_REMOTEVIEWS”/>
<uses-permission android:name=”android.permission.BIND_SCREENING_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_TELECOM_CONNECTION_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_TEXT_SERVICE”></uses-permission>
<uses-permission android:name=”android.permission.BIND_TV_INPUT”></uses-permission>
<uses-permission android:name=”android.permission.BIND_VOICE_INTERACTION”></uses-permission>
<uses-permission android:name=”android.permission.BIND_*_SERVICE”></uses-permission>
<uses-permission android:name=”android.permission.BIND_VR_LISTENER_SERVICE”/>
<uses-permission android:name=”android.permission.BIND_WALLPAPER”/>
<uses-permission android:name=”android.permission.BLUETOOTH_PRIVILEGED”/>
<uses-permission android:name=”android.permission.BROADCAST_PACKAGE_REMOVED”/>
<uses-permission android:name=”android.permission.BROADCAST_SMS”/>
<uses-permission android:name=”android.permission.BROADCAST_WAP_PUSH”/>
<uses-permission android:name=”android.permission.CALL_PRIVILEGED”/>
<uses-permission android:name=”android.permission.CAPTURE_AUDIO_OUTPUT”/>
<uses-permission android:name=”android.permission.CAPTURE_SECURE_VIDEO_OUTPUT”/>
<uses-permission android:name=”android.permission.CAPTURE_VIDEO_OUTPUT”/>
<uses-permission android:name=”android.permission.CHANGE_COMPONENT_ENABLED_STATE”/>
<uses-permission android:name=”android.permission.CHANGE_CONFIGURATION”/>
<uses-permission android:name=”android.permission.CLEAR_APP_CACHE”/>
<uses-permission android:name=”android.permission.CONTROL_LOCATION_UPDATES”/>
<uses-permission android:name=”android.permission.DELETE_CACHE_FILES”/>
<uses-permission android:name=”android.permission.DELETE_PACKAGES”/>
<uses-permission android:name=”android.permission.DIAGNOSTIC”/>
<uses-permission android:name=”android.permission.DUMP”></uses-permission>
<uses-permission android:name=”android.permission.FACTORY_TEST”></uses-permission>
<uses-permission android:name=”android.permission.GET_ACCOUNTS_PRIVILEGED”/>
<uses-permission android:name=”android.permission.GLOBAL_SEARCH”/>
<uses-permission android:name=”android.permission.INSTALL_LOCATION_PROVIDER”/>
<uses-permission android:name=”android.permission.INSTALL_PACKAGES”/>
<uses-permission android:name=”android.permission.LOCATION_HARDWARE”/>
<uses-permission android:name=”android.permission.MANAGE_DOCUMENTS”/>
<uses-permission android:name=”android.permission.MASTER_CLEAR”/>
<uses-permission android:name=”android.permission.MEDIA_CONTENT_CONTROL”/>
<uses-permission android:name=”android.permission.MODIFY_PHONE_STATE”/>
<uses-permission android:name=”android.permission.MOUNT_FORMAT_FILESYSTEMS”/>
<uses-permission android:name=”android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>
<uses-permission android:name=”android.permission.PACKAGE_USAGE_STATS”></uses-permission>
<uses-permission android:name=”android.permission.READ_FRAME_BUFFER”/>
<uses-permission android:name=”android.permission.READ_INPUT_STATE”/>
<uses-permission android:name=”android.permission.READ_LOGS”/>
<uses-permission android:name=”android.permission.REBOOT”/>
<uses-permission android:name=”android.permission.SEND_RESPOND_VIA_MESSAGE”/>
<uses-permission android:name=”android.permission.SET_ALWAYS_FINISH”/>
<uses-permission android:name=”android.permission.SET_ANIMATION_SCALE”/>
<uses-permission android:name=”android.permission.SET_DEBUG_APP”/>
<uses-permission android:name=”android.permission.SET_PREFERRED_APPLICATIONS”></uses-permission>
<uses-permission android:name=”android.permission.SET_PROCESS_LIMIT”></uses-permission>
<uses-permission android:name=”android.permission.SET_TIME”></uses-permission>
<uses-permission android:name=”android.permission.SET_TIME_ZONE”></uses-permission>
<uses-permission android:name=”android.permission.SIGNAL_PERSISTENT_PROCESSES”/>
<uses-permission android:name=”android.permission.STATUS_BAR”/>
<uses-permission android:name=”android.permission.UPDATE_DEVICE_STATS”/>
<uses-permission android:name=”android.permission.WRITE_APN_SETTINGS”/>
<uses-permission android:name=”android.permission.WRITE_GSERVICES”/>
<uses-permission android:name=”android.permission.WRITE_SECURE_SETTINGS”/>
<uses-permission android:name=”android.permission.WRITE_SETTINGS”/>
但是,光这些还不够,还需要解决两个坑

1.关于FileProvider的问题

我们需要提前在RePlugin中申请注册,Replugin是不会动态申请FileProvider

所以,需要在宿主程序中添加:

<provider
android:name=”android.support.v4.content.FileProvider”
android:authorities=”宿主包名.fileProvider”
android:exported=”false”
android:grantUriPermissions=”true”>
<meta-data
android:name=”android.support.FILE_PROVIDER_PATHS”
android:resource=”@xml/file_paths” />
</provider>
注意一点:此处填写宿主包名,当我们在插件中使用context.getPackageName()的时候,拿到的是宿主包名

2.在Android Things中APP默认是横屏,但当我们加载打开插件时,会发现APP被强制改为了竖屏

在这里,尝试反编译宿主APK,打开AndroidManifest文件后,发现screenOrientation的值为1,故RePlugin框架默认是将所有的Activity坑位默认设置的portrait竖屏

<activity
android:theme=”@ref/0x01030006″
android:name=”com.xx.a.ActivityP2NRNTS1″
android:exported=”false”
android:process=”:p2″
android:screenOrientation=”1″
android:configChanges=”0x4b0″ />
解决方案如下,修改RePlugin源码,找到replugin-host-gradle源码,修改ComponentsGenerator类,添加oriL变量,然后将所有的oriV改为oriL

 

Android studio安装与配置

Android studio安装与配置

1、首先下载Android studio安装包,可以从http://www.android-studio.org/下载*新版本,这里采用3.0版本进行演示,对应安装包为android-studio-ide-171.4408382-windows.exe,安装包大小681 MB,安装包不带SDK

 

 

%title插图%num

2、下载好该安装包之后,点击进行安装,依次出现以下界面

%title插图%num%title插图%num

 

%title插图%num

在这里自己选择程序安装路径

%title插图%num

%title插图%num%title插图%num

%title插图%num

这里Android studio程序安装完毕,但是还需要继续对其进行配置;勾选Start Android Studio,然后点击finish启动AS,出现下图

%title插图%num

 

 

 

 

选择第二项,然后点击ok,出现下面的启动界面

%title插图%num

在启动的时候会弹出下图

%title插图%num

点击cancel,然后进入到了AS的安装向导界面

%title插图%num

点击next进入UI界面主题选择界面,可以选择自己喜欢的风格,这里选择Darcula风格

%title插图%num

%title插图%num

这里需要指定SDK的本地路径,如果之前电脑中已经存在SDK,可以指定该路径,后续就可以不用下载SDK;我这里演示本地没有安装过SDK的场景,这里暂时可以指定一个后续将保存SDK的路径;

%title插图%num

%title插图%num

点击Finish后,开始自动下载SDK(注意,此时需要保证电脑联网)

%title插图%num%title插图%num

 

 

 

下载完成SDK后,点击Finish进入AS的欢迎界面

%title插图%num

3、配置AS*次运行环境,并且能成功编译运行一个APP,以helloworld为例。

点击上图中的Start a new Android Studio project新建一个工程,进入下面的界面

%title插图%num

%title插图%num%title插图%num

 

%title插图%num%title插图%num

到此,一个工程建立完成,*次建立的工程会发现卡在下面的启动界面

%title插图%num

*次建立工程卡在该界面的时候,是因为在从网上下载gradle构建工具,由于从国外站点下载,网速很慢,这里点击取消关闭该界面,采用手动配置gradle;

首先找到.gradle文件夹的路径,一般是在用户账号文件夹下,比如我这里是C:\Users\issuser\.gradle

会发现该文件夹下生成了下图中的文件

%title插图%num

 

 

这里的gradle-4.1,指的是版本,它会根据你的AS版本自动生成,此时我们可以去网上下载一个gradle-4.1-all.zip压缩包,然后放到该路径下并且进行解压,注意一定要放到这个随机生成的一长串字符的文件夹下面,如下图

%title插图%num

%title插图%num

此时点击图中下方的链接进行SDK下载,这里可能一次下载之后,执行Try again之后这里还是会显示报错,那么就再点击下载一次,然后再点击Try again,直到报错解除。(除了该解决办法,还可以手动更改build.gradle文件中的compileSdkVersion,buildToolsVersion
targetSdkVersion为对应的27也可以进行解决,这个后续再讲)

%title插图%num%title插图%num

%title插图%num%title插图%num

上述gradle构建完成之后,就可以点击下图中的build apk编译打包apk文件了,生成的apk文件路径如下图所示

%title插图%num

生成apk文件之后,导出该apk文件到模拟器或者真机上面进行安装,运行效果图如下

%title插图%num

 

 

 

 

至此,Android Studio的安装以及开发环境就配置好了。

 

CentOS配置简单的MC服务器配置

首先自己的硬件要求有:

%title插图%num

需要的东西我全部例举出来了

安装服务器操作系统:
这个我不想演示了,很简单,百度上教程一大堆,下面直接跳到配置环节。

(1)安装SSH服务:
yum -y install openssh-server
如果提示以下情况:

未安装”yum”请执行
sudo apt install yum

就直接按照上面的提示直接安装yum,(这种情况出现在*个别的老版本CentOS里面)

安装完毕之后查看自己的服务器地址:
ifconfig //查看本机内网IPV4地址

在SSH管理软件中输入本机的登陆账号以及密码之后安装vsftpd

yum -y install vsftpd //安装FTP组件

XFTP软件连接之后自己找一个文件夹做游戏的根目录,作者使用/var/mcserver目录

在Windows管理机下用.txt文件编辑

java -Xms 6144M -Xmx 1024M -jar XXXX.jar //XXXX.jar是你的核心文件名字

把后缀从txt改成.sh

在控制台输入:

cd /var/mcserver
ls -al
sh ./start.sh

直接启动核心文件选择语言版本,此服务器启动成功!!!!

服务器连接问题:
不用什么软件,直接使用一个很简单的
用路由器自带的ddns功能,直接设置xxxxx.AP厂商.cn
当然,公网的IPV4地址还是重要滴!
没有公网地址是无法正常解析的,否则就需要内网穿透,不穿透无效。

如果使用的是内网联机用的服务器,只需要内网IPV4地址+服务器开启端口号
内网服务器的配置方式和上面一样,如果想要开启某些氪金系统,就搭配MySQL,Mircosoft SQL就算了,我之前尝试过,答案是不能(不知道是我的设置的问题还是其他的**总之这个目前用的人很少,所以不做教程,想折腾的自己去研究研究)

android studio和jdk环境设置

文章目录
1、Jdk安装及环境设置
1.1、 安装jdk一步步安装就可以 了,接着是设置环境变量
1.2、 新增JAVA_HOME(名字大小写一定要一致)
1.3、 新增classpath .;%JAVA_HOME%\lib
1.4、 path字段下变量值新增%JAVA_HOME%\bin
1.5、判断jdk是否安装成功标志
2、Android studio 安装及环境设置
2.1、当新建项目后,编译后面没有自动显示app那个运行项的话,那么只要选择

jdk的下载地址
https://www.oracle.com/java/technologies/javase-downloads.html

 

1、Jdk安装及环境设置
1.1、 安装jdk一步步安装就可以 了,接着是设置环境变量

%title插图%num
1.2、 新增JAVA_HOME(名字大小写一定要一致)

%title插图%num
1.3、 新增classpath .;%JAVA_HOME%\lib

%title插图%num
1.4、 path字段下变量值新增%JAVA_HOME%\bin

%title插图%num
1.5、判断jdk是否安装成功标志
Cmd窗口输入java, javac都能提示正确信息

%title插图%num
Android studio 下载地址
http://www.android-studio.org/

2、Android studio 安装及环境设置
全部一步步单击往下安装就可以,

2.1、当新建项目后,编译后面没有自动显示app那个运行项的话,那么只要选择
File–>sync project with Gradle file 让其自动去下载文件就可以了。

%title插图%num

只需10步教你Centos7搭建MC服务器

  1. 安装java,不会网上很多。
  2. 下载spigot https://getbukkit.org/download/spigot
  3. 执行 java -jar spigot-1.12.2.jar
  4. 执行肯定是失败,因为需要同意eula.txt。
  • vim eula.txt 然后设置eula=true
  1. 再执行jar包
  2. 执行成功显示 Done!,此时需要删除进程,还需要配置。
  • jps -l 查看哪个PID,然后用kill -9 [PID]
  1. vim server.propertis
  1. 防火墙放行25565端口
  • firewall-cmd –zone=public –add-port=25565/tcp –permanent
  • firewall-cmd –reload
  1. 让java进程后台执行,shell脚本
  • vim start.sh
  1. #!/bin/sh
  2. java -Xmx1G -jar spigot-1.12.2.jar &
  1. 启动 sh start.sh

使用AndroidStudio编译NDK的方法及错误解决方案

参考资料:
【android ndk】macos环境下Android Studio中利用gradle编译jni模块及配置: http://demo.netfoucs.com/ashqal/article/details/21869151
ANDROID STUDIO, GRADLE AND NDK INTEGRATION: http://ph0b.com/android-studio-gradle-and-ndk-integration/
Gradle Plugin User Guide: http://tools.android.com/tech-docs/new-build-system/user-guide
New Build System: http://tools.android.com/tech-docs/new-build-system

实践证明:
0.4.2只有在gradle1.10版本下创建只包含AndroidLibrary模块的工程时才能正常编译,gradle1.9版本不可以。
0.4.6使用gradle1.10可以。
0.5.0无论是gradle1.10还是gradle1.11版本都可以生成so库。
0.5.5的不能编译NDK,无论是gradle1.10还是gradle1.11版本都不能生成so库,屙血尿脓。

下载AndroidStudio:
AndroidStudio的历史版本下载列表: http://tools.android.com/download/studio/canary

下载NDK:
下载链接: http://developer.android.com/tools/sdk/ndk/index.html,注意NDK一定要r9+版本的,否则编译时会出现如下 错误:
Execution failed for task ‘:hellojni:compileDebugNdk’.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
D:\ndk\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=F:\androidstudio\test\hellojni\build\ndk\debug\Android.mk APP_PLATFORM=android-19 NDK_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\obj NDK_LIBS_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a
Error Code:
2
Output:
D:/ndk/build/core/setup-app.mk:63: *** Android NDK: Aborting . Stop.

下载gradle:
gradle-1.9-all.zip: http://download.csdn.net/detail/xxhongdev/6834859
gradle-1.10-all.zip: http://download.csdn.net/detail/xinghuacheng/7026815
gradle-1.11-all.zip: http://download.csdn.net/detail/d1387968/7097249

通过“AndroidStudio历史版本下载列表”下载的历史版本通常是绿色的压缩包,可以直接解压缩使用,但是不包含SDK,需要额外下载SDK,由于之前下载了ADT(版本:adt20131030),所以后面直接使用ADT目录下的SDK。通过 http://developer.android.com/sdk/installing/studio.html首页下载的AndroidStudio为安装版本,包含了SDK,可以下载后直接安装,首次使用创建项目会比较慢,可以参考“ AndroidStudio创建项目时一直处于building“project name”gradle project info的解决办法”来解决。

创建项目:
运行AndroidStudio后,创建新项目,新项目会有一个默认的Module,这里项目名称为JNIDemo,Module为app。

%title插图%num

然后通过向导完成项目的创建。

AndroidStudio还是非常慢的,长时间处于这种状态:

%title插图%num

经过漫长的等待后终于完成项目的创建,然后在这个项目下创建一个Module,New Module->Android Library:

%title插图%num

不勾选“Create activity”然后点击“Finish”完成创建,此时项目结构如图:

%title插图%num

app和hellojni均为JNIDemo下的两个Module,这里把hellojni作为生成so库的NDK开发层,把app作为调用so库的APK引用开发层。

在hellojni模块的src/main下创建jni目录,并在jni目录下新建文件main.cpp,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include <assert.h>
#include <sys/types.h>
#include <android/log.h>

#define LOG_TAG “Hellojni”
#define LOGE(…) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

//注册native api的类#define JNIREG_CLASS “com/example/test9/app/MainActivity”

extern “C” {
JNIEXPORT void msg(JNIEnv *env, jobject clazz, jstring str);
};

//jstring to char* char* jstringTostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass(“java/lang/String”);
jstring strencode = env->NewStringUTF(“utf-8”);
jmethodID mid = env->GetMethodID(clsstring, “getBytes”, “(Ljava/lang/String;)[B”);
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}

JNIEXPORT void msg(JNIEnv *env, jobject clazz, jstring str)
{
char *pszstr = NULL;

pszstr = jstringTostring(env, str);
LOGI(“%s”, pszstr);
free(pszstr);
}

/**
* Table of methods associated with a single class.
*/static JNINativeMethod gMethods[] = {
{ “msg”, “(Ljava/lang/String;)V”, (void*)msg},
};

/*
* Register native methods for all classes we know about.
*/static int registerNativeMethods(JNIEnv* env)
{
int nError = 0;
jclass clazz = NULL;

clazz = env->FindClass(JNIREG_CLASS);
if (clazz == NULL) {
LOGE(“clazz is null”);
return JNI_FALSE;
}

nError = env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]) );
if ( nError < 0 ) {
LOGE(“RegisterNatives error: %d num: %d”,nError, sizeof(gMethods) / sizeof(gMethods[0]) );
return JNI_FALSE;
}

return JNI_TRUE;
}

/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;

if(vm->GetEnv((void**) &env,JNI_VERSION_1_6) != JNI_OK){
return -1;
}
assert(env != NULL);

if (!registerNativeMethods(env)) {
LOGE(“registerNativeMethods failed”);
return -1;
}

/* success — return valid version number */
result = JNI_VERSION_1_6;

return result;
}
这里只导出一个msg函数打印传递进来的字符串,仅作测试。再在jni目录下新建一个empty.cpp文件,内容为空,这个是为了解决NDK的bug所作的,以防编译出错。

打开local.properties,设置正确的SDK路径和NDK路径:
sdk.dir=D\:/adt20131030/sdk
ndk.dir=D\:/ndk
打开项目gradle/wrapper目录下的gradle-wrapper.properties文件,修改:
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-all.zip
为:
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
并打开项目根目录下的build.gradle文件,修改:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath ‘com.android.tools.build:gradle:0.7.+’
}
}

allprojects {
repositories {
mavenCentral()
}
}
为(指定使用gradle1.10则修改为0.9.+,指定使用gradle1.11则修改为0.9.2):
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath ‘com.android.tools.build:gradle:0.9.+’
}
}

allprojects {
repositories {
mavenCentral()
}
}
解释:参考 http://tools.android.com/tech-docs/new-build-system知道
0.7.0
Requires Gradle 1.9
Requires Studio 0.4.0

0.9.0
Compatible with Gradle 1.10 and 1.11
Using Gradle 1.11 requires Android Studio 0.5.0
如果配置的是0.7.+则默认使用gradle1.9,如果设置为0.9.+则默认使用gradle1.10。

另外还需要注意的是gradle1.9下没有buildTypes标签,需要将debug、release标签直接放在android标签内,在gradle1.10下debug、release需要放在buildTypes标签内,buildTypes在android内。这里hellojni配置的build.gradle文件内容如下:
assert gradle.gradleVersion >= “1.10”

apply plugin: ‘android-library’

android {
compileSdkVersion 19
buildToolsVersion “19.0.3”

defaultConfig {
minSdkVersion 8
targetSdkVersion 16
versionCode 1
versionName “1.0”
}

buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.txt’
ndk {
moduleName “hellojni”
abiFilters “armeabi”, “armeabi-v7a”, “x86”
}
}

debug {
ndk {
moduleName “hellojni”
//stl “stlport_shared”
ldLibs “log”, “z”, “m”
//cFlags “-Wall -Wextra -I ” + projectDir + “/src/main/jni/include”
abiFilters “armeabi”, “armeabi-v7a”, “x86”
}
}
}

productFlavors {
x86 {
versionCode Integer.parseInt(“6” + defaultConfig.versionCode)
ndk {
abiFilter “x86”
}
}
mips {
versionCode Integer.parseInt(“4” + defaultConfig.versionCode)
ndk {
abiFilter “mips”
}
}
armv7 {
versionCode Integer.parseInt(“2” + defaultConfig.versionCode)
ndk {
abiFilter “armeabi-v7a”
}
}
arm {
versionCode Integer.parseInt(“1” + defaultConfig.versionCode)
ndk {
abiFilters “armeabi”, “armeabi-v7a”
}
}
fat
}
}

dependencies {
compile ‘com.android.support:appcompat-v7:19.+’
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
}
然后选择hellojni项目右键“Make Module hellojni”,等待一段时间后会在项目下生成build-ndk目录,目录下会有一些不同版本的so库文件生成,如图:

%title插图%num

注意这里的Android.mk文件每次编译都会重新由工具自动生成,而非手动编辑的,我觉得这一点设计就比较差劲。例如如果想要使用log输出函数__android_log_print,需要添加“LOCAL_LDLIBS :=  -llog”,则在build.gradle文件中添加如下的配置:
debug {
ndk {
ldLibs “log”
}
}
由gradle根据配置再去生成Android.mk文件,*后再调用ndk进行编译。

右键工程选择Open Module Settings,选择Modules-app,打开Dependencies选项卡点击“+”号,选择Module dependency,在打开的对话框中选择hellojni。

%title插图%num

但是测试发现设置依赖没有效果,如果直接编译app,hellojni并没有编译,仍需要手动编译hellojni。

调用native函数:

app项目中,在MainActivity类中声明native函数:
public native void msg(String str);
并添加静态代码加载hellojni库:
static {
System.loadLibrary(“hellojni”);
}
在MainActivity::onCreate中调用native函数打印一句log:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
msg(“MainActivity onCreate”);
}

还需要将hellojni生成的so库文件打包进apk,仍需要配置build.gradle文件,添加:
task copyNativeLibs(type: Copy) {
from fileTree(dir: ‘../hellojni/build/ndk/arm/debug/lib’, include: ‘armeabi/*.so’) into ‘build/lib’
}
tasks.withType(Compile) {
compileTask -> compileTask.dependsOn copyNativeLibs
}
clean.dependsOn ‘cleanCopyNativeLibs’
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
pkgTask.jniFolders = [new File(buildDir, ‘lib’)]
}
参考:“Android Studio添加so库” http://blog.csdn.net/caesardadi/article/details/18264399
其中copyNativeLibs任务是从相对app的项目路径’../hellojni/build/ndk/arm/debug/lib’下复制所有armeabi子目录的so文件到本项目build目录下的lib目录中,执行效果:

%title插图%num

这样*后打包生成的apk包才会包含有hellojni的so库文件。

测试:

编译运行app,apk安装完毕运行时输出log信息:

后面列出了可能出现的gradle错误以及解决方案,以供参考。

错误:
Execution failed for task ‘:hellojni:compileDebugNdk’.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
D:\ndk\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=F:\androidstudio\test\hellojni\build\ndk\debug\Android.mk APP_PLATFORM=android-19 NDK_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\obj NDK_LIBS_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a
Error Code:
2
Output:
make.exe: *** No rule to make target `F:\androidstudio\test\hellojni\build\ndk\debug\obj/local/armeabi/objs/jnimain/F_\androidstudio\test\hellojni\src\main\jni’, needed by `F:\androidstudio\test\hellojni\build\ndk\debug\obj/local/armeabi/objs/jnimain/F_\androidstudio\test\hellojni\src\main\jni\hellojni.o’. Stop.
解决方案:
这是NDK在Windows下一个bug,当只编译一个文件时出现,解决方法就是再添加一个空的文件即可。
原文见 http://ph0b.com/android-studio-gradle-and-ndk-integration/:
This may come from a current NDK bug on Windows, when there is only one source file to compile. You only need to add one empty source to make it work again.

错误:
Could not determine the dependencies of task ‘:hellojni:compileArmDebugJava’.
> failed to find Build Tools revision 19.0.3
解决方案:
这个Build Tools是指“Android SDK Build-tools”,打开SDK Manager勾选相应版本(例如这里是19.0.3)安装即可。

%title插图%num

错误:
FAILURE: Build failed with an exception.

* What went wrong:
Task ‘assembleArmDebug’ not found in project ‘:hellojni’. Some candidates are: ‘assembleDebug’.

* Try:
Run gradle tasks to get a list of available tasks. Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output.
解决方案:
在 android { }中添加:
productFlavors{
arm {
}
}
若有类似错误可以参考加入相应的标签:
productFlavors {
x86 {
versionCode Integer.parseInt(“6” + defaultConfig.versionCode)
ndk {
abiFilter “x86”
}
}
mips {
versionCode Integer.parseInt(“4” + defaultConfig.versionCode)
ndk {
abiFilter “mips”
}
}
armv7 {
versionCode Integer.parseInt(“2” + defaultConfig.versionCode)
ndk {
abiFilter “armeabi-v7a”
}
}
arm {
versionCode Integer.parseInt(“1” + defaultConfig.versionCode)
ndk {
abiFilter “armeabi”
//abiFilters “armeabi”, “armeabi-v7a”
}
}
fat
}

错误:
Execution failed for task ‘:hellojni:compileDebugNdk’.
> java.io.IOException: Cannot run program “D:\ndk\ndk-build”: CreateProcess error=193, %1 ??????Ч?? Win32 ??ó
解决方案:
在使用gradle1.9版本时遇到,使用gradle1.10版本来解决。

错误:
A problem occurred evaluating project ‘:app’.
> Could not create plugin of type ‘AppPlugin’.
解决方案:
Don’t use latest Gradle (version 1.10), downgrade to 1.9。参考: http://blog.vyvazil.eu/tag/android-studio/
但是如果我们使用gradle1.9版本的话又会出现 错误:
Execution failed for task ‘:hellojni:compileDebugNdk’.
> java.io.IOException: Cannot run program “D:\ndk\ndk-build”: CreateProcess error=193, %1 ??????Ч?? Win32 ??ó
无论使用哪个版本都有问题,后来仔细查看了下’AppPlugin’这个错误是出现在‘app’模块上的而非‘hellojni’模块上,于是考虑新建工程项目并且只在该工程下建立一个库模块,不再创建app模块,如图:

%title插图%num

这里不勾选“Create custom launcher icon”和“Create activity”,直接finish完成,其他配置参考前述,*后编译后可以生成so库文件:

%title插图%num
错误:
这个错误忘记记录了囧

解决方案:
File-Settings-Gradle-Gradle VM options:-Xmx512m

%title插图%num