在Android應用中,咱們常須要記錄用戶設置的一些偏好參數,,此時咱們就須要用SharedPreferences和Editor將這些信息保存下來,在下次登陸時讀取。java
SharedPreferences保存的數據主要相似於配置信息格式的數據,所以它保存數據的形式爲key-value對,下面咱們來看下實例代碼。android
首先是界面佈局,比較簡單,就是一個普通的登錄界面.sql
<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階段咱們加載本地的數據。api
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
咱們先來考慮這樣一個問題:ide
打開手機設置,選擇應用管理,選擇任意一個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