Android notes
发布日期:2021-11-16 18:19:04 浏览次数:6 分类:技术文章

本文共 52113 字,大约阅读时间需要 173 分钟。

4月20

Makefile -a 与判断; -f 文件存在且为规则文件; -g 文件存在且设置了SGID; -h 文件为一符号链接;-b -c -d -e -p(管道) -r -w -x; -o 文件存在且被有效用户ID拥有; -k 存在且设置了sticky位的值;-u 文件存在且设置了SUID;-s 文件大小大于0;-z string 指stirng大小为0;-n sting 则不为0; -o 或判断 expr1 -o expr2;
特殊变量:
$0    ( 正在被执行命令的名字。对于shell 脚本而言,这是被激活命令的路径 )
$n    (该变量与脚本被激活时所带的参数相对应。n 是正整数,与参数位置相对应 ($1,$2...))
$#    ( 提供脚本的参数号 )
$*    ( 所有这些参数都被双引号引住。若一个脚本接收两个参数,$* 等于 $1$2)
$@    (所有这些参数都分别被双引号引住。若一个脚本接收到两个参数,$@ 等价于 $1$2)
$?    (前一个命令执行后的退出状态 )
$$    ( 当前shell 的进程号。对于shell 脚本,这是其正在执行时的进程 ID)
$!    ( 前一个后台命令的进程号 )
[root@www ~]# sed [-nefr] [動作]
選項與參數:
-n  :使用安靜(silent)模式。在一般 sed 的用法中,所有來自 STDIN
      的資料一般都會被列出到螢幕上。但如果加上 -n 參數後,則只有經過
      sed 特殊處理的那一行(或者動作)才會被列出來。
-e  :直接在指令列模式上進行 sed 的動作編輯;
-f  :直接將 sed 的動作寫在一個檔案內, -f filename 則可以執行 filename 內的
      sed 動作;
-r  :sed 的動作支援的是延伸型正規表示法的語法。(預設是基礎正規表示法語法)
-i  :直接修改讀取的檔案內容,而不是由螢幕輸出。
動作說明:  [n1[,n2]]function
n1, n2 :不見得會存在,一般代表『選擇進行動作的行數』,舉例來說,如果我的動作
         是需要在 10 到 20 行之間進行的,則『 10,20[動作行為] 』
function 有底下這些咚咚:
a   :新增, a 的後面可以接字串,而這些字串會在新的一行出現(目前的下一行)~
c   :取代, c 的後面可以接字串,這些字串可以取代 n1,n2 之間的行!
d   :刪除,因為是刪除啊,所以 d 後面通常不接任何咚咚;
i   :插入, i 的後面可以接字串,而這些字串會在新的一行出現(目前的上一行);
p   :列印,亦即將某個選擇的資料印出。通常 p 會與參數 sed -n 一起運作~
s   :取代,可以直接進行取代的工作哩!通常這個 s 的動作可以搭配
      正規表示法!例如 1,20s/old/new/g 就是啦
是要刪除第 3 到最後一行,則是『 nl /etc/passwd | sed '3,$d' 』的啦,那個錢字號『 $ 』代表最後一行!
範例一:將 /etc/passwd 的內容列出並且列印行號,同時,請將第 2~5 行刪除!
[root@www ~]# nl /etc/passwd | sed '2,5d'
     1  root:x:0:0:root:/root:/bin/bash
     6  sync:x:5:0:sync:/sbin:/bin/sync
     7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
範例二:承上題,在第二行後(亦即是加在第三行)加上『drink tea?』字樣!
[root@www ~]# nl /etc/passwd | sed '2a drink tea'
     1  root:x:0:0:root:/root:/bin/bash
     2  bin:x:1:1:bin:/bin:/sbin/nologin
drink tea
     3  daemon:x:2:2:daemon:/sbin:/sbin/nologin
範例四:我想將第2-5行的內容取代成為『No 2-5 number』呢?
[root@www ~]# nl /etc/passwd | sed '2,5c No 2-5 number'
     1  root:x:0:0:root:/root:/bin/bash
No 2-5 number
     6  sync:x:5:0:sync:/sbin:/bin/sync
範例五:僅列出 /etc/passwd 檔案內的第 5-7 行
[root@www ~]# nl /etc/passwd | sed -n '5,7p'
     5  lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
     6  sync:x:5:0:sync:/sbin:/bin/sync
     7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
sed 's/要被取代的字串/新的字串/g'
步驟一:先觀察原始訊息,利用 /sbin/ifconfig  查詢 IP 為何?
[root@www ~]# /sbin/ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:90:CC:A6:34:84
          inet addr:192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::290:ccff:fea6:3484/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
.....(以下省略).....
# 因為我們還沒有講到 IP ,這裡你先有個概念即可啊!我們的重點在第二行,
# 也就是 192.168.1.100 那一行而已!先利用關鍵字捉出那一行!
步驟二:利用關鍵字配合 grep 擷取出關鍵的一行資料
[root@www ~]# /sbin/ifconfig eth0 | grep 'inet addr'
          inet addr:192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
# 當場僅剩下一行!接下來,我們要將開始到 addr: 通通刪除,就是像底下這樣:
# inet addr:192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
# 上面的刪除關鍵在於『 ^.*inet addr: 』啦!正規表示法出現! ^_^
步驟三:將 IP 前面的部分予以刪除
[root@www ~]# /sbin/ifconfig eth0 | grep 'inet addr' | /
>  sed 's/^.*addr://g'
192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
# 仔細與上個步驟比較一下,前面的部分不見了!接下來則是刪除後續的部分,亦即:
# 192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
# 此時所需的正規表示法為:『 Bcast.*$ 』就是啦!
步驟四:將 IP 後面的部分予以刪除
[root@www ~]# /sbin/ifconfig eth0 | grep 'inet addr' | /
>  sed 's/^.*addr://g' | sed 's/Bcast.*$//g'
192.168.1.100
4月21
查找当前目前以下所有文件包含String的文件
find . -name "*.*" |xargs grep string
4月23
ERROR: input directory 'zh_CN' does not exist 注释掉 LOCAL_AAPT_FLAGS := -0
capitalized 大小写切换
adb logcat -s MyTag 设置过滤tag
out/target/common/obj/APPS/TestHello_intermediates/src/R.java:10: duplicate class: com.borqs.R
public final class R {
             ^
1 error
duplicate class error 删除R.java即可
Apr 29
no rule to create xml file it indicates there is no need to create coping file .
下面介绍vi 的一般用法,本文适合初学者入门,但不适合作为手册进行查询,如果需要以命令导向的手册,请使用$ man vi|more ,或看这里的:vi 命令一览表。
打开文件进入编辑
通话设置
编辑一个文件:
号码
$ vi example.txt
返回结果处理
$ vi file1 file2 ... fileN
如果使用vi 编辑的某个文件没有正常保存退出,会遗留一个隐含的文件:.filename.swp ,使用如下命令恢复从最后一次保存到非正常退出前所做的编辑:
$ vi -r filename
5-14
转载:
注意当前模式
进入文件编辑后,vi有四种模式,一种是插入模式(按键盘i 进入),这个模式下键盘输入会作为字符增加到文件中。
另一种是编辑模式,这个状态下可以直接输入命令来移动光标,删除字符等操作,不需要回车确认,每一个命令按键都会直接发生作用。在插入模式下按ESC 键会回到编辑模式。
第三种是命令模式,在编辑模式下按冒号":" 即进入命令模式,这时键盘输入的字符会出现在屏幕下方,回车后,命令执行。这个模式下可以输入保存、字符串搜索、退出编辑等命令。命令模式下,按回车或者ESC 键,回到编辑模式。
第四种是搜索模式,在编辑模式下输入斜杠"/" 或者问号"?" 即进入搜索模式。
上面的四种模式是我为了描述方便所起的名字,下面将引用到。
光标移动
光标的上、下、左、右移动,分别对应按键:k、j、h、l (编辑模式)。
定位到最后一行,输入大写G(编辑模式)。
回到第一行,输入小写g(编辑模式)。
跳到第n 行,在命令模式下输入数字n ,回车。
显示当前所在行数和列数,编辑模式下输入ctrl+g 。
插入内容
在当前字符前面插入内容,编辑模式下输入i ,进入了插入模式,然后输入内容,输入完成之后,按ESC 回到编辑模式。
在当前字符后面插入内容,编辑模式下输入a ,其它操作与上面相同。
在本行首插入内容,编辑模式下输入大写I 。其它操作与上面相同。
在本行尾追加内容,编辑模式下输入大写A 。其它操作与上面相同。
删除
删除当前光标的字符,编辑模式下输入x ,如果希望删除当前光标前面的字符,输入大写X 。
删除一行,编辑模式下连续两次输入d 。删除一个单词,编辑模式下依次输入dw 。
从当前光标删除至行尾,编辑模式下依次输入d$ 。
撤销操作
撤销刚才的一次修改,编辑模式下输入u ,撤销对这一整行的修改,输入大写U 。
如果希望撤销对文件的所有修改,命令模式下依次输入e! ,回车(输入冒号":" 进入命令模式,:e!)。
拷贝粘贴
拷贝一行内容,编辑模式下连续输入两次y 。
将剪贴板中的一行粘贴到当前行的后面,编辑模式下输入p 。
拷贝n 行内容,编辑模式下输入数字n(n是要拷贝的行数,从当前行算起),然后输入y ,拷贝完成,输入p 粘贴多行。
保存/退出
如果保存并关闭文件,命令模式下输入x ,回车即可。
如果保存,同时继续文件编辑,命令模式下输入w ,回车。
如果放弃所有更改,直接退出,命令模式下输入q! ,回车。
命令模式下输入wq 与x 等价。
在两个文件中复制粘贴
需要将file2 的某行粘贴到file1 中。
同时打开两个文件,中间用空格分隔:
$ vi file1.txt file2.txt
打开后当前编辑的为file1 ,在命令模式下输入n ,切换到file2。
找到相关行,编辑模式下输入yy ,再进入命令模式,输入大写N ,切换回上一个文件(以打开顺序为准),找到相关位置,编辑模式下输入p ,粘贴完毕。
 
搜索字符串
要搜索字符串abc ,在搜索模式下(编辑模式下输入斜杠"/" 或者问号"?" 即进入搜索模式),输入abc ,回车。
如果找到,光标会定位到从当前位置到第一个出现字符串abc 的位置,输入n 会跳到下一个abc 出现的位置,输入大写N ,跳到上一个abc 出现的位置。
斜杠"/" 与问号"?" 的区别,前者向后正向搜索,后者向前反向搜索。
替换字符串
要将当前行中的所有字符串abc 替换成efg ,命令模式下输入s/abc/efg/g ,回车。
上面命令只会替换abc 而不会替换ABC 或者aBc 。如果希望忽略大小写,加上参数i ,s/abc/efg/gi ,回车。
如果希望进行全文件范围替换,命令模式下输入%s/abc/efg/g ,回车。
如果替换的字符串中包含了例如斜杠"/" 这样的特殊字符,使用反斜杠"/" 转义。
命令模式下,输入%s///image///img/gi ,回车
以上命令会将文件中所有的字符串/image 替换成 /img ,并且不区分image 的大小写。
查找单一目录的字串
find /ptc -name "*.jsp" -print -exec grep "version" {} /;
这个命令显示在/ptc目录下扩展名为.jsp且内容包含version字符串的文件,显示文件目录和包含这个字符串的行。
find /ptc -exec grep -l "sjh" {} /;
在/ptc下查找内容包含“sjh"字符串的文件。显示文件目录。
find /tmpftp |xargs egrep version
find 里面的-exec效率没 xargs 逐条递送处理的效果好
grep 查找也没 egrep 效率快。 不过只能对非特殊字符。
find -name "*.*" |xargs grep version
5月18日
每次点Key它不会去执行onKey 而是ArrayOutOfBounds 应该从最简单开始找 onKey值没有传入 仔细重新看表达式 看区别 也许就藏在最简单的地方。容易忽略的地方。。
linux sed 批量替换多个文件中的字符串
sed -i "s/oldstring/newstring/g" `grep oldstring -rl yourdir`
例如:替换/home下所有文件中的www.admin99.net为admin99.net
sed -i "s/www.admin99.net/admin99.net/g" `grep www.admin99.net -rl /home`
5-21
popupWindow 添加全屏视图 就那样~~~
      Android系统框架和上层应用是类java(不是正统的sun java)开发的,实现了自己的java虚拟机dalvik,既然用java虚拟机和java开发,一般都会认为效率低下。其实不然,在基本主流的智能手机的软件平台上,android的执行速度是最快的。
       那么android效率为什么这么的高呢?特别是一个应用程序的启动时间很短,本文主要从以下个八方面进行分析:
1、 资源文件的优化读取。
我们知道android在UI开发时有个很大的好处是xml文件来描述UI,这样有个好处是只要修改UI不用修改代码就可以修改界面的布局、显示风格和字体大小等。界面定义变得灵活方便。xml配置UI在qtopia运用也有但是这么强大并且也不广泛,因为xml文件有个不足是解析xml的效率很低。
Android是怎么做的呢?
Android在编译的时候就把xml文件进行了优化,android应用程序在解析时变得非常的高效。我们看到apk文件解压后会有个优化过的资源文件。
2、 安装时进行优化dex文件
Android的应用程序都打包成一个apk文件,实际上就是一个zip文件。系统第一次起来或应用程序第一次安装时,系统就把apk文件解压了,把可执行文件dex优化成odex文件并放在/data/dalvik-cache目录下。优化后的dex文件启动速度会加快。这解释了为什么 android系统第一次启动是比较慢,以后起来很快了。
可能有人会问:为什么不在编译时直接优化呢?第⑤项会回答这个问题。
3、 制作数据库
Android的图形应用是加载整个sd卡内的所有图像的,但是为什么很快呢?其实android提前把数据做成了数据库,所以不用每次扫描整个这个sd卡,大大加快了启动速度。
4、 高效的虚拟机
Android是基于类java虚拟机dalvik,一般的java虚拟机是基于栈的,而dalvik是基于寄存器的。实事求是说我对两者的区别了解不是很深入,不过网上有专门的相关文论进行分析。我的简单理解是栈的实现方式相对容易,相关数据是在内存中的栈里,而操作寄存器里数据的速度明显快与内存里的数据处理。
5、 充分挖掘CPU的性能
Android刚出来的时候虽然支持arm cpu,实际上只支持armv5te的指令集的,因为android系统专门为armv5te 进行了优化,充分利用armv5te的执行流水线来提高执行的效率,这也是在500M的三星2440运行效果不是很好,而在200M的omap cpu上运行比较流畅的原因了,所以在最新的代码中有专门针对x86和armv4的优化部分。
6、 优化和裁剪的libc库
Libc库几乎是所以库和程序的基础,但是android没有直接利用libc库,而是自己开发了一个库:bionic,它实现了libc库的绝大多数的函数并根据平台进行了优化,但是有系统很少用并且消耗资源的少数函数是不支持的。它只有几百k,节省了空间同时也提高了执行效率。实际上体现了 20-80原则,抓住少数重要的适当舍弃不必要的。
7、 充分利用linux系统特性
分析过linux内核的朋友知道,linux fork一个新的进程是非常高效的,利用了COW机制。Android是每个进程是个独立的虚拟机(听说这么设计是为安全考虑,某个时候进程崩溃了不会影响这个系统和其他进程。)android里每个进程都是基于虚拟机的,并且也要加载基本的库,实际上这些都是共享。所以android启动一个新的程序实际上并不消耗很多的内存和cpu资源。
同时android在后台有个empty process运行,实际上就是运行一个虚拟机,当要启动一个应用时就直接在其上继续运行,qtopia也有这个机制。
Android系统在开机流程中:启动虚拟机—》启动system server –》启动launcher。当初分析代码时疑惑为什么不直接启动system server?(qtopia就是直接启动server),实际上也利用了linux的这个特性。
这个特性说的比较简略,不过要真的把他解释清楚可能需要很大的篇幅。
8、 高效的paint机制
这个特性可能跟启动关系不大,但是也是android高效的特性之一。界面变化时大部分实际上不是全屏内容变化的,只是局部变化,android 会根据变化的内容只是跟新局部的内容,也提高了效率。这个也提醒我们在开发应用程序时,重载paint方法时尽量不要paint全屏内容。
sed -i "s/40px/@dimen//keyoffset_4/" `grep "40px" -rl development/samples/BorqsTest/res/xml/`
sed -i "s/a:b=/"@c//d/"/a:b=/"@d//e/"/" `grep -rl 'a:b="@c//d' Test`
sed -i "s/borqs:keyExtraHeight=/"@dimen//keyExtraHeight/"/borqs:keyHeight=/"@dimen//colum_fun_key_height/"/" `grep -rl 'borqs:keyExtraHeight' development/samples/BorqsTest/res/`
sed -i "s/<Row/<Row borqs:verticalGap=/"@dimen//key_padding/"/" `grep -rl '<Row' BorqsNew/res/xml/kbd_keypad_ab.xml`
sed -i "s/borqs:keyOffset=/"@dimen//keyoffset_[0-9]/"//" `grep -rl 'borqs:keyOffset' development/samples/BorqsTest/res/`
sed -i "s//"15%p/"//"20%p/"/" `grep -rl '15%p' BorqsTest/res/xml/kbd_keypad.xml`
diff -r -q 目录1 目录2
# 删除当前目录下所有 dep 文件
find . -name *.dep -type f -exec rm -f {} /;
# 删除当前目录下所有 linux64GccDPOpt 目录(包括里面的文件)
find . -name "linux64GccDPOpt" -type d -exec rm -rf {} /;
6月13
启动 Zygote
-Xzygote /system/bin --zygote --start-system-server
AndroidRuntime->AppRuntime
int main(int argc,const char* const argv[])
{
  AppRuntime runtime;生成AndroidRuntime实例
  ...
  AndroidRuntime.Start("com.android.internal.os.ZygoteInit",startSystemServer);
}
其中AndroidRuntime.Start("com.android.internal.os.ZygoteInit",startSystemServer);
呼叫Android::Start(const char* className,const bool startSystemServer)
/framework/base/core/jni/AndroidRuntime.cpp
该函数的处理内容:
1.处理Jave Virtual Machine的一些参数选项;
2.创建Dalvik Java虚拟机,JNI_CreateJavaVM(&mJavaVM,&env,&initArgs);
3.注册Android Runtime中的JNI接口给虚拟机;
4.呼叫Java类com.android.internal.os.ZygoteInit的main函数
在类com.android.internal.os.ZygoteInit的main函数中,
1.注册Zygote socket用来接收请求;
2.加载preloaded class、resources用来加快启动速度,文件清单在framework.jar中的preloaded-classes,framework-res.apk中的res中;
3.启动System Server;
  fork出独立的进程名称为system-server,呼叫com.android.server.SystemServer类的main函数;
  在HandleSystemServerProcess函数中,RuntimeInit.ZygoteInit调用会呼叫AppRuntime的OnZygoteInit函数
4.RuntimeInit.ZygoteInit函数会呼叫com.android.server.SystemServer类的main函数。
  在此main函数中,系统首先加载android_server共享库libandroid_server.so 源代码位于/framework/base/service/jni
  在该库中有定义JNI_OnLoad函数,所以Dalvik在加载libandroid_server.so 的时候会首先呼叫该JNI_OnLoad函数,该函数将android server注册到Java虚拟机中,包括 KeyInputQueue,HardwareService,AlarmManager,BatteryService,SensorService,SystemServer 等;
  呼叫在libanroid_server.so中注册的native函数init1,该函数位于/frameworks/base/services/jni/com_android_server_SystemServer.cpp中;
  init1函数呼叫libsystem_server中的system_init函数,该函数位于/frameworks/base/cmds /system_server/library/system_init.cpp中,该函数将SurfaceFlinger/AudioFlinger /MediaPlayer/CameraService等组件注册到ServiceManager中
  system_init函数反过来呼叫java类com.android.server.SystemServer的init2函数;
5.在init2函数中,android创建了serverthread,在该thread中android开始注册各种service到service manager中
包括EntropyService,PowerManager,ActivityManager,Telephony,PackageManager,ContentManager,ContentProvider,
BatteryService,HardwareService,AlarmManager等等。
  注意该线程使用Looper来执行thread
至此android system server启动完成。
6月18日
以前说过 WindowManagerService.java 用来把驱动消息封装成事件,发送出去。
以前说过Activity.java 里View接受事件的顺序。
今天需要研究 WindowManagerService 如何把Event. 丢给当前的Activity.
ViewRoot 这个类在android的UI结构中扮演的是一个中间者的角色,连接的是PhoneWindow跟WindowManagerService.在每个 PhoneWindow创建的时候,系统都会向WindowManger中的一个保存View数组增加PhoneWindow的DecorView对象,WindowManger在保存好这个View对象的同时,也会新创建一个ViewRoot对象用来沟通WindowManagerService。可以查看WindowManagerImpl跟ViewRoot中的代码,臧春杰同时ViewRoot中我们可以看到一个W类型,该类型派生自 IWindow.stubIwindowSession则是WindowManagerService中的远程调用接口,创建了一个 PhoneWindow,也就创建了一个ViewRoot,并将在WindowManagerService注册。接下来简单的看下KeyEvent的传递。qisda changer在WindowManagerService中
focus.mClient.dispatchKey(event);
这个段代码的mClient则就是ViewRoot中的W类型,通过远程调用,可以看看ViewRoot的处理:
boolean handled = mView != null? mView.dispatchKeyEventPreIme
接下来就到了mDecor里,也就是整个View树的根。然后比较臧春杰代码坐标判断是哪个View. 如果不在任何View范围里,就把消息给PhoneWindow.因为实现了Activity的CallBack 在Window不处理时候给Activity.
哎,还是非常模糊,好多东西不细致,理解不透彻。怎么办呢?
Handler.java
都知道Handler主要用来做线程间通信,为什么要用线程间通信? 如何进行通信?
Uithread要做定时更新,需要另外一个线程提供定时然后通知ui。
如何通信。用handle  sendMessage postMessage等。 需要理轻 Handler 和那个线程绑定的。 如何判别?
一个线程有个Looper管理一个MessageQueu  Uithread已经实现了这个Looper . 也有个这个队列。
Looper.getMainLooper返回了UI臧春杰代码线程的Looper。 New Handler(这个UILooper) 那么这个Hander就和UI绑定了。
如果 要自己创建线程。实现消息队列。 Looper.prepare 创建消息队列。 Looper.loop开始循环。
然后创建handler(Looper.myLooper) 实现帮定。
Handler和谁绑定有looper决定。
Uithread已经有Looper,可以直接使用。创建空的handler就是绑定主线程。
我们自己的线程需通过prepare loop 创建消息队列。
Home startActivity(intent)
execStartActivity
ActivityManagerService-->startActivity
startActivityLocked-->
---->
else
  {
  startSpecificActivityLocked(next, true, false); //2439L 定义:1628L
   
  realStartActivityLocked() //1640L 定义:1524L  there is a judge (if(app.thread != null)..
  //1651L 定义:1654L
  startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity", r.intent.getComponent());
  //1717L 定义:1721L
  startProcessLocked(app, hostingType, hostingNameStr);
  //1768L 定义:Process.java 222L(frameworks/base/core/java/android/os)
  int pid = Process.start("android.app.ActivityThread",...)
  startViaZygote(processClass, niceName, uid, gid, gids,debugFlags, zygoteArgs);
  pid = zygoteSendArgsAndGetPid(argsForZygote);
  sZygoteWriter.write(Integer.toString(args.size())) ;
  }
  runSelectLoopMode();
  done = peers.get(index).runOnce();
  forkAndSpecialize() //Zygote.java (dalvik/libcore/dalvik/src/main/java/dalvik/system )
  Dalvik_dalvik_system_Zygote_forkAndSpecialize() //dalvik_system_Zygote.c (dalvik/vm/native)
  forkAndSpecializeCommon()
  setSignalHandler()
  RETURN_INT(pid);
  ActivityThread main()
  ActivityThread attach() //ActivityThread.java 3870p (frameworks/base/core/java/android/app)
  mgr.attachApplication(mAppThread)
  //ActivityManagerService.java 4677p (frameworks/base/services/java/com/android/server/ am)
  attachApplication()
  //ActivityManagerService.java 4677p (frameworks/base/services/java/com/android/server/ am)
  attachApplicationLocked()
  if (realStartActivityLocked(hr, app, true, true)) //ActivityManagerService.java 4609p (frameworks/base/services/java/com/android/server/ am)
  realStartActivityLocked()
  //ActivityManagerService.java (frameworks/base/services/java/com/android/server/ am)
  app.thread.scheduleLaunchActivity(new Intent(r.intent), r,r.info, r.icicle, results, newIntents, !andResume,isNextTransitionForward());
  scheduleLaunchActivity()
  queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
  ActivityThread.H.handleMessage()
  handleLaunchActivity() //ActivityThread.java (frameworks/base/core/java/android/app)
  performLaunchActivity() //ActivityThread.java (frameworks/base/core/java/android/app)
  activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
  /
  init 守护进程:
  //andriod init 函数启动过程分析:
  在main循环中会重复调用
  drain_action_queue();
  restart_processes();
  static void restart_processes()
  {
  process_needs_restart = 0;
  service_for_each_flags(SVC_RESTARTING,
  restart_service_if_needed);
  }
  通过循环检测服务列表service_list 中每个服务的 svc->flags 标记,如果为 SVC_RESTARTING,
  那么在满足条件的情况下调用:restart_service_if_needed
  通过 service_start 来再次启动该服务。
  ActivityManagerService.main
  I/SystemServer( 45): Starting Power Manager.
  I/ServiceManager( 26): service 'SurfaceFlinger' died
  D/Zygote ( 30): Process 45 terminated by signal (11)
  I/Zygote ( 30): Exit zygote because system server (45) has terminated
  通过错误信息发现程序在调用 SurfaceFlinger服务的时候被中止。
  Service_manager.c (frameworks/base/cmds/servicemanager):
  LOGI("service '%s' died/n", str8(si->name));
  Binder.c (frameworks/base/cmds/servicemanager):
  death->func(bs, death->ptr);
  Binder.c (kernel/drivers/misc)中的函数
  binder_thread_read()
  struct binder_work *w;
  switch (w->type)
  为 BINDER_WORK_DEAD_BINDER 的时候
  binder_parse()中
  当 cmd 为 BR_DEAD_BINDER的时候
  执行 death->func(bs, death->ptr)
  因为函数
  int do_add_service(struct binder_state *bs,
  uint16_t *s, unsigned len,
  void *ptr, unsigned uid)
  的 si->death.func = svcinfo_death;
  所以 death->func(bs, death->ptr) 实际上执行的是
  svcinfo_death()//Service_manager.c (frameworks/base/cmds/servicemanager)
  所以会打印出:service 'SurfaceFlinger' died
  I/ServiceManager( 26): service 'SurfaceFlinger' died
  Thread::run
  _threadLoop() // Threads.cpp (frameworks/base/libs/utils)
  status_t SurfaceFlinger::readyToRun()
  mBootAnimation = new BootAnimation(this);
Handler属于某个线程,取决Handlerd对象在哪个线程中建立。Handler在构建时做了如下的默认动作:
    * 从线程上下文取得Looper。
    * 通过Looper获取到消息队列并记录在自己的成员mQueue变量中
Android bind 原理
在android中有一个程序员大量使用的操作,就是bindservice,通过一个service可以实现代码的复用以及解耦,所以对这个bindservice的理解的深度直接影响了一个开发人员对于android系统开发的整体的掌握情况。
    好啦,废话不多说,首先我们来看一个我开发过程中遇到的例子。
    在开发中我们常常要用到这们一种情况,我们的UI逻辑和为UI逻辑提供服务的一些算法的是需要解耦的。这样,当我们UI和算法各自独立发生变化的时候,只要接口没有改变,对于UI的开发人员和算法的开发人员来将,就可以不去考虑,这个对与开发的好处,我想不用多说了。
    Android的Service是基于IBinder的, IBinder其实就是windows上的com,这个以后我会有详细的文章来解释。
    要使用service首先要通过AndroidMainifest.xml注册,这样在安装你的包的时候,系统就会识别出你所编写的serice,这样就可以提供给别的应用使用了。
    一个典型service的AndroidMainifest.xml如下:
           <service android:name="your_service_name"
                android:exported="true"
                android:process="your_service_process_name">
                <intent-filter>
                    <action android:name="intent_string_to_start_your_service" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </service>
    这个service域要放在xml文件的application域中,其中android:name的意思是你的service的名字,可以是你想要的任意的字符串。android:process是当这个service被你的调用启动起来以后在系统中进程的名字。如果这个域没有写,那么默认的启动的是 local的service,就是将service放到本地进程的主线程中,如何你之定了一个进程的名字,那么系统就会启动一个独立的进程作为你的服务,在shell中,通过ps就可以看到这个进程的名字。action android:name的意思是说那个Intent会启动这个service。
   在添加了xml文件以后,就是正式的开写java的代码了。
   首先,要创建一个aidl文件,熟悉windows上com的同志,看到这个名字都会会心的一笑,因为这个和windows上的midl是一样的,估计就是一帮搞windows的人被google挖走了。android和根据这个aidl自动的声称一个infterface的文件做为service的接口文件。
   这里我提供一个例子:
   package com.motorola.inputmethod.base;
   interface IYourAidl {
      int A(int i, in int[] j, out int[] k );
      int B(int i, in int[] j, out int[] k );
  }
  在这里我们要注意的是你所定义的接口的变量的类型,android为一个他所默认的类型提供了相应的parcel服务类。如果你的参数的类型是一些 java的基本类型,或是string, arrayd等,是没有问题的,当如果参数是你自己定义的class类型,那么你就要写一个自己的parcel类了,这个以后我们还会说。还有就是,大家看如果A中的参数是int这些简单的类型的,那么没问题,如果是像int[]这样的类型的话,那么你要在类型的情面加上in 或是 out来告诉系统这个是一个输入还是输出参数。
   在写完aidl文件以后我们在添加一个对应与aidl文件的Android.mk文件。如下:
   LOCAL_PATH:= $(call my-dir)
   include $(CLEAR_VARS)
     LOCAL_SRC_FILES := /
           path_to_your_aidl_file/your_test.aidl
        
     LOCAL_MODULE := com.android.youraidl
     include $(BUILD_STATIC_JAVA_LIBRARY)
 OK,在搞定aidl以后,我们开始实现一个使用这个aidl的service,其实一个service可以还有任何一个对象,不一定要含有aidl 的,这里我们只是使用了aidl作为service的一个对象,其实service是service,aidl是aidl,这个希望大家能够理解。
  一个service如下:
  import android.app.Service;
  import path_to_your_aidl_file.IYourAidl;
  public class your_service_name extends Service {
      public void onCreate() {
        super.onCreate();
        log.d("service onCreate")
      }
 
      public void onDestroy() {
        super.onDestroy();
        log.d("service onDestroy")
      }
     
      private final IYourAidl.Stub mBinder = new IYourAidl.Stub(){
         public int A(int i, in int[] j, out int[] k ){
            log.d("aidl implimention do something in A");
         }
         public int B(int i, in int[] j, out int[] k ){
           log.d("aidl implimention do something in B");
         }
      }
      public IBinder onBind(Intent intent) {
        return mBinder;
      }
   }
   在你实现的service中,首先要保证你的service的名字和AndroidMainifest.xml中的android:name的名字是一样的。然后,在你的service中要实现onCreate和onDestroy两个接口,onBind操作返回的是一个IBinder的子类,在这里我们返回的是aidl的一个实现。在你启动一个service的时候,service首先调用onCreate然后调用onBind返回service本地的一个对象。
   在service中,引为aidl是一个抽象类,所以我们要提供一分基于该接口的实现。
   好了,现在service这边就准备好了,当你的service被安装到系统以后,一个客户端就可启动他了。 启动代码如下:
   import android.content.ServiceConnection;
   import path_to_your_aidl_file.IYourAidl;
   import your_service_name;
   .............
 
           private IYourAidl mAidl;
           private class YourServiceConnection implements ServiceConnection {
           
            public void onServiceConnected(ComponentName name, IBinder service) {
                mAidl = IYourAidl.Stub.asInterface(service);
            }
   
            public void onServiceDisconnected(ComponentName name) {}
        }
   
      private YourServiceConnection mServiceConnection;
      public  boolean startService(){
          if (null == mAidl) {
              Intent serviceIntent = new Intent("intent_string_to_start_your_service");
              if (null == mServiceConnection) {
                  mServiceConnection =
                      new YourServiceConnection();
              }
              // Bind service
              if (bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
                  return true;
              } else {
                  return false;
              }
          }
          return true;
      }
      当客户端调用startService的时候,最忠会调用bindService, 他的第一个参数是一个intent就是和xml文件相同的Intent名字的一个intent。第二个参数是一个ServiceConnection对象,作为回调函数, Context.BIND_AUTO_CREATE表示的是自动绑定。
      在用户调用bindservice以后,系统会检查是否有符合Intent的service,如果有的话就创建了,如果没有的话,就创建一个新的 service,并调用service的onCreate接口。同时如果制定了ServiceConnection对象和 BIND_AUTO_CREATE参数,那么就会调用service的onBind接口,onBind接口返回的IBinder对象传个 ServiceConnection的onServiceConnected的第二个参数,在onServiceConnected中通过mAidl = IYourAidl.Stub.asInterface(service);将远程的一个变量和本地的一个变量绑定。从而实现了RPC调用,这样,用户进程就是可以像使用本地变量一样通过PRC调用远程的操作了。BindService操作是一个异步的操作,所以什么时候能调用 onServiceConnected是不一定的,如果在用户进程中bindservice以后直接使用mAidl而没有判断的话,很可能会有错误。
    同时如果通过mAidl调用远程操作的话,需要将操作包含在               
               try{
                    mAidl.A(.....)
                }
                catch(RemoteException e) {
                    Log.e("error", e.getMessage());
                }
     中
   以上是我对service使用的一个例子,其实在Service背后支持着他的是IBinder的IPC机制,这个我会专门用文章来讲。
/
635  which adb
  636  . build/envsetup.sh
  637  tapas
  638  traceview input.trace &
  639  dmtracedump input.trace > input.trace.dump
  640  traceview input.trace &
  641  vi input.trace.dump
  642  adb shell ps
  643  adb shell am profile 2586 start /tmp/acore.trace
  644  adb shell am profile 2586 stop
  645  adb pull /tmp/acore.trace .
  646  dmtracedump acore.trace > acore.trace.dump
  647  vi acore.trace.dump
  648  traceview acore.trace&
svn log vendor/oms/packages/inputmethods/BorqsIME/src/ | grep b437 > 1.log
root@BX5ZT2X:/home/b437/codes/dragon# vi 1.log
root@BX5ZT2X:/home/b437/codes/dragon# svn log -v -r 44777
------------------------------------------------------------------------
r44777 | b437 | 2010-06-28 10:18:00 +0800 (Mon, 28 Jun 2010) | 5 lines
Changed paths:
   M /branches/dragon/vendor/oms/packages/inputmethods/BorqsIME/src/com/android/inputmethod/borqs/WritePadView.java
Tao Lan: [settings]Modify penColor
borqsbt:0089700
reviewers:zhang JinShan
rootcause:The problem of pen color value
------------------------------------------------------------------------
root@BX5ZT2X:/home/b437/codes/dragon# svn diff -r 44777:44776 vendor/oms/packages/inputmethods/BorqsIME/src/com/android/inputmethod/borqs/WritePadView.java
Index: vendor/oms/packages/inputmethods/BorqsIME/src/com/android/inputmethod/borqs/WritePadView.java
===================================================================
--- vendor/oms/packages/inputmethods/BorqsIME/src/com/android/inputmethod/borqs/WritePadView.java    (revision 44777)
+++ vendor/oms/packages/inputmethods/BorqsIME/src/com/android/inputmethod/borqs/WritePadView.java    (revision 44776)
@@ -501,7 +501,7 @@
      }
     
      private int getPenColor() {
-        int valueV = 0xFF800080;
+        int valueV = 0xFFF000FF;
         switch (IMESettings.getHWRPenColor()) {
         case 1:
             valueV = Color.RED;
@@ -513,7 +513,7 @@
             valueV = Color.BLUE;
             break;
         case 4:
-            valueV = 0xFF800080;
+            valueV = 0xFFF000FF;
             break;
         case 5:
             valueV = Color.GRAY;
@@ -528,7 +528,7 @@
             valueV = Color.BLACK;
             break;
         default:
-            valueV = 0xFF800080;
+            valueV = 0xFFF000FF;
         }
         
         return valueV;
root@BX5ZT2X:/home/b437/codes/dragon# vi
adb root 超级用户
7月23
维吾尔语用7月23号版本 oscar
mount -t yaffs2 -o remount /dev/block/mtdblock6 /system
mount -t yaffs2 -o remount /dev/block/mtdblock10 /opl
mkdir -p /temp/a/b/c/
sudo adb root || su
如何分析android应用性能,java函数调用关系。
1.mksdcard  -l sdcard  512M /root/.android/sdcard.img
2.在应用程序中加入trace 的开始和结束函数(详细说明参考开发文档)
   // start tracing to "/sdcard/calc.trace"
     Debug . startMethodTracing ( "calc" );    --------calc是你要生成的.trace文件的名字
  // ..
  // stop tracing
  Log.d("lizhenghui","NNNNNNN stop traceview debug");
  Debug . stopMethodTracing ();
3 . 在eclipse 的run Configuraton-->Target 项的 Additional Emulator Command Line Options下指定Emulator启动时的sdcard目录: -sdcard /root/.android/sdcard.img
4.从eclipse运行android运用。
5. 退出运用程序, 用logcat 查看上面的log信息是否出现,确保trace文件不为空。
6。adb ls /sdcard/
    会看到一个文件:calc.trace 就是我们需要的文件。
7。把trace文件下载到本地。
 adb pull /sdcard/calc.trace ./
8。traceview calc.trace
    打开了trace文件,用点耐心看看帮助文档,可以知道这个文件上部分为时间关系图,反映的就是在什么时间执行什么函数,下部分为函数关系图,反映的就是函数的调用关系。下部分各项的含义:
Name:列出的是所有的调用项,前面的数字是编号,展开可以看到有的有Parent 和Children子项,就是指被调用和调用。
Incl: inclusive时间占总时间的白分比
inclusive: 调用占用时间。(包括了所有的子方法的调用时间)
Excl: 执行占总时间的白分比。
Exclusive: 执行时间,不包含子方法的时间。
Calls+Recur Calls/Total: 调用和重复调用的次数
Time/Call: 总的时间。(ms)
9.生成函数调用关系图
  A. apt-get install graphviz
  B. dmtacedump  -g aa.png calc.trace
  C. ls 查看是否生成aa.png 图片。
10。用图片工具打开图片。
google account: 553759735@qq.com 33220233 电话号码 国家号
11。图片上函数显示格式 (用开发文档上的做例子,我用的是远程外网)
用户辞典备份
     <ref> callname (<inc-ms>, <exc-ms>,<numcalls>)
    * ref -- 编号
    * callname -- 函数名
    * <inc--ms>--调用时间
    * <exc-ms> -- 执行时间
    * <numcalls> -- 被调用的次数
systemReady -- > resumeTopActivity --> topRuningActivity == null --> resumeHomeActivity
startProcessLocked  process.start
Input Event Detect and Dispatch
The input event dispatch engine is in WindowManagerService.java. WindowManagerService.java creates a thread to read input event from KeyInputQueue.java and dispatches the event to the window which has current focus through binder.
                // Retrieve next event, waiting only as long as the next
                // repeat timeout.  If the configuration has changed, then
                // don't wait at all -- we'll report the change as soon as
                // we have processed all events.
                QueuedEvent ev = mQueue.getEvent(
                    (int)((!configChanged && curTime < nextKeyTime)
                            ? (nextKeyTime-curTime) : 0));
If an input event is read, it judges the input event type. Currently support three input event types: key, trackball and pointer. Then according to event type call corresponding dispatch function to the window which has current focus through binder. For example, for key event, it calls the following code.
            focus.mClient.dispatchKey(event);
At the lowest level, Android reads the real input event (keyboard, mouse or touch) from Linux input device. The corresponding source code is EventHub.cpp. For key input event, Android maps scan code to key code according to a key layout map file. OEM needs customize the key layout map file to match the needs of his own device. It uses the following method to find out the key layout map file.
        // a more descriptive name
        ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);
        devname[sizeof(devname)-1] = 0;
        device->name = devname;
 
        // replace all the spaces with underscores
        strcpy(tmpfn, devname);
        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
            *p = '_';
 
        // find the .kl file we need for this device
        const char* root = getenv("ANDROID_ROOT");
        snprintf(keylayoutFilename, sizeof(keylayoutFilename),
                 "%s/usr/keylayout/%s.kl", root, tmpfn);
        bool defaultKeymap = false;
        if (access(keylayoutFilename, R_OK)) {
            snprintf(keylayoutFilename, sizeof(keylayoutFilename),
                     "%s/usr/keylayout/%s", root, "qwerty.kl");
            defaultKeymap = true;
        }
        device->layoutMap->load(keylayoutFilename);
OEM can get the key layout map file name during Android booting, because Android will log the name. The JAVA layer wrapper is KeyInputQueue.java which is used by WindowManagerService.java. It calls EventHub.cpp through JNI. com_android_server_KeyInputQueue.cpp is the JNI implementation.
sPlicy == Plicy.java --> makeNewWindow --> PhoneWindow  --> window.java -->setWindowManager --> (LocalWindowManager) setContainer -->
ActivityManagerService main-->ActivityThread new ActivityThread.systemMain-->thread.attach--> ActivityManagerService mgr.attachApplication-->attachApplicationLocked-->if(realStartActivityLocked) -->app.thread.scheduleLaunchActivity-->ActivityThread scheduleLaunchActivity-->queueOrSendMessage(H.LAUNCH_ACTIVITY, r)-->handleMessage-->handleLaunchActivity-->performLaunchActivity --> Activity activity.attach-->PlicyManager.makeNewWindow(this) mWindow.setCallback(this)
Input Event Processing
When an activity is to be launched, ActivityManagerService.java calls ActivityThread.java for creating the activity.
                activity.attach(appContext, this, getInstrumentation(), r.token, app,
                        r.intent, r.activityInfo, title, r.parent, r.embeddedID,
                        r.lastNonConfigurationInstance, config);
Then Activity.java creates a PhoneWindow.java instance to represent the activity. Each PhoneWindow.java contains a DecorView.java instance as the root of any views in the activity.
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
After an activity is created, ActivityManagerService.java calls ActivityThread.java for resume the activity. At this time, ActivityThread.java calls WindowManagerImpl.java to add the DecorView.java instance. In the ActivityThread-->handleResumeActivity -->
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();   Activity->getWindowManager, from activity-->attach-->Window->setWindowManager-->LocalwindowManager->addView-->WindowManagerImp--> addView-->root.setView(view, wparams, panelParentView); --- ViewRoot implements IWindow.Stub and WindowManagerService implements IWindowSession.Stub.
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                wm.addView(decor, l);
WindowManagerImpl.java news a ViewRoot.java instance. ViewRoot.java has a static member which will be only initialized once for each process. It’s used to let WindowManagerService.java know that there is a process linked now.
            if (!mInitialized) {
                try {
                    sWindowSession = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"))
                            .openSession(new Binder());
                    mInitialized = true;
                } catch (RemoteException e) {
                }
            }
After ViewRoot.java instance is created, WindowManagerImpl.java calls its setView API to bind the ViewRoot.java for the DecorView.java.
        // do this last because it fires off messages to start doing things
        root.setView(view, wparams, panelParentView);
In setView API, ViewRoot.java finally draws the DecorView.java and registers an IWindow instance to WindowManagerService.java.
                    res = sWindowSession.add(mWindow, attrs,
                            getHostVisibility(), mCoveredInsets);
After that WindowManagerService.java directly communicates (including dispatching input event) with this IWindow instance in ViewRoot.java. Then ViewRoot.java calls View.java to process input event. For example, for key event, dispatchKeyEvent API in View.java will be called.
    public boolean dispatchKeyEvent(KeyEvent event) {
        // If any attached key listener a first crack at the event.
        //noinspection SimplifiableIfStatement
        if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
            return true;
        }
        return event.dispatch(this);
    }
View.java detects if any key listener is registered for this view. If so, the key event will be handled by the key listener. Otherwise, it calls OnKeyDown/OnKeyUp as usual.
All the key listener implementations are under frameworks/base/core/java/android/text/method folder.
MultiTapKeyListener.java       - if the keypad is NUMERIC keypad, this listener is used to transform digit inputs to characters.
QwertyKeyListener.java – if the keypad is QWERTY keypad, this listener is used.
Every other JAVA process works in a different way. It’s controlled by system_server while forked by zygote. When any JAVA process other than system_server is forked from zygote, it automatically calls ActivityThread.java’s main static function(See Process.java and the following code snippet).
try {
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
logAndPrintError (newStderr, "Error starting. ", ex);
}
The ActivityThread.java’s main function creates an instance of ActivityThread. ActivityThread then creates an instance of ApplicationThread. The ApplicationThread will work as an IBinder object to interact with ActivityManagerService in system_server. The new process does nothting at this time other than waiting IPC call from system_server. The Application and ApplicationContext object won’t be created at this time. Actually it’s deferred to when the process really works, eg. start an activity, receive intent or start a service.
For example, when start an activity, ActivityManagerService know which process the to-be-launched activity should run in, so it will RPC call ApplicationThread’s scheduleLaunchActivity to launch a new activity in that process. ApplicationThread then post a message to let ActivityThread know it needs to start an activity. ActivityThread then creates Application and ApplicationContext object. After that, it calls Instrumentation, then Instrumentation finally calls JAVA dalvik VM to really create an activity JAVA object.
checksum missmatch
   1.  Check out the latest revision of the corrupted directory into a temporary directory
   2. Delete the munged SVN revision files
   3. Copy the correct SVN revision files into the working directory
在onCreate函数中初始化:
mGestureDetector = new GestureDetector(this);
 
参数是OnGestureListener,然后让TestActivity实现 OnGestureListener 和OnDoubleTapListener接口:
RuntimeInit Native 在AndroidRutime.cpp 里
throw exception 接收 运行
monkey -s 100 --pct-touch 30 --throttle 400 -v 100000 --ignore-crashes --monitor-native-crashes
monkey -p com.android.todo.TaskEditor -s 150 --pct-touch 90 -v 100000 --ignore-crashes --monitor-native-crashes
最优方案
implicity yinghan
explicity mingque
ackage org.kodejava.example.commons.lang;
02.
03.import org.apache.commons.lang.StringUtils;
04.
05.public class CheckEmptyString {
06.    public static void main(String[] args) {
07.        String var1 = null;
08.        String var2 = "";
09.        String var3 = "    /t/t/t";
10.        String var4 = "Hello World";
11.
12.        System.out.println("var1 is blank? = " + StringUtils.isBlank(var1));
13.        System.out.println("var2 is blank? = " + StringUtils.isBlank(var2));
14.        System.out.println("var3 is blank? = " + StringUtils.isBlank(var3));
15.        System.out.println("var4 is blank? = " + StringUtils.isBlank(var4));
16.
17.        System.out.println("var1 is not blank? = " + StringUtils.isNotBlank(var1));
18.        System.out.println("var2 is not blank? = " + StringUtils.isNotBlank(var2));
19.        System.out.println("var3 is not blank? = " + StringUtils.isNotBlank(var3));
20.        System.out.println("var4 is not blank? = " + StringUtils.isNotBlank(var4));
21.
22.        System.out.println("var1 is empty? = " + StringUtils.isEmpty(var1));
23.        System.out.println("var2 is empty? = " + StringUtils.isEmpty(var2));
24.        System.out.println("var3 is empty? = " + StringUtils.isEmpty(var3));
25.        System.out.println("var4 is empty? = " + StringUtils.isEmpty(var4));
26.
27.        System.out.println("var1 is not empty? = " + StringUtils.isNotEmpty(var1));
28.        System.out.println("var2 is not empty? = " + StringUtils.isNotEmpty(var2));
29.        System.out.println("var3 is not empty? = " + StringUtils.isNotEmpty(var3));
30.        System.out.println("var4 is not empty? = " + StringUtils.isNotEmpty(var4));
The Result:
var1 is blank? = true
var2 is blank? = true
var3 is blank? = true
var4 is blank? = false
var1 is not blank? = false
var2 is not blank? = false
var3 is not blank? = false
var4 is not blank? = true
var1 is empty? = true
var2 is empty? = true
var3 is empty? = false
var4 is empty? = false
var1 is not empty? = false
var2 is not empty? = false
var3 is not empty? = true
var4 is not empty? = true
if ( s == null ) echo ( "was null" );
else if ( s.length() == 0 ) echo ( "was empty" );
else if ( s.trim().length () == 0 ) echo ( "was blank or other whitespace" );
cat /proc/cpuinfo | grep processor |wc -l
dicttrie 是一个两层的辞典,严格说并不是树状结构,其中第一层是一个LmaNodeLE0类型的数组,在程序中是root_这个动态数组。每个元素如图dicttrie.odg所示。第二层是一个LmaNodeGE1类型的数组,在程序中是nodes_ge1_这个动态数组,每个元素如图dicttrie.odg所示。
 其实这两个数组中是对应的汉语拼音,储存在spl_idx字段上,这个值是在spellingtrie生成时确定的,例如'ai'的spl_idx是31,root_中包含了所有的汉字,并按照spl_idx排了序,以拼音'ang'为例(下面是含'ang'的所有词汇), spellingtrie生成时其spel_idx为33, 同音字个数为6个即 num_of_homo = 6,其son就是‘ang’的下一个字拼音不同的个数,经过观察可知,六个以'ang'开头的词的第二个字的拼音都不相同,所以'ang'的 num_of_homo = 6,在生成
dict的时候经过程序中相关算法的计算,得出‘ang’的son_1st_off = 391,
homo_idx_buf_off = 463, 即nodes_ge1_[391]为'ang'的first son,通过下面的观察应该知道nodes_ge1_[391].spl_idx 对应的是'da'的spelling_idx,经查证确实如此。关于homo_idx_buf_off的计算比较复杂,同音词都放在DictTrie的lma_idx_buf_数组中。
醠 0.930130992851 1 ang
骯 1.34170755231 1 angs
昻 2.26727616676 1 ang
肮 15.7621930961 0 ang
盎 45.8593409192 0 ang
昂 1474.61550226 0 ang
昂达 257.3078729 0 ang da
昂贵 1036.37823089 0 ang gui
盎然 144.143005824 0 ang ran
肮脏 572.671468255 0 ang zang
盎司 171.831677206 0 ang si
昂扬 126.464093517 0 ang yang
举例说明汉语词是怎么在这样的字典中保存的
“爱美 393.063006485 0 ai mei”——这是汉字文件的原始数据,在生成dict的时候,会根据一些信息排序。'ai'的spelling_idx是31,经过映射,'ai'被保存在root_[2]中,root_[2]的信息如下:
son_1st_off = 87,homo_idx_buf_off = 85,spl_idx = 31,num_of_son = 69, num_of_homo = 49
在lma_idx_buf_[87]放着'ai'的第一个son,经检查是‘ai’,继续往下在lma_idx_buf_[87+29]的地方找到spl_idx=228的'mei',具体信息如下:
son_1st_off_l=0,homo_idx_buf_off_l=183,spl_idx=228,num_of_son=0,num_of_homo=2,son_1st_off_h=0,homo_idx_buf_off_h=0
son_1st_off_l=0说明其下没有son,也就是说'爱美'不是某个词的一部分,所以num_of_son=0。num_of_homo=2是因为,跟‘爱美’同音的一共是两个。
输入法的大概执行过程是这样的:
 如果输入的是'zhe',经过splparser,在spellingtrie中找到其相应的spe_idx=428,再映射到usrdic的root层的第399个元素root_[399],具体信息如下,
son_1st_off = 43678,homo_idx_buf_off = 61612,spl_idx = 428,num_of_son = 118,num_of_homo = 49,
  意思是有son 118个,同音字 49个,其第一个son在nodes_ge1_的下标是43678,其同音字在lma_idx_buf_的下标为61612*3(因为程序将每个同音字的id映射成三个字段),查找
lma_idx_buf_[61612*3], 经计算得到id=13819,一直到lma_idx_buf_[(61612+49)*3]都是‘zhe’的同音字,映射到相应的id后 在DictList的buf_[id]中取出这些汉字显示在屏幕上。
Building a simple APK
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage
   
  # Tell it to build an APK
  include $(BUILD_PACKAGE)
Building a APK that depends on a static .jar file
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # List of static libraries to include in the package
  LOCAL_STATIC_JAVA_LIBRARIES := static-library
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage
   
  # Tell it to build an APK
  include $(BUILD_PACKAGE)
Building a APK that should be signed with the platform key
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   汉子和字母 drawtext
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage
   
  LOCAL_CERTIFICATE := platform
   
  # Tell it to build an APK
  include $(BUILD_PACKAGE)
Building a APK that should be signed with a specific vendor key
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage
   
  LOCAL_CERTIFICATE := vendor/example/certs/app
   
  # Tell it to build an APK
  include $(BUILD_PACKAGE)
Adding a prebuilt APK
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Module name should match apk name to be installed.
  LOCAL_MODULE := LocalModuleName
  LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
  LOCAL_MODULE_CLASS := APPS
  LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
   
  include $(BUILD_PREBUILT)
字符清空 java
Adding a Static Java Library
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Any libraries that this library depends on
  LOCAL_JAVA_LIBRARIES := android.test.runner
   
  # The name of the jar file to create
  LOCAL_MODULE := sample
   
  # Build a static jar file.
  include $(BUILD_STATIC_JAVA_LIBRARY)
rm /data/data/oms.bru/databases/*.db
 1、父类的构造函数是否一定会执行?
  2、是先执行子类的构造函数还是先执行父类的构造函数?
  3、如果父类有多个构造函数,那么 Java 会选择哪一个?
  - 父类的构造函数是否一定会执行?
  是的,父类的构造函数一定会执行。所以如果某个类的层次很深,那么它创建对象时就会要执行一大堆的构造函数。
  - 是先执行子类的构造函数还是先执行父类的构造函数?
  Java 会顺着继承结构往上一直找到 Object,然后从 Object 开始往下依次执行构造函数。先执行父类的构造函数,那么子类的构造函数执行的时候就不需要担心父类的成员是否初始化好了。
  - 如果父类有多个构造函数,那么 Java 会选择哪一个?
  如果父类有多个构造函数,那么子类可以在构造函数中选择其中一个(且最多只能选择一个)来执行。如果子类没有选择,那么 Java 将会执行父类的缺省构造函数。
printf 显示宽字符
1 服务器端 new RemoteCallBackList  存储接口 mCallBackList;
2 客户端 new callback 接口mCallBack 然后调用服务器端的 自定义的一个函数 registerMyCallBack --》在该函数里 mCallBackList.register(mCallBack) 即可在服务器端的变化告知客户端。
在android上开发的程序将会在不同分辨率的手机上运行。为了让程序外观不至于相差太大,所以引入了dip的概念。比如定义一个矩形10 x 10dip. 在分辨率为160dpi 的屏上,比如G1,正好是10 x 10像素。而在240 dpi 的屏,则是15 x 15 像素. 换算公式为 pixs = dips * (density/160). density 就是屏的分辨率
check包名一致性  classNotFound 是包名跟路径不一致~~~
# ackage com.Aina.Android;  
#   
# import java.util.Vector;  
#   
# import android.graphics.Canvas;  
# import android.graphics.Color;  
# import android.graphics.Paint;  
# import android.graphics.Paint.FontMetrics;  
# import android.view.KeyEvent;  
#   
# public class TextUtil {  
#   
#     private int mTextPosx = 0;// x坐标  
#     private int mTextPosy = 0;// y坐标  
#     private int mTextWidth = 0;// 绘制宽度  
#     private int mTextHeight = 0;// 绘制高度  
#     private int mFontHeight = 0;// 绘制字体高度  
#     private int mPageLineNum = 0;// 每一页显示的行数  
#     private int mCanvasBGColor = 0;// 背景颜色  
#     private int mFontColor = 0;// 字体颜色  
#     private int mAlpha = 0;// Alpha值  
#     private int mRealLine = 0;// 字符串真实的行数  
#     private int mCurrentLine = 0;// 当前行  
#     private int mTextSize = 0;// 字体大小  
#     private String mStrText = "";  
#     private Vector mString = null;  
#     private Paint mPaint = null;  
#   
#     public TextUtil(String StrText, int x, int y, int w, int h, int bgcolor,  
#             int textcolor, int alpha, int textsize) {  
#         mPaint = new Paint();  
#         mString = new Vector();  
#         this.mStrText = StrText;  
#         this.mTextPosx = x;  
#         this.mTextPosy = y;  
#         this.mTextWidth = w;  
#         this.mTextHeight = h;  
#         this.mCanvasBGColor = bgcolor;  
#         this.mFontColor = textcolor;  
#         this.mAlpha = alpha;  
#         this.mTextSize = textsize;  
#     }  
#   
#     public void InitText() {  
#         mString.clear();// 清空Vector  
#         // 对画笔属性的设置  
# //      mPaint.setARGB(this.mAlpha, Color.red(this.mFontColor), Color  
# //              .green(this.mFontColor), Color.blue(this.mFontColor));  
#         mPaint.setTextSize(this.mTextSize);  
#         mPaint.setColor(Color.BLUE);  
#         this.GetTextIfon();  
#     }  
#   
#     /**
#      * 得到字符串信息包括行数,页数等信息
#      */  
#     public void GetTextIfon() {  
#         char ch;  
#         int w = 0;  
#         int istart = 0;  
#         FontMetrics fm = mPaint.getFontMetrics();// 得到系统默认字体属性  
#         mFontHeight = (int) (Math.ceil(fm.descent - fm.top) + 2);// 获得字体高度  
#         mPageLineNum = mTextHeight / mFontHeight;// 获得行数  
#         int count = this.mStrText.length();  
#         for (int i = 0; i < count; i++) {  
#             ch = this.mStrText.charAt(i);  
#             float[] widths = new float[1];  
#             String str = String.valueOf(ch);  
#             mPaint.getTextWidths(str, widths);  
#             if (ch == '/n') {  
#                 mRealLine++;// 真实的行数加一  
#                 mString.addElement(this.mStrText.substring(istart, i));  
#                 istart = i + 1;  
#                 w = 0;  
#             } else {  
#                 w += (int) Math.ceil(widths[0]);  
#                 if (w > this.mTextWidth) {  
#                     mRealLine++;// 真实的行数加一  
#                     mString.addElement(this.mStrText.substring(istart, i));  
#                     istart = i;  
#                     i--;  
#                     w = 0;  
#                 } else {  
#                     if (i == count - 1) {  
#                         mRealLine++;// 真实的行数加一  
#                         mString.addElement(this.mStrText.substring(istart,  
#                                 count));  
#                     }  
#                 }  
#             }  
#         }  
#     }  
#   
#     /**
#      * 绘制字符串
#      *  
#      * @param canvas
#      */  
#     public void DrawText(Canvas canvas) {  
#         for (int i = this.mCurrentLine, j = 0; i < this.mRealLine; i++, j++) {  
#             if (j > this.mPageLineNum) {  
#                 break;  
#             }  
#             canvas.drawText((String) (mString.elementAt(i)), this.mTextPosx,  
#                     this.mTextPosy + this.mFontHeight * j, mPaint);  
#         }  
#     }  
#     /**
#      * 翻页等按键处理
#      * @param keyCode
#      * @param event
#      * @return
#      */  
#     public boolean KeyDown(int keyCode, KeyEvent event)  
#     {  
#         if (keyCode == KeyEvent.KEYCODE_DPAD_UP)  
#         {  
#             if (this.mCurrentLine > 0)  
#             {  
#                 this.mCurrentLine--;  
#             }  
#         }  
#         else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)  
#         {  
#             if ((this.mCurrentLine + this.mPageLineNum) < (this.mRealLine - 1))  
#             {  
#                 this.mCurrentLine++;  
#             }  
#         }  
#         return false;  
#     }  
# }  
   1. package com.Aina.Android;  
   2.   
   3. import android.content.Context;  
   4. import android.graphics.Canvas;  
   5. import android.graphics.Color;  
   6. import android.graphics.Paint;  
   7. import android.view.KeyEvent;  
   8. import android.view.View;  
   9.   
  10. public class MyView extends View implements Runnable{  
  11.   
  12.     private Paint mPaint;  
  13.     private int mICount = 0;  
  14.     private TextUtil mTextUtil;  
  15.   
  16.     public MyView(Context context) {  
  17.         super(context);  
  18.         mPaint = new Paint();  
  19.         String string = "测试自动换行(第1行)/n/n测试自动换行(第2行)/n测试自动换行(第3行)/n测试自动换行(第4行)/n测试自动换行(第5行)/n测试自动换行(第6行)/n测试自动换行(第7行)";  
  20.         mTextUtil = new TextUtil(string, 15, 150, 300, 80, 0x000000, 0xff00ff,  
  21.                 255, 16);  
  22.         mTextUtil.InitText();  
  23.         new Thread(this).start();  
  24.     }  
  25.   
  26.     @Override  
  27.     protected void onDraw(Canvas canvas) {  
  28.         super.onDraw(canvas);  
  29.         canvas.drawColor(Color.BLACK);  
  30.         mPaint.setAntiAlias(true);  
  31.         if (mICount < 100) {  
  32.             mICount++;  
  33.         }  
  34.         mPaint.setColor(Color.RED);  
  35.         canvas.drawText("装在进度:" + mICount + "%......", 10, 20, mPaint);  
  36.         mTextUtil.DrawText(canvas);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void run() {  
  41.         Thread.currentThread();  
  42.         while(!Thread.interrupted()){  
  43.             try{  
  44.                 Thread.sleep(10);  
  45.             }catch(Exception ex){  
  46.                 ex.printStackTrace();  
  47.                 Thread.currentThread().interrupt();  
  48.             }  
  49.             this.postInvalidate();  
  50.         }  
  51.     }  
  52.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  53.           
  54.         return mTextUtil.KeyDown(keyCode, event);  
  55.     }  
  56. }  
1、synchronized关键字的作用域有二种:
1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
不显示提示信息
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
svn 解决冲突 恢复3个临时文件
replace    
autobj =  a.o b.o c.o
all_dir =src src/common src/core src/engine
SRC_DIR =src/
OBJ_DIR=obj/
然后有:
$(foreach file,$(autobj:.o=.cpp),$(foreach dir,$(all_dir),$(subst $(SRC_DIR),$(OBJ_DIR),$(firstword $(subst .cpp,.o,$(wildcard $(SRC_DIR)$(dir)/$(file)))))))
你在上次解决了所有问题
解释:
1:foreach file,$(autobj:.o=.cpp)这半句的意思是,从上面autobj里头取每一个.o文件,将后缀改成.c;
2:$(foreach dir,$(all_dir),$(subst $(SRC_DIR),$(OBJ_DIR)这半句意思是,从all_dir把路径取出来,将字符串替换,src/将替换成obj/
3:$(firstword $(subst .cpp,.o,$(wildcard $(SRC_DIR)$(dir)/$(file))))))) 这半句是利用wildcard从目录下提取这个.cpp文件,如果有就返回.cpp文件带目录的文件名,如果没有就返回空,然后再将返回的文件名.cpp 替换成.o,然后再提供这个这个字符串的第一个字给上一层替换;
svn 解决冲突 删除了自己的
最后,这个整个效果是,给定一堆.o文件名,给定一堆的目录,如果在某个目录下具有该.o对应的.cpp文件,则返回这个点.cpp文件的长路径名,这样就可以自动搜索目录,写makefile就去掉了一些繁琐,对于大工程复杂目录,方便很多
server read data
try {   
   // 创建一个 URL 对象   
   URL url = new URL(your_url);   
 
   // 创建一个 URL 连接,如果有代理的话可以指定一个代理。   
   URLConnection connection = url.openConnection(Proxy_yours);   
   // 对于 HTTP 连接可以直接转换成 HttpURLConnection,这样就可以使用一些 HTTP 连接特定的方法,如 setRequestMethod() 等:
   //HttpURLConnection connection =
//      (HttpURLConnection)url.openConnection(Proxy_yours);   
   // 在开始和服务器连接之前,可能需要设置一些网络参数   
   connection.setConnectTimeout(10000);   
   connection.addRequestProperty("User-Agent",
                                           "J2me/MIDP2.0");   
   // 连接到服务器   
   connection.connect();   
   // 与服务器交互:
   OutputStream outStream = connection.getOutputStream();   
   ObjectOutputStream objOutput = new ObjectOutputStream(outStream);   
   objOutput.writeObject(new String("this is a string..."));   
   objOutput.flush();   
   InputStream in = connection.getInputStream();   
   // 处理数据   
   ...   
    } catch (Exception e) {
      // 网络读写操作往往会产生一些异常,所以在具体编写网络应用时   
      // 最好捕捉每一个具体以采取相应措施   
    }
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(FileUrl, "application/system-update");
startActivity(intent);
Shift KeyState override setShifted isShifted setShiftLocked
192.168.6.11 guest guest  
public void onClick(View view) {
...
// try to write the content
try {
  // open myfilename.txt for writing
  OutputStreamWriter out = new OutputStreamWriter(openFileOutput("myfilename.txt",0));
  // write the contents on mySettings to the file
  out.write(mySettings);
  // close the file
  out.close();
} catch (java.io.IOException e) {
  //do something if an IOException occurs.
}
  try {
    // open the file for reading
    InputStream in = openFileInput("myfilename.txt");
 
    // if file the available for reading
    if (in) {
      // prepare the file for reading
      InputStreamReader input = new InputStreamReader(in);
      BufferedReader buffreader = new BufferedReader(inputreader);
 
      String line;
 
      // read every line of the file into the line-variable, on line at the time
      while (( line = buffreader.readLine())) {
        // do something with the settings from the file
      }
 
    }
 
    // close the file again
    in.close();
  } catch (java.io.FileNotFoundException e) {
    // do something if the myfilename.txt does not exits
  }
anddroid 下载文件
try {
//set the download URL, a url that points to a file on the internet
//this is the file to be downloaded
URL url = new URL(“http://somewhere.com/some/webhosted/file”);
//create the new connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
//set up some things on the connection
urlConnection.setRequestMethod(“GET”);
urlConnection.setDoOutput(true);
//and connect!
urlConnection.connect();
//set the path where we want to save the file
//in this case, going to save it on the root directory of the
//sd card.
File SDCardRoot = Environment.getExternalStorageDirectory();
//create a new file, specifying the path, and the filename
//which we want to save the file as.
File file = new File(SDCardRoot,”somefile.ext”);
//this will be used to write the downloaded data into the file we created
FileOutputStream fileOutput = new FileOutputStream(file);
//this will be used in reading the data from the internet
InputStream inputStream = urlConnection.getInputStream();
//this is the total size of the file
int totalSize = urlConnection.getContentLength();
//variable to store total downloaded bytes
int downloadedSize = 0;
//create a buffer…
byte[] buffer = new byte[1024];
int bufferLength = 0; //used to store a temporary size of the buffer
//now, read through the input buffer and write the contents to the file
while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
//add the data in the buffer to the file in the file output stream (the file on the sd card
fileOutput.write(buffer, 0, bufferLength);
//add up the size so we know how much is downloaded
downloadedSize += bufferLength;
//this is where you would do something to report the prgress, like this maybe
updateProgress(downloadedSize, totalSize);
}
//close the output stream when done
fileOutput.close();
//catch some possible errors…
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
   1.
      package com.helloandroid.imagedownloader;
   2.
       
   3.
      import java.io.BufferedInputStream;
   4.
      import java.io.File;
   5.
      import java.io.FileOutputStream;
   6.
      import java.io.IOException;
   7.
      import java.io.InputStream;
   8.
      import java.net.URL;
   9.
      import java.net.URLConnection;
  10.
       
  11.
      import org.apache.http.util.ByteArrayBuffer;
  12.
       
  13.
      import android.util.Log;
  14.
       
  15.
      public class ImageManager {
  16.
       
  17.
              private final String PATH = "/data/data/com.helloandroid.imagedownloader/";  //put the downloaded file here
  18.
             
  19.
       
  20.
              public void DownloadFromUrl(String imageURL, String fileName) {  //this is the downloader method
  21.
                      try {
  22.
                              URL url = new URL("http://yoursite.com/" + imageURL); //you can write here any link
  23.
                              File file = new File(fileName);
  24.
       
  25.
                              long startTime = System.currentTimeMillis();
  26.
                              Log.d("ImageManager", "download begining");
  27.
                              Log.d("ImageManager", "download url:" + url);
  28.
                              Log.d("ImageManager", "downloaded file name:" + fileName);
  29.
                              /* Open a connection to that URL. */
  30.
                              URLConnection ucon = url.openConnection();
  31.
       
  32.
                              /*
  33.
                               * Define InputStreams to read from the URLConnection.
  34.
                               */
  35.
                              InputStream is = ucon.getInputStream();
  36.
                              BufferedInputStream bis = new BufferedInputStream(is);
  37.
       
  38.
                              /*
  39.
                               * Read bytes to the Buffer until there is nothing more to read(-1).
  40.
                               */
  41.
                              ByteArrayBuffer baf = new ByteArrayBuffer(50);
  42.
                              int current = 0;
  43.
                              while ((current = bis.read()) != -1) {
  44.
                                      baf.append((byte) current);
  45.
                              }
  46.
       
  47.
                              /* Convert the Bytes read to a String. */
  48.
                              FileOutputStream fos = new FileOutputStream(file);
  49.
                              fos.write(baf.toByteArray());
  50.
                              fos.close();
  51.
                              Log.d("ImageManager", "download ready in"
  52.
                                              + ((System.currentTimeMillis() - startTime) / 1000)
  53.
                                              + " sec");
  54.
       
  55.
                      } catch (IOException e) {
  56.
                              Log.d("ImageManager", "Error: " + e);
  57.
                      }
  58.
       
  59.
              }
  60.
      }
#
  <uses-permission android:name="android.permission.INTERNET"></uses-permission>
#
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
#
        <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>

转载地址:https://blog.csdn.net/taoshengyang/article/details/5958381 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:windows get android source
下一篇:linux boot process

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年03月22日 12时41分35秒