編輯:關於android開發
之前介紹了Android系統下三種數據存儲形式,今天補充介紹另外兩種,分別是內容提供者和網絡存儲。有些人可能認為內存提供者和網絡存儲更偏向於對數據的操作而不是數據的存儲,但這兩種方式確實與數據有關,所以這裡還是將這兩種形式簡要的說明一下。
Content Provider:
Content Provider,中文名是內存提供者,Android四大組件之一,內容提供者是應用程序之間共享數據的接口,以數據庫形式存入手機內存,可以共享自己的數據給其他應用使用。之所以需要設計一個單獨的控件來操作數據,是為了實現應用程序之間的數據傳遞。通過查看DDMS中的目錄結構可以看出,數據庫文件對於其他應用來說是不可讀、不可寫,而日常生活中又需要獲取其他應用的數據,尤其是系統自帶軟件的數據。比如打開QQ或者微信時會提示是否同步聯系人,又比如備份短信的時候,這些都需要訪問和操作其他應用的數據庫。因此谷歌工程師在底層軟件中集成了大量的方法利用內存提供者的原理,類似於在數據庫中提供一個對外訪問的路徑,供其他應用訪問。
為了更好的理解內存提供者的工作原理,可以自定義一個內容提示者來幫助理解。首先寫一個類繼承ContentProvider,實現該類中的方法,包括一些增刪改查和數據初始化的方法,可以在方法中實現對數據庫的增刪改查操作。數據庫本來是不對外開放的,所以為保護數據,類中的方法原始返回數據均是空類型。為保證數據的安全性,可以創建一個UriMatcher對象,利用addURIf方法添加Uri的路徑規則,在每一次進行數據操作時先判斷傳入的路徑是否符合命名規則。使用內存提供者需要在配置文件中添加provider標簽,指定主機名。只有當訪問者與內容提供者的主機名一致時,才可以建立數據連接。在另一個應用中實現對內存提供者的訪問。具體操作是:創建內容提供者解析器,定義要訪問的Uri的路徑。Uri路徑有著固定的格式:”content://主機名/匹配字符”。 利用內容提供者解析器進行增刪改查,和要操作的數據庫之間建立聯系。以上內容通常用來理解內容提供者的工作原理,實際工作中很少用到自定義的內容提示者。實際中用的比較多的是用內容提供者操作系統聯系人、系統短信等系統應用的數據庫。
內容提供者操作系統應用時相對簡單,需要用到的大部分程序已經在底層實現,要做的是調用各種方法和相關的參數。需要關注的參數有Uri路徑、數據庫的表單結構。可以通過查看底層的代碼獲取相應的參數。其中有些常用的參數可以記下來,方便調用。比如獲取全部短信的Uri路徑是: content://sms。與聯系人有關的數據庫表單有三個raw_contacts、data、mimetypes。操作raw_contacts 表的Uri是: content://com.android.contacts/raw_contacts,操作data 表的Uri是: content://com.android.contacts/data。本文以短信的備份、還原來演示利用內容提供者訪問短信數據庫。
先看一下短信和手機聯系人有關的數據庫所在的路徑。短信在Android 模擬器下存放在的路徑是:/data/data/com.android.providers.telephony/databases/目錄,聯系人在Android 模擬器下存放在的路徑是:/data/data/com.android.providers.contacts/databases/目錄。對於短信數據庫我們關心的表數據有:address、type、body、date,分別表示發送者號碼、短信類型(收還是發)、短信內容、日期。對於聯系人數據庫的三張表一定要按照一定的順序依次查找才能得到相關的數據,在這不做解釋。盡管開發的時候不需要了解短信和手機聯系人的數據庫路徑,但是要明白短信和手機聯系人的數據是存在數據庫中的,同時數據庫對外是不開放的。
與短信有關的數據庫的目錄結構:
本文給出的案例是短信的備份和還原,從而實現對系統應用數據庫的操作。首先利用內容提供者查詢到短信數據庫裡的詳細參數,將該數據以Xml文件的形式存入到指定的文件夾。利用xml解析得到數據,將獲取的數據存在一個工具類中,這樣就能用ListView將數據顯示在界面上。具體實現如下。
package com.example.contentprovider; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.util.Xml; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; /** * 短信的備份和還原 * 添加寫短信和讀短信的權限 * <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.WRITE_SMS"/> * @author Huang */ public class MainActivity extends Activity { private static final String TAG = "MainActivity"; private ListView lv; private List<Person> mlist; private Myadpter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.lv); datafresh(); } //數據刷新,一般用到ListView時最好記得刷新數據否則不顯示 public void datafresh(){ mlist = getList(); if(adapter == null){ adapter = new Myadpter(); }else { adapter.notifyDataSetChanged(); } } //用ListView顯示短信內容 public class Myadpter extends BaseAdapter{ public int getCount() { // TODO Auto-generated method stub return mlist.size(); } public Object getItem(int position) { // TODO Auto-generated method stub return null; } public long getItemId(int position) { // TODO Auto-generated method stub return 0; } public View getView(int position, View convertView, ViewGroup parent) { TextView tv = new TextView(MainActivity.this); tv.setText(mlist.get(position).toString()); return tv; } } //短信備份 public void bankup(View view){ ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://sms"); Cursor cursor = resolver.query(uri, new String[]{"address","body","type","date"}, null, null, null); while(cursor.moveToNext()){ String address = cursor.getString(0); String body = cursor.getString(1); String type = cursor.getString(2); String date = cursor.getString(3); //序列化,把短信以Xml文件的形式存儲 XmlSerializer serializer = Xml.newSerializer(); File file = new File(getFilesDir(),"info.xml"); try { FileOutputStream fos = new FileOutputStream(file); serializer.setOutput(fos, "utf-8"); serializer.startDocument("utf-8", true); serializer.startTag(null, "person"); serializer.startTag(null, "address"); serializer.text(address); serializer.endTag(null, "address"); serializer.startTag(null, "body"); serializer.text(body); serializer.endTag(null, "body"); serializer.startTag(null, "type"); serializer.text(type); serializer.endTag(null, "type"); serializer.startTag(null, "date"); serializer.text(date); serializer.endTag(null, "date"); serializer.endTag(null, "person"); serializer.endDocument(); fos.close(); // Toast.makeText(this, "數據保存成功", 0).show(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void restore(View view){ datafresh(); lv.setAdapter(adapter); } //利用Xml解析得到短信內容 private List<Person> getList() { ContentResolver resolver = getContentResolver(); List<Person> list = new ArrayList();//創建一個Person類存儲標簽內容 Person p = new Person(); File file = new File(getFilesDir(),"info.xml"); XmlPullParser pullParser = Xml.newPullParser(); try { FileInputStream fis = new FileInputStream(file); pullParser.setInput(fis, "utf-8"); int mtype = pullParser.getEventType(); while(mtype != XmlPullParser.END_DOCUMENT){ String name = pullParser.getName(); switch (mtype){ case XmlPullParser.START_TAG: if("address".equals(name)){ String address = pullParser.nextText(); p.setAddress(address); }else if("body".equals(name)){ String body = pullParser.nextText(); p.setBody(body); }else if("type".equals(name)){ String type = pullParser.nextText(); p.setType(type); }else if("date".equals(name)){ String date = pullParser.nextText(); p.setDate(date); } break; case XmlPullParser.END_TAG: if("person".equals(name)){ list.add(p); } break; } mtype = pullParser.next(); } Log.i(TAG, list.toString()); return list; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } }
效果演示,我的虛擬機中只存了一條短信:
網絡存儲:
網絡存儲是最容易理解的一種存儲方式了。其實說簡單點就是文件的上傳和下載。經常聽到的雲備份就是這種形式。優勢也很明顯,即把數據存儲到服務器,不存儲在本地,使用的時候直接從網絡獲取,避免了手機端信息丟失以及其他的安全隱患。因此,對於這種形式就不作多的解釋,直接給出一個文件的上傳和下載的實例來演示網絡存儲。
代碼實現:
package com.example.upload; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import org.apache.http.Header; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpResponseHandler; import com.loopj.android.http.RequestParams; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.text.TextUtils; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { protected static final int SUCCESS = 1; protected static final int ERORR = 2; private EditText et_path; private ImageView iv; //訪問網絡操作耗時,需要在子線程中加一個代理 private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case SUCCESS: Bitmap bm = (Bitmap) msg.obj; iv.setImageBitmap(bm); break; case ERORR: Toast.makeText(MainActivity.this, "圖片獲取失敗", 0).show(); break; } super.handleMessage(msg); }}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_path = (EditText) findViewById(R.id.et_path); iv = (ImageView) findViewById(R.id.iv); } //上傳程序 public void upload(View view){ // String path = et_path.getText().toString().trim(); // 這裡為方便把路徑寫死,這種方式不太正規,一般可以讀et_path來訪問
String path = "/mnt/sdcard/info.txt"; if(TextUtils.isEmpty(path)){ Toast.makeText(this, "路徑不能為空", 0).show(); return; } File file = new File(path); AsyncHttpClient client = new AsyncHttpClient(); RequestParams param = new RequestParams(); try { param.put("file", file); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } client.post("http://192.168.1.114:8080/",param, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "提交成功", 0).show(); } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "提交失敗", 0).show(); } }); } //下載程序 public void download(View view){ new Thread(){ public void run() { try { //這裡為方便把路徑寫死,可以讀et_path來訪問,兩者結果一樣 URL url = new URL("http://192.168.1.114:8080/demo1.png"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); int code = conn.getResponseCode(); if(code == 200){ InputStream is = conn.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(is); Message msg = Message.obtain(); msg.what = SUCCESS; msg.obj = bitmap; handler.sendMessage(msg); is.close(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Message msg = Message.obtain(); msg.what = ERORR; handler.sendMessage(msg); } }; }.start(); } }
至此五種數據存儲全部實現。當然,實際開發中可能比這更復雜,會嵌入到別的知識點中,但數據操作無疑是最為基本的一步,是整體項目開發的基礎。
Java的進化? Kotlin初探與集成Android項目 介紹: Statically typed programming language for the JVM
Android反射機制實現與原理,android反射機制本文介紹Android反射機制實現與原理,在介紹之前,要和Java進行比較,所以先看下Java中的反射相關知識:
android:數據存貯方式-SharedPreference SharedPreference: 1.是一種輕型的數據存貯方式 2.本質是基於xml文件存貯key_
Android項目開發實戰之使用Fragment和FragmentTabHost搭建底部菜單(一) 學習在於實用,只有把自己學到的東西真正的融入到我們的開