Android Studio 1.4主工程中使用gradle:1.3.0生成包含NDK的APK没有包含非".so"后缀的文件

Android Studio 1.4主工程中使用gradle:1.3.0,编译NDK,其中生成两个文件,一个是.so的动态链接库,另一个是可执行程序,没有扩展名,在最后生成的APK中没有包含非".so"后缀的文件。这个问题纠结了好久,最后追踪到了Android Studio对应的Gradle代码文件中。发现在

Android Studio\gradle\m2repository\com\android\tools\build\builder\1.3.0\builder-1.3.0-sources.jar!\com\android\builder\internal\packaging\Packager.java

文件中有一个变量

private static final Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",Pattern.CASE_INSENSITIVE);

这个变量决定了最后的打包时候的过滤条件。

具体的代码如下图所示:ndk_build_not_include
最后的解决方案,就是把编译之后的文件重新命名成为“.so”。

这边的build.gradle的配置如下:

(其他配置参考Android Studio 1.2 开发JNI工程

task ndkBuild(type: Exec) {
	def Properties localProps = new Properties()
	localProps.load(new FileInputStream("local.properties"))
	def ndk_dir=localProps['ndk.dir']

	def ndk_build_cmd = "$ndk_dir/ndk-build"
	if (Os.isFamily(Os.FAMILY_WINDOWS)) {
		ndk_build_cmd = "$ndk_dir/ndk-build.cmd"
	}
	if(ndkBuild_Debug_Enabled) {
		commandLine ndk_build_cmd,
				'-j', Runtime.runtime.availableProcessors(),
				"NDK_PROJECT_PATH=$rootDir/app/src/main",
				"NDK_OUT=$buildDir/native/obj",
				"NDK_DEBUG=1"
	} else{
		commandLine ndk_build_cmd,
				'-j', Runtime.runtime.availableProcessors(),
				"NDK_PROJECT_PATH=$rootDir/app/src/main",
				"NDK_OUT=$buildDir/native/obj"
	}
	doLast {
		/* com.android.build.gradle.tasks.PackageApplication->doFullTaskAction->getBuilder().packageApk
		*Android Studio\gradle\m2repository\com\android\tools\build\builder\1.3.0\builder-1.3.0-sources.jar!\com\android\builder\internal\packaging\Packager.java
		*中的过滤变量     private static final Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",Pattern.CASE_INSENSITIVE);
		* 不能包含名字不是SO的其他文件
		*/
		File file = new File("$rootDir/app/src/main/libs");
		File[] files = file.listFiles(new FilenameFilter() {
			/**
			 * 测试指定文件是否应该包含在某一文件列表中。
			 *
			 * @param dir
			 *            被找到的文件所在的目录。
			 * @param name
			 *            文件的名称。
			 * @return 当且仅当该名称应该包含在文件列表中时返回 true;否则返回
			 *         false。返回true时,该文件将被允许添加到文件列表中,否则不能添加到文件列表中。
			 */
			public boolean accept(File dir, String name) {
				File f = new File(dir, name);
				if (f.isDirectory()) {
					File rf = new File(f.getPath(), "Standalone");
					if(rf.exists()){
						return true;
					}
					return false;
				}
				else {
					return false;// 否则返回false。
				}
			}
		});
		for(File f : files) {
			File src_file = new File(f.getAbsolutePath(), "Standalone");
			File dst_file = new File(f.getAbsolutePath(), "Standalone.so");
			dst_file.delete();
			src_file.renameTo(dst_file)
		}
	}
}

Windows下android-ndk-r10e执行ndk-gdb.py报告“ERROR: Non-debuggable application installed on the target device.”

Windows下android-ndk-r10e执行ndk-gdb.py报告“ERROR: Non-debuggable application installed on the target device.”

使用命令:

#ndk-gdb.py --verbose

具体信息如下所示。android-ndk-r10e-ndk-gdb-py

使用apk-tool反编译了一下APK包,AndroidManifest.xml中的“android:debuggable="true"”已经设置了,lib\armeabi目录下面也已经存在gdb.setup,gdbserver。奇怪了!使用pyScripter跟踪ndk-gdb.py,在684行附近发现,脚本找错目录了。ndk-gdb-py-684

解决方法比较简单,拷贝一份“android-ndk-r10e\prebuilt\android-arm”,并且重新命名成为 “android-ndk-r10e\prebuilt\android-armeabi”即可。

建立Eclipse插件镜像服务器

使用Eclipse的时候,免不了要下载插件,可以插件的服务器一般都在国外,尤其是Android开发中的ADT(Android Developer Tools)插件,因此最好自己搭建一个镜像服务器。

常见的Eclipse插件分为两种组织方式,一种使用 Eclipse的P2插件来进行组织,一种使用site.xml进行组织。我们分别进行处理。

  • Mirror based on p2 information

对于P2方式的插件,使用Eclipse的 P2 mirror tool 可以比较方便的进行插件的离线镜像。
以下载ADT为例子:

$eclipse -nosplash -verbose -application org.eclipse.equinox.p2.metadata.repository.mirrorApplication -source https://dl-ssl.google.com/android/eclipse/ -destination ./plugins

$eclipse -nosplash -verbose -application org.eclipse.equinox.p2.artifact.repository.mirrorApplication -source https://dl-ssl.google.com/android/eclipse/ -destination ./plugins

简单介绍一下,P2采用两种文件对插件进行管理,metadataartifact,其中metadata可以理解为文件的描述信息,以及索引信息。而artifact则是实实在在的插件文件了。

因此,上面的命令,第一个是下载metadata,第二个是下载artifact。请注意命令中的细微区别。

  • Mirror based on site.xml information
java -jar $eclipse_home/plugins/org.eclipse.equinox.launcher_*.jar -application org.eclipse.update.core.standaloneUpdate -command mirror -from $from -to $to
  • 本站提供的镜像地址

ADT 23.0.7(August 2015)
Eclipse 3.8 http://www.mobibrw.com/eclipse/plugins/ADT/3.8/
Eclipse 4.5 http://www.mobibrw.com/eclipse/plugins/ADT/4.5/

  • 参考链接

1.Tool for downloading eclipse plugins from update sites
2.Equinox p2 Repository Mirroring
3.Downloading Eclipse plug-in update sites for offline installation
4.Equinox/p2/Ant Tasks/Partial Mirroring/Example

C++11 error: unable to find string literal operator 'operator"

一个简单的宏

#define LOG_INFORMATION(x, ...) LOG_ME("%s:%d, "x, __FUNCTION__,__LINE__, ##__VA_ARGS__)

一直都可以正常编译,但是当启用C++11的时候,报告编译错误

error: unable to find string literal operator 'operator""x'

网上搜了一下,说是C++11要求,当字符串跟变量连接的时候,必须增加一个空格才行。因此简单修改如下即可。

#if __cplusplus < 201103L
#define LOG_INFORMATION(x, ...) LOG_ME("%s:%d, "x, __FUNCTION__,__LINE__, ##__VA_ARGS__)
#else
#define LOG_INFORMATION(x, ...) LOG_ME("%s:%d, " x, __FUNCTION__,__LINE__, ##__VA_ARGS__)
#endif

注意:在“android-ndk-r10e”中使用的GCC版本,GCC-4.9之前的版本都存在"__cplusplus"宏定义错误的问题,需要参考“NDK下GCC定义__cplusplus不正确的问题”,把GCC升级到4.9

Android 5.0 模拟器休眠唤醒(黑屏)

Android 5.0 模拟器默认在一分钟之内没有操作,就会关闭屏幕,出现黑屏的情况,如下图所示.android_simulator_black_screen

注意,这不是死掉了,只是休眠了而已。解除休眠的方式是按“ESC”或者“F7”按键即可。如果需要测试休眠的话,“F7”按键是个不错的选择。

Cura 15.04 实心打印

Cura 15.04 打印物体,默认是对打印对象进行部分填充,来节省打印耗材,但是打印一些中空的结构的时候,会出现边框强度不够的情况,导致边缘的强度非常差,这时候需要设置完全填充。如下设置即可。注意: 设置后,最好关闭一下软件重新打开,出现过修改无效的情况!

Cura_15_04_Fill

NDK 链接第三方静态库的方法

将NDK编译的第三方静态拷贝到JNI目录下,在Android.mk中添加如下代码

以openssl静态库(libcrypto-static.a)为例

第一种链接方法:LOCAL_LDFLAGS := libcrypto-static.a(不推荐,有编译警告

第二种链接方法:LOCAL_LDLIBS := libcrypto-static.a(不推荐,有编译警告

第三种链接方法:(推荐

include $(CLEAR_VARS)
LOCAL_MODULE := third_static_lib (可以随便起一个名字)
LOCAL_SRC_FILES := libcrypto-static.a
include $(PREBUILT_STATIC_LIBRARY)
//在你要编译的模块中引用third_static_lib
LOCAL_STATIC_LIBRARIES := third_static_lib

Ubuntu自动备份远程服务器上WordPress的脚本

Ubuntu 自动备份远程服务器上Wordpress的脚本,分为两部分,一个是从远程服务器上面运行的"hostback.sh",一个是本地运行的 “backup.sh”,运行的时候,"hostback.sh"会被发送到服务器上面去执行,备份完成后,会自动从服务器上面删除。
运行命令为:

$ expect backup.sh

hostback.sh

#!/bin/sh
LogFile=~/backup/backup-`date +%Y%m%d`.log                #指定日志的名字
BakDir=~/backup                            #备份文件存放的路径
MD5File=~/backup/md5-`date +%Y%m%d`.txt
Sha1File=~/backup/sha1-`date +%Y%m%d`.txt

#create backup directory

if [ -d $BakDir ]
then
    cd $BakDir
    touch $LogFile
else
    mkdir -p $BakDir
    cd $BakDir
    touch $LogFile
fi


#backup wordpress
datadump=`which mysqldump`
wordpressdb="wordpress"                                   #wordpress数据库的名字
wordpresspath=/var/www         			#wordpress程序文件的位置
mysqluser="root"                              #数据库的用户名
userpass="password"                             #用户密码
backupwordpress_tar_gz=$wordpressdb.`date +%Y%m%d`.tar.gz
backupwordpress_sql=$wordpressdb.`date +%Y%m%d`.sql

if $datadump -u $mysqluser --password=$userpass -h localhost --opt $wordpressdb > $backupwordpress_sql 2>&1
then
    echo " backup $wordpressdb success" >> $LogFile
else
    echo " backup $wordpressdb error" >> $LogFile
    exit 1
fi

#检验文件尾部是否存在 “-- Dump completed on”,如果存在不存在,则说明备份出错了。
if [ 0 -eq "$(sed '/^$/!h;$!d;g' $backupwordpress_sql | grep -c "Dump completed on")" ]; 
then
	echo " backup $wordpressdb error" >> $LogFile
	exit 1	
fi

#使用h参数的目的在于把软连接指向的实际内容打包进入,而不是仅仅打包一个软连接
if tar czpfh $backupwordpress_tar_gz $wordpresspath $backupwordpress_sql >/dev/null 2>&1
then
    echo " backup wordpress success" >> $LogFile
    rm -f $wordpressdb.`date +%Y%m%d`.sql
else
    echo " backup wordperss error" >> $LogFile
    exit 1
fi

md5sum $backupwordpress_tar_gz >> $MD5File
sha1sum $backupwordpress_tar_gz >> $Sha1File

backup.sh

#!/usr/bin/expect -f
#开启内部动作调试输出,观察是否正确执行 1 代表打开调试,0代表关闭调试
exp_internal 0
set HostAddr "www.mobibrw.com"
set HostPort 22
set UserName "user"
set BackupShell "hostback.sh"
set Password "password"
set BakDir "backup"
set timeout -1

#由于通过FTP传输的文件格式可能\n被替换为\r\n 的情况,因此需要执行一下 dos2unix 转化到 \n
spawn dos2unix $BackupShell
expect eof

spawn ssh $HostAddr -p $HostPort -l $UserName  rm -rf ~/$BackupShell 
expect -re ".*assword:" 
send "$Password\r" 
expect eof

spawn scp $BackupShell $UserName@$HostAddr:~/$BackupShell
expect -re ".*assword:" 
send "$Password\r" 
expect eof

spawn ssh $HostAddr -p $HostPort -l $UserName  chmod +x ~/$BackupShell
expect -re ".*assword:"
send "$Password\r"
expect eof 

#解决在群晖NAS系统上TCP超时问题,增加心跳保持参数
#同时注意,远端语言有可能不是英文,因此需要增加LC_ALL=C强制切换到英文,否则后续匹配可能无法完成
spawn ssh $HostAddr -o TCPKeepAlive=yes -o ServerAliveInterval=30 -p $HostPort -l $UserName "LC_ALL=C sudo -E bash ~/$BackupShell"
expect -re ".*assword:" 
send "$Password\r" 
expect eof

spawn scp -r $UserName@$HostAddr:~/$BakDir  ./
expect -re ".*assword:" 
send "$Password\r" 
expect eof

#backup-20151227.log\nbash: warning: setlocale: LC_ALL: cannot change locale (zh_CN.utf8) 这种情况要注意
proc getFilterFile { host port user bakdir password filter regx} {
 spawn ssh $host -p $port -l $user  ls  ~/$bakdir | grep $filter
 expect -re ".*assword:"
 send "$password\r"
 expect -re "$regx"
 expect eof
 return [string trimright $expect_out(0,string)]
}

cd ./$BakDir

#校验MD5,SHA1
set MD5File [getFilterFile $HostAddr $HostPort $UserName $BakDir $Password "md5" "md5(.*).txt"]
puts stdout $MD5File

#修改语言环境,否则md5sum,sha1sum返回的结果中可能不会出现"OK",而是会出现“确定”
#“失败”
if {[info exists ::env(LANG)]==1} {
	set ORG_LANG "$env(LANG)"
	puts stdout "$env(LANG)"
	set env(LANG) "C"
	puts stdout "$env(LANG)"
}

if {[info exists ::env(LC_ALL)]==1} {
	set ORG_LC_ALL "$env(LC_ALL)"
	puts stdout "$env(LC_ALL)"
	set env(LC_ALL) "C"
	puts stdout "$env(LC_ALL)"
}

spawn md5sum -c $MD5File
expect -re ".*OK"
expect eof

set Sha1File [getFilterFile $HostAddr $HostPort $UserName $BakDir $Password "sha1" "sha1(.*).txt"]
puts stdout $Sha1File

spawn sha1sum -c $Sha1File
expect -re ".*OK"
expect eof

#还原语言设置
if {[info exists ::env(LANG)]==1} {
	puts stdout "$env(LANG)"
	set env(LANG) "$ORG_LANG"
	puts stdout "$env(LANG)"
}
if {[info exists ::env(LC_ALL)]==1} {
	puts stdout "$env(LC_ALL)"
	set env(LC_ALL) "$ORG_LC_ALL"
	puts stdout "$env(LC_ALL)"
}


#打印本次的备份日志出来
set LogFile [getFilterFile $HostAddr $HostPort $UserName $BakDir $Password "backup" "backup(.*).log"]
puts stdout $LogFile

set f [ open $LogFile r]
while { [ gets $f line ] >= 0 } { puts stdout $line;}

#删除远端的备份脚本
spawn ssh $HostAddr -p $HostPort -l $UserName sudo rm -rf ~/$BackupShell
expect -re ".*assword:" 
send "$Password\r" 
expect eof

#删除远端的备份目录
spawn ssh $HostAddr -p $HostPort -l $UserName sudo rm -rf ~/$BakDir
expect -re ".*assword:" 
send "$Password\r" 
expect eof

Windows 10下面Android模拟器无法拖动问题的解决

Windows 10下面Android模拟器往往会顶到桌面的最上面,也就是上面的最大最小关闭按钮那一栏超过屏幕的最上面的边缘,导致无法拖动。这个时候是非常痛苦的。简单的修正这个问题的办法如下:
在底层任务栏右击,点击“层叠显示窗口”就可以了。
AndroidSimulatorShow