随笔 - 33, 文章 - 0, 评论 - 12, 引用 - 0
数据加载中……

2011年5月18日

Activity四种启动模式

Activity启动方式有四种,分别是:

standard
singleTop
singleTask
singleInstance

可以根据实际的需求为Activity设置对应的启动模式,从而可以避免创建大量重复的Activity等问题。

设置Activity的启动模式,只需要在AndroidManifest.xml里对应的<activity>标签设置android:launchMode属性,例如:

  1. <activity  
  2.     android:name=".A1"  
  3.     android:launchMode="standard" />  

下面是这四种模式的作用:

standard
默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。

例如:
若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击,又会再新启一个在它之上……
点back键会依照栈顺序依次退出。

singleTop
可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。

例如:
若我有两个Activity名为B1,B2,两个Activity内容功能完全相同,都有两个按钮可以跳到B1或者B2,唯一不同的是B1为standard,B2为singleTop。
若我意图打开的顺序为B1->B2->B2,则实际打开的顺序为B1->B2(后一次意图打开B2,实际只调用了前一个的onNewIntent方法)
若我意图打开的顺序为B1->B2->B1->B2,则实际打开的顺序与意图的一致,为B1->B2->B1->B2。


singleTask
只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。

例如:
若我的应用程序中有三个Activity,C1,C2,C3,三个Activity可互相启动,其中C2为singleTask模式,那么,无论我在这个程序中如何点击启动,如:C1->C2->C3->C2->C3->C1-C2,C1,C3可能存在多个实例,但是C2只会存在一个,并且这三个Activity都在同一个task里面。
但是C1->C2->C3->C2->C3->C1-C2,这样的操作过程实际应该是如下这样的,因为singleTask会把task中在其之上的其它Activity destory掉。
操作:C1->C2          C1->C2->C3          C1->C2->C3->C2            C1->C2->C3->C2->C3->C1             C1->C2->C3->C2->C3->C1-C2
实际:C1->C2          C1->C2->C3          C1->C2                              C1->C2->C3->C1                               C1->C2

若是别的应用程序打开C2,则会新启一个task。
如别的应用Other中有一个activity,taskId为200,从它打开C2,则C2的taskIdI不会为200,例如C2的taskId为201,那么再从C2打开C1、C3,则C2、C3的taskId仍为201。
注意:如果此时你点击home,然后再打开Other,发现这时显示的肯定会是Other应用中的内容,而不会是我们应用中的C1 C2 C3中的其中一个。


singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。

例如:
程序有三个ActivityD1,D2,D3,三个Activity可互相启动,其中D2为singleInstance模式。那么程序从D1开始运行,假设D1的taskId为200,那么从D1启动D2时,D2会新启动一个task,即D2与D1不在一个task中运行。假设D2的taskId为201,再从D2启动D3时,D3的taskId为200,也就是说它被压到了D1启动的任务栈中。

若是在别的应用程序打开D2,假设Other的taskId为200,打开D2,D2会新建一个task运行,假设它的taskId为201,那么如果这时再从D2启动D1或者D3,则又会再创建一个task,因此,若操作步骤为other->D2->D1,这过程就涉及到了3个task了。

posted @ 2015-06-23 11:04 建华 阅读(248) | 评论 (0)编辑 收藏

蓝牙发送文件

系统4.0.3以后的
      File file=new File("");
 Uri uri1 = Uri.fromFile(file);
 Intent intent = new Intent();    
intent.setAction(Intent.ACTION_SEND);   
 //intent.setType("audio/*"); 
  intent.setType("application/octet-stream");
ComponentName comp=new ComponentName("com.mediatek.bluetooth","com.mediatek.bluetooth.BluetoothShareGatewayActivity");
intent.setComponent(comp);
 intent.putExtra(Intent.EXTRA_STREAM, uri1);
startActivity(intent);
系统4.0.3以前的
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
//这个类型函数是自己工具类的方法,你可以自己设置文件类型,例如图片文件:image/*  
  //不想写类型直接*/*也是可以的
intent.setType("audio/*");
//这里setClassName就是指定蓝牙,不写这句就弹出选择用什么发送
  //有蓝牙啊,gmail啊,彩信之类的
intent.setClassName("com.android.bluetooth" , "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File("")));
startActivity(intent);


  ArrayList<Uri> uris = new ArrayList<Uri>();
   uris.add(Uri.fromFile(new File("/sdcard/111.txt")));
   uris.add(Uri.fromFile(new File("/sdcard/222.txt")));
   
               Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.setType("video/*");
intent.setClassName("com.android.bluetooth" , "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
//intent.setClassName("com.mediatek.bluetooth","com.mediatek.bluetooth.BluetoothShareGatewayActivity");
//intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File("/sdcard/111.txt")) );
//intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File("/sdcard/222.txt")));
intent.putExtra(Intent.EXTRA_STREAM, uris);
startActivity(intent);

posted @ 2013-02-20 08:45 建华 阅读(642) | 评论 (0)编辑 收藏

Android中关于dip和px以及转换的总结

我们在页面布局的时候,经常会设置容器的长度,但是到底该使用哪个作为长度的单位而懊恼。在Android中支持的描述大小区域的类型有以下几种:

px(pixels)——像素:不同的设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。

dip(device independent pixels)——设备独立像素:这个和设备硬件有关,一般我们为了支持WCGA、HVGA和QVGA推荐使用这个,不依赖于像素。等同于dp。

sp(scaled pixels—best for text size)——带比例的像素。

pt(points)——磅:1pt = 1/72英寸

in(inches)——英寸

mm(millimeters)——毫米

到底px和dip有什么区别呢?

dip != px

主要是针对不同设备而言的。在Density是160的设备上,比如用AVDManager创建的默认模拟器,dip和px是一样的但是如果同样的代码,换到不同Density的设备上,比如换到一个Density是240的设备上,dip和px体现出来的大小就不一样了。px不管在什么样的设备上都是那样长,但是dip会根据设备变化;WQVGA屏density=120;QVGA屏density=120;HVGA屏density=160;WVGA屏density=240;density值表示每英寸有多少个显示点,与分辨率是两个概念。

当屏幕density=240时使用hdpi标签的资源
当屏幕density=160时,使用mdpi标签的资源
当屏幕density=120时,使用ldpi标签的资源。

不加任何标签的资源是各种分辨率情况下共用的。布局时尽量使用单位dip,少使用px。

换算公式为:
pixs =dips * (densityDpi/160). 
dips=(pixs*160)/densityDpi

如何获得设备的屏幕分辨率和屏幕密度?
例如分辨率为480*800,密度为240的设备,方法如下


DisplayMetrics dm =getResources().getDisplayMetrics();
dm.densityDpi=240
dm.widthPixels=480
dm.heightPixels=800

posted @ 2013-02-01 10:08 建华 阅读(4986) | 评论 (2)编辑 收藏

在android模拟器中永久删除自带应用程序

首先启动android模拟器。
打开cmd命令行窗口。输入adb -s emulator-5554 shell
此时可以管理系统文件夹了,再输入ls
可以看到列出了文件夹和文件,输入cd system/app再输入ls
可以看到系统自带的应用程序apk文件,删除你想要删除的,例如Phone.apk,输入rm Phone.apk
此时会看到提示说rm failed for Phone.apk, Read-only file system那是因为这些是只读文件,我们没有权限删除它。所以接下来要做的是获取权限,首先查看权限,输入mount
可以看到/dev/block/mtdblock0 /system yaffs2 ro 0 0说明在system这个地方我们没有权限那么接下来我们就来获取权限,输入mount -o remount,rw -t yaffs2 /dev/block/mtdblock0 /system
没有提示错误,再次查看权限,输入mount
可以看到/dev/block/mtdblock0 /system yaffs2 rw 0 0
说明我们已经获取到权限了此时再输入rm Phone.apk就可以成功删除了

最后一点,就算你成功删除了,android模拟器每次启动时也会恢复回来。
那么如何永久删除呢,很简单,删除SdkSetup.apk,输入rm SdkSetup.apk
还没完,找到avd目录(一般在我的文档),进入xxxx.avd目录,删除cache.img和userdata-qemu.img
还有还有,找到%SDK_HOME%/platforms/android-X/images/system.img,复制到上面的目录中。
最后最后,再重启模拟器,大功告成!

posted @ 2012-12-26 18:17 建华 阅读(738) | 评论 (0)编辑 收藏

surfaceView 与View 的区别

如果你的游戏不吃CPU,用View就比较好,符合标准Android操作方式,由系统决定刷新surface的时机。

  但如果很不幸的,你做不到不让你的程序吃CPU,你就只好使用SurfaceView来强制刷新surface了,不然系统的UI进程很可能抢不过你那些吃CPU的线程。

  当然其实不止这两种方法来刷新Surface的,这两种只是纯Java应用比较常见的方法。

  SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。

  那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。

   当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

  所以基于以上,根据游戏特点,一般分成两类。

  1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

  2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

posted @ 2012-03-23 16:33 建华 阅读(247) | 评论 (0)编辑 收藏

android多国语言文件夹

android多国语言文件夹文件汇总如下:

中文(中国):values-zh-rCN

中文(台湾):values-zh-rTW

中文(香港):values-zh-rHK

英语(美国):values-en-rUS

英语(英国):values-en-rGB

英文(澳大利亚):values-en-rAU

英文(加拿大):values-en-rCA

英文(爱尔兰):values-en-rIE

英文(印度):values-en-rIN

英文(新西兰):values-en-rNZ

英文(新加坡):values-en-rSG

英文(南非):values-en-rZA

阿拉伯文(埃及):values-ar-rEG

阿拉伯文(以色列):values-ar-rIL

保加利亚文:  values-bg-rBG

加泰罗尼亚文:values-ca-rES

捷克文:values-cs-rCZ

丹麦文:values-da-rDK

德文(奥地利):values-de-rAT

德文(瑞士):values-de-rCH

德文(德国):values-de-rDE

德文(列支敦士登):values-de-rLI

希腊文:values-el-rGR

西班牙文(西班牙):values-es-rES

西班牙文(美国):values-es-rUS

芬兰文(芬兰):values-fi-rFI

法文(比利时):values-fr-rBE

法文(加拿大):values-fr-rCA

法文(瑞士):values-fr-rCH

法文(法国):values-fr-rFR

希伯来文:values-iw-rIL

印地文:values-hi-rIN

克罗里亚文:values-hr-rHR

匈牙利文:values-hu-rHU

印度尼西亚文:values-in-rID

意大利文(瑞士):values-it-rCH

意大利文(意大利):values-it-rIT

日文:values-ja-rJP

韩文:values-ko-rKR

立陶宛文:valueslt-rLT

拉脱维亚文:values-lv-rLV

挪威博克马尔文:values-nb-rNO

荷兰文(比利时):values-nl-BE

荷兰文(荷兰):values-nl-rNL

波兰文:values-pl-rPL

葡萄牙文(巴西):values-pt-rBR

葡萄牙文(葡萄牙):values-pt-rPT

罗马尼亚文:values-ro-rRO

俄文:values-ru-rRU

斯洛伐克文:values-sk-rSK

斯洛文尼亚文:values-sl-rSI

塞尔维亚文:values-sr-rRS

瑞典文:values-sv-rSE

泰文:values-th-rTH

塔加洛语:values-tl-rPH

土耳其文:values--r-rTR

乌克兰文:values-uk-rUA

越南文:values-vi-rVN

posted @ 2012-02-09 15:21 建华 阅读(4843) | 评论 (0)编辑 收藏

如果task中已经有这个activity A,那么就把A拿到task的最顶层,而不是创建一个新的activity

Intent.FLAG_ACTIVITY_REORDER_TO_FRONT的意思是,如果task中已经有这个activity A,那么就把A拿到task的最顶层,而不是创建一个新的activity。

所以不加flag也不会影响界面的切过去,只是会影响task的顺序而已。

posted @ 2011-12-20 19:31 建华 阅读(285) | 评论 (0)编辑 收藏

Android开发小知识 ListView几个比较特别的属性

Android:stackFromBottom="true" 设置该属性之后你做好的列表就会显示在列表的最下面,值为true和false
android:transcriptMode="alwaysScroll" 要用ListView或者其它显示大量Items的控件实时跟踪或者查看信息,并且希望最新的条目可以自动滚动到可视范围内。通过设置的控件 transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。 c
acheColorHint属性,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性
android:background="@drawable/bg",不过当你这么做以后,发现背景是变了,但是当你拖动,或者点击list空白位置的 时候发现ListItem都变成黑色的了,破坏了整体效果。 如果只是换背景的颜色的话,可以直接指定
android:cacheColorHint为你所要的颜色,如果你是用图片做背景的话,那也只要将
android:cacheColorHint指定为透明(#00000000)就可以了
android:divider="@drawable/list_driver" 其中 @drawable/list_driver 是一个图片资源,如果不想显示分割线则只要设置为android:divider="@drawable/@null" 就可以了 android:scrollbars="none"与setVerticalScrollBarEnabled(true);的效果是一样的,不活动的 时候隐藏,活动的时候也隐藏 android:fadeScrollbars="true" 配置ListView布局的时候,设置这个属性为true就可以实现滚动条的自动隐藏和显示。 fadingEdge属性,上边和下边有黑色的阴影 android:fadingEdge="none" 设置后没有阴影了

posted @ 2011-12-14 20:14 建华 阅读(422) | 评论 (0)编辑 收藏

做一个透明的activity

<activity android:name=".usual.activity.Declaration" android:theme="@android:style/Theme.Translucent.NoTitleBar" />

posted @ 2011-12-13 14:13 建华 阅读(322) | 评论 (0)编辑 收藏

draw9patch不失真背景

1.背景自适应且不失真问题的存在
      制作自适应背景图片是UI开发的一个广泛问题,也是界面设计师渴望解决的问题,我相信我们彼此都深有体会。
      比如,列表的背景图一定,但是列表的高度随着列表数据项会发生变化;标题栏的背景,无论横屏还是竖屏,高分辨率还是低分辨率,都能自动填充满,而且不失真等等背景问题。
      根据以往的经验,我们一般采用先切图后拼凑的做法,这种做法本来我想在这里和大家介绍一下,其实有的时候还是很有用的,但是说起来会比较麻烦,就不说这个非重点了,略去,如果大家真的要介绍,在回复中说明,我再考虑一下。
     Android针对这种情况,专门制作了一种.9.PNG格式来解决这个问题。

2.9.PNG格式。
      我不想在这里过多的讨论PNG格式的定义问题。但是.9.PNG确实是标准的PNG格式,只是在最外面一圈额外增加1px的边框,这个1px的边框就是 用来定义图片中可扩展的和静态不变的区域。特别说明,left和top边框中交叉部分是可拉伸部分,未选中部分是静态区域部分。right和bottom 边框中交叉部分则是内容部分(变相的相当于定义看一个内边距,神似padding功能,后面我会单独介绍一下),这个参数是可选的, 如下图。
     在Android中以9.PNG格式的图片未背景,则能够自定义拉伸而不失真,比如系统的Button就是一个典型的例子。 
     其实呢,无论是left和top,还是right和bottom都是把图片分成9块 (边角四块是不能缩放的,其他的四块则是允许缩放的),所以叫做9.PNG。

3. 使用Draw9Patch.jar制作9.PNG图片之定义拉伸区域。
      前面已经了解到9.PNG格式的工作方式,下面我们使用谷歌提供的Draw9Patch(运行android-sdk-windows\tools目录下的Draw9Patch.bat)来制作.9.PNG图片。
      第一步:准备要拉伸的图片。
                                                           
      非常小的一张图片,我希望以此为背景,中间部分填充文章内容。
      第二步:制作.9.PNG图片
      打开Draw9Patch,把图片拖进去,如下:

      默认的拉伸是整体拉伸,其实边框部分我们并不想拉伸,好,我们自己来定义拉伸区域,如下图:

       然后点击File,导出为content.9.png。
       第三步:在layout文件中使用制作的 .9.PNG图片.
       新建工程Draw9Patch,默认主Activity为Draw9PatchActivity.java:

1@Override
2public void onCreate(Bundle savedInstanceState)
3{
4    super.onCreate(savedInstanceState);
5    setContentView(R.layout.main);
6}

      我们把content.9.png文件拷贝到/res/drawable文件夹下,打开/res/layout目录下的main.xml,申明如下:

01<?xml version="1.0" encoding="utf-8"?>
02<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:orientation="vertical"
04    android:layout_width="fill_parent"
05    android:layout_height="fill_parent"
06    android:background="#777"
07    android:padding="8dip"
08    >
09    <TextView
10    android:layout_width="fill_parent"
11    android:layout_height="wrap_content"
12    android:text="正文:A NinePatchDrawable graphic is a stretchable bitmap image."
13    android:background="@drawable/content"
14    android:textColor="#000"
15    />
16</LinearLayout>

     如图,

     我们修改text,

01<?xml version="1.0" encoding="utf-8"?>
02<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:orientation="vertical"
04    android:layout_width="fill_parent"
05    android:layout_height="fill_parent"
06    android:background="#777"
07    android:padding="8dip"
08    >
09<TextView
10    android:layout_width="fill_parent"
11    android:layout_height="wrap_content"
12    android:text=" 正文:A NinePatchDrawable graphic is a stretchable bitmap image, which Android will automatically resize to accommodate the contents of the View in which you have placed it as the background. A NinePatch drawable is a standard PNG image that includes an extra 1-pixel-wide border."
13    android:background="@drawable/content"
14    android:textColor="#000"
15    />
16</LinearLayout>

      如图,

      可以看出,边框非常的清晰。下图是未使用.9.PNG的对比图,而且也不是我们要的效果:

      到这里为止,我们已经基本会制作.9.PNG图片了。为了知识体系的全面性和深入性,我们继续。

4.使用Draw9Patch.jar制作9.PNG图片之定义内容区域。
      是不是觉得文字和边距挨的太近,好,我们使用right和bottom边的线来定义内容区域,来达到增大内边距的目的。

      我们定义了一个很小的内容区域,其他的地方则自动充当边框,从而使内边距显的很大,如下图,

      在这里,我要特别说明,一开始为了增大内边距,很容易惯性思维,在<TextView>中申明 android:padding="10dip" 之类的,我在这里劝告朋友们不要这么做,一是你将无法预知你的显示,二是这比较混淆,因为设置内容区域就是确定padding,所以我在前面部分说他们是 神似。我个人认为通过内容区域设定padding比在布局xml中定义padding更优雅,更简洁!
      关于Draw9Patch工具的其他使用说明,我在次不再累述,因为要说的话太多,为了节省篇幅,请参考官方文档。

5.制作.9.PNG的高级技巧。
       对于初学Draw9Patch的人来说,这可以算是高级技巧,那就是:拉伸区域,可以不是连续的,可以不止一块,而且是和自定义的边框线的长度成正比。
       直接上图说明:
 

6.SDK中如何处理9.PNG图片。
      SDK专门针对9.PNG做了定义和处理,这里我们只是做个简单的流程分析,Bitmap在读取图像流数据的时候,会把判断图片的 NinePatchChunk(9Patch数据块),如果NinePatchChunk不为空,则是 NinePatchDrawable,NinePatchDrawable则又会交给NinePatch处理:

1setNinePatchState(new NinePatchState(
2               new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"),
3               padding, dither), r);

      NinePatch检验成功则调用本地方法,绘制出最终的图片:

1nativeDraw(canvas.mNativeCanvas, location,
2                mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0,
3                canvas.mDensity, mBitmap.mDensity);

7.android系统中大量应用了9.PNG图片。
     通过解压随便一个rom,找到里面的framework_res.apk,里面有大量的9.PNG格式文件,被广泛的应用起来,比如常见的有:
     按钮:  
     解锁:  
     下拉框:  
     标题栏:
     Toast:
      还有搜索,键盘,放大缩小控件,时间加减等等,我就不一一列举。

8.最后送上一些图例,以飨读者,以做后鉴:
 
赏图1 本人之作

 赏图2 下拉按钮
 
赏图3 文章头部背景 
 
赏图4 系统头部背景
转载于
http://www.cnblogs.com/qianxudetianxia/archive/2011/04/17/2017591.html

posted @ 2011-11-24 15:00 建华 阅读(1272) | 评论 (1)编辑 收藏

android 保存图片到数据库

方法一:
Java代码 复制代码
  1. public void saveIcon(Bitmap icon) {   
  2.         if (icon == null) {   
  3.             return;   
  4.         }   
  5.   
  6.         // 最终图标要保存到浏览器的内部数据库中,系统程序均保存为SQLite格式,Browser也不例外,因为图片是二进制的所以使用字节数组存储数据库的   
  7.         // BLOB类型   
  8.         final ByteArrayOutputStream os = new ByteArrayOutputStream();   
  9.         // 将Bitmap压缩成PNG编码,质量为100%存储           
  10.         icon.compress(Bitmap.CompressFormat.PNG, 100, os);    
  11.         // 构造SQLite的Content对象,这里也可以使用raw   
  12.         ContentValues values = new ContentValues();    
  13.         // 写入数据库的Browser.BookmarkColumns.TOUCH_ICON字段   
  14.         values.put(Browser.BookmarkColumns.TOUCH_ICON, os.toByteArray());    
  15.            
  16.         DBUtil.update(....);//调用更新或者插入到数据库的方法   
  17.     } 


方法二:如果数据表入口时一个content:URI

 

Java代码 复制代码
  1. import android.provider.MediaStore.Images.Media;   
  2. import android.content.ContentValues;   
  3. import java.io.OutputStream;   
  4.   
  5. // Save the name and description of an image in a ContentValues map.     
  6. ContentValues values = new ContentValues(3);   
  7. values.put(Media.DISPLAY_NAME, "road_trip_1");   
  8. values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");   
  9. values.put(Media.MIME_TYPE, "image/jpeg");   
  10.   
  11. // Add a new record without the bitmap, but with the values just set.   
  12. // insert() returns the URI of the new record.   
  13. Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);   
  14.   
  15. // Now get a handle to the file for that record, and save the data into it.   
  16. // Here, sourceBitmap is a Bitmap object representing the file to save to the database.   
  17. try {   
  18.     OutputStream outStream = getContentResolver().openOutputStream(uri);   
  19.     sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);   
  20.     outStream.close();   
  21. catch (Exception e) {   
  22.     Log.e(TAG, "exception while writing image", e);   


posted @ 2011-11-17 14:09 建华 阅读(1654) | 评论 (0)编辑 收藏

代码命名

1、成员变量(全局变量)命名首字母以m开头第二个字母大写;(e.g  int mIndex = 0)                                                                                                            
2、常量全部大写                                                                                                                                                                                                           3、静态的变量命名首字母以s开头第二个字母大写;(e.g  static int sIndex = 0)          

posted @ 2011-11-04 10:03 建华 阅读(150) | 评论 (0)编辑 收藏

android混淆apk出错Proguard returned with error code 1. See console解决办法”

原文出自:http://blog.csdn.net/aa4790139/article/details/6754230 

第一种情况:
Proguard returned with error code 1. See console 
Error: C:/Documents (系统找不到指定文件) 
后来发现是因为将整个工程放到了桌面上,而桌面的目录是C:/Documents and Settings/Administrator/桌面,在这里面有空格,而proguard进行发编译的时候是不允许有空格的
如果换了正确路径还不好用的话,直接删除proguard就好了

注意:SDK和程序路径最好不要有空格符

第二种情况:

Proguard returned with error code 1. See console 
异常:

java.lang.ArrayIndexOutOfBoundsException

解决办法:将proguard.cfg中的"-dontpreverify"改成“-dontoptimize

参考文章:http://groups.google.com/group/android-developers/browse_thread/thread/eca3b0f5ce6ad00f

posted @ 2011-11-02 19:56 建华 阅读(3961) | 评论 (0)编辑 收藏

怎么建一个自己的博客

建一个自己的博客,既可以练习php,mysql,还能了解一些网站基础知识。好了现在我就吧自己建立玩站的过程写下来,供大家参考!过程是漫长的,只有自己摸索,才能不断增加经验,才能自己解决问题!

首先我们得要个空间,这个空间是用来放网页文件滴,其实网站就是一个文件夹,里面放了许多网页,在静态网页中,当你用浏览器访问这个文件时它会首先访问index.html,自己动手做过的同学肯定知道,当你把别人网站拷下来的时候体会就明显了,我就这么干的(好像很废话)。

1.我前前后后申请了N多空间,不是空间太小,就是无法登陆,或者DNS解析不上,所以我推荐用www.simplefreeweb.com,完全免费滴,等一晚上就把账号密码发过来了,后台有很多工具mysql,phpmyadmin等等(额- -!我忘了说一点,懂一点数据库和php的同学上手快一些,因为你连数据库,表都不知道,那出现问题你都不知道在哪里)。

2.注册好能登陆的前提下,再到cn.wordpress.org下载他们的wordpress(这是别人做好的玩站模板,直接可用,里面是php文件如果你自己有自信比他做的好,或者练习php,那就自己做吧)解压上传到空间,上传工具很多,我用的是filezilla,用www.simplefreeweb.com提供的ftp站好密码,上传到ftp里面的www目录下,记住上传wordpress文件下的文件,不要把wordpress文件夹一起传上去,要不然你要访问yourname.simplefreeweb.com/wordpress才能访问。

3.上传完成,用simplefreeweb给你的后台登陆网址登陆,在里面建立数据库,再向数据库添加用户的时候一定要勾选全部权限,要不然在后面wordpress安装的时候会连接出现问题!

4.建立好数据库后,登陆你的网站yourname.simplefreeweb.com,会出现wordpress安装导向,按照步骤就可以啦!

5.yourname.simplefreeweb.com这个是人家的二级域名,既不个性,有很难记,所以我们得要个自己的。网上的顶级域名很多但价格不菲,而且申请麻烦,所以我建议到http://www.dot.tk申请,很方便!

绑定域名,就是将这个域名指向你的网站,方法主要是域名解析,免费的解析商也很多,很久都没消息(我的就是,ywww.simplefreeweb.com自带的解析不给力啊)。但是,tk里面有个域名跳转,当你申请完后把你的yourname.simplefreeweb.com填进去,这样当访问你的域名,如我的www.liubos.k时候,直接跳到liubo.simplefreeweb.com。好处是方便,缺点是当别人访问非主页时,还是显示原来的网址!又等一晚上!第二天再访问自己的网站www.xxx.tk吧!说到xxx,我又邪恶了,呵呵!


posted @ 2011-10-31 11:34 建华 阅读(2371) | 评论 (6)编辑 收藏

android 使用contentobserver监听数据库内容变化

     摘要: android 使用contentobserver监听数据库内容变化在android中经常会用到改变数据库内容后再去使用数据库更新的内容,很多人会重新去query一遍,但是这样的问题就是程序会特别占内存,而且有可能会搂关cursor而导致程序内存未释放等等。其实android内部提供了一种ContentObserver的东西来监听数据库内容的变化。ContentObserver的构造函数需要一个参...  阅读全文

posted @ 2011-10-27 22:11 建华 阅读(4934) | 评论 (1)编辑 收藏

Android 彩信的发送

最近有个需求,不去调用系统界面发送彩信功能。做过发送短信功能的同学可能第一反应是这样:
不使用 StartActivity,像发短信那样,调用一个类似于发短信的方法
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneCode, null, text, null, null);
可以实现吗? 答案是否定的,因为android上根本就没有提供发送彩信的接口,如果你想发送彩信,对不起,请调用系统彩信app界面,如下
            Intent sendIntent = new Intent(Intent.ACTION_SEND,  Uri.parse("mms://"));
	    sendIntent.setType("image/jpeg");
	    String url = "file://sdcard//tmpPhoto.jpg";
	    sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(url));
	    startActivity(Intent.createChooser(sendIntent, "MMS:"));

但是这种方法往往不能满足我们的需求,能不能不调用系统界面,自己实现发送彩信呢?经过几天的努力,终于找到了解决办法。
第一步:先构造出你要发送的彩信内容,即构建一个pdu,需要用到以下几个类,这些类都是从android源码的MMS应用中mms.pdu包中copy出来的。你需要将pdu包中的所有类

都拷贝到你的工程中,然后自己酌情调通。
   final SendReq sendRequest = new SendReq();
   final PduBody pduBody = new PduBody();

final PduPart part = new PduPart();//存放附件,每个附件是一个part,如果添加多个附件,就想body中add多个part。

   pduBody.addPart(partPdu);
   sendRequest.setBody(pduBody);
   final PduComposer composer = new PduComposer(ctx, sendRequest);

final byte[] bytesToSend = composer.make(); //将彩信的内容以及主题等信息转化成byte数组,准备通过http协议发送到 ”http://mmsc.monternet.com”;

 第二步:发送彩信到彩信中心。
 构建pdu的代码:
  String subject = "测试彩信";
		    String recipient = "接收彩信的号码";//138xxxxxxx
		    final SendReq sendRequest = new SendReq();
		    final EncodedStringValue[] sub = EncodedStringValue.extract(subject);
		    if (sub != null &amp;&amp; sub.length &gt; 0) {
		    	sendRequest.setSubject(sub[0]);
		    }
		    final EncodedStringValue[] phoneNumbers = EncodedStringValue.extract(recipient);
		    if (phoneNumbers != null &amp;&amp; phoneNumbers.length &gt; 0) {
		    	sendRequest.addTo(phoneNumbers[0]);
		    }
		    final PduBody pduBody = new PduBody();
		    final PduPart part = new PduPart();
		    part.setName("sample".getBytes());
		    part.setContentType("image/png".getBytes());
		    String furl = "file://mnt/sdcard//1.jpg";
 
		    		final PduPart partPdu = new PduPart();
		    		partPdu.setCharset(CharacterSets.UTF_8);//UTF_16
		    		partPdu.setName(part.getName());
		    		partPdu.setContentType(part.getContentType());
		    		partPdu.setDataUri(Uri.parse(furl));
		    		pduBody.addPart(partPdu);   
 
		    sendRequest.setBody(pduBody);
		    final PduComposer composer = new PduComposer(ctx, sendRequest);
		    final byte[] bytesToSend = composer.make();
 
		    Thread t = new Thread(new Runnable() {
 
				@Override
				public void run() {
					try {
						HttpConnectInterface.sendMMS(ctx,  bytesToSend);
//
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
		    t.start();
发送pdu到彩信中心的代码:
        public static String mmscUrl = "http://mmsc.monternet.com";
//	public static String mmscUrl = "http://www.baidu.com/";
	public static String mmsProxy = "10.0.0.172";
	public static String mmsProt = "80";
 
       private static String HDR_VALUE_ACCEPT_LANGUAGE = "";
    // Definition for necessary HTTP headers.
       private static final String HDR_KEY_ACCEPT = "Accept";
       private static final String HDR_KEY_ACCEPT_LANGUAGE = "Accept-Language";
 
    private static final String HDR_VALUE_ACCEPT =
        "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
public static byte[] sendMMS(Context context, byte[] pdu)throws IOException{
		HDR_VALUE_ACCEPT_LANGUAGE = getHttpAcceptLanguage();
 
		if (mmscUrl == null) {
            throw new IllegalArgumentException("URL must not be null.");
        }
 
        HttpClient client = null;
        try {
	        // Make sure to use a proxy which supports CONNECT.
	        client = HttpConnector.buileClient(context);
	        HttpPost post = new HttpPost(mmscUrl);
	        //mms PUD START
	        ByteArrayEntity entity = new ByteArrayEntity(pdu);
			entity.setContentType("application/vnd.wap.mms-message");
	        post.setEntity(entity);
	        post.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);
	        post.addHeader(HDR_KEY_ACCEPT_LANGUAGE, HDR_VALUE_ACCEPT_LANGUAGE);
	        //mms PUD END
	        HttpParams params = client.getParams();
	        HttpProtocolParams.setContentCharset(params, "UTF-8");
	        HttpResponse response = client.execute(post);
 
			LogUtility.showLog(tag, "111");
	        StatusLine status = response.getStatusLine();
	        LogUtility.showLog(tag, "status "+status.getStatusCode());
	        if (status.getStatusCode() != 200) { // HTTP 200 is not success.
            	LogUtility.showLog(tag, "!200");
                throw new IOException("HTTP error: " + status.getReasonPhrase());
            }
	        HttpEntity resentity = response.getEntity();
            byte[] body = null;
            if (resentity != null) {
                try {
                    if (resentity.getContentLength() &gt; 0) {
                        body = new byte[(int) resentity.getContentLength()];
                        DataInputStream dis = new DataInputStream(resentity.getContent());
                        try {
                            dis.readFully(body);
                        } finally {
                            try {
                                dis.close();
                            } catch (IOException e) {
                                Log.e(tag, "Error closing input stream: " + e.getMessage());
                            }
                        }
                    }
                } finally {
                    if (entity != null) {
                        entity.consumeContent();
                    }
                }
            }
            LogUtility.showLog(tag, "result:"+new String(body));
            return body;
		}  catch (IllegalStateException e) {
			LogUtility.showLog(tag, "",e);
//            handleHttpConnectionException(e, mmscUrl);
        } catch (IllegalArgumentException e) {
        	LogUtility.showLog(tag, "",e);
//            handleHttpConnectionException(e, mmscUrl);
        } catch (SocketException e) {
        	LogUtility.showLog(tag, "",e);
//            handleHttpConnectionException(e, mmscUrl);
        } catch (Exception e) {
        	LogUtility.showLog(tag, "",e);
        	//handleHttpConnectionException(e, mmscUrl);
        } finally {
            if (client != null) {
//                client.;
            }
        }
		return new byte[0];
	}

至此,彩信的发送算是完成了。
总结:android的彩信相关操作都是没有api的,包括彩信的读取、发送、存储。这些过程都是需要手动去完成的。想要弄懂这些过程,需要仔细阅读android源码中的mms这个app。还有就是去研究mmssms.db数据库,因为彩信的读取和存储其实都是对mmssms.db这个数据库的操作过程。而且因为这个是共享的数据库,所以只能用ContentProvider这个组件去操作db。

总之,想要研究彩信这块(包括普通短信),你就必须的研究mmssms.db的操作方法,多多了解每个表对应的哪个uri,每个uri能提供什么样的操作,那些字段代表短信的那些属性等。
最后推荐个好用的sqlite查看工具:SQLite Database Browser。


posted @ 2011-08-16 14:50 建华 阅读(4515) | 评论 (2)编辑 收藏

Activity的生命周期

注意到在Activity的API中有大量的onXXXX形式的函数定义,除了我们前面用到的onCreate以外,还有onStart,onStop以及onPause等等。从字面上看,它们是一些事件回调,那么次序又是如何的呢?其实这种事情,自己做个实验最明白不过了。在做这个实验之前,我们先得找到在Android中的Log是如何输出的。

显然,我们要用的是android.util.log类,这个类相当的简单易用,因为它提供的全是一些静态方法:
Log.v(String tag, String msg);        //VERBOSE
Log.d(String tag, String msg);       //DEBUG    
Log.i(String tag, String msg);        //INFO
Log.w(String tag, String msg);     //WARN
Log.e(String tag, String msg);      //ERROR
前面的tag是由我们定义的一个标识,一般可以用“类名_方法名“来定义。
输出的LOG信息,如果用Eclipse+ADT开发,在LogCat中就可以看到,否则用adb logcat也行,不过我是从来都依赖于IDE环境的。

好了,现在我们修改前面的HelloThree代码:
   public void onStart()
    
{
        
super.onStart();
        Log.v(TAG,
"onStart");
    }

    
public void onStop()
    
{
        
super.onStop();
        Log.v(TAG,
"onStop");
    }

    
public void onResume()
    
{
        
super.onResume();
        Log.v(TAG,
"onResume");
    }

    
public void onRestart()
    
{
        
super.onRestart();
        Log.v(TAG,
"onReStart");
    }

    
public void onPause()
    
{
        
super.onPause();
        Log.v(TAG,
"onPause");
    }

    
public void onDestroy()
    
{
        
super.onDestroy();
        Log.v(TAG,
"onDestroy");
    }

    
public void onFreeze(Bundle outState)
    
{
        
super.onFreeze(outState);
        Log.v(TAG,
"onFreeze");
    }
在HelloThreeB中也同样增加这样的代码,编译,运行一下,从logcat中分析输出的日志。
在启动第一个界面Activity One时,它的次序是:
onCreate (ONE) - onStart (ONE) - onResume(ONE) 
虽然是第一次启动,也要走一遍这个resume事件。然后,我们点goto跳到第二个Activity Two中(前一个没有关闭),这时走的次序是:
onFreeze(ONE) - onPause(ONE) - onCreate(TWO) - onStart(TWO) - onResume(TWO) - onStop(ONE)
说明,第二个Activity Two在启动前,One会经历一个:冻结、暂停的过程,在启动Two后,One才会被停止?
然后,我们再点back回到第一个界面,这时走的次序是:
onPause(TWO) - onActivityResult(ONE) - onStart(ONE) - onRestart(ONE) - onResume(ONE) - onStop(TWO) - onDestroy(TWO)
说明,返回时,Two没有经历冻结就直接暂停了,在One接收参数,重启后,Two就停止并被销毁了。
最后,我们点一下Exit退出应用,它的次序是:
onPause(ONE) - onStop(ONE) - onDestroy(ONE)
说明如果我们用了finish的话,不会有freeze,但是仍会经历pause - stop才被销毁。

这里有点疑问的是:为什么回来时先是Start才是Restart?可是文档中的图上画的却是先restart再start的啊?不过,后面的表格中的描述好象是正确的,start后面总是跟着resume(如果是第一次)或者restart(如果原来被stop掉了,这种情况会在start与resume中插一个restart)。

下面不跑例子了,看看文档吧。

1.Android用Activity Stack来管理多个Activity,所以呢,同一时刻只会有最顶上的那个Activity是处于active或者running状态。其它的Activity都被压在下面了。

2.如果非活动的Activity仍是可见的(即如果上面压着的是一个非全屏的Activity或透明的Activity),它是处于paused状态的。在系统内存不足的情况下,paused状态的Activity是有可被系统杀掉的。只是不明白,如果它被干掉了,界面上的显示又会变成什么模样?看来下回有必要研究一下这种情况了。

3.几个事件的配对可以比较清楚地理解它们的关系。Create与Destroy配成一对,叫entrie lifetime,在创建时分配资源,则在销毁时释放资源;往上一点还有Start与Stop一对,叫visible lifetime,表达的是可见与非可见这么一个过程;最顶上的就是Resume和Pause这一对了,叫foreground lifetime,表达的了是否处于激活状态的过程。

4.因此,我们实现的Activity派生类,要重载两个重要的方法:onCreate()进行初始化操作,onPause()保存当前操作的结果。

除了Activity Lifecycle以外,Android还有一个Process Lifecycle的说明:

在内存不足的时候,Android是会主动清理门户的,那它又是如何判断哪个process是可以清掉的呢?文档中也提到了它的重要性排序:

1.最容易被清掉的是empty process,空进程是指那些没有Activity与之绑定,也没有任何应用程序组件(如Services或者IntentReceiver)与之绑定的进程,也就是说在这个process中没有任何activity或者service之类的东西,它们仅仅是作为一个cache,在启动新的Activity时可以提高速度。它们是会被优先清掉的。因此建议,我们的后台操作,最好是作成Service的形式,也就是说应该在Activity中启动一个Service去执行这些操作。

2.接下来就是background activity了,也就是被stop掉了那些activity所处的process,那些不可见的Activity被清掉的确是安全的,系统维持着一个LRU列表,多个处于background的activity都在这里面,系统可以根据LRU列表判断哪些activity是可以被清掉的,以及其中哪一个应该是最先被清掉。不过,文档中提到在这个已被清掉的Activity又被重新创建的时候,它的onCreate会被调用,参数就是onFreeze时的那个Bundle。不过这里有一点不明白的是,难道这个Activity被killed时,Android会帮它保留着这个Bundle吗?

3.然后就轮到service process了,这是一个与Service绑定的进程,由startService方法启动。虽然它们不为用户所见,但一般是在处理一些长时间的操作(例如MP3的播放),系统会保护它,除非真的没有内存可用了。

4.接着又轮到那些visible activity了,或者说visible process。前面也谈到这个情况,被Paused的Activity也是有可能会被系统清掉,不过相对来说,它已经是处于一个比较安全的位置了。

5.最安全应该就是那个foreground activity了,不到迫不得已它是不会被清掉的。这种process不仅包括resume之后的activity,也包括那些onReceiveIntent之后的IntentReceiver实例。

在Android Application的生命周期的讨论中,文档也提到了一些需要注意的事项:因为Android应用程序的生存期并不是由应用本身直接控制的,而是由Android系统平台进行管理的,所以,对于我们开发者而言,需要了解不同的组件Activity、Service和IntentReceiver的生命,切记的是:如果组件的选择不当,很有可能系统会杀掉一个正在进行重要工作的进程。

posted @ 2011-08-03 15:41 建华 阅读(216) | 评论 (0)编辑 收藏

Eclipse+ADT进行Android应用程序的代码混淆和签名

启动代码混淆功能

在较新版本的Android tools和ADT,项目工程里面是带有proguard.cfg的代码混淆配置文件,但默认是没有启动这个配置的,需要手动地在default.properties里面添加指定这个配置文件:
# Project target.
target=android-3
proguard.config=proguard.cfg
然后按F5刷新当前项目工程,这时候Eclipse检测了文件的变动而重新编译!

生成签名发布apk

1.Eclipse工程中右键工程,弹出选项中选择 android工具-生成签名应用包:
右键选择
2.选择需要打包的android项目工程(注:这里会自动选择当前的Project的):
3.如果已有私钥文件,选择私钥文件 输入密码,如果没有私钥文件见 第6和7步创建私钥文件:
4.输入私钥别名和密码:
5.选择APK存储的位置,并完成设置 开始生成:
6.没有私钥文件的情况,创建私钥文件(注:这里私钥文件的Location位置最好自己选择一个新位置,便于牢记,而且最好把这个私钥文件备份到其他地方去以免丢失,因为应用程序的更新需要同一私钥文件):
7.输入私钥文件所需信息,并创建(注:这里的密码是用于Key的别名的,和上面的KeyStore文件的不同,这点可以看步骤3和4。另外下面的名字,开发者资料等是不需要全部填写的,dialog会有提示的):
这时候生成的apk,我发现是比debug版本的要小!如果你发现没有变小的话,请确认项目工程是重新编译的!但代码混淆的效果一般般,基本上还是可以看到原来的语句!

posted @ 2011-06-23 19:42 建华 阅读(1016) | 评论 (0)编辑 收藏

Android的Task和Activity相关

     摘要: android:allowTaskReparenting    用来标记Activity能否从启动的Task移动到有着affinity的Task(当这个Task进入到前台时)——“true”,表示能移动,“false”,表示它必须呆在启动时呆在的那个Task里。   ...  阅读全文

posted @ 2011-06-23 17:42 建华 阅读(12456) | 评论 (0)编辑 收藏

Android APK 签名

Apk签名首先要有一个keystore的签名用的文件.
keystore是由jdk自带的工具keytool生成的.具体生成方式参考一下:
开始->运行->cmd->cd 到你安装的jdk的目录这里我是 C:\Program Files\Java\jdk1.6.0_10\bin
然后输入:keytool -genkey -alias asaiAndroid.keystore -keyalg RSA -validity 20000 -keystore asaiAndroid.keystore
-alias 后跟的是别名这里是 asaiAndroid.keystore
-keyalg 是加密方式这里是 RSA
-validity 是有效期 这里是 20000
-keystore 就是要生成的keystore的名称 这里是 asaiAndroid.keystore
然后按回车
按回车后首先会提示你输入密码:这个在签名时要用的要记住了哦。
然后会再确认你的密码。
之后会依次叫你输入 姓名,组织单位,组织名称,城市区域,省份名称,国家代码等。
参考:
 
运行完可以在 C:\Program Files\Java\jdk1.6.0_10\bin 里找到刚才生产的keyStore文件

好现在开始给Apk签名了:
在 C:\Program Files\Java\jdk1.6.0_10\bin 还提供一个工具 jarsigner.exe
好现在可以在刚才的命令行后继续运行以下命令给APK签名:
jarsigner -verbose -keystore asaiAndroid.keystore -signedjar LotteryOnline_signed.apk LotteryOnline.apk asaiAndroid.keystore
-keystore:keystore 的名称
LotteryOnline_signed.apk  是签完名后的APK
LotteryOnline.apk 是签名前的apk
然后按回车:会要求输入刚才设置的密码,输入后按回车就开始签名了。
参考:
 
运行成功后在 C:\Program Files\Java\jdk1.6.0_10\bin 目录下会多出一个被签名的apk文件,
参考:
 

posted @ 2011-06-23 17:40 建华 阅读(826) | 评论 (0)编辑 收藏

Android 内置图片

     摘要: alert_dark_framealert_light_framearrow_down_floatarrow_up_floatbottom_barbtn_defaultbtn_default_smallbtn_dialogbtn_dropdownbtn_minusbtn_plusbtn_radiobtn_starbtn_star_big_offbtn_star_big_onbutton_onoff...  阅读全文

posted @ 2011-06-13 11:24 建华 阅读(1418) | 评论 (0)编辑 收藏

http://www.infoq.com/cn

posted @ 2011-05-27 23:00 建华 阅读(214) | 评论 (0)编辑 收藏

JAVA反射机制的学习

     摘要: JAVA语言中的反射机制:    在Java 运行时 环境中,对于任意一个类,能否知道这个类有哪些属性和方法?    对于任意一个对象,能否调用他的方法?这些答案是肯定的,这种动态获取类的信息,以及动态调用类的方法的功能来源于JAVA的反射。从而使java具有动态语言的特性。  JAVA反射机制主要提供了以下功能:&n...  阅读全文

posted @ 2011-05-23 13:37 建华 阅读(578) | 评论 (0)编辑 收藏

语言的分类

1、汇编语言
2、解释性语言-------运行慢,没解释一行,运行一行
3、编译性语言-------直接编译成目标程序,再运行

posted @ 2011-05-22 15:23 建华 阅读(134) | 评论 (0)编辑 收藏

Android

posted @ 2011-05-18 15:21 建华 阅读(123) | 评论 (0)编辑 收藏