关键字:Android,Activity,Window,View



一般来讲,Activity就是一个可视的人机交互界面。

每一个Activity都有一个默认的Window,一般来讲,这个Window都是全屏的,当然也有例外,比如Dialog的Window就是非全屏的。Activity也可以利用默认Window以外的其它的Window,比如弹出一个Dialog对话框。

Window里可见的内容,就是一层一层的View。Window里的View是通过setContentView这个接口set进去的,这个ContentView就是Window里处于最根部的View。

关键字:Failure [INSTALL_FAILED_CONTAINER_ERROR]



从网上搜来的解决方法,如果遇到 INSTALL_FAILED_CONTAINER_ERROR 这种错误可以一试。



there is a file you need to remove ( logcat details it )



can’t remember what it is but this should work



find /mnt /data/local -iname "*.tmp" -exec rm {} ;



edit, remembered wrong, see http://forum.cyanoge…dpost__p__24587
; cheers Kyle Potts !!




rm /mnt/secure/asec/smdl2tmp1.asec



and please, if it says something like permission denied .. su !!!

关键字:Android,Eclipse,Debug Certificate expired on



用Eclipse编译的APK默认的Certificate是Debug类型的,Debug类型的证书的有效期限一般是一年。



当我们用Eclipse编译遇到下面的问题时,可以用后面的方法解决。

Error generating final archive: Debug Certificate expired on 10-11-20 下午6:40



The simple solution is to just delete the file “debug.keystore” which is stored in your home directory under “~/.android” (OSX, Linux). A Windows
Vista/7 user will find the file in the “C:Users<user<.android folder.




To prevent this brain training procedure for a while a decided to generate a key which lasts 1000 days instead of just the full year. Startup the
OSX terminal app or the Linux terminal and go to the “.android” folder. Delete the old certificate file first. Then issue the following command from the command line:




keytool -genkey -keypass android -keystore debug.keystore -alias androiddebugkey -storepass android -validity 1000 -dname “CN=Android Debug,O=Android,C=US”



Now there should be a new certificate file sitting in the folder which lasts 1000 days。

关键字:Android,TextView,获取,计算,宽度,



我们知道,在Android平台上,只有当一个View真正的layout到屏幕上之后,我们才可以通过getWidth()或者getMeasuredWidth()得到它的宽高,如果有一个一行的TextView,我们需要在它layout到屏幕上之前就知道它大概要占多宽呢?可以借助下面的方法:



TextPaint paint = textView.getPaint();

float len = paint.measureText(string);

在 android 的API中有提供 SystemClock.setCurrentTimeMillis()函数来修改系统时间,可惜无论你怎么调用这个函数都是没用的,无论模拟器还是真机,在logcat中总会得到"Unable to open alarm driver: Permission
denied ".这个函数需要root权限或者运行与系统进程中才可以用。




        本来以为就没有办法在应用程序这一层改系统时间了,后来在网上搜了好久,知道这个目的还是可以达到的。"
?7 w% m5 n( I# ~) t7 Q& H: {




        第一个方法简单点,不过需要在Android系统源码的环境下用make来编译:

7 l# J8 h- s* N/ ^" @1 `

        1. 在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。8
O9 h* `- o1 x/ A1 D4 {




        2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform这一行



        3. 使用mm命令来编译,生成的apk就有修改系统时间的权限了。



        第二个方法麻烦点,不过不用开虚拟机跑到源码环境下用make来编译:



        1. 同上,加入android:sharedUserId="android.uid.system"这个属性。+
[1 b5 A* F’ a- V9 f" K




        2. 使用eclipse编译出apk文件,但是这个apk文件是不能用的。



        3. 用压缩软件打开apk文件,删掉META-INF目录下的CERT.SF和CERT.RSA两个文件。  T+
L’ m, {% G




        4. 使用目标系统的platform密钥来重新给apk文件签名。这步比较麻烦,首先找到密钥文件,在我的Android源码目录中的位置是"buildtargetproductsecurity",下面的platform.pk8和platform.x509.pem两个文件。然后用Android提供的Signapk工具来签名,signapk的源代码是在"buildtoolssignapk"下,用法为"signapk
platform.x509.pem platform.pk8 input.apk output.apk",文件名最好使用绝对路径防止找不到,也可以修改源代码直接使用。


, m/ i* t* ?9 t

        这样最后得到的apk和第一个方法是一样的。9
T, I* i+ K) l; s


3 B. {$ B+ Y8 e9 v

        最后解释一下原理,首先加入android:sharedUserId="android.uid.system"这个属性。通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中。那么把程序的UID配成android.uid.system,也就是要让程序运行在系统进程中,这样就有权限来修改系统时间了。!
[; j# T3 z- x  a


7 M1 Z6 [8 u% A& i/ h2 K* y

        只是加入UID还不够,如果这时候安装APK的话发现无法安装,提示签名不符,原因是程序想要运行在系统进程中还要有目标系统的platform key,就是上面第二个方法提到的platform.pk8和platform.x509.pem两个文件。用这两个key签名后apk才真正可以放入系统进程中。第一个方法中加入LOCAL_CERTIFICATE
:= platform其实就是用这两个key来签名。




        这也有一个问题,就是这样生成的程序只有在原始的Android系统或者是自己编译的系统中才可以用,因为这样的系统才可以拿到platform.pk8和platform.x509.pem两个文件。要是别家公司做的Android上连安装都安装不了。试试原始的Android中的key来签名,程序在模拟器上运行OK,不过放到G3上安装直接提示"Package
… has no signatures that match those in shared user android.uid.system",这样也是保护了系统的安全。


% G+ V; X% e+ _) `

        最最后还说下,这个android:sharedUserId属性不只可以把apk放到系统进程中,也可以配置多个APK运行在一个进程中,这样可以共享数据,应该会很有用的。#
C& M) h% u: O! i. R


& a- s7 j4 W8 h6 m5 W$ x

9 q, A, f7 Z4 C. B8 r! L0 M3 D7 j

博主补充:#
w: k" T9 x2 P- P3 O


– X# j’ A1 j; f9 p5 j8 s2 ?" r

signapk编译结束后在 android目录下/out/host/linux-x86/framework/signapk.jar:
A  j’ |; a’ i1 ^


使用方法:java -jar signapk.jar platform.x509.pem platform.pk8 test.apk test_signed.apk

实践证明,第二种方法不需要删掉META-INF目录下的CERT.SF和CERT.RSA两个文件,直接signapk就可以。

关键字:Android,Canvas,save(),restore()3
p) v8 n* 7 d6 r+ Z




int android.graphics.Canvas.save()4
A4 K- S+ r; p* B4 x" f+ k( ^0 d




Saves the current matrix and clip onto a private stack. Subsequent calls to translate,scale,rotate,skew,concat or clipRect,clipPath will all operate
as usual, but when the balancing call to restore() is made, those calls will be forgotten, and the settings that existed before the save() will be reinstated.


; [( ~4 t6 g8 k; ]! W. `. n: Z2 W- L

Returns:

    The value to pass to restoreToCount() to balance this save()
J. A7 f% r0 o5 x




void android.graphics.Canvas.restore()



This call balances a previous call to save(), and is used to remove all modifications to the matrix/clip state since the last save call. It is an
error to call restore() more times than save() was called.
4 h& V, V) Z( j  F



Canvas的save()和restore()只是对Canvas的Matrix和Clip相关的内容进行保存和恢复。/
h1 P6 U. }+ v! J


  l$ u$ f% U: J5 h

在下面的示例代码中,原始的样子是这样的:.
a4 [, ^: y2 q


1.png 

如果注释掉了canvas.save();和canvas.restore();就会变成下面这样:9
l" g* L+ i) ]. t. v* Z


2.png 

Android中是没有MIME类型注册的概念的,相反的,任何文件类型或者MIME类型的关联性打开,是通过广播Intent来实现的。也就是说,你如果想要注册自己为.txt类型的查看器,必须为你的Activity加入intent-filter来获取到一个来源所发出的Intent Broadcasting,从而达到关联的目的。

一个合法的File Manager在执行Open动作时,应该是发出一个Intent.VIEW Action。所以你的intent-filter应该是这样子的:

<intent-filter>  
<action android:name="android.intent.action.VIEW" />  
<category android:name="android.intent.category.DEFAULT" />  
<category android:name="android.intent.category.BROWSABLE" />  
<data android:scheme="file" />  
<data android:mimeType="*/*" />  
<data android:host="*" />  
<data android:pathPattern=".*\.txt" />  
</intent-filter>  
<intent-filter>  
<action android:name="android.intent.action.VIEW" />  
<category android:name="android.intent.category.DEFAULT" />  
<category android:name="android.intent.category.BROWSABLE" />  
<data android:scheme="content" />  
<data android:host="*" />  
<data android:pathPattern=".*\.txt" />  
</intent-filter>   
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\.txt" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:host="*" />
<data android:pathPattern=".*\.txt" />
</intent-filter> 

 
在这当中,data块缺一不可,你必须同时申明android:scheme, android:host, android:pathPattern, 方可令pathPattern正确生效。对于content scheme,将会匹配诸如:content://com.metago.astro.filesystem/sdcard/txt /pg17155.txt 这样的请求,你的Activity将可以有效地打开这样的txt文件。对于file scheme,大致会匹配诸如:file:///sdcard/txt/pg17155.txt这样的请求,并且,如果有必要,你可以加入
android:mimeType限定。

也就是说,尽管Android SDK的层面上,你不可能了解到哪些mime已经注册,一个自定义的mime如何注册,但对于基础系统(linux os layer)来说,内置的mime type还是存在的,这方面你可以参考Intent SDK文档和Notebook SDK Sample获得一鳞半爪的信息。

对于你自己想要关联的文件后缀,则应该使用上面的intent-filter组合来完成关联。
一旦intent-filter关联完成,在Activity的onCreate,onNewIntent中将可通过:
Intent intent=getIntent();
Uri uri=(Uri)intent.getData();
String path=uri.getPath(); 
序列获得关联打开文件的绝对路径。

这样的方法在ASTRO文件管理器中测试通过,并且在另一个测试project中进行了测试和证明机制有效。不过,在Root Explorer,And Explorer中均失败了,原因正在于这几个浏览器并不执行符合Android SDK规范的File Open操作。
可见,随意性是值得注意的坏招数。

关键字:Android,Intent,Activities

有时候我们需要知道都有哪些Activity能响应自己发出去的这个Intent。

下面是从语音识别的Demo里摘取的部分代码:

        // Check to see if a recognition activity is present
        PackageManager pm = getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(
                new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
        if (activities.size() != 0) {
            speakButton.setOnClickListener(this);
        } else {
            speakButton.setEnabled(false);
            speakButton.setText("Recognizer not present");
        }