androidfloatint
㈠ 安卓如何自己定义一个数据类型然后我可以private 这个数据类型!
java数据类型【Android基础第6篇】
微价值网 星河
前面我们讲了Android开发环境搭建,包括模拟器与真机调试方法,本期开始,我们逐步介绍Android应用开发中常用的一些Java编程基础知识,包括数据类型、分支与循环控制、类与对象的设计、函数与接口等。
首先来看Java数据类型,总体上可以分为基本类型(数值型、字符型、布尔型)和引用类型两种。
一、基本类型 1、数值型
包括整数类型(又包含byte、短整型short、整数int和长整型long)、 浮点类型(包含float以及双精度double)。
其中,整数类型默认为int,浮点型默认为double,如果要表示long型数据或float型数据,要在相应的数值后面加上l、L或f、F,否则会出现编译问题。
2、字符型
一个char类型型的变量,对应一个字符,例如’a’,这种类型的变量在Java中由两个字节(byte)组成。这种类型不区分正负,为无符号类型。
3、布尔类型
也即boolean类型,只有两个取值:false和true。注意这种类型的变量,不能转换成任何其他数据类型。
Java中所有的基本数据类型都有固定的取值范围、占用固定大小的内存空间,而不受具体操作系统的影响,这样可以保证Java程序的跨平台、可移植性。
对于基本数据类型,一个需要注意的问题是类型转换,包括自动转换与强制转换,在后面会结合实例详细分析。
二、引用类型
引用类型可以分为三种:类(Classs)、数组类型(Array)、接口类型(Interface)。 引用在某种语义上等同于C\C++语言中的指针(Pointer),指向一个相应类型(类、数组、接口)的实例对象。
当一个引用类型不指向任何对象时,其值为一个特殊值:null。引用的默认值就是null。
例如,我们定义了一个Student学生类型: class Student {
private String mName; //姓名 private String mSex; //性别 private int mAge; //年龄 private String mSchool; //学校 … }
其中mAge成员变量为int整形,是基本数据类型,这样在实例化、创建一个Student对象的时候,mAge变量自动赋值为0。但是对于其中的引用类型对象mName来说,此时只会分配一个引用,指向null, 当你想使用mName对象的时候,必须实例化(也即创建一个String类型的mName对象)之后才能使用,否则会引起异常报错Java数据类型【Android基础第6篇】
微价值网 星河
前面我们讲了Android开发环境搭建,包括模拟器与真机调试方法,本期开始,我们逐步介绍Android应用开发中常用的一些Java编程基础知识,包括数据类型、分支与循环控制、类与对象的设计、函数与接口等。
首先来看Java数据类型,总体上可以分为基本类型(数值型、字符型、布尔型)和引用类型两种。
一、基本类型 1、数值型
包括整数类型(又包含byte、短整型short、整数int和长整型long)、 浮点类型(包含float以及双精度double)。
其中,整数类型默认为int,浮点型默认为double,如果要表示long型数据或float型数据,要在相应的数值后面加上l、L或f、F,否则会出现编译问题。
2、字符型
一个char类型型的变量,对应一个字符,例如’a’,这种类型的变量在Java中由两个字节(byte)组成。这种类型不区分正负,为无符号类型。
3、布尔类型
也即boolean类型,只有两个取值:false和true。注意这种类型的变量,不能转换成任何其他数据类型。
Java中所有的基本数据类型都有固定的取值范围、占用固定大小的内存空间,而不受具体操作系统的影响,这样可以保证Java程序的跨平台、可移植性。
对于基本数据类型,一个需要注意的问题是类型转换,包括自动转换与强制转换,在后面会结合实例详细分析。
二、引用类型
引用类型可以分为三种:类(Classs)、数组类型(Array)、接口类型(Interface)。 引用在某种语义上等同于C\C++语言中的指针(Pointer),指向一个相应类型(类、数组、接口)的实例对象。
当一个引用类型不指向任何对象时,其值为一个特殊值:null。引用的默认值就是null。
例如,我们定义了一个Student学生类型: class Student {
private String mName; //姓名 private String mSex; //性别 private int mAge; //年龄 private String mSchool; //学校 … }
其中mAge成员变量为int整形,是基本数据类型,这样在实例化、创建一个Student对象的时候,mAge变量自动赋值为0。但是对于其中的引用类型对象mName来说,此时只会分配一个引用,指向null, 当你想使用mName对象的时候,必须实例化(也即创建一个String类型的mName对象)之后才能使用,否则会引起异常报错
㈡ Android Cursor(光标)解析
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
Cursor cursor = db.rawQuery("select *from User",null);
1.光标的行数:int getCount()
2.当前光标的位置:int getPosition()
返回的值从零开始, 当第一次返回行集时游标将位于位置 -1,即第一行之前。在返回最后一行之后,对 next() 的另一个调用将使光标离开最后一个条目,位于 count() 的位置。
3.从当前位置应用的偏移量:boolean move(int offset)
将光标从当前位置向前或向后移动一个相对量。 正偏移向前移动,负偏移向后移动。 如果最终位置在结果集的边界之外,则结果位置将分别固定为 -1 或 count(),具体取决于该值是在集合的前端还是末尾。如果请求的目的地可达,此方法将返回 true,否则返回 false。
4.将光标移动到绝对位置:boolean moveToPosition(int position)
值的有效范围是 -1 <= 位置 <= 计数。如果请求目的地可达,此方法将返回 true,否则返回 false。
5.将光标移动到第一行:boolean moveToFirst()
6.将光标移动到最后一行:boolean moveToLast()
7.将光标移动到下一行:boolean moveToNext()
8.将光标移动到上一行:boolean moveToPrevious()
9.返回光标是否指向第一行:boolean isFirst()
10.返回光标是否指向最后一行:boolean isLast()
11.返回光标是否指向第一行之前的位置:boolean isBeforeFirst()
12.返回光标是否指向最后一行之后的位置:boolean isAfterLast()
13.给定列名的从零开始的列索引,如果列名不存在,则返回 -1:int getColumnIndex(String columnName)
14.给定列名的从零开始的索引,如果该列不存在则抛出非法参数异常:int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException
15.给定的从零开始的列索引处的列名:String getColumnName(int columnIndex)
16.返回一个字符串数组,其中按列在结果中的顺序保存结果集中所有列的名称。:String[] getColumnNames()
17.返回总列数:int getColumnCount()
18.各类型返回值
(1)以字节数组的形式返回请求列的值:byte[] getBlob(int columnIndex)
(2)以字符串形式返回请求列的值:String getString(int columnIndex)
(3)以整数形式返回请求列的值:int getInt(int columnIndex)
(4)以 long 形式返回请求列的值:long getLong(int columnIndex)
(5)以浮点数形式返回请求列的值:float getFloat(int columnIndex)
(6)以双精度形式返回请求列的值:double getDouble(int columnIndex)
(7)返回给定列值的数据类型:int getType(int columnIndex)
(8)列值是否为空:boolean isNull(int columnIndex)
(9)以短形式返回请求列的值:short getShort(int columnIndex)
19.检索请求的列文本并将其存储在提供的缓冲区中:void StringToBuffer(int columnIndex, CharArrayBuffer buffer)
20.关闭游标:void close()
21.游标是否关闭:boolean isClosed()
22.注册一个观察者,当支持此游标的内容发生变化时调用该观察者:void registerContentObserver(ContentObserver observer)
23.销毁注册的观察者:void unregisterContentObserver(ContentObserver observer)
24.注册一个观察者,当数据集的内容发生变化时被调用:void registerDataSetObserver(DataSetObserver observer)
25.销毁注册的观察者:void unregisterDataSetObserver(DataSetObserver observer)
26.注册以查看内容 URI 的更改。这可以是特定数据行的 URI,也可以是内容类型的通用URI:void setNotificationUri(ContentResolver cr, Uri uri)
cr是上下文,uri是需要观看的内容
27.是否所有光标移动都应导致调用 onMove():boolean getWantsAllOnMoveCalls()
只有在此方法返回 true 时,才会跨进程调用 onMove()
28.返回一组额外的值:Bundle getExtras()
29.光标用户与光标通信的带外方式:Bundle respond(Bundle extras)
30.设置 Bundle 返回的getExtras():void setExtras(Bundle extras)
㈢ 如何在android平台上使用js直接调用Java方法
在Cocos2d-js 3.0beta中加入了一个新特性,在Android平台上我们可以通过反射直接在js中调用java的静态方法。它的使用方法很简单:
var o = jsb.reflection.callStaticMethod(className, methodName, methodSignature, parameters...)
在callStaticMethod方法中,我们通过传入Java的类名,方法名,方法签名,参数就可以直接调用Java的静态方法,并且可以获得Java方法的返回值。下面介绍的类名和方法签名可能会有一点奇怪,但是Java的规范就是如此的。
类名
参数中的类名必须是包含Java包路径的完整类名,例如我们在org.cocos2dx.javascript这个包下面写了一个Test类:
package org.cocos2dx.javascript;
public class Test {
public static void hello(String msg){
System.out.println(msg);
}
public static int sum(int a, int b){
return a + b;
}
public static int sum(int a){
return a + 2;
}
}
那么这个Test类的完整类名应该是org/cocos2dx/javascript/Test,注意这里必须是斜线/,而不是在Java代码中我们习惯的点。
方法名
方法名很简单,就是方法本来的名字,例如sum方法的名字就是sum。
方法签名
方法签名稍微有一点复杂,最简单的方法签名是()V,它表示一个没有参数没有返回值的方法。其他一些例子:
(I)V表示参数为一个int,没有返回值的方法;
(I)I表示参数为一个int,返回值为int的方法;
(IF)Z表示参数为一个int和一个float,返回值为boolean的方法;
现在有一些理解了吧,括号内的符号表示参数类型,括号后面的符号表示返回值类型。因为Java是允许函数重载的,可以有多个方法名相同但是参数返回值不同的方法,方法签名正是用来帮助区分这些相同名字的方法的。
目前Cocos2d-js中支持的Java类型签名有下面4种:
参数可以是0个或任意多个,直接使用js中的number,bool和string就可以。
使用示例
我们将会调用上面的Test类中的静态方法:
//调用hello方法
jsb.reflection.callStaticMethod("org/cocos2dx/javascript/Test", "hello", "(Ljava/lang/String)V", "this is a message from js");
//调用第一个sum方法
var result = jsb.reflection.callStaticMethod("org/cocos2dx/javascript/Test", "sum", "(II)I", 3, 7);
cc.log(result); //10
//调用第二个sum方法
var result = jsb.reflection.callStaticMethod("org/cocos2dx/javascript/Test", "sum", "(I)I", 3);
cc.log(result); //5
在你的控制台会有正确的输出的,这很简单吧。
注意
另外有一点需要注意的就是,在android应用中,cocos的渲染和js的逻辑是在gl线程中进行的,而android本身的UI更新是在app的ui线程进行的,所以如果我们在js中调用的Java方法有任何刷新UI的操作,都需要在ui线程进行。
例如,在下面的例子中我们会调用一个Java方法,它弹出一个android的Alert对话框。
//给我们熟悉的AppActivity类稍微加点东西
public class AppActivity extends Cocos2dxActivity {
private static AppActivity app = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = this;
}
public static void showAlertDialog(final String title,final String message) {
//这里一定要使用runOnUiThread
app.runOnUiThread(new Runnable() {
@Override
public void run() {
AlertDialog alertDialog = new AlertDialog.Builder(app).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setIcon(R.drawable.icon);
alertDialog.show();
}
});
}
}
然后我们在js中调用
jsb.reflection.callStaticMethod("org/cocos2dx/javascript/AppActivity", "showAlertDialog", "(Ljava/lang/String;Ljava/lang/String;)V", "title", "hahahahha");
这样调用你就可以看到一个android原生的Alert对话框了。
再加点料
现在我们可以从js调用Java了,那么能不能反过来?当然可以! 在你的项目中包含Cocos2dxJavascriptJavaBridge,这个类有一个evalString方法可以执行js代码,它位于frameworks\js-bindings\bindings\manual\platform\android\java\src\org\cocos2dx\lib文件夹下。我们将会给刚才的Alert对话框增加一个按钮,并在它的响应中执行js。和上面的情况相反,这次执行js代码必须在gl线程中进行。
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//一定要在GL线程中执行
app.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxJavascriptJavaBridge.evalString("cc.log(\"Javascript Java bridge!\")");
}
});
}
});
这样在点击OK按钮后,你应该可以在控制台看到正确的输出。evalString可以执行任何js代码,并且它可以访问到你在js代码中的对象。
㈣ android中怎么把float转换成Int
1、采用强转的方式
floata=1.1L;//定义一个float型变量,变量名为a,值为1.1,L表示是浮点型
intb=(int)a;//用(int)强制转换为整型b
2、采用Math.round方式
floata=1.1L;//定义一个float型变量,变量名为a,值为1.1,L表示是浮点型
intb=Math.round(a);//采用round方式转换为整型
㈤ android 数据存储的几种方式
总体的来讲,数据存储方式有三种:一个是文件,一个是数据库,另一个则是网络。其中文件和数据库可能用的稍多一些,文件用起来较为方便,程序可以自己定义格式;数据库用起稍烦锁一些,但它有它的优点,比如在海量数据时性能优越,有查询功能,可以加密,可以加锁,可以跨应用,跨平台等等;网络,则用于比较重要的事情,比如科研,勘探,航空等实时采集到的数据需要马上通过网络传输到数据处理中心进行存储并进行处理。 对于Android平台来讲,它的存储方式也不外乎这几种,按方式总体来分,也是文件,数据库和网络。但从开发者的角度来讲它可以分为以下五种方式: 1.SharedPreferences共享偏好 2.Internal Storage内部存储空间 3.External Storage外部存储空间 4.SQLite Database数据库 5.Internet网络 这几种方式各自有各自的优点和缺点,要根据不同的实际情况来选择,而无法给出统一的标准。下面就各种方式谈谈它们的优缺点,以及最合适的使用情况: 1.Shared Preferences共享偏好 SharedPreferences是用来存储一些Key/Value类似的成对的基本数据类型,注意,它只能存储基本数据类型,也即int, long, boolean, String, float。事实上它完全相当于一个HashMap,唯一不同的就是HashMap中的Value可以是任何对象,而SharedPreferences中的值只能存储基本数据类型(primitive types)。 对于它的使用方法,可以参考Android Developer Guide,这里不重复。 如此来看,最适合SharedPreferences的地方就是保存配置信息,因为很多配置信息都是Key/Value。事实上,在Android当中SharedPreferences使用最多的地方也是用来保存配置(Settings)信息,系统中的Settings中这样,各个应用中的Settings也是这样。并且,Android中为了方便的使用SharedPreferences保存配置信息,它来专门有PreferenceActivity用来封装。也就是说如果你想在应用程序中创建配置(Settings),你可以直接使用PreferenceActivity和一些相关的专门为Preference封装的组件,而不用再直接去创建,读取和保存SharedPreference,Framework中的这些组件会为你做这些事。 再谈谈一些使用SharedPreference时的技巧,它只能保存基本数据类型,但假如我想保存一个数组,怎么办?可以把数据进行处理,把它转化成一个String,取出的时候再还原就好了;再如,如想保存一个对象,怎么办,同样,可以把对象序列化成为字符序列,或转成String(Object.toString()),或是把它的HashCode(Object.hashCode())当成Value保存进去。 总之,SharedPreferences使用起来十分的方便,可以灵活应用,因为它简单方便,所以能用它就尽量不要用文件或是数据库。 1.Internal Storage内部存储空间 所谓的内部存储与外部存储,是指是否是手机内置。手机内置的存储空间,称为内部存储,它是手机一旦出厂就无法改变,它也是手机的硬件指标之一,通常来讲手机内置存储空间越大意味着手机价格会越贵(很多地方把它称为手机内存,但我们做软件的知道,这并不准确,内存是指手机运行时存储程序,数据和指令的地方;这里应该是手机内部存储的简称为内存,而并非严格意义上的内存)。 内部存储空间十分有限,因而显得可贵,所以我们要尽可能避免使用;另外,它也是系统本身和系统应用程序主要的数据存储所在地,一旦内部存储空间耗尽,手机也就无法使用了。所以对于内部存储空间,我们要尽量避免使用。上面所谈到的Shared Preferences和下面要谈到的SQLite数据库也都是存储在内部存储空间上的。 Android本身来讲是一个Linux操作系统,所以它的内部存储空间,对于应用程序和用户来讲就是“/data/data"目录。它与其他的(外部的存储)相比有着比较稳定,存储方便,操作简单,更加安全(因为可以控制访问权限)等优点。而它唯一的缺点就是它比较有限,比较可贵。 虽然,可以非常容易的知道程序本身的数据所在路径,所有的应用程序的数据路径都是“/data/data/app-package-name/”,所有的程序用到的数据,比如libs库,SharedPreferences都是存放在这个路径下面。但我们在使用的时候最好不要,或是千万不要直接引用这个路径。 使用内部存储主要有二个方式,一个是文件操作,一个是文件夹操作。无论哪种方式,Context中都提供了相应的函数来支持,使用Context不但操作简单方便,最重要的是Context会帮助我们管理这些文件,也可以方便帮助我们控制文件的访问权限。先来系统的说下Context中关于文件和文件夹操作的函数有哪些。 a. 创建一个文件,并打开成一个文件输出流,需要提供一个String,作为文件名 1.FileOutputStream output = Context.openOutputFile(filename, Context.MODE_PRIVATE); 2.output.write(data);// use output to write whatever you like 3.output.close(); 1.FileOutputStream output = Context.openOutputFile(filename, Context.MODE_PRIVATE); output.write(data);// use output to write whatever you like output.close(); b. 同样,想打开一个文件作为输入的话,也是只需要提供文件名 1.FileInputStream input = Context.openInputFile(filename); 2.input.read(); 3.input.close(); 1.FileInputStream input = Context.openInputFile(filename); input.read(); input.close(); c. 列出所有的已创建的文件 1.String[] files = Context.fileList(); 2.for (String file : files) { 3. Log.e(TAG, "file is " + file); 4.} 1.String[] files = Context.fileList(); for (String file : files) { Log.e(TAG, "file is " + file); } d. 删除文件,能创建就要能够删除,当然也会提供了删除文件的接口,它也非常简单,只需要提供文件名 1.if (Context.deleteFile(filename)) { 2. Log.e(TAG, "delete file " + filename + " sucessfully“); 3.} else { 4. Log.e(TAG, "failed to delete file " + filename); 5.} 1.if (Context.deleteFile(filename)) { Log.e(TAG, "delete file " + filename + " sucessfully“); } else { Log.e(TAG, "failed to delete file " + filename); } e. 获取文件已创建文件的路径,它返回一个文件对象用于操作路径 1.File fileDir = Context.getFileDir(); 2.Log.e(TAG, "fileDir " + fileDir.getAbsolutePath(); 1.File fileDir = Context.getFileDir(); Log.e(TAG, "fileDir " + fileDir.getAbsolutePath(); f. 创建一个目录,需要传入目录名称,它返回 一个文件对象用到操作路径 1.File workDir = Context.getDir(dirName, Context.MODE_PRIVATE); 2.Log.e(TAG, "workdir " + workDir.getAbsolutePath(); 1.File workDir = Context.getDir(dirName, Context.MODE_PRIVATE); Log.e(TAG, "workdir " + workDir.getAbsolutePath(); g. 以File对象方式查看所创建文件,需要传入文件名,会返回文件对象 1.File store = Context.openFileStreamPath(filename); 2.Log.e(TAG, "store " + store.length()); 1.File store = Context.openFileStreamPath(filename); Log.e(TAG, "store " + store.length()); h. 获取Cache路径,无需要传入参数,返回文件对象 1.File cachedir = Context.getCacheDir(); 2.Log.e(TAG, "cachedir " + cacheDir.getAbsolutePath()); 1.File cachedir = Context.getCacheDir(); Log.e(TAG, "cachedir " + cacheDir.getAbsolutePath()); 总结一下文件相关操作,可以得出以下三个特点: 1. 文件操作只需要向函数提供文件名,所以程序自己只需要维护文件名即可; 2. 不用自己去创建文件对象和输入、输出流,提供文件名就可以返回File对象或输入输出流 3. 对于路径操作返回的都是文件对象。 如前所述,内部存储空间有限,可贵,安全,稳定,所以应该用来保存比较重要的数据,比如用户信息资料,口令秘码等不需要与其他应用程序共享的数据。也可以用来创建临时文件,但一定要注意及时删除。另外,对于内部存储还有一个非常重要的特点,那就是在应用程序被卸载时,应用程序在内部存储空间的文件数据将全部被删除。系统这样做的原因很简单,就是因为内部存储很有限,它必须保证它的可用性,因为一旦添满,系统将无法再正常工作。 1.External Storage外部存储空间 再来谈谈手机外部存储空间,与内部存储空间相对,外部存储空间是指手机出厂的时候不存在,用户在使用时候可以自由添加的外部存储介质比如TS卡,SD卡等闪存储介质。这些闪存介质由最初的空间小价格贵,到现在的大容量价格便宜,所以几乎每个支持外部存储的手机上面都有大容量(大于等于2G)的闪存卡。 Android也是不例外,它完全支持外部存储介质。其实更确切的说,它是要依赖于外部存储卡的,因为对于Android系统,如果没有外部存储卡,很多的系统应用无法使用,比如多媒体相关的应用程序无法使用。虽然Android很依赖,但是外部存储卡也有它自身的特点,它最大的优点就是存储空间大,基本上你可无限制的使用,也不怎么担心去清除数据。就目前来看,很多程序都在使用外部存储卡,但很少有程序去主动清理数据,所以无论你的SD卡有多大,它的可用空间却越来越少。与内部存储不同的是,当程序卸载时,它在外部存储所创建的文件数据是不会被清除的。所以清理外部存储空间的责任丢给了用户自己,每隔一段时间就去查看下SD卡,发现无用数据立马删除。外部存储的缺点就是不是很稳定,对于Android手机来讲可以说,很不稳定,本身闪存介质就容易出问题,SD卡处于不能正常使用的状态十分多。 先来说说外部存储相关的使用方法和API: a. Check media availability检查介质的可用性 如前所述,外部存储介质的稳定性十分的差,所以在使用之前一定要先检查它的可用性,如果可用再去用 view plain to clipboardprint? 1.final String state = Environment.getExternalStorageState(); 2.if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us } view plain to clipboardprint? 1.final String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us } final String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us } b. Get the directory获取外部存储卡的路径 事实上,外部存储卡的路径是“/mnt/sdcard",所以你直接这样写去访问也能访问的到。鉴于可读性和可移植性的考虑,建议这样写: view plain to clipboardprint? 1.File sdcardDir = Environment.getExternalStorageDirectory(); view plain to clipboardprint? 1.File sdcardDir = Environment.getExternalStorageDirectory(); File sdcardDir = Environment.getExternalStorageDirectory(); c. For API 8 or greater, there are some other useful APIs helping to manager files and directories. 如果你使用API 8(Android 2.2)或者更高,那么SDK中又多了几个操作外部存储文件和路径的接口,文档中也建议开始者更加规范的使用SD卡。比如,创建相应的目录去存储相应的数据,Music,Picture,Video等。应用程序目录也变成了"/Android/data/package-name/data"。具体的使用可以参考文档,这里不重复。当然,就像编程规范一样,这里只是规范,你完全可以不遵守它,但出于可读性和可移植性,还是建议按照文档建议的去做。 下面总结一下使用时应该注意的一些和外部存储的特点: a. 外部存储卡不是随时想用就能够用的,所以一定要记得在使用之前检查它的可用性 b. 存储在外部存储卡上的数据是所有应用程序都可见,用户也可见(使用FileManager),所以安全性不是很好,虽然文档声称可以在外部存储卡上写程序私有数据,但貌似没用,用FileManager仍然可以删除或编辑文件(Market上面的FileManager功能都十分的强大,能让用户看到SD卡中的所有文件,和操作能看到的文件)。 c. Android手机支持把外部存储卡Mount至PC做为U盘,当连接数据线时,这时SD卡变成了U盘连接到了另外的操作系统中。什么意思,就是在Android当中虽然有的文件属性(隐藏,私有等),到了PC上就不一定管用了,用户在PC上可以随意操作文件(这就是第二点中所提及的)。 d. 如果使用外部存储卡保存数据,一定要额外做好异常处理:外部存储卡不可用时把数据存入哪里;可用的时候再怎么同步数据(这是比较头疼的地方,可行的做法就是当SD卡不可用时不准用户写数据,但这用户体验又不是很好,但如你所知,很多应用都这么干);你的数据被破坏了。当然常见的异常也要考虑,比如空间满了,无法写入,磁盘坏道等。 1.SQLite Database数据库 Android对数据库的支持很好,它本身集成了SQLite数据库,每个应用都可以方便的使用它,或者更确切的说,Android完全依赖于SQLite数据库,它所有的系统数据和用到的结构化数据都存储在数据库中。 它具有以下优点: a. 效率出众,这是无可否认的 b. 十分适合存储结构化数据 c. 方便在不同的Activity,甚至不同的应用之间传递数据 先前有一篇文章讲到了不同Activity和不同应用之间传递数据的麻烦,特别是对于大型数据结构,因为Activity虽是Java对象,但去无法像使用其他类对象那样去创建一个实例然后使用它,更无法给Activity加上Setters和Getters(虽然这样做了没有编译错误)。比较好的解决方案就是把结构化数据写入数据库,然后在不同的Activity之间传递它们的Uri。 d. 由专门的ContentProvider来帮忙管理和维护数据库 e. 可以方便的设置访问权限,私有还是都可见 f. 操作方便,使用标准的CRUDE语句,ContentResolver.query(), update(), delete() insert(),详见ContentResolver g. 良好的可移植性和通用性,用标准的SQL语句就能实现CRUDE 对于它的使用方法可以去参考文档,这里也说不清楚。 1.Internet网络 网络是比较不靠谱的一个,因为移动终端的网络稳定性,以及所产生的流量让人伤不起,用户更伤不起。但若是对于非常重要的实时数据,或是需要发送给远端服务器处理的,也可以考虑使用网络实时发送。这已经有先例了,Apple和Google就是这样,iPhone设备和Android设备都会在用户不知情的情况 下收集用户的信息,然后又在用户不知情的情况 下发送到Apple和Google的服务器上,也就是所谓的“跟踪门”。除此之外,智能手机(特别是Android和火热的iPhone)上面的应用程序都会偷偷的在后台运行,收集用户数据,然后再偷偷的发服务器,直接伤害是用户流量,请看先前的文章。 对比这几种方式,可以总结下: 1. 简单数据和配置信息,SharedPreference是首选; 2. 如果SharedPreferences不够用,那么就创建一个数据库 3. 结构化数据,一定要创建数据库,虽然这稍显烦锁,但是好处无穷 4. 文件就是用来存储文件(也即非配置信息或结构化数据),如文本文件,二进制文件,PC文件,多媒体文件,下载的文件等等。 5. 尽量不要创建文件 6. 如果创建文件,如果是私密文件或是重要文件,就存储在内部存储,否则放到外部存储 7. 不要收集用户数据,更不要发到网络上,虽然你们也有很多无奈。用户也无奈,也无辜,但更无助 平台为开发者准备了这么多的方式固然是一件好事,但我们要认清每一种的优点和缺点,根据实际情况选择最合适的。还有一个原则就是最简单原则,也就是说能用简单的方式处理,就不要用复杂的方式。比如存储几个数据或简单对象,用SharedPreference也能做到,何必还去写个ContentProvider呢?