• 安卓第十夜 亚当的诞生


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

  • 相关阅读:
    MVC 5 Scaffolder + EntityFramework+UnitOfWork Pattern 代码生成工具集成Visual Studio 2013
    Asp.Net MVC +EntityFramework主从表新增编辑操作的实现(删除操作怎么实现?)
    asp.net MVC 5 Scaffolding多层架构代码生成向导开源项目(邀请你的参与)
    Asp.net mvc 5 CRUD代码自动生成工具- vs.net 2013 Saffolding功能扩展
    Asp.net webform scaffolding结合Generic Unit of Work & (Extensible) Repositories Framework代码生成向导
    MVC中的默认Model绑定者DefaultModelBinder
    MVC中Action参数绑定的过程
    MVC中Action的执行过程
    MVC的控制器的激活过程,我们从MvcHandler开始讲,前面的事情以后再讲
    d
  • 原文地址:https://www.cnblogs.com/vamei/p/3925660.html
一二三 - 开发者的网上家园