Android WebView JS 与 Java Native 相互调用示例

直接上代码,有注释,学习Android过程记录。

Android Activity代码

布局文件LinearLayout布局,orentation=vertical,一个EditText,一个Button一个WebView,长什么样子的话想象一下就好了。

html文件,同样非常简单

参考资料:developer.android.com WebView

源码下载:WebViewNJs

apktool反编译与重新打包学习,apk反编译与重新签名学习,keytool-jarsigner

有个玩无线电的朋友问我会不会破解软件,我说太难的搞不了,有些是混淆过的;他说有个好像是测无线电天线的设备(天调?)是绑定蓝牙mac的让我给搞定下,安卓的app。

于是完成之后记录下。之前没事时候用过apktool,jd-gui,dex2jar等工具还有在线的,不过玩着玩着就扔掉了,这次答应了人家,起码弄出个123吧。

本次用到的工具:

1.apktool2.2

2.keytool,位于jdk/bin目录

3.jarsigner,位于jdk/bin目录

最初使用的直接apk包改扩展名zip,拿出classex.dex,然后dex2jar,拿到java文件之后想把源码直接拿出来复制到一个新工程里去,跟重新做差不多,因为看到里面只有四个Activity :

DeviceListActivity.java

DrawChart.java

MainActivity.java

MyApplication.java

比较简单,字面意思都能猜出是干嘛的,设备列表Activity,显示匹配/未匹配的蓝牙设备吧?DrawChart绘制曲线的吧?MainActivity和MyApplication都不用说了,主界面和入口,哇塞我敏锐的直觉囧。。。然后软件运行截图是这样的:

Mini60X APP运行截图
Mini60X APP运行截图

玩无线电的就是高大上,直接看不懂。。。

打开设备列表Activity之后就能看到写死的蓝牙地址,于是数组里再加入一个

但是复制到新工程之后就各种报错,比如do{ return;xxx;xxx; }while(1);这种,do里面直接return,肯定是不对的,然后各种重新反编译最后都不能直接拿来用,是不是我想的太简单了。。。虽然拿到了源码,但是有错误,尤其DrawChart里面的代码,gdi编程的错误真心搞不了。

然后又使用apktool d xxx.apk反编译得到smali文件列表(smali语法也玩不转)找到DeviceListActivity直接替换掉数组里面的蓝牙mac地址,ok第一步算完成了;

接着使用apk b xxx(xxx代表刚才反编译得到的文件路径),重新打包,在xxx/dist目录下会生成新的apk文件;这个时候apk是没有签名的,也不能安装到安卓手机中,需要对apk文件进行签名,过程如下:

1.首先生成签名文件,直接切换至%JAVA_HOME%/bin目录,使用
keytool -genkey -alias new.keystore -keyalg RSA -validity 20000 -keystore new.keystore
命令生成文件,中间不断下一步,输入密码填信息什么的,上面的语句就会在当前目录生成的签名文件new.keystore

2.使用jarsigner签名apk,将apk拷贝至%JAVA_HOME%/bin目录,输入命令:
jarsigner -verbose -keystore new.keystore -signedjar Mini60X-signed.apk Mini60X.apk new.keystore

new.keystore就是1步生成的签名文件,Mini60X-signed.apk就是要生成的签名之后的文件名,Mini60X.apk是你的apk文件名,运行结果如下:

jarsigner签名apk运行结果

就会在当前目录生成Mini60X-signed.apk,下面的Warning我没管它,然后就安装到手机上吧。

声明:本文只为学习安卓打包与签名机制使用,不针对任何app生产厂商,本文及作者不承担任何法律内外责任。

Android Studio 启动 Device Monitor报错:An error has occurred. See the log file…

详细错误如图:

Android Studio启动Device Monitor报错提示对话框

D:\xxx\sdk是我的sdk路径,于是到此目录下查看日志文件,描述如下:

错误信息说目录是只读的,但目录只有C:/Users/Lison Liou/.android,后面就没有了,于是干脆给目录加上Everyone用户的Full Control权限,结果还是不行。

后来在StackOverFlow上找到答案,Android DDMS (Monitor) does not start if user profile contains a space in it,说DDMS不会启动如果用户名包含空格,我的用户名的确包含空格,但是之前也是好用的,不解。

解决方法如下:以管理员身份运行cmd(必须),输入如下:

将带空格的用户与正常用户名建立链接(软连接或者硬链接就不知道了),Lison Liou是我的计算机用户,成功后会提示:

symbolic link created for Lison%20Liou <<===>> Lison Liou

搞定。

 

使用Netty进行Android与Server端通信实现文字发送接收与图片上传 – 原创

ANOTHER TITLE:

Let’s use netty to achieve text send and receive and  image transfer to server based on android and jdk1.7

就让我来Rap<MC Hotdog>,使用netty进行安卓端发送接收文字,并且附带发送图片功能,折腾了几天的netty总算有点眉目了,做下记录。

需求是安卓端拍照上传到服务器,服务器返回上传成功或失败bla bla bla,需求比较简单。最初使用的传统的post加multipart/form-data,一般的手机照片大小1-2M,测试没问题,但实际情况是有的用户使用3G网络,有的4G,有的WIFI还有的使用2G,网络环境稍微有点差的上传图片时间持续到十多分钟(我都不好意思说了),然后想到了使用socket,然后google到了netty,有的大神说这样的需求没必要上netty啊,其实我就是想通过这个需求学习下关于socket,关于netty,关于channel,关于byte[],关于NIO,多学习点总是没坏处的,后面我也可以牛逼点说玩过高性能基于事件的异步网络框架了。

刚开始直接啃的netty.io的document,照葫芦画瓢抄完了TimeServer之后就满世界的google其他的netty知识,比较零散,然后入手了《Netty权威指南第二版》,啃了几章之后怎么也是TimeServer,不过对NIO一些基本组件有了一个初步的认识。

OK,入正题,我要实现一个Demo,就说是个Netty的聊天室吧(群聊,没有单聊的功能,demo味儿十足),不过客户端换成了安卓,一个服务器对多个客户端,所有数据经过服务器端,要定义中间信息交换组件。关于Google的Protobuf还有MessagePack和JBoss Marshalling这些编解码中间件都没有玩过,似乎有点看不懂,我的需求很简单,如下图(抱歉键盘党多年手已废掉,可能只有我自己看得懂):

Echo-Server、Echo-Pojo、Lets's Chat Relationship

一共包含三个对象:

Echo-Pojo,信息交换中间对象,用于Android端与Server端封装byte[]之后的序列化对象。

Let’s Chat,安卓客户端,构建EchoMessage或EchoFile对象,然后发送到channel;EchoMessage与EchoFile用于服务端判断信息对象的类别。

Echo-Server,服务端。ChannelRead时判断信息对象类型,决定是广播到所有用户还是保存文件。

对于ByteBuf对象一直没有弄很明白,所以上述也就没有使用。

来张安卓端(小米PAD)Let’s Chat的运行截图:

Let's Chat小米Pad运行截图
Let’s Chat小米Pad运行截图

三星手机Let’s Chat运行截图:

Let's Chat三星手机运行截图
Let’s Chat三星手机运行截图

服务端(Echo-Server)的运行截图:

Echo-Server输出日志与说明截图
Echo-Server输出日志与说明截图

上传的文件:

QQ截图20160518132859

文件名格式为:Build.MANUFACTURE+UUID.randomUUID()+”.jpg”,第二张图片为12.9M,上传时间六七秒钟,应该还有优化的空间。

Echo-Pojo的代码就不截图了,直接下载查看就可以,或者也可以继续扩展,目前只是图片跟文字。

上述功能一共分为三个程序,Echo-Pojo信息交换对象使用eclipse-Mars2开发,Echo-Server使用Intellij idea开发,Let’s Chat使用Android Studio2,有点蛋疼,但好在实现了功能,中间折腾的很多,尤其公司电脑跟家里电脑jdk不一致,一个1.8,一个1.7,因为这个出了很多莫名的错误,也浪费了很多时间,中间的各种折腾各种烦躁就不记录了。

代码注释比较少,也有很多废弃的没有删掉,只是实现了功能可以运行,没有做完全整理,有问题可以直接回复或者@EMAIL ME

借鉴刘源的NettyFileTransfer,只是我把客户端修改成了Android,然后Netty版本我用的是4.0.36Final,感谢大神。

剩下的就是把功能揉进项目里了,Keep Fighting !

项目开源地址:https://github.com/LisonLiou/netty-learning.git

Android Camera: start preview failed; API10. Android 2.3 startPreview失败解决方法

代码中使用了开源项目(OSChina 客户端2.0)的源码,zxing做二维码扫描识别。搬过来之后在Android 2.3机器运行报错:
Unexpected error initializing camera java.lang.RuntimeException: startPreview failed

startPreview失败报错详细如下:

找到了一些文章,有的说Camera的open与startPreview不能多次调用,需要设置标记,查看OSChina的源码之后发现有标记,不是这个。

后来找到说低版本的SDK需要setType一下,设置成:SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS,尝试后依然没有解决问题。

最后是借鉴了这篇文章(不知道是不是原始链接)解决了问题,holderView要放在holder的allCallback前面,完整如下:

就是这两句:

if(Build.VERSION.SDK_INT<11)
scanPreview.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

scanPreview.getHolder().addCallback(this);

OSChina客户端的2.0支持的最低版本是15,已经不再支持API10,不能PullRequest一下了。

Android控件之带清空按钮(功能)的AutoCompleteTextView自动提示

功能折腾完了记录一下。带删除按钮的AutoCompleteTextView,普通的自动提示控件用起来比较简单,准备好数组给控件setAdapter一下行了,这里要说的是提取sqlite中的数据绑定并且加上清空按钮,先来张图片。

带删除按钮的AutoCompleteTextView

最初没有用过AutoCompleteTextView的时候,就直接百度了下,想必都会得到这样一段代码:

上面的代码简单,但是不大实用,比如我的需求就是,要有清空按钮。然后继续百度,又找到了这篇文章(继承AutoCompleteTextView自定义控件ClearableAutoCompleteTextView,自定义adapter方式实现)

然后就闭着眼抄了一下,修改一点,满足了需求。其实碰上问题就百度这种方式是很不对的,所有的控件使用方法在Android Develop Docs中都有介绍:/sdk/docs/reference/android/widget/AutoCompleteTextView.html。哎,我们都是应用程序猿,但我们又连API都不看。。。或许你觉得密密麻麻的英文看着头疼?或许你又翻不了墙?或许吧,还是那句老话,成功的人找方法,而失败的人嘛,对吧。

今天想给AutoCompleteTextView加个图标,就是上图左侧那个放大镜按钮,于是找好素材,设置drawableLeft,运行,发现没有效果。于是查看自定义控件的源码(就是上面使用自定义adapter的那个链接里提到的),发现是在init方法中绑定了setOnTouchListener, 然后onTouchListener中又通过MotionEvent判断触摸的区域,而触摸的对象使用的是setCompoundDrawablesWithIntrinsicBounds,这不就是drawableLeft、drawableRight所对应的方法嘛,果断删除自定义控件ClearableAutoCompleteTextView,使用原生AutoCompleteTextView,加入drawableLeft放大镜按钮,drawableRight使用删除小图标,然后对控件setOnTouchListener,代码如下:

一下子就腰不疼腿不酸了。注意“触摸点位置判断”那行,意思是当前触摸点如果大于AutoCompleteTextView的宽度减去paddingRight的值再剪掉一个固定值(R.dimen.space_4_touch,我这里是10dip),就算触摸到了删除小图标;固定值是多少根据情况来,值越大可以触摸的空间越大,相反越小,用户可能就点击不到那块区域(删除小图标的区域)从而激发不了事件,这也算是优化用户体验的一种手段吧。

所以,按照上述代码,也可以举一反三为放大镜按钮加上事件,或者为删除小图标设置隐藏/显示条件,比如文本框没有内容时删除图标隐藏,有内容则显示等。

Continue reading

Android Debug Bridge adb – 安卓调试桥 官方文档汉化记录(原创)

名词释义:
模拟器:指使用ADT或者第三方软件创建的安卓模拟器
安卓设备(设备):指运行安卓系统的硬件、含手机、Pad,TV等

Android Debug Bride简称adb,是一个多用途的命令行工具,能够让你与你的安卓模拟器或者安卓设备进行通讯。它是一个包含三个组件的C-S程序:

  1. 客户端。运行在你的开发机上。你可以通过shell发送一条adb命令用开调用一个客户端。其他的Android工具例如ADT插件和DDMS也可以创建adb客户端。
  2. 服务端。运行在你的开发机的后台进程中,服务端管理客户端与运行在模拟器或设备上的adb守护进程的通信工作。
  3. 守护进程。是一个运行在每个模拟器或者设备上的后台进程。

可以在<sdk>/plateform-tools目录找到adb工具。

当你启动一个adb客户端的时候,客户端会首先检查是否已经有adb服务端进程在运行。如果没有,就开启服务进程。当服务进程开启之后,它会绑定本地TCP的5037端口并且监听来自adb客户端的命令(所有的adb客户端都使用5037端口与服务端进行通信)。

然后服务端与所有正在运行的模拟器/设备建立连接。服务端通过扫描模拟器/设备的开放端口(5555-5585范围内的奇数端口)来定位设备。当服务端发现adb守护进程时,就与这个端口建立连接。需要注意的是模拟器/设备获取的是一对连续的端口号(偶数端口号用于控制台连接,奇数端口号用于adb连接)示例:

模拟器1   控制台端口号:5554
模拟器1   adb端口号:5555
模拟器2   控制台端口号:5556
模拟器2   adb端口号:5557

如上所述,模拟器已经与adb的5555端口建立了连接,与控制台监听5554端口是一样的。

一旦服务器与所有模拟器建立连接,就可以通过adb命令访问这些设备。基于服务端管理着所有模拟器/设备并且处理多个adb客户端的命令,你可以通过任意一个adb客户端控制任意的模拟器/设备(或者通过脚本)。

Enabling adb Debugging 开启adb调试


为使用usb连接的设备使用开启adb,需要在设备的系统设置->开发者选项中开启USB调试。

在Android4.2或者更高版本的系统中,开发者选项默认是隐藏的。显示的话,需要依次点击设置->关于手机,连续点击内部版本号(Build Number)七次。然后返回上一个界面的底部会出现开发者选项。

在有些设备中,开发者选项界面可能所在的位置或者名称会有不同。

注意:当用电脑连接一个运行着Android4.4.2或者更高版本的系统,系统会弹出一个对话框(显示RSA key)询问是否允许通过这台电脑进行调试。这个安全机制确保用户的设备不会被USB调试或者执行其他adb命令,除非用户点击对话框的确认按钮。这需要你的adb版本是1.0.31(在SDK Plateform-tools r16.0.01或者更高版本中)为了调试运行Android4.2.2或更高版本的系统。
更多关于连接一个通过USB连接的设备,请阅读Using Hardware Devices。

Syntax 语法


你可以从开发机上通过一个命令行工具发送一条adb命令(或者一个脚本),用法如下:

如果当前只连接了一台模拟器/设备,adb命令就会发送到这台默认的设备。如果有多个模拟器/设备在运行并且已连接,就需要使用-d-e、或者-s选项来指定目标设备并且执行命令。

Commands 命令


下面这张表列出了所有adb支持的命令,以及他们的解释及用途。
表1 可用的adb命令

类别 命令/选项 描述 注释
Target Device -d 向唯一连接的USB设备执行一条adb命令 如果连接多台设备将返回错误
-e 向唯一连接的模拟器执行一条adb命令 如果正在运行多个模拟器将返回错误
-s &lt;serialNumber&gt; 向指定的模拟器/设备执行一条adb命令,模拟器名称例如:emulator-5556 See Directing
Commands to a Specific Emulator/Device Instance
.
General devices 打印所有已经附加的模拟器/设备列表 See Querying for Emulator/Device Instances for more information.
help 打印adb支持的命令列表
version 打印adb版本号
Debug logcat [option] [filter-specs] 将log data输出到屏幕
bugreport 将系统信息,状态,logcat数据输出到屏幕
jdwp 在一个给定的设备上打印可用的JDWP进程列表 可以使用端口转发 jdwp:<pid> port 转发指定的连接到一个特定的JDWP进程,例如:
adb forward tcp:8000 jdwp:472
jdb -attach localhost:8000

 

Data install &lt;path-to-apk&gt; 将一个安卓应用程序(指定apk文件的全路径)发送到模拟器/设备
pull &lt;remote&gt; &lt;local&gt; 从模拟器/设备拷贝一个指定的文件到开发机。
push &lt;local&gt; &lt;remote&gt; 将一个指定的文件拷贝至模拟器/设备上。
Ports and Networking forward &lt;local&gt; &lt;remote&gt; 将本地socket连接的指定端口转发至指定的远程模拟器/设备端口 指定端口可以使用这些格式:

  • tcp:&lt;portnum&gt;
  • local:&lt;UNIX domain socket name&gt;
  • dev:&lt;character device name&gt;
  • jdwp:&lt;pid&gt;
ppp &lt;tty&gt; [parm]... 通过USB运行PPP(点对点协议):

  • &lt;tty&gt; — the tty for PPP stream. For example dev:/dev/omap_csmi_ttyl.
  • [parm]... — zero or more PPP/PPPD options, such as defaultroute, local, notty, etc.

    注意不要自动启动一个PPP连接

Scripting get-serialno 打印adb示例的序列号串 查看 Querying for Emulator/Device Instances 获取更多信息
get-state 打印模拟器/设备的adb状态
wait-for-device 在设备上线前阻止命令执行。 可以向adb预置这条命令,这种情况下,adb会等待模拟器/设备连接后才执行adb命令,示例:

注意这条命令不会导致adb一只等待整个系统启动完毕。因此,你不应该预置一条需要等待整个系统完全启动的命令。举例来说,安装命令需要Android Package Manager启动,而Android Package Manager需要整个系统完全启动,类似于这样的命令

会发送安装命令当模拟器或设备连接到adb服务端,但之前Android系统已经完全启动,因此会得到一个错误的结果。

Server start-server 检查adb服务端进程是否正在运行,如果没有运行,则运行它。
kill-server 结束adb服务端进程
Shell shell 在目标模拟器/设备上开启一个远端shell 查看 Issuing Shell Commands 获取更多内容
shell [shellCommand] 在目标模拟器/设备上发送一个shell命令执行后退出。

要不先到这儿。

Android解决:The content of the adapter has changed but ListView did not receive a notification

项目中的错误,记录一下。

完整的错误信息如下:

大意是说adapter内容已改变,但listview未收到通知,上下文不同步了。

精简一下,我有两个ActivityA和ActivityB。首先打开ActivityA,显示一个列表(listView),然后点击一个按钮startActivityForResult打开了B,B中添加了信息提交后setResult到A。B负责添加数据,添加完数据回到A,通知A重新load一遍远端数据,没问题(不要问我为什么要重新load一遍)。

在Activity A中单独起了一个线程去处理任务(请求远端service获取数据),handler收到message之后启动这个线程t,然后Activity A又startActivityForResult了另一个Activity B(这时候ActivityA已经onPause了,但是线程依然在运行,而且还在handler的callback队列中),当Activity B处理完逻辑setResult给Activity A(并且把自己给finish掉),需要让ActivityA重新请求service数据,也就是说线程t要重新运行一次,这时候handler里面已经有线程t这个对象了,然后就导致了上述的错误(好像这么说很牵强啊),参照CSDN牛人的文章,还是线程与上下文的事。

解决办法,在ActivityA的onPause将线程t从handler的callback中移除(然后handler再次收到同样的请求service的message时会重新将此线程t加入到callback队列中),如下:

 

ActivityA的完整代码如下:

贴首歌在:

新解:

还是上面的错误信息,注意这句:

确保adapter的内容不要在后台线程中修改,只能从UI线程中修改。而我的代码是直接message交给handler去处理的,难道这也算后台线程?于是想到了Runnable对象,它是依附于UI线程的,然后使用了handler.post(new Runnable())的方法,测试成功。

Android坑之java.net.SocketException: Permission denied

OK,不知道这算不算是一个坑,记录一下吧,一上午都过去了。。。

先上代码,简单的网路请求上传图片,做了精简,只留关键部分。
HttpURLConnection请求
一执行到con.getOutputStream()就报错:java.net.SocketException Permission denied刚开始以为代码抄的不好,各种百度,发现没有加权限:

<uses-permission android:name=”android.permission.INTERNET” />

结果加上也不好使,又陆续加了
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />

都不好使。。。后来单开线程请求也不好使(代码已省略)。

然后各种百度找到的都是那几篇文章, 要么是模拟器忘了开sd卡,可是我用的真机啊。。。
然后各种胡思乱想,难道是因为我的手机没root?这倒是跟没有权限的用户执行命令的时候没权限是同样的提示啊。。。难道我的内存卡没有mount?难道我的图片存到了系统空间,我没有权限读?bla bla bla

后来找大神要了段代码,发现跟我写的一样,同样报错。。。
知道后来又搜到了这篇文章:
《Android之EACCES (Permission denied)与Permission denied异常探密》
当中的第二条,permission写的地方不对,检查了我的manifest发现我的permission节写在了application里面(为什么当初我要这么做?我也不知道,为什么别的permission没有报错,我tm怎么知道),然后把所有permission拿出来放到与application平级,终于报的错不一样啦。。。。
java.net.ProtocalException Does not support outputOK,另一个坑走起。

 

记@Android Studio使用初体验杂记

最初时候用eclipse JUNO,谷歌给出的adt-bundle包下载下来什么都有了,sdk manager、plateform-tools、adb等等,只需要自己翻墙下载相应的sdk就可以helloworld了,当时的Android Studio 1.x版本那个难用啊。。。

但是google已经宣布不在支持eclipse了,虽我等已经习惯了eclipse,但是谷歌大法说用新的,用旧的出问题不负责,至少android开发领域,eclipse的用户与资料不会在上升了,于是这两天捣鼓了下Android Studio,杂记下。

1.Android Studio基于IntelliJ IDE,我也没怎么用过,不过响应速度确实是快。

2.Android Studio可以使用ADT-Bundle包中的sdk目录,若之前已经下载了个版本的SDK,则可以直接指向此目录,无需二次下载,无需费时费力费硬盘。

3.之前整理代码的快捷键是Ctrl+Shift+F,现已加入肯德基豪华午餐:Ctrl+Alt+L,此热键与QQ锁界面的热键冲突,怎么办自己看着办。

4.关于Android Studio的默认界面(真是丑的一比)。修改IDE的字体及字号:File>Settings 左上角搜索框输入appearance,找到Appearance & Behavior > Appearance,右边勾选Override default fonts by(not recommended),然后Name跟Size就自己看着选了。

5.关于编辑器的字体(主题)设置,我使用的是Darcula。File>Settings>Editor>Colors & Fonts,点击Save As按钮,将当前主题另存一份,确定之后,在点击左侧树菜单Colors & Fonts下的Font,然后选择字体跟字号,OK。

6.Android Studio的布局实时预览,赞一个。

7.Android Studio的Develper Services赞一个,好像可以一键增加AD,Analytics还有推送那些东西?暂时还没研究。

8.对于Gradle的概念还是没有弄的很明白,跟make有什么区别?Groovy更是不懂了。

9.那么Maven到底是怎么回事,统一各版本jar包集合平台?不冲突依赖解决方案吗。。。

10.集成的ADT Manager好像比JUNO那个启动快很多。

11.Android Studio启动时会很卡,其他有些操作有时候也会很卡,多注意看看控制台输出,IDE加入了Gradle自动构建,很多时候都在帮你clean或者rebuild或者render项目文件,所以会卡,但是自动提示功能我觉得比宇宙最强IDE Visual Studio还要强了。

12.如何删除项目?新建了个hello world删不掉了吧。File>Project Structure,在下面的Modules中选中要删除的项目,点击上面的”减号“按钮。

暂时就这些,hello world去了Android Studio主界面截图