20189200余超 2018-2019-2 移动平台应用开发实践第十周作业
20189200余超 2018-2019-2 移动平台应用开发实践第十周作业
偏好
在Android应用中,我们常需要记录用户设置的一些偏好参数,,此时我们就需要用SharedPreferences和Editor将这些信息保存下来,在下次登录时读取。
SharedPreferences保存的数据主要类似于配置信息格式的数据,因此它保存数据的形式为key-value对,下面我们来看下实例代码。
首先是界面布局,比较简单,就是一个普通的登陆界面.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/account"
/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/password"
android:layout_below="@id/account"
/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/password"
android:text="保存参数"
android:id="@+id/save"
android:onClick="save"
/>
</RelativeLayout>
这是自定义的Preferences 类,用来实现数据的保存 ,可在Android的内置存储空间产生一文件。
import android.R.integer;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.widget.EditText;
public class Preferences {
private Context context;
public Preferences(Context context)
{
this.context=context;
}
public void save(String name, Integer valueOf)
{
//保存文件名字为"shared",保存形式为Context.MODE_PRIVATE即该数据只能被本应用读取
SharedPreferences preferences=context.getSharedPreferences("shared",Context.MODE_PRIVATE);
Editor editor=preferences.edit();
editor.putString("name", name);
editor.putInt("age", valueOf);
editor.commit();//提交数据
}
}
下面是Mainactivity的代码。在activity的oncreate阶段我们加载本地的数据。
import java.util.HashMap;
import java.util.Map;
import android.R.integer;
import android.os.Bundle;
import android.app.Activity;
import android.content.SharedPreferences;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
private EditText account,passworad;
Preferences prefer;//自定义的类
SharedPreferences preference;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
account=(EditText)findViewById(R.id.account);
passworad=(EditText)findViewById(R.id.password);
//获取本地的数据
preference=getSharedPreferences("shared", MODE_PRIVATE);
Map<String, String> map=new HashMap<String, String>();
map.put("name",preference.getString("name",""));
map.put("age", String.valueOf(preference.getInt("age", 0)));
account.setText(map.get("name"));
passworad.setText(map.get("age"));
}
//保存文件的方法
public void save(View v) {
String name=account.getText().toString();
String age=passworad.getText().toString();
prefer=new Preferences(this);
prefer.save(name,Integer.valueOf(age));
Toast.makeText(getApplicationContext(), "保存完成", Toast.LENGTH_SHORT).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
我们看一下效果.
点击保存参数,出现保存完成则说明我们已经保存成功了,在下次登录的时候可以看到这些参数还在。因为记录文件是在内置空间中的,所以我们在SD卡中找不到该文件,
如果有root权限的手机可以下载个RE文件管理,我们可以再/data/data/的路径找到很多应用程序的内置文件夹,我们可以在这些文件夹中看到一个shared_prefs文件夹,
里面就有我们刚刚设置而产生的xml文件。
操作空间
我们先来考虑这样一个问题:
打开手机设置,选择应用管理,选择任意一个App,然后你会看到两个按钮,一个是清除缓存,另一个是清除数据,那么当我们点击清除缓存的时候清除的是哪里的数据?当我们点击清除数据的时候又是清除的哪里的数据?读完本文相信你会有答案。
在android开发中我们常常听到这样几个概念,内存,内部存储,外部存储,很多人常常将这三个东西搞混,那么我们今天就先来详细说说这三个东西是怎么回事?
内存,我们在英文中称作memory,内部存储,我们称为InternalStorage,外部存储我们称为ExternalStorage,这在英文中本不会产生歧义,但是当我们翻译为中文之后,前两个都简称为内存,于是,混了。
那么究竟什么是内部存储什么是外部存储呢?
首先我们打开DDMS,有一个File Explorer,如下:
彻底理解android中的内部存储与外部存储0
这里有三个文件夹需要我们重视,一个是data,一个是mnt,一个是storage,我们下面就详细说说这三个文件夹。
1.内部存储
data文件夹就是我们常说的内部存储,当我们打开data文件夹之后(没有root的手机不能打开该文件夹),里边有两个文件夹值得我们关注,如下:
一个文件夹是app文件夹,还有一个文件夹就是data文件夹,app文件夹里存放着我们所有安装的app的apk文件,其实,当我们调试一个app的时候,可以看到控制台输出的内容,有一项是uploading .....就是上传我们的apk到这个文件夹,上传成功之后才开始安装。另一个重要的文件夹就是data文件夹了,这个文件夹里边都是一些包名,打开这些包名之后我们会看到这样的一些文件:
1.data/data/包名/shared_prefs
2.data/data/包名/databases
3.data/data/包名/files
4.data/data/包名/cache
如果打开过data文件,应该都知道这些文件夹是干什么用的,我们在使用sharedPreferenced的时候,将数据持久化存储于本地,其实就是存在这个文件中的xml文件里,我们App里边的数据库文件就存储于databases文件夹中,还有我们的普通数据存储在files中,缓存文件存储在cache文件夹中,存储在这里的文件我们都称之为内部存储。
2.外部存储
外部存储才是我们平时操作最多的,外部存储一般就是我们上面看到的storage文件夹,当然也有可能是mnt文件夹,这个不同厂家有可能不一样。
一般来说,在storage文件夹中有一个sdcard文件夹,这个文件夹中的文件又分为两类,一类是公有目录,还有一类是私有目录,其中的公有目录有九大类,比如DCIM、DOWNLOAD等这种系统为我们创建的文件夹,私有目录就是Android这个文件夹,这个文件夹打开之后里边有一个data文件夹,打开这个data文件夹,里边有许多包名组成的文件夹。
说到这里,我想大家应该已经可以分清楚什么是内部存储什么是外部存储了吧?好,分清楚之后我们就要看看怎么来操作内部存储和外部存储了。
3.操作存储空间
首先,经过上面的分析,大家已经明白了,什么是内部存储,什么是外部存储,以及这两种存储方式分别存储在什么位置,一般来说,我们不会自己去操作内部存储空间,没有root权限的话,我们也没法操作内部存储空间,事实上内部存储主要是由系统来维护的。不过在代码中我们是可以访问到这个文件夹的。由于内部存储空间有限,在开发中我们一般都是操作外部存储空间,Google官方建议我们App的数据应该存储在外部存储的私有目录中该App的包名下,这样当用户卸载掉App之后,相关的数据会一并删除,如果你直接在/storage/sdcard目录下创建了一个应用的文件夹,那么当你删除应用的时候,这个文件夹就不会被删除。
经过以上的介绍,我们可以总结出下面一个表格:
一目了然,什么是内部存储,什么是外部存储。
如果按照路径的特征,我们又可以将文件存储的路径分为两大类,一类是路径中含有包名的,一类是路径中不含有包名的,含有包名的路径,因为和某个App有关,所以对这些文件夹的访问都是调用Context里边的方法,而不含有包名的路径,和某一个App无关,我们可以通过Environment中的方法来访问。如下图:
操作数据库
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 3); // 执行这句并不会创建数据库文件
Button btnCreateDatabase = (Button) findViewById(R.id.button);
btnCreateDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase(); // 执行这句才会创建数据库文件
}
});
}
}
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book (" +
"id integer primary key autoincrement, " +
"author text, " +
"price real," +
"pages integer, " +
"name text)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
/**
* 数据库已经创建过了, 则不会执行到,如果不存在数据库则会执行
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK); // 执行这句才会创建表
Toast.makeText(mContext, "create succeeded", Toast.LENGTH_SHORT).show();
}
/**
* 创建数据库时不会执行,增大版本号升级时才会执行到
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 在这里面可以把旧的表 drop掉 从新创建新表,
// 但如果数据比较重要更好的做法还是把旧表的数据迁移到新表上,比如升级qq聊天记录被删掉肯定招骂
Toast.makeText(mContext, "onUpgrade oldVersion:" + oldVersion + " newVersion:" + newVersion, Toast.LENGTH_SHORT).show();
}
}
验证数据库文件是否存在的方法看最后部分
剩下的工作就是对数据库表的增删改查了
首先通过下面的代码获得一个引用以便操作数据库
1 SQLiteDatabase db = dbHelper.getWritableDatabase();
对于增删改都可以用 db.execSQL(String sql); 来执行sql语句。 例如增加一条记录
1 db.execSQL("insert into book(name , author, pages, price) values("Android数据库操作指南", "panda fang", 200, 35.5)");
遇到字符串要转义 有没有觉得很蛋疼, 用下面的方法就好多了
1 db.execSQL("insert into book(name , author, pages, price) values(?, ? ,? ,? )", new String[]{"Android数据库操作指南", "panda fang", "200", "35.5"});
sql 中用 ? 占位 后面传入真正的参数, 由于在创建表的时候已经约定pages 和 price字段的数据类型为integer和real, 虽然代码中写的是字符串并不影响,存入数据库会自动处理的。数组嘛,必须与其他的元素类型一致。 这第二个方式是 execSQL(String sql)的重载方法 api是 public void execSQL(String sql, Object[] bindArgs) throws SQLException
对于查询则要使用 rawQuery(String sql, String[] selectionArgs) , 因为 execSQL返回void ,而查询需要访问查询结果。方法如下:
Cursor cursor = db.rawQuery("select * from book", null);
while (cursor.moveToNext())
{
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
Log.i(TAG, "name:" + name + " author:" + author);
}
cursor.close();
如何检查数据库文件是否存在,以及检查表中的数据呢。
前提是使用模拟器或者root过的真机。从 android studio 菜单中 Tools -> Android -> Android Device Monitor -> File Explorer 找到 data/data/程序包名/databases 目录
查看是否存在数据库文件。如果存在可以导出到电脑上, 用 以下工具查看数据库中的表
获取图片
import java.io.FileNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import com.maikefengchao.daixu.R;
public class WriteArticle_CompeterelayActivity extends BaseActivity {
private ImageView im_upload_img;
@Override
public void initView(Bundle savedInstanceState){
setContentView(R.layout.view_write_competerelay);
im_upload_img = (ImageView)findViewById(R.id.write_competerelay_cover_iv);
}
@Override
protected void setListener() {
im_upload_img.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
/* 开启Pictures画面Type设定为image */
intent.setType("image/*");
/* 使用Intent.ACTION_GET_CONTENT这个Action */
intent.setAction(Intent.ACTION_GET_CONTENT);
/* 取得相片后返回本画面 */
startActivityForResult(intent, 1);
}
});
}
@Override
protected void processLogic(Bundle saveInstanceState) {
}
//获取本地图片
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
Uri uri = data.getData();
String img_url = uri.getPath();//这是本机的图片路径
ContentResolver cr = this.getContentResolver();
try {
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri));
ImageView imageView = (ImageView) findViewById(R.id.write_competerelay_cover_iv);
/* 将Bitmap设定到ImageView */
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
Log.e("Exception", e.getMessage(),e);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}
statistics.sh