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

posted @ 2019-05-04 16:08  余超20189220  阅读(156)  评论(0编辑  收藏  举报