安卓第十夜 亚当的诞生

作者: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

时间: 2024-10-03 07:49:59

安卓第十夜 亚当的诞生的相关文章

安卓第五夜 维纳斯的诞生

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!   之前各讲中,分别讲解了安卓的开发环境.架构和基本概念.从这一讲开始,我将制作一个简单的应用,并通过逐步升级它的功能,连带出安卓开发的多个情境. <维纳斯的诞生>是文艺复兴早期名画.相传美神维纳斯从海的泡沫中诞生,波提切利用大胆的笔触描绘这一古典神话.画面洋溢着对人体和美的热爱.如果在中世纪或者西班牙宗教审判时期,这幅画足够波提切利上火刑架了.    任务描述 我将制作一个简

10天学安卓-第十天

原文:10天学安卓-第十天 本次是这个教程的最后一篇了,我们的APP开发基本上已经可以宣告完成了,接下来的工作就是如何发布推广运营了.   广告平台 古人云:兵马未动,粮草先行.我们身为APP开发者就需要考虑如何从APP盈利,目前通常的做法主要有APP收费.APP免费+内购及嵌入广告三种方式,我们这个简单的APP想让用户付费是比较有难度的,那么就只能通过嵌入广告的方式来赚取一点广告费了. 我经常使用的广告平台就是Admob了,不过Admob国内被屏蔽,但是广告依然可以展示,各位程序员一定要学习科

安卓第四夜 概念漫游(下)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!   在安卓第三夜 概念漫游(上)中,我介绍了安卓最基本的功能单元和Intent的连接方式.在这个骨架之上,我们可以进一步增加一些与开发密切相关的重要概念.   Context 一个应用是由多个Activity和Service这样的功能单元组成.一个应用共享有一个Application Context对象.在功能单元内部,可以用特定的方法来调用该对象.正如名字所表示的,Contex

科技讯推荐:安卓手机十款必备应用软件

&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;   不少朋友因为基于安卓系统的海量应用选择了购买安卓手机,然而面对茫茫多的应用,却不知该选择哪款.下面小编就为大家推荐几款手机必备应用. 1.微信 微信是一款手机通信软件,支持通过手机网络发送语音短信.视频.图片和文字,支持视频聊天,还能根据地理位置找到附近的人,以及通过朋友圈分享自己的生活,带给朋友们全新的移动沟通体验.支持iOS.Android.塞班.WP等多种

安卓第六夜 凡高的自画像

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!   在上一讲中,我已经制作了一个简单的Android应用.项目的主要文件包括: MainActivity.java activity_main.xml 在这一讲,我将拓展应用的功能,从而允许用户输入个人信息.   <自画像>,凡高.凡高一生不得志,精神更是越来越差.在割掉自己的耳朵一部分后,画家给自己留下了这幅自画像.在当时,这幅画依然是无人问津.   描述 我将创建一个新的A

安卓第八夜 玛丽莲梦露

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!    上一讲说明了数据库中存取数据的方法.这一讲将以条目的视图方式,来以相似的视图方式,显示多个数据对象.这种方式特别适合于显示从数据库中取出的多个结构相似的数据,比如多个联系人,或者多个联系人分类. <玛丽莲梦露>,这是一副现代艺术作品.听到玛丽莲梦露自杀的消息后,现代艺术家沃霍尔深为震惊.他通过重复玛丽莲梦露的形象,创作了这幅波普艺术的名作.每一个形象既是重复,又有变化.

安卓第七夜 雅典学院

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!    我之前只使用了一种持续保存数据的方法,即SharedPreferences.然而,SharedPreferences只能存储少量松散的数据,并不适合大量数据的存储.安卓带有SQLite数据库,它是一个简单版本的关系型数据库,可以应对更复杂的数据存取需求.我将在这里说明安卓中该数据库的使用方法.这里只专注于安卓中SQLite数据库的接口使用,并没有深入关系型数据库和SQL语言

安卓第三夜 概念漫游(上)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!    拿起一个手机,总是满屏的应用图标.点击图标,选择应用,玩一把游戏,刷一下微博,写一篇日记."叮咚"一声,邮件应用提醒有一封新来的邮件.在安卓里,眼花缭乱的应用承载着各种有趣的功能.欢迎来到安卓应用的世界. 每一个应用都是存储在手机中的一段小程序.编写这段小程序,是每个安卓开发者的主要工作.程序运行后成为一个进程后,在进程的内存空间中新建一个Dalvik虚拟机.程序

Java快速教程

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!      Java是面向对象语言.这门语言其实相当年轻,于1995年才出现,由Sun公司出品.James Gosling领导了Java的项目小组.该项目的最初只想为家电设计一门容易移植的语言.然而,在获得了Netscape浏览器支持后,Java快速推广,应用广泛. Java受到C和C++的强烈影响.Java与C++相近,都是静态类型,但移除了C++中容易出错的一些特征,比如指针和