转自(http://www.sunote.info/category/android/ )
前几天参加了支付和移动互联网峰会,写了一些想法 。这几天还在思考一个问题:如何才能信任一个手机应用呢?
对于基于网页的应用,这个问题比较好解决。我们在浏览器里打开某网站地址,可以通过检查URL是否正确,对于https的地址,还可以查看安全证书。如果这些都没有问题,就可以确认访问到的网站是我们希望的,而不是李鬼。
对于应用程序,有数字签名来保证一个程序是某开发者发布且没有被修改过。当确认签名没有问题的时候,就可以放心使用了。
看看上面两种情况,为什么能信任一个以前没有接触过的网页或应用呢?我觉得这是信任的传递过程。对于网页应用,首先,我信任使用的浏览器,才确定浏 览器展示给我的URL和安全证书是网页本身的;我信任安全证书签发机构,才信任证书的使用者。这是把对浏览器和证书签发机构的信任传递到网页上。对于应用 程序,我信任操作系统,才确定展示给我的数字签名的确是应用本身的;我信任数字签名签发机构,才信签名的使用者。这是把对操作系统和数字签名签发机构的信 任传递到应用上。
到了手机上,对于网页,没有什么差别,但对于应用来说,情况有所不同。
看看Android的情况。每个应用发布时都需要签名,但这个签名是应用开发者自己创建的签名,没有第三方机构签发。手机上的操作系统还是可以信任的,但操作系统并不能把应用的签名情况展示给用户。
单单对于一个应用本身,我无法确认是不是声称的开发者发布的。当我要找某银行的应用时,Google market上的应用都不一定可靠,因为任何人都可以发布一个声称是银行的应用。Google在发布环境基本没有审察,即使是钓鱼也会在Market上被 用户看到。这条路无法建立信任,我通常会到银行的官方网站,通过其提供的链接来下载应用。这相当与把对网站的信任传递到应用上。
在iPhone上,情况好一些。因为app store有发布前的审察机制,肯定不能100%过滤,但会提高钓鱼者成本。对于银行、交易相关应用,还是小心一些为好,最好也通过官方网站链接访问。
应用内支付IAP (In-App Purchase),问题就更大了。信任链在应用这就已经断掉了,应用的身份无法信任,我怎么能信任这个应用展示给我的支付界面呢?看到Paypal、 Alipay都在做这个工作,但就目前的情况来说,我不看好。对于钓鱼,恐怕都是事后处理,而不能在之前防范。有用户被钓鱼,自然不敢使用;有防范意识的 用户,知道钓鱼程序无法识别的话,可能会拒绝所有程序。这样,这个市场随着一些用户被钓鱼和一些用户增强防范意识,会变得越来越小。
App store也有IAP,但使用的是自己的支付手段,钓鱼者最多得到用户的app store帐号或者让用户用这个帐号购买一些东西,但交易后如果用户发现,可以向app store申诉。这样,钓鱼者即使钓鱼成功也未必能有收益,买卖就不划算了。这个方式并不是建立信任链,而是通过控制支付达到的。
但第三方支付就不这么容易了。Alipay似乎可以在其界面输入银行帐号密码来直接从银行转帐。钓鱼这可以仿造支付界面,骗取银行信息。对 Alipay来说,他的任何代码和服务器都没有参与这个行为,怎么能找他负责呢?银行也没有看到有这笔交易,除非用户在输入信息后立即发现问题,否则银行 也无法发现。
即使Alipay使用了淘宝的收货后确认付款的机制,也是有问题的,原因仍然是在于对应用没有信任。不像网页上,操作都是在淘宝/支付宝的域名下完 成,手机上的交易是在一个没有信任关系的应用里进行,我并不知道我的帐号信息发给alipay服务器的同时是否还被这个应用留了一份。不过,如果使用收货 后确认付款的机制加上OTP (one time password),应该就会安全了,因为即使应用留了一份帐号信息,也是无效的。
总之,由于手机系统的原因,信任不能传递到应用,仅仅在手机应用本身的支付是不安全的,要做的话,需要在手机之外打些补丁。
<!-- Google Translate Element -->
<!-- end of menu -->
如果在一个项目中需要用到相同的布局设计,可以通过<include /> 标签来重用layout代码,该标签在android开发文档中没有相关的介绍。在android主屏程序中 用到了这个标签:
<com.android.launcher.Workspace android:id="@+id/workspace" android:layout_width="fill_parent" android:layout_height="fill_parent" launcher:defaultScreen="1"> <include android:id="@+id/cell1" layout="@layout/workspace_screen" /> <include android:id="@+id/cell2" layout="@layout/workspace_screen" /> <include android:id="@+id/cell3" layout="@layout/workspace_screen" /> </com.android.launcher.Workspace>这样可以多次引用一个布局片段而不用重复的复制、粘贴。通过include标签也可以覆写一些属性的值,例如上面的示例就覆写了引用的layout中的id值。下面是另外一个示例:
<include android:layout_width="fill_parent" layout="@layout/image_holder" /> <include android:layout_width="256dip" layout="@layout/image_holder" /> 使用<merge /> 标签来减少视图层级结构 在Android layout文件中需要一个顶级容器来容纳其他的组件,而不能直接放置多个组件,例如如下的代码 :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="/blog_article/@drawable/golden_gate/index.html" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Golden Gate" />
</FrameLayout>
上面的代码显示一个图片,然后在图片上方显示一个title, 结果如下图: android activity的默认布局为FrameLayout,这样上面的布局代码就有2层FrameLayout,通过HierarchyViewer 工具看到的结构如下: 如果能在layout文件中把FrameLayout声明去掉就可以进一步优化布局代码了。 但是由于布局代码需要外层容器容纳,如果 直接删除FrameLayout则该文件就不是合法的布局文件。这种情况下就可以使用<merge /> 标签了。 修改为如下代码就可以消除多余的FrameLayout了:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="/blog_article/@drawable/golden_gate/index.html" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Golden Gate" />
</merge>
通过HierarchyViewer工具看到的结构如下: merge也有一些使用限制: 只能用于xml layout文件的根元素;在代码中inflate一个以merge为根元素的 布局文件时候,你需要指定一个ViewGroup 作为其容器,并且要设置attachToRoot 为true,详细信息参考 inflate()函数doc。 上面示例项目代码: http://progx.org/users/Gfx/android/MergeLayout.zip
废话少说,直接上代码,看不懂的地方自己去网上查,以下代码都在我本地运行正常:
新建一个FileRW的android的项目:
新建一个com.sharandroi.file包
新建一个FileService.java的具体操作类:
package com.sharandroi.file;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
public class FileService {
private Context context;
public FileService(Context context) {
this.context = context;
}
public void save(String content) throws Exception {
FileOutputStream outStream = context.openFileOutput("sharpandroid.txt",
Context.MODE_WORLD_WRITEABLE);
outStream.write(content.getBytes());// 写数据
outStream.close();// 关闭输出流
}
public String read() throws Throwable{
FileInputStream instream = context.openFileInput("sharpandroid.txt");
byte[] data = readFile(instream);
return new String(data);
}
public byte[] readFile(InputStream inStream) throws Throwable{
int len = 0;
byte[] buffer = new byte[1024];
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
while((len = inStream.read(buffer))!=-1){
outStream.write(buffer,0,len);
}
outStream.close();
return outStream.toByteArray();
}
}
新建一个单元测试类:FileServiceTest.java
package com.sharandroi.file;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.test.AndroidTestCase;
import android.util.Log;
public class FileServiceTest extends AndroidTestCase {
public void testSave() throws Throwable{
FileService fileService = new FileService(getContext());
fileService.save("qqqqqqqqqqqqqqqqqqqqqqqqqq");
Log.i("aaa", fileService.read());
}
}
AndroidManifest.xml配置添加权限:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sharandroi.file" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
<activity android:name=".MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.sharandroi.file" android:label="Tests for My App" />
</manifest>
上面的代码把需要用的包都导进来在我本地能跑起来
File Exploreer的下面看到data/data/com.sharandroi.file/下面有个sharpandroid.txt文件倒出来就可以看到内容了