[转载]Android APK反编译详解(附图) - IT-Homer - 博客频道 - CSDN.NET

mikel阅读(896)

[转载]Android APK反编译详解(附图) – IT-Homer – 博客频道 – CSDN.NET.

这段时间在学Android应用开发,在想既然是用Java开发的应该很好反编译从而得到源代码吧,google了一下,确实很简单,以下是我的实践过程。

在此郑重声明,贴出来的目的不是为了去破解人家的软件,完全是一种学习的态度,不过好像通过这种方式也可以去汉化一些外国软件。

 

 

本文Android反编译教程,测试环境:

Win7 Ultimate x64

Ubuntu 12.04 x86_x64

反编译工具包 下载 (2012-10-10更新)

 

一、Apk反编译得到Java源代码

下载上述反编译工具包,打开apk2java目录下的dex2jar-0.0.9.9文件夹,内含apk反编译成java源码工具,以及源码查看工具。

apk反编译工具dex2jar,是将apk中的classes.dex转化成jar文件

源码查看工具jdgui,是一个反编译工具,可以直接查看反编译后的jar包源代码

 

dex2jar 和 jdgui 最新版本下载,分别见google code:

dex2jar(google code)

jdgui(google code),最新版本请见 官方

 

具体步骤:

首先将apk文件后缀改为zip并解压,得到其中的classes.dex,它就是java文件编译再通过dx工具打包而成的,将classes.dex复制到dex2jar.bat所在目录dex2jar-0.0.9.9文件夹。

在命令行下定位到dex2jar.bat所在目录,运行

dex2jar.bat    classes.dex

生成

classes_dex2jar.jar

 

然后,进入jdgui文件夹双击jd-gui.exe,打开上面生成的jar包classes_dex2jar.jar,即可看到源代码了,如下图:

HelloAndroid源码在反编译前后的对照如下:

 

 

二、apk反编译生成程序的源代码和图片、XML配置、语言资源等文件

如果是汉化软件,这将特别有用

首先还是要下载上述反编译工具包,其中最新的apktool,请到google code下载

apktool(google code)

 

具体步骤:

下载上述反编译工具包,打开apk2java目录下的apktool1.4.1文件夹,内含三个文件:aapt.exeapktool.batapktool.jar 

注:里面的apktool_bk.jar是备份的老版本,最好用最新的apktool.jar

 

在命令行下定位到apktool.bat文件夹,输入以下命令:apktool.bat  d  -f   abc123.apk   abc123,如下图:

上图中,apktool.bat 命令行解释:apktool.bat   d  -f    [apk文件 ]   [输出文件夹]

 

反编译的文件如下(AndroidManifest.xml为例):

 

将反编译完的文件重新打包成apk,很简单,输入apktool.bat   b    abc123(你编译出来文件夹)即可,命令如下:

打包apk后的文件在目录C:\HelloAndroid下,生成了两个文件夹:

build

dist

其中,打包生成的HelloAndroid.apk,在上面的dist文件夹下,Ok

 

 

三、 图形化反编译apk

上述步骤一、二讲述了命令行反编译apk,现在提供一种图形化反编译工具:Androidfby

首先,下载上述反编译工具包,打开Androidfby目录,双击Android反编译工具.exe,就可以浏览打开要反编译的apk

 

 
本文反编译工具包整理历史版本:

Android反编译工具包(升级)  (2012-10-10)

Android反编译工具 (2012-08-21)

android反编译工具 (2011-08-28)

 

 

本博客反编译方法,仅供参考学习使用,禁止用于非法和商业等用途,谢谢!

 

转载Android apk反编译学习【天天酷跑】 - SegmentFault

mikel阅读(922)

转载Android apk反编译学习【天天酷跑】 – SegmentFault.

微信游戏出来之后,网上出现很多破解教程和攻略,修改最多的有无敌打飞机,无限弹药,刷分数,天天酷跑刷金币等。看了下破解方法不外乎以下两种:

  1. 反编译游戏apk包,修改smali字节码,重新打包。
  2. 暂停运行时软件,在内存中查找关键游戏数值,直接修改内存数据。

(此外听朋友说起其他破解方式,如修改本地cache文件中的程序数据,但前提得是数据没有存储于服务端)

工作时长期疲于应对应用层开发,早该学习一些基础的破解和防破解技术,也有助于保护自己开发的应用,且最近痴迷《天天酷跑》,天天努力赶超小伙伴, 遂尝试将其作为“破解”对象娱乐一下(这可比玩出高分再向小伙伴炫耀有趣多啦),由于之前没有任何破解经验,并不知道能破解到什么程度,抱着学习的态度一 步步尝试之。

动手之前先学习前辈经验,这里有一篇打飞机破解文章,讲解详细清晰:http://www.blogjava.net/zh-weir/archive/2013/08/14/402821.html

归纳一下文章内容,微信客户端和其游戏是平台与插件关系,整个打飞机游戏是一个jar包,位于微信apk的assets目录下,微信通过动态加载的方式运行,其中包含classex.dexresso库 (用JD-GUI打开这个jar包可以看到)。动态加载时会拷贝该jar包到data目录,解压后将其中的资源释放出来。拷贝之前将进行一次签名校验,如 果jar包被修改过,签名校验将无法通过,想要破解游戏,必须对jar包中的代码进行修改,那就得找到绕过签名校验的方法。

开始之前先学习一下签名校验的原理和如何绕过签名校验。

Android签名生成步骤和原理:

我们在App开发完成后将其编译为apk包,这个时候包中没有签名信息,如果直接通过adb install安装到手机上,将会报错:[INSTALL_PARSE_FAILED_NO_CERTIFICATES]

解压apk包可以发现未签名的apk中不包含META-INF目录,该目录在签名后生成,其中包含签名信息相关的3个文件CERT.RSACERT.SFMANIFEST.MF,签名过程就是这3个文件的生成过程。

签名第一步:遍历apk中所有文件(entry),逐一生成SHA-1数字签名,然后通过Base64编码,写入MANIFEST.MF文件。

vim查看MANIFEST.MF文件内容如下:

Manifest-Version: 1.0
Created-By: 1.0 (Android)

Name: res/drawable-hdpi/m03_0.png
SHA1-Digest: Odo4l7R6YcmMl9HtbhLpb8hChts=

Name: res/layout/shop_fav_item.xml
SHA1-Digest: CVvGtpAo1cLWo7OQfR3tZQwYOY4=
......

第二步:使用SHA1-RSA算法,用私钥对MANIFEST.MF摘要文件签名,并对其中每个文件摘要签名,生成CERT.SF文件

vim查看其内容如下:

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: 4pIv24erc5QG+i/kLOOhG6n+EdY=

Name: res/drawable-hdpi/m03_0.png
SHA1-Digest: 9ZYIfLFplSPcDEIxLJl0JUzmJLs=

Name: res/layout/shop_fav_item.xml
SHA1-Digest: nqQ+nd6yZn3sB5LfHltWOYlXJMA=

......

(SHA1-Digest-Manifest:是对MANIFEST.MF文件的摘要签名后的值)

第三步:生成CERT.RSA文件,其中记录了RSA公钥,加密算法等信息用于签名校验,该文件是二进制格式,有兴趣可以研究下。

插入学习一下RSA加密和签名原理:

RSA有2种应用方式,加密和验证,这里只用到了验证,为了使整个实现过程更加清晰明了,先学习下其原理,这里我们可以假定加密和验证过程无法被破解,通过私钥加密的信息唯有公钥才能解密。

RSA加密过程(以客户端A发送一条消息给客户端B为例,其中A持有公钥,B持有私钥):

  1. 客户端A消息原文:msg
  2. 将msg映射为整数(实际上分为很多段整数):Int(msg)
  3. 用公钥进行加密:En( Int(msg) ) )
  4. ————发送加密后的信息————
  5. 客户端B收到加密信息:En( Int(msg) )
  6. 用私钥解密:De( En( Int( msg) ) ) = Int(msg)
  7. 还原msg:UnInt( Int(msg) ) = msg

(通过公钥加密的信息唯有私钥才能解密,可以确保信息不会泄漏)

RSA签名与验证过程(客户端A发送一条签名后消息给客户端B,B验证该消息是否为A发出或者未被篡改,其中A持有私钥,B持有公钥):

  1. 客户端A消息原文:msg
  2. 对msg提取摘要:Dig(msg)
  3. 对摘要Base64编码:Base64( Dig(msg) )
  4. 用私钥进行签名(把摘要当作已经加密后的内容,用私钥解密):Sign( Base64( Dig(msg) ) )
  5. ————发送消息和签名————
  6. 客户端B收到信息和签名:msg 和 Sign( Base64( Dig(msg) ) )
  7. 对msg提取摘要:Dig(msg)
  8. 对摘要Base64编码:Base64( Dig(msg) )
  9. 将摘要当作未加密过的内容,用公钥加密:En( Base64( Dig(msg) ) )
  10. 签名验证,即判断En( Base64( Dig(msg) ) ) 和Sign( Base64( Dig(msg) ) )是否相等。(这里的Sign跟上面的De一模一样)

(通过公钥加密的信息唯有私钥才能解密,可以验证这条消息肯定是A发出来的)

Android里面签名验证的实现:

覆盖安装时用旧版本的公钥对新安装apk包中MANIFEST.MF进行加密,生成新的MANIFEST.MF’文件,再和原始的 MANIFEST.MF文件对比所有的摘要信息是否一致,以此确保是否为同一个机构发布的升级app。(实际当然不是生成新的MANIFEST.MF’文 件,可以这么理解)

了解签名原理之后,再结合一下签名机制的应用场景:

  1. 个人开发者或机构想要通过Android Market发布app,必须使用官方提供的keystore证书为程序签名,作用是防止你的app被恶意覆盖安装。(app升级时若签名不一致将不能覆盖安装)。
  2. 运行时程序自检签名(如果签名不符可以直接crash,强行退出,插件调用不起来等)。

回到如何绕过打飞机插件签名验证问题,首先破解微信之后不可能用私钥重新签名,所以覆盖安装不考虑,卸载正版微信即可。其次则是考虑如何绕过打飞机 插件加载时的签名校验,作者尝试利用ANDROID-8219321漏洞(修改插件包内容而签名校验时计算出的校验值保持不变),但利用该漏洞将会在插件 包中产生2个classex.dex文件,导致文件改动,而打飞机插件包的文件名中巧妙的包含了该jar包的md5值,任何jar包的改动均将导致md5 值发生改变,微信插件加载时将进行签名+插件md5值双重校验,要想绕过只有修改微信的smali code,找到md5校验方法然后屏蔽掉(利用漏洞的话签名就不用屏蔽)。

作者因为没找到md5校验代码,最终采用了系统app+屏蔽签名校验的方法(系统app签名校验时只会校验mainfest.xml文件,但是必须 安装在system/app目录下,手机需要root然后将system分区mount为可写),也即jar包可以修改,修改后计算出新的md5并且重命 名即可, 安装时微信的签名校验依旧可以通过,同时屏蔽微信对插件的签名校验,大功告成!接来下就是对打飞机插件的smali code一顿狂改了。

仔细分析了好久发现作者在绕过签名这里有些多余,既然不可能使用私钥签名破解后的app,绕过签名的意义就在于可以覆盖安装正版微信(签名保持“不 变”,利用ANDROID-8219321漏洞,或者安装到system/app目录下作为系统应用,然而既然决定安装到system/app目录下,考 虑安装时微信的签名校验依旧可以通过这个问题纯属多余)。我们完全可以使用自签名,给破解后的微信app和插件用自己的证书签名。这样微信app和插件均可以随意修改之。

从签名的验证和一系列绕过办法可以发现比较靠谱的安全策略是程序运行时使用navite代码进行签名自检,比如将1.0版本apk中 MANIFEST.MF文件的签名值存储在云端,程序运行时通过native代码将用公钥解密后的值发送至云端进行对比,只有这种方法破解难度较大。

搞清楚了打飞机的破解原理,接下来尝试破解天天酷跑,然而实际情况却有很多不一样。

首先天天酷跑和打飞机两者的插件机制不一样,天天酷跑是一个独立应用,和微信属于app间相互跳转关系。此外天天酷跑的游戏代码几乎全在native层实现(NPEEngine),基本上无法修改,令人沮丧,刚开始的时候并没有意识到这一点,但是无论无何还是要玩一玩的。

使用apktool反编译之后,可以修改其中的res资源,ogg声音,配置文件,smali code。

然后重新打包,使用自签名,重新安装。运行之后通过微信好友连接玩不了-_-!,应该是签名校验或者其他的什么校验没通过(没有进一步研究),但是可以连接qq好友,qq使用sso登录,校验竟然通过了,也算是发现个漏洞。

游戏大部分资源都在assets目录中,其中的图片,配置,地图等都是二进制格式的无法修改。只有少量几个json和xml的配置文件可以改改,但 都涉及不到核心,只能简单修改下默认角色的重力,弹跳速度等,动态性做得太好,很多资源都是运行时从服务端load,比如新增的角色,升级信息,宠物,坐 骑等。

其次学习了下smali code,这个简单又好玩,最后附上修改的截图,只能拿去逗一下小伙伴…(另外换了下icon,恶搞的声音,角色弹跳属性)

启动时加个对话框:

dialog

邀请或分享给好友时,修改展示文案,并把下载链接替换为破解app的下载地址

screenshot

[转载]开源分布式计算引擎 & 开源搜索引擎 Iveely 0.5.0 为大数据而生 - Iveely Liu - 博客园

mikel阅读(801)

[转载]开源分布式计算引擎 & 开源搜索引擎 Iveely 0.5.0 为大数据而生 – Iveely Liu – 博客园.

产生背景

 08年的时候,我开始接触搜索引擎,当时遇到的第一个难题就是大数据实时并发 处理,当时实验室的机器我们可以随便用,至少二三十台机器,可以,却没有程序可以将这些机器的计算性能整合起来,后来听说了Hadoop,但是当时的 hadoop还很脆弱(记得没错是0.2.0),源码量也很少,用了很久,发现它不适合我们的搜索引擎。
   后来没办法,我在程序中将爬虫和数据处理写成分布式网络通信的。但是导致代码非常臃肿,而且每一个应用程序的运行,都需要写一套网络通信和任务分布。 09年下半年,中某地区断网半年,只能访问该地区本地的网络,然后想到我们搜索引擎的商业运营的机会来了,当时用了大约10台机器(实验室超低配置机器, 内存512M,单核),可是访问量大了之后,系统直接崩溃,调试和运营都遇到巨大的挑战。后来我不断的重构搜索引擎,发现怎么做都很困难。
  于是,我一直在思考,我们需要一个平台或者叫计算引擎,让程序员写的程序,这些程序浑然天成的支持分布式计算,这还不够,还希望能够实时返回计算结果,而程序员却不知道有多少台机器在为运行它的程序
  这就是我构思的分布式计算引擎,而今天,它已经实现,尽管不完美。它是我承诺的Iveely Search Engine 0.5.0 的分布式搜索引擎的基础,它的名字叫Iveely Computing,一款开源分布式计算引擎,为大数据实时处理而生(Iveely是“I void everything,except love you!”的首字母简写)。开源主页、源码下载:https://github.com/Fanping/iveely/releases。最初我们命名的开发代号叫:Dream,Dream平台曾经在新华网出现过,早期版本的数据分析。

 基本结构

 整个Iveely Computing的基础库是Iveely Framework,Iveely Framework基础结构如下图所示:

主要是在进程,文本处理、日志、网络、算法和数据结构上,进行了二次封装,整 个底层框架依然是.NET Framework。Iveely Framework与Iveely Computing是两个project,但是Computing对Framework会有依赖。Iveely Computing基础结构如下图所示:

     整个过程涉及6个主要exe文件,每个exe都是Iveely Computing中非常重要的一环。
     Iveely.CloudComputing.Cacher.exe:分布式缓存。用于分布式存放小数据。采用一致性hash算法。
     Iveely.CloudComputing.Merger.exe:全局运算器。在分布式计算中,常常会涉及到全局运算,每一个节点都在计算,那么当遇到全局求和、求平均等等之类的时候,就需要Merger来操作。
     Iveely.CloudComputing.StateCenter.exe:状态控制中心。用于当前所有worker、Merger的控制运算中心。
     Iveely.CloudComputing.Worker.exe:用户任务的真正执行者。
     Iveely.CloudComputing.Supervisor.exe:用于监控Worker的执行情况,倘若Worker异常中断,则自动重启worker。
     Iveely.CloudComputing.Client.exe:用户的控制台操作程序,用户提交程序,查看自己应用程序的运行状态。

编译部署

我在微软一年多,离开的时候才发现,学到最多的不是程序、不是架构,而是用户 体验,总结一句话:你的程序,不要消极用户心情。所以我在部署的时候,依然采用了“一键式本地部署”。在开源社区,这是很多开源人需要学习微软的一点,很 多开源程序,搭建编译部署步骤非常繁琐,很容易消极使用者心情。
  编译
  编译之前,请先在开源主页上 下载0.5.0源码。无须下载其它任何第三方软件,你所下载的源码,将会是Iveely Computing中任何dll的源码。下载后解压,直接用Visual Studio 2012以上版本打开整个解决方案(Iveely.sln),打开之后,你平时怎么编译,现在就怎么编译。为了更好的在开发中尽可能测试到Iveely Computing,Debug Build和Release Build有一定区别:
  Debug Build:解决方案中,所有代码均会被编译,包括测试代码,编译后生成的文件在..\ Iveely\Iveely.CloudComputing\Debug
  Release Build:解决方案中,只有被使用到的代码会被编译,编译后生成的文件在..\Iveely\Iveely.CloudComputing\Release。
  本地部署

  在本地,一键式部署,只需要在你编译生成的文件加下找到“Deploy-Local-Environment.bat”双击,整个本地环境将会全部搭建完毕。“Deploy-Local-Environment.bat”文件中的批处理内容如下:

              start “StateCenter” Iveely.CloudComputing.StateCenter.exe
              start “Supervisor” Iveely.CloudComputing.Supervisor.exe
              start “Worker:8001” Iveely.CloudComputing.Worker.exe 8001
              start “Worker:8002” Iveely.CloudComputing.Worker.exe 8002
              start “Merger” Iveely.CloudComputing.Merger.exe
              start “Cacher” Iveely.CloudComputing.Cacher.exe

  其实就是启动应用程序的命令,Worker的数量可根据自己机器的性能进行调整。当然也有相应的关闭的批处理文件,也是在编程生成的文件夹下“Stop-Local-Environment.bat”,文件中的批处理内容如下:

              @taskkill /fi “WINDOWTITLE eq StateCenter”
              @taskkill /fi “WINDOWTITLE eq Supervisor”
              @taskkill /fi “WINDOWTITLE eq Worker:8001”
              @taskkill /fi “WINDOWTITLE eq Worker:8002”
              @taskkill /fi “WINDOWTITLE eq Merger”
              @taskkill /fi “WINDOWTITLE eq Cacher”

  当在启动的时候,如果Worker的数量自己有调整,请记得在“Stop-Local-Environment.bat”文件中也做相应调整。

  集群部署
   在实际应用当中,更多的是集群部署整个Iveely Computing。不管如何部署,请把编译后的文件夹“..\Iveely\Iveely.CloudComputing\Release”拷贝到你的 目标机器上。Merger和StateCenter请拷贝到性能较好的机器中去,并首先双击运行。Worker根据任务的多少去部署,一般100G的数 据,部署在4台内存4G、CPUi5双核的机器上就很容易处理,每台机器启动两个worker。Cache根据项目缓存是否经常用到,如果经常用到,建议 多放在几台之中,如果整个过程你认为你不会用到缓存,至少请部署一台,但是缓存适合非常高效的小数据交换。建议整个过程中,多用缓存,少用Merge。

编写自己分布式程序

  编写您的应用程序之前,您必须了解,Iveely Computing有哪些指令Client.exe支持直接Main函数传递方式和普通命令行方式,二者效果一致。命令行指令格式如下:

  1) submit[file path][namespace.classname][app name]
  提交应用程序指令。[file path]是指应用程序的脚本路径;[namespace.classname]是指代码脚本中,执行的命名空间和类名;[app name]是这个应用程序的名称。例如:
Submit C:\\Example.cs MyNameSpace.ClassName ExampleApp
  2) split[file path][remote path][split string] [partition keys]
切分本地大数据文件到各个节点指令。[file path]是本地大文件的路径;[remote path]是结点里面的路径。例如:
Split C:\\BigData.txt /Data/big.txt
[split string]与[partition keys]属于可选项,当没有此参数时,partition按照大小平均分配,如果有这两个参数,则按照partition key进行分配,有可能数据不均匀。例如:
Split C:\\BigData.txt /Data/big.txt , 2 3
含义是切分本地文件C:\\BigData.txt到远程服务器结点,命名为/Data/big.txt 切分符号为“,”,按照第二列和第三列进行partition。
  3) download[remote path][file path]
下载各个节点的数据到本地指令。[remote path]是节点中的数据路径;[file path]是本地存储的路径。例如:
Download /Data/big.txt C:\\BigData.txt
  4) delete[remote path]
删除某个数据指令。删除操作将会在整个系统的每一个节点中删除,[remote path]是节点中数据的路径。例如:
Delete /Data/big.txt
  5) rename[file path][new file Name]
  重命名操作指令。[file path]是在远程节点中的文件路径名称。[new file Name]重新命名的名字。例如:
Rename /Data/big.txt newbig.txt
更改后的实际文件名:/Data/newbig.txt。
  6) list[/folder]
显示文件目录指令。[/folder]是指目录路径,默认是根目录。例如:
  7) kill [task name]
  用于停止正在运行的程序。
  8) task
  显示所有正在运行的程序。
  9) exit
  退出指令。
  现在开始你的编程之旅,方法及其简单,新建你的C#应用程序,并且添加Iveely.CloudComputing.Client引用如下图


  Iveely.Framework和NDatabase也是允许添加的,其它非.NET引用添加将会导致错误。在你的C#工程中,添加一个类,继承类Application,并实现该类的方法Run。this.Init(args)方法需要显示添加。
  在这之下,你可以按照您以前的思维编写任意的程序了,写完之后,按照下面的方法提交:


  1. 双击Iveely.CloudComputing.Client.exe。
  2. 按照submit命令提交您的程序。

实践示例

  词频统计(Word Count)应该是所有分布式计算中的“Hello World”程序,不管是Hadoop,还是Storm都是采用“Word Count”为大家打开初步认识。Iveely Computing依然采用“Word Count”作为初级入门应用。
  按照常用的逻辑思维,我们会考虑用一个哈希表来存放,如果插入的单词存在,则+1,如果不存在则放入哈希表,次数为1。在Iveely Computing中也是采用这样的方法进行计算。

View Code

  这就是Iveely Computing中实际用到的代码,唯一不同的地方在于最后一行代码Mathematics.CombineTable(table),其实也就是开发 需要清楚的知道,某些代码是本地运行代码,某些代码是全局运行代码。如果不执行Mathematic.CombineTable(table),则 table将是本地运行的结果,而非全局运行的结果,结果理所当然是错误的。在此,再次说明,如果本地代码运行完毕,需要生成全局数据,则需要调用 Mathematic里面的方法,生成全局数据,调用完后的数据如果需要写到文件,则记得global=true。反之,global=false。示例源码请参见源码包中的Example_WordCount.cs文件。演示视频下载[8M]

Iveely Search Engine 0.5.0

      如果您觉得WordCount的示例,不足以为您证明Iveely Computing强大的能力,那么下面一个希望您能够喜欢-迟来的Iveely Search Engine 0.5.0。Iveely SE沉寂了大半年,很多社区朋友,问我是否停止了Iveely SE前进的步伐,我说没有,沉寂是为了更大一步的跨越。在0.4.0的时候,我说IveelySE的0.5.0版本要实现分布式。今天它结合Iveely Computing一起诞生了。源码在这里下载。
IveelySE 0.5.0的代码量不足800行,也许你觉得吃惊,但这正是Iveely Computing的魅力。先看下运行截图。

     Iveely SE 0.5.0在0.4.0上的至少有下列三大突破:
     1. 强大的分布式计算引擎的支持, 即Iveely Computing的支持。将0.4.0的独立应用程序变成了分布式应用。将Iveely Computing中的分布式缓存、存储、计算引入,使得0.5.0的计算性能获得超越。基于Iveely Computing也使得容灾处理变得自动化,而无须人工干涉。
     2. 插件式功 能扩展,模块更加独立。在0.4.0中,若为搜索引擎添加了新的功能,则需要停止0.4.0然后重新启动。在0.5.0中,添加的新功能将不会影响以前的 功能正常运行,只需将新的功能写成基于Iveely Computing的应用程序,随时监控该功能的运行情况,也可随时中止。
     3. 支持问题式搜索、关联性搜索。问题式搜索是指,例如搜索“金正恩的姑父是谁?”的时候,答案直接能够给出“张成泽”,下列截图中的第一条记录及时问题式搜索结构[更多这样的示例,参见我的微博]。关联性搜索,是指给出一个事或物,能够直接给出与它相关联的事物。下图中的右边关系图即是。

      Iveely SE 0.5.0是基于Iveely Computing上运行起来的,因此在Iveely Computing环境搭建完毕之后,需要通过Iveely.CloudComputing.Client.exe提交搜索程序:submit Backstage.cs Iveely.SearchEngine.Backstage Searcher。如下图所示:

然后通过task指令,查看运行状态,可以看到当前有两个结点正 在运行,这就表示搜索引擎已经在两个结点运行起来。搜索页面需要自己定制,在Iveely.SearchEngine中有一个Library类,可以直接 调用。若您采用其它编程语言,可以调用Query.asmx这个Web Service。这样很简单的就把搜索引擎搭建好了。搜索效果如前面两图所示。

捐助我们

也许您会很奇怪,为何需要捐助,我们需要服务器。数据量越来越大,我们自身的服务器已经不能继续支撑我们的服务器,我们渴望将Iveely.com运行起来,可是硬件限制了我们。因此,我们恳请若您想在硬件上捐助我们,可以捐助服务器[这里捐助]或者支付宝资助[这里资助],我们将心存感激。我们正如wiki一样,让每个人平等自由的获得知识,我们将继续致力于开源事业,倘若达到一定条件,我们将会让Iveely.com正式运行起来。

结    语

  世界变化很快,在我们这个浮躁的世界,我们希望自己能坚持理想,总有一天,我们都会逝去,不管是在过去,还是当前,甚至未来,我们需要的不仅仅的是生活的物质基础,我们还需要生活的快乐,而最快乐的,莫过于为理想而奋斗。

转载Vmware10下安装Mac OS X Mountain Lion10.8.5_软件应用论坛_太平洋电脑网产品论坛

mikel阅读(833)

转载Vmware10下安装Mac OS X Mountain Lion10.8.5_软件应用论坛_太平洋电脑网产品论坛.

最近,不断有人问起,如何在vmware下安装MAC系统。起因是以前曾发过一篇贴,在vmware8下安装MAC的方法。于是,重新下载了最新版苹果系统10.8.5,终于成功安装。现将注意事项及过程与各位朋友共享。

一、准备工作:
(1)虚拟机系统:vmware workstation 10,(或vmware player),请自行在网上下载。
(2)vmware的MAC补丁包。如果你使用的是vmware10,请下载补丁包V1.20,如果是vmware8或9,请下载补丁包V1.10补丁包120是卡饭论坛中的网友hpmlo提供的。
(3)苹果最新版操作系统OS X Mountain Lion,下载地址:http://kuai.xunlei.com/d/dBhJEAIdmABxhjZS8e7

二、安装软件(这一部分是关键,过了这一关,后边安装就很简单了)
由于此前大家常用的mac补丁unlock 110,在vmware 10下无法使用,故此需要使用unlock 120来打补丁。所以,在使用补丁前,请注意查看自己使用的vmware版本。

由于苹果系统是DMG镜像,虚拟机不支持此格式,故需要转换为ISO格式。特别说明:先用“好压”或ultraISO,把下载的DMG文件中2.hfs文件解压出来,然后从2.hfs文件中,把那个4G的文件提取出来,文件名是installESD.dmg,然后把这个installESD.dmg,利用ultraISO转换成ISO格式。否则,安装不上,不要怪我没讲清楚。

运行vmware补丁:把下载的补丁解压后,会有好几个文件夹,分别针对不同的操作系统。请以系统管理员身份运行windows文件夹下的install.cmd程序即可。
点击查看原图

三、建虚拟机
按照vmware提示,创建苹果系统的虚拟机,在这一步,做如下选择:

点击查看原图
我下载的是10.8.5,所以选择mac OS X 10.8。大家根据自己下载的苹果系统做相应的选择。OS X 10.8.5的最低内存需求是2G,否则无法安装。所以,物理内存小的朋友们,就不用安装了10.8.5了。

其它步骤,全部采用vmware的默认值,不用做任何修改。

四、安装苹果系统
点击查看原图

特别注意:在下边这一步,一定要先运行“磁盘工具”,先对磁盘进行分区。否则的话,安装OSX时,会找不到硬盘。
点击查看原图

先磁盘工具进行分区,然后执行“重新安装OS X”就可以安装OSX了。
点击查看原图

安装完OS X后,要先打vmware tools,否则显示效果大受影响。安装方法:不能直接点“安装vmware tools”。在光驱设置那里,选择“浏览”
点击查看原图
然后选择vmware所在的文件夹,会看到“darwin.iso”文件,打开这个文件。
点击查看原图

这时,会在MAC系统中看到光驱图标变成了“VMware Tools”,双击就进入安装画面。
点击查看原图

现在,可以尽情享受MAC的乐趣了。这次发现的,还不错。
点击查看原图

欢迎各位兄弟姐妹交流。

[转载]Android AlertDialog.Builder()-Android开发进阶&经验分享-eoe Android开发者社区_Android开发论坛

mikel阅读(903)

[转载]Android AlertDialog.Builder()-Android开发进阶&经验分享-eoe Android开发者社区_Android开发论坛.
那我们先了解什么是AlertDialog?什么是AlertDialog.Builder?且两者有什么区别?
AlertDialog是Dialog的一个直接子类,AlertDialog也是Android系统当中最常用的对话框之一。

一个AlertDialog可以有两个以上的Button,可以对一个AlertDialog设置相应的信息。比如 title,massage,setSingleChoiceItems,setPositiveButton,setNegativeButton等 等。
但不能直接通过AlertDialog的构造函数来生产一个AlertDialog。研究AlertDialog的源码发现AlertDialog所 有的构造方法都是写保护的所以不能通过:AlertDialog alertDialog = new AlertDialog();来得到。AlertDialog构造方法源码如下:

protected AlertDialog(Context context) {
this(context, com.android.internal.R.style.Theme_Dialog_Alert);
}

protected AlertDialog(Context context, int theme) {
super(context, theme);
mAlert = new AlertController(context, this, getWindow());
}

protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, com.android.internal.R.style.Theme_Dialog_Alert);
setCancelable(cancelable);
setOnCancelListener(cancelListener);
mAlert = new AlertController(context, this, getWindow());
}

只能通过:

AlertDialog.Builder alertDialog =new AlertDialog.Builder(this);
来得到。那就通过一个具体的实例来说说吧(这里用一个最常用的对话框为例):



package eoe.oyah;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;

public class TestsActivity extends Activity {

 
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

alertDialog.setTitle("title").setMessage("message").setPositiveButton("okBuuon",new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog,int which) {
}
}).setNegativeButton("exitButton",

new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
}
}).[b]setCancelable(false).[/b]create().show();
}

}

针对AlertDialog中设置了确定和取消按钮,一般来说确定为执行某个动作,取消就是不执行,但是如果用户点击了系统的Back键,此时就会将AlertDialog关闭,而并没有执行预期的取消的操作。
  此时需要关注一个方法setCancelable(false) 该方法定义设置该AlertDialog是否可以被Back键取消,如果不设置默认为true.

  下面是一些扩展:

//根据AlertDialog.Builder 创建 相应的 AlertDialog
AlertDialog alertDialogs = alertDialog.create();
//用dismiss();方法来清除创建过的AlertDialog
alertDialogs.dismiss();

以上所采用的都是AlertDialog 系统默认的布局 现在说自定义布局的情况 并添加一个用于取消AlertDialog 的 Button
  定义其布局 main.xml


< ?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
>

< LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
< /LinearLayout>

 
< /LinearLayout>

[转载]Android 自定义View之继承view重写onDraw - 梅义 - 博客园

mikel阅读(993)

[转载]Android 自定义View之继承view重写onDraw – 梅义 – 博客园.

Android自定义View的方式有很多,可以继承View也可以继承更高级一点的控件,比如TextView,RelativeLayout等等。

今天主要讲继承View,重写onDraw,onMeasure等

先看效果图(一个可以显示当前进度的控件)

 

第一步:添加属性定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="BuyProgressView">
        <attr format="integer" name="bought_color" />
        <!-- 已买颜色 -->
        <attr format="integer" name="base_color" />
        <!-- 底色 -->
        <attr format="integer" name="text_color" />
        <!-- 文本颜色 -->
        <attr format="dimension" name="text_size" />
        <!-- 文本字体大小 -->
    </declare-styleable>
</resources>

 

第二步:实现自定义View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class ProgressBarDialog extends Dialog {
    private ProgressBar mProgress; // 进度提示控件
    private TextView mMessageView; // 进度消息控件
    private String mMessage; // 进度消息字符串
    private boolean mIndeterminate; // 是否indeterminate模式
    private Drawable mIndeterminateDrawable; // 进度提示图片;
    public ProgressBarDialog(Context context) {
        super(context, R.style.DialogTheme);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.d_progress_spinner);
        // 初始化进度控件
        mProgress = (ProgressBar) findViewById(R.id.progress);
        // 初始化信息控件
        mMessageView = (TextView) findViewById(R.id.message);
        // 显示消息
        setMessage(mMessage);
        // 设置indeterminate模式
        setIndeterminate(mIndeterminate);
        // 设置进度控件提示图片
        if (mIndeterminateDrawable != null) {
            setIndeterminateDrawable(mIndeterminateDrawable);
        }
    }
    @Override
    public void onAttachedToWindow() {
        // TODO Auto-generated method stub
        super.onAttachedToWindow();
        Window w = getWindow();
        WindowManager.LayoutParams lp = w.getAttributes();
        DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
        lp.width = dm.widthPixels;
        lp.height = dm.heightPixels;
        // onWindowAttributesChanged(lp);
        lp.format=PixelFormat.TRANSLUCENT;
        lp.gravity=Gravity.CENTER;
        lp.dimAmount=0.1f;
        w.setAttributes(lp);
    }
    /**
     * 设置进度提示图片资源
     *
     * @param d
     */
    public void setIndeterminateDrawable(Drawable d) {
        if (mProgress != null) {
            mProgress.setIndeterminateDrawable(d);
        } else {
            mIndeterminateDrawable = d;
        }
    }
    /**
     * 设置indeterminate模式
     *
     * @param indeterminate
     */
    public void setIndeterminate(boolean indeterminate) {
        if (mProgress != null) {
            mProgress.setIndeterminate(indeterminate);
        } else {
            mIndeterminate = indeterminate;
        }
    }
    /**
     * 是否indeterminate模式
     */
    public boolean isIndeterminate() {
        if (mProgress != null) {
            return mProgress.isIndeterminate();
        }
        return mIndeterminate;
    }
    /**
     * 设置提示信息
     *
     * @param message
     */
    public void setMessage(String message) {
        if (mMessageView != null) {
            // 已有控件,直接修改显示;
            if (!StringUtils.isNull(message)) {
                mMessageView.setText(message);
                mMessageView.setVisibility(View.VISIBLE);
            } else {
                mMessageView.setText(message);
                mMessageView.setVisibility(View.GONE);
            }
        }
        mMessage = message;
    }
}

第三步:在xml layout中引用

1
2
3
4
5
6
7
8
9
10
11
12
<com.lottery.ui.view.BuyProgressView
               Android:id="@+id/progress"
               Android:layout_width="50dp"
               android:gravity="center"
               buyProgressView:base_color="0xffffce2b"
               buyProgressView:bought_color="0xffdc1a38"
               buyProgressView:text_color="0xff3d6500"
               buyProgressView:text_size="12sp"
               android:layout_height="50dp"/>

 

实现原理比较简单,就不上传工程代码了,这个是我之前的一个项目中自己写的一个控件。

[转载]Android Service学习之AIDL实例 - 梅义 - 博客园

mikel阅读(997)

[转载]Android Service学习之AIDL实例 – 梅义 – 博客园.

注:本文是我早些时候在eoe上发布的一篇文章

由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在 Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。

以下讲对如何实现AIDL调用进行讲解,本文包括AIDL接口调用,回调,以及传递复杂数据类型

Demo运行效果图

Aidl文件
Person.aidl

1
2
package com.archermind.aidl;
parcelable Person;

ITaskBinder.aidl

1
2
3
4
5
6
7
8
9
10
11
package com.archermind.aidl;
import com.archermind.aidl.ITaskCallBack;
import com.archermind.aidl.Person;
interface ITaskBinder {
   void fuc01();
   void fuc02();
   String fuc03(in Person person);
   void registerCallBack(ITaskCallBack cb);
   void unregisterCallBack();
}

ITaskCallBack.aidl

1
2
3
4
5
package com.archermind.aidl;
interface ITaskCallBack {
    void onActionBack(String str);
}

先看Myservice的代码
服务端实现了AIDL的相关接口,供客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return mBinder;
    }
    
    private final ITaskBinder.Stub mBinder=new ITaskBinder.Stub() {
        
        @Override
        public void unregisterCallBack() throws RemoteException {
            // TODO Auto-generated method stub
            Log.v(TAG,"unregisterCallBack...");
            mTaskCallBack=null;
        }
        
        @Override
        public void registerCallBack(ITaskCallBack cb) throws RemoteException {
            // TODO Auto-generated method stub
            Log.v(TAG,"registerCallBack...");
            mTaskCallBack=cb;
        }
        
        @Override
        public void fuc01() throws RemoteException {
            // TODO Auto-generated method stub
            Log.v(TAG,"fuc01...");
        }
        @Override
        public void fuc02() throws RemoteException {
            // TODO Auto-generated method stub
            Log.v(TAG,"fuc02...");
            mTaskCallBack.onActionBack("hello world");
        }
        @Override
        public String fuc03(Person person) throws RemoteException {
            // TODO Auto-generated method stub
            String name=person.getName();
            String descrip=person.getDescrip();
            int sex=person.getSex();
            String ret="";
            if(sex==0){
                ret="Hello "+name+",you are handsome"+"("+descrip+")";
            }else{
                ret="Hello "+name+",you are beautiful"+"("+descrip+")";
            }
            return ret;
        }
    };

客户端实现回调接口

1
2
3
4
5
6
7
8
9
private final ITaskCallBack.Stub mCallBack=new ITaskCallBack.Stub() {
        @Override
        public void onActionBack(String str) throws RemoteException {
            // TODO Auto-generated method stub
            Log.v(TAG,"onActionBack str="+str);
        }
        
    };

客户端如何与服务器绑定并建立连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Intent intent=new Intent("com.archermind.aidl.myservice");
bindService(intent,mConnection,BIND_AUTO_CREATE);
private ServiceConnection mConnection=new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            mService=ITaskBinder.Stub.asInterface(service);
            try {
                mService.registerCallBack(mCallBack);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Log.v(TAG,"onServiceConnected");
        }

源码工程:http://www.eoeAndroid.com/forum.php?mod=attachment&aid=NzQ0OTZ8ZjczNWI2NGN8MTM4ODY1NTQ1MnwxNjc0MnwyMDM5NTc%3D

[转载]C# 脚本代码自动登录淘宝获取用户信息 - Ivan Zou - 博客园

mikel阅读(962)

[转载]C# 脚本代码自动登录淘宝获取用户信息 – Ivan Zou – 博客园.

最近遇到的一个需求是如何让程序自动登录淘宝, 获取用户名称等信息. 其实这个利用SS (SpiderStudio的简称) 实现起来非常简单. 十数行代码就可以做到非常完善:

1. 运行SS (下载地址), 编写脚本打开淘宝登陆页面:

Default.Navigate("https://login.taobao.com/member/login.jhtml");
Default.Ready();

2. 找到用户名, 密码的输入框, 输入信息, 点击登录按钮:

复制代码
var user = Default.SelectSingleNode("#TPL_username_1");
user.Focus(); //模拟点击用户名文本框
user.Attr("value", "noxxxxxxx"); //输入用户名
Default.Wait(1); //等待一秒
Default.SelectSingleNode("#TPL_password_1").Attr("value", "*******"); //找到密码框并输入密码
Default.Wait(1); //等待一秒
var btnLogin = Default.SelectSingleNode("#J_SubmitStatic");
btnLogin.Click(); //点击登录按钮
复制代码

请注意代码中有两处出现Default.Wait(1). 这是为了控制节奏. 淘宝对机器人防范严格, 如果我们输入过快, 淘宝就会追加要求输入验证码. 如果我们控制一下节奏, 淘宝会认为这就是一个正常的登录, 于是不要验证码也是能够实现登录的!

3. 等待登录完成, 页面跳转:

Default.Reset();
Default.Ready();

4. 获取用户姓名的节点, 并显示出来:

var name = Default.SelectSingleNode("div.name>a");
MessageBox.Show(name.Text());

5. 完整的脚本代码:

复制代码
public void Run()
{
    Default.Navigate("https://login.taobao.com/member/login.jhtml");
    Default.Ready();
    var user = Default.SelectSingleNode("#TPL_username_1");
    user.Focus();
    user.Attr("value", "nxxxxxxx");
    Default.Wait(1);
    Default.SelectSingleNode("#TPL_password_1").Attr("value", "********");
    Default.Wait(1);
    var btnLogin = Default.SelectSingleNode("#J_SubmitStatic");
    btnLogin.Click();
    Default.Reset();
    Default.Ready();
    var name = Default.SelectSingleNode("div.name>a");
    MessageBox.Show(name.Text());
}
复制代码

6. 运行效果:

7. 和Spider Studio采集以及相关脚本编写的其他文章:

C#中另辟蹊径解决JSON / XML互转的问题

C#代码获取或设置Iframe中的HTML

示例 – C#脚本代码采集搜狐NBA球员, 球队和比赛实况

C#中利用JQuery实现视频网站的缩略图采集

API – 使用Default对象 – 基础篇

示例 – 如何翻页

示例 – 向百度说 Hello world! 并获得回应.

Spider Studio 社区信息

[转载]android Gallery实现异步加载网络图片 并只加载当前停止页面图-Android开发源码下载-eoe Android开发者社区_Android开发论坛

mikel阅读(908)

GalleryWhetherStop

通过[转载]android Gallery实现异步加载网络图片 并只加载当前停止页面图-Android开发源码下载-eoe Android开发者社区_Android开发论坛.

之前在网上找了很多都没有这方面的资料,大概的效果是当Gallery滑动时不下载图片,当Gallery滑动停止时加载当前页面图片,自己花了一点时间大概的实现了,如果各位有更好的意见欢迎说出来大家一起学习。
先上图看看效果:
这图是在加载图片时显示的默认图片,当图片加载完成则替换。


图片加载完成效果图

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Gallery;
public class MyActivity extends Activity implements OnItemClickListener{
public static HashMap&lt;String,Bitmap&gt; imagesCache=new HashMap&lt;String, Bitmap&gt;(); //图片缓存
private Gallery images_ga;
public static ImageAdapter imageAdapter;
private int num=0;
List urls = new ArrayList(); //所有图片地址List
List url = new ArrayList(); //需要下载图片的url地址

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_1);
Files.mkdir(this);
init();
}
private void init(){
Bitmap image= BitmapFactory.decodeResource(getResources(),R.drawable.default_movie_post);
imagesCache.put("background_non_load",image); //设置缓存中默认的图片
images_ga = (Gallery) findViewById(R.id.gallery);
urls.add("http://hiphotos.baidu.com/baidu/pic/item/f603918fa0ec08fabf7a641659ee3d6d55fbda0d.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/43a7d933c895d143d011bf9273f082025aaf071f.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/63d0f703918fa0ec2ebf584b269759ee3d6ddb7f.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/5ab5c9ea15ce36d31ed8387f3af33a87e850b1a5.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/8601a18b87d6277f6e46217628381f30e924fc2c.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/b48f8c54acf9964c3a29350e.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/bd3eb13533fa828b48da6aabfd1f4134960a5af9.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/29381f30e924b899da3ce5706e061d950a7bf672.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/bd3eb13533fa828b48da6aabfd1f4134960a5af9.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/4bed2e738bd4b31cd73d63fd87d6277f9e2ff877.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/caef76094b36acaf92b619b87cd98d1001e99c24.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/8435e5dde71190efd6154d95ce1b9d16fcfa608a.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/b3de9c824ba1d4cd6d81190f.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/e0fe9925cc2c683834a80f11.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/0bd162d9f2d3572c65911a988a13632762d0c307.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/ac6eddc451da81cb2ac1708a5266d01609243155.jpg");

urls.add("http://hiphotos.baidu.com/baidu/pic/item/1bd5ad6e8416d98080cb4a48.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/3c6d55fbb2fb43169d0508ca20a4462309f7d36c.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/faedab64034f78f0daf3664a79310a55b2191c8a.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/2fdda3cc7cd98d10b05af088213fb80e7aec90f9.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/b8014a90f603738d9536f39bb31bb051f819ec0f.jpg");
urls.add("http://hiphotos.baidu.com/baidu/pic/item/2fdda3cc7cd98d10b05af088213fb80e7aec90f9.jpg");
imageAdapter = new ImageAdapter(urls, this);
images_ga.setAdapter(imageAdapter);
images_ga.setOnItemClickListener(this);
images_ga.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView&lt;?&gt; arg0, View arg1, int arg2, long arg3) {
num=arg2;
Log.i("mahua", "ItemSelected=="+arg2);
GalleryWhetherStop();
}
@Override
public void onNothingSelected(AdapterView&lt;?&gt; arg0) {
// TODO Auto-generated method stub

}
});
}

@Override
public void onItemClick(AdapterView&lt;?&gt; arg0, View arg1, int arg2, long arg3) {
Log.i("GOLF", "第"+arg2+"个被点击了");
}

/**
* 判断Gallery滚动是否停止,如果停止则加载当前页面的图片
*/
private void GalleryWhetherStop() {
Runnable runnable = new Runnable() {
public void run() {
try {
int index =0;
index = num;
Thread.sleep(1000);
if (index == num) {
url.add(urls.get(num));
if(num!=0 &amp;&amp; urls.get(num-1)!=null) {
url.add(urls.get(num-1));
}
if(num!=urls.size()-1 &amp;&amp; urls.get(num+1)!=null) {
url.add(urls.get(num+1));
}
Message m = new Message();
m.what = 1;
mHandler.sendMessage(m);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
}

// 加载图片的异步任务
class LoadImageTask extends AsyncTask&lt;String, Void, Bitmap&gt; {
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
}
@Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}

@Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
try {
String url = params[0];
boolean isExists = Files.compare(url); //这里是自己写的工具类,判断本地缓存是否已经下载过图片
if (isExists == false) {//如果不存在就去下载图片
Net net = new Net();
byte[] data = net.downloadResource(MyActivity.this, url);
bitmap = BitmapFactory
.decodeByteArray(data, 0, data.length);
imagesCache.put(url, bitmap); // 把下载好的图片保存到缓存中
Files.saveImage(url, data);
} else {//如果存在直接读取缓存图片
byte[] data = Files.readImage(url);
bitmap = BitmapFactory
.decodeByteArray(data, 0, data.length);
imagesCache.put(url, bitmap); // 把下载好的图片保存到缓存中
}
Message m = new Message();//图片加载完成通知重新加载
m.what = 0;
mHandler.sendMessage(m);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}

}

private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
try {
switch (msg.what) {
case 0: {
imageAdapter.notifyDataSetChanged();
break;
}
case 1: {
for(int i=0; i&lt;url.size(); i++) {
LoadImageTask task = new LoadImageTask();//异步加载图片
task.execute(url.get(i));
Log.i("mahua", url.get(i));
}
url.clear();
}
}
super.handleMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode==KeyEvent.KEYCODE_BACK){
System.exit(0);
}
return super.onKeyDown(keyCode, event);
}
}
import java.util.List;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;

public class ImageAdapter extends BaseAdapter {
public static final BaseAdapter Adapter = null;
private List imageUrls; // 图片地址list
private Context context;
int mGalleryItemBackground;

public ImageAdapter(List imageUrls, Context context) {
this.imageUrls = imageUrls;
this.context = context;
// /*
// * 使用在res/values/attrs.xml中的定义 的Gallery属性.
// */
TypedArray a = context.obtainStyledAttributes(R.styleable.Gallery1);
/* 取得Gallery属性的Index id */
mGalleryItemBackground = a.getResourceId(
R.styleable.Gallery1_android_galleryItemBackground, 0);
/* 让对象的styleable属性能够反复使用 */
a.recycle();
}
public int getCount() {
return imageUrls.size();
}
public Object getItem(int position) {
return imageUrls.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
Bitmap image;
ImageView view = new ImageView(context);
image = MyActivity.imagesCache.get(imageUrls.get(position));
// 从缓存中读取图片
if (image == null) {
image = MyActivity.imagesCache.get("background_non_load");
}
// 设置所有图片的资源地址
view.setImageBitmap(image);
view.setScaleType(ImageView.ScaleType.FIT_XY);
view.setLayoutParams(new Gallery.LayoutParams(240, 320));
view.setBackgroundResource(mGalleryItemBackground);
/* 设置Gallery背景图 */
return view;
}
}
<!--?xml version="1.0" encoding="utf-8"?-->


<!-- These are the attributes that we want to retrieve from the theme in view/Gallery1.java -->







主要的结构就这样了,下载文件和文件保存自己随便写写就行了。如有更好的建议欢迎提出了,大家一起分享学习。

最后应要求附上源码。
GallerySync.rar (172.86 KB, 下载次数: 1282)

[转载]Android:反编译、修改代码、重新编译-Android开发实例教程-eoe Android开发者社区_Android开发论坛

mikel阅读(958)

[转载]Android:反编译、修改代码、重新编译-Android开发实例教程-eoe Android开发者社区_Android开发论坛.

末尾有相关工具下载和使用方法

一、反编译
反编译我使用的是AntiDroid  V1.4.2,下载地址 https://code.google.com/p/anti-droid/

 

1.打开下载好的AntiDroid,选择“反编译apk”
2.填好路径,点击“反编译apk”即可得到一个包含资源文件的文件夹”123.dp”
资源文件夹

 

smali目录下的就是反编译出来的smali文件,这是一种类似于汇编语言的代码,阅读起来有一定的困难,为了便于阅读,我们再将apk文件反编译成jar的包
4.照样是使用 AntiDroid,这次选择“转换dex为jar”,选择apk的路径和存储反编译的jar的路径,点击转换后的到“123.jar”文件
二、修改源代码
1. 下载 jd-gui,用jd-gui打开刚刚反编译的123.jar,整个软件的代码结构就能看到了

 

2. 现在就是最麻烦的一步了,我们要分析整个代码结构,从中找到你要修改的代码

 

3. 确定修改什么代码后并不能直接修改java代码,而是要回到开始的smali文件里面修改,然后重新编译

 

三、重新编译
由于我这儿使用AntiDroid重新编译会报错,我使用的是apktool进行编译
1. 下载apktool,地址 https://code.google.com/p/Android-apktool/
2. 使用命令 apktool b [目录路径] 编译,编译成功后会在原文件夹生成build和dist两个文件夹,dist里面就有编译好的apk文件
3. 使用AntiDroid对新生成的apk签名,点击AntiDroid的“apk签名”,选择dist里面的apk文件,输出到任意文件夹都可
4. 将签名的apk安装到Android手机,现在你就可以看到效果了

应大家要求,把相关工具整体出来了,给大家下载:
1. Anti-droid
项目地址: http://code.google.com/p/anti-droid
使用方法: http://www.iwind.me/?p=138
下载: AntiDroid V1.4.2.rar (5.71 MB, 下载次数: 411)

2. apktool
项目地址: https://code.google.com/p/android-apktool/
使用方法: http://www.iwind.me/?p=99
下载: apktool1.5.2.rar (2.72 MB, 下载次数: 228)

3. jd-gui
官方地址: http://java.decompiler.free.fr/?q=jdgui
使用方法: 这个比较简单,和大部分软件差不多,直接打开jar等文件即可
下载: jd-gui-0.3.5.windows.zip (770.97 KB, 下载次数: 137)