安卓第十夜 亞當的誕生


作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!

 

上一講介紹了用WebView來抓取一個網頁內容。這一講我將介紹如何在安卓內部直接進行HTTP通信。

《亞當的誕生》,西斯廷禮拜堂的吊頂畫,米開朗基羅之作。當時的教皇強迫沉迷於雕塑的米開朗基羅畫巨幅壁畫。米開朗基羅認為這是在浪費自己的才華,充滿憤怒的作畫。當然,他又成功了。

 

描述

這一講中,我將使用JSON,將數據庫備份到遠程的數據庫中。反過來我也將從遠程數據庫中抓取條目,並放入安卓的數據庫。相關的安卓知識點包括:

  • 線程
  • HTTP通信
  • JSON

 

增加ActionBar

我首先在首頁上增加一菜單,用於觸發下載和上傳功能。這是通過ActionBar實現的。ActionBar在頁面的頂端增加一個橫幅。這個橫幅上可以有應用的圖標、文字信息和選項菜單(OptionMenu)。

 

我在布局文件res/menu/main.xml中定義ActionBar的菜單:

<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" >

    <item android:id="@+id/action_upload" android:orderInCategory="100" android:title="Upload" app:showAsAction="never"/>  
    <item android:id="@+id/action_download" android:orderInCategory="100" android:title="Download" app:showAsAction="never"/>
</menu>

 

現在,修改之前的MainActivity.java。MainActivity將繼承ActionBarActivity。這樣,MainActivity頁面的頂端將增加一條ActionBar。接下來,我要覆蓋ActionBarActivity的兩個方法。一個是onCreateOptionsMenu()方法。在這個方法中,我綁定上面的視圖文件到ActionBar上。另一個方法onOptionsItemSelected(),主要用於說明菜單各個選項被點擊后的動作。代碼如下:

package me.vamei.vamei;import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.v7.app.ActionBarActivity;import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView;public class MainActivity extends ActionBarActivity implements OnClickListener { private SharedPreferences sharedPref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sharedPref = this.getSharedPreferences("me.vamei.vamei", Context.MODE_PRIVATE); Button btn1 = (Button) findViewById(R.id.author); btn1.setOnClickListener(this); Button btn2 = (Button) findViewById(R.id.category); btn2.setOnClickListener(this); } @Override protected void onResume() { super.onResume(); TextView nameView = (TextView) findViewById(R.id.welcome); // retrieve content from shared preference, with key "name"
        String   welcome  = "Welcome, " + sharedPref.getString("name", "unknown") + "!"; nameView.setText(welcome); } // method for interface OnClickListener
 @Override public void onClick(View v) { Intent intent; // Routing to different view elements
        switch(v.getId()) { case R.id.author: intent = new Intent(this, SelfEditActivity.class); startActivity(intent); break; case R.id.category: intent = new Intent(this, CategoryActivity.class); startActivity(intent); break; } } @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; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId(); switch (id) { case R.id.action_download: return true; case R.id.action_upload: return true; } return super.onOptionsItemSelected(item); } }

在上面,我的onOptionsItemSelected()方法還沒有包含具體的功能。我將在下一部分為該方法增加功能。 運行應用后效果如下:

 

HTTP獲得數據

下一步,我將增加"Download"按鈕點擊后的功能。按鈕點擊后,應用將訪問互聯網,並獲得URL指向的.js文件。獲得.js文件后,我從該文件中提取JSON對象,這個對象中包含一些新的Category名字。最后,我在數據庫中增加Category條目:

@Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId(); switch (id) { case R.id.action_download: Thread thread = new Thread() { @Override public void run(){ try{ // Http Get
 InputStream content; HttpClient httpclient = new DefaultHttpClient(); HttpResponse response = httpclient.execute( new HttpGet("http://files.cnblogs.com/vamei/android_contact.js")); content = response.getEntity().getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(content)); final StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); } content.close(); // Parse JSON Object and Save to DB
                        JSONObject receivedObject = new JSONObject(sb.toString()); JSONArray categoryObjects = receivedObject.getJSONArray("category"); ContactsManager cm = new ContactsManager(getApplicationContext()); JSONObject categoryObject; for (int i=0; i< categoryObjects.length(); i++) { categoryObject = categoryObjects.getJSONObject(i); String name = categoryObject.getString("name"); Category category = new Category(name); cm.createCategory(category); } } catch (Exception e) { Log.i("Http Error", e.getMessage().toString()); } } }; thread.start();
break; }
return super.onOptionsItemSelected(item); }

注意到,上面的網絡訪問部分啟動了一個新線程Thread。為了確保界面的流暢,安卓規定網絡訪問不能在負責圖畫界面的主線程中進行。所以,我們必須把網絡訪問放在一個新的線程中。我們通過異步的方式進行網絡訪問,將在下一部分介紹。

程序中的JSONObject和JSONArray用於解析接收到的JSON字符串。

 

使用AsyncTask

AsyncTask在背景進程中工作。AsyncTask分為工作准備、工作進行和工作完成三個部分。AsyncTask有三個方法,onPreExecute(), doInBackground(), onPostExecute()分別代表這三個部分的任務。其中,doInBackground在背景進程中進行,因此可以把網絡訪問放入其中。此外,在doInBackground中,可以通過調用publishProgress(),來更新任務的進度。進度更新后,AsyncTask將調用onProgressUpdate()方法。

 

AsyncTask有三個類型<X, Y, Z>。它們分別是doInBackground(X), onProgressUpdate(Y)和onPostExecute(Z)的參數類型。此外,doInBackground()方法的返回值將成為onPostExecute()的參數,因此doInBackground()方法的返回值類型也是Z。

當工作完成,AsyncTask會通知主線程。AsyncTask與Thread的目的相同,但它異步的調用方式更方便編寫,也更容易實現主線程和背景線程之間的信息傳遞。我下面會實現Upload的對應功能,即把Category表轉成JSON,再把該JSON字符串發送到特定的URL。

@Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId(); switch (id) { case R.id.action_download: Thread thread = new Thread() { @Override public void run(){ try{ InputStream content; // Http Get
                        HttpClient httpclient = new DefaultHttpClient(); HttpResponse response = httpclient.execute( new HttpGet("http://files.cnblogs.com/vamei/android_contact.js")); content = response.getEntity().getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(content)); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); } content.close(); // Parse JSON Object and Save to DB
                        JSONObject receivedObject = new JSONObject(sb.toString()); JSONArray categoryObjects = receivedObject.getJSONArray("category"); ContactsManager cm = new ContactsManager(getApplicationContext()); JSONObject categoryObject; for (int i=0; i< categoryObjects.length(); i++) { categoryObject = categoryObjects.getJSONObject(i); String name = categoryObject.getString("name"); Category category = new Category(name); cm.createCategory(category); } } catch (Exception e) { Log.i("Http Error", e.getMessage().toString()); } } }; thread.start(); break; // upload action
          case R.id.action_upload: UploadTask newTask = new UploadTask(); newTask.execute("http://files.cnblogs.com/"); break; } return super.onOptionsItemSelected(item); } private class UploadTask extends AsyncTask <String, String, String> { /*main worker*/ @Override protected String doInBackground(String...params) { ContactsManager cm = new ContactsManager(getApplicationContext()); List<Category> categories = cm.getAllCategories(); JSONObject sendObject = new JSONObject(); JSONArray categoryObjects = new JSONArray(); try { for (int i=0; i<categories.size(); i++) { JSONObject categoryObject = new JSONObject(); categoryObject.put("name", categories.get(i).getName()); categoryObjects.put(categoryObject); } sendObject.put("category", categoryObjects); // update progress once
                publishProgress("JSON DONE"); // posting to URL 
                HttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(params[0]); StringEntity se = new StringEntity(sendObject.toString()); se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); httpPost.setEntity(se); HttpResponse httpResponse = httpClient.execute(httpPost); // update progress again
            publishProgress("NETWORK DONE"); return httpResponse.getStatusLine().toString(); } catch (Exception e) { e.printStackTrace(); return "Crashed"; } } /*after background work is done*/ @Override protected void onPostExecute(String result) { Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show(); } /*when progress is updated*/ @Override protected void onProgressUpdate(String...params) { Toast.makeText(MainActivity.this, params[0], Toast.LENGTH_SHORT).show(); } }

這里的URL並不能處理POST方法。如果有興趣,可以使用上一部分的Play框架,自制接受POST的服務器,並處理這里上傳的JSON。

 

總結

ActionBarActivity

Thread, AsyncTask

JSONObject, JSONArray

Http, get and post


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM