Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android基礎第六篇(上)

Android基礎第六篇(上)

編輯:關於Android編程

1. 網頁源碼查看器


網頁源碼查看器案例實現在EditText中輸入網址,點擊按鈕獲取,獲取到網頁源碼,顯示在TextView上。
在IE浏覽器中,快捷鍵Shift+F12可以調出httpwatch。用來查看發送請求的一些信息。
案例效果圖:
這裡寫圖片描述
使用api:HttpURLConnection ,用於發送或接收數據。
由於是網絡請求,所以需要加入聯網權限:

源碼查看器實現邏輯:

new Thread() {
    public void run() {
        try {
            //獲取EditText中輸入的網址路徑
            String path = et_path.getText().toString().trim();
            //創建URL對象,參數傳入我們需要訪問的網址路徑
            URL url = new URL(path);
            //通過URL的openConnection()方法獲取一個HttpURLConnection對象,用來發送和接收網絡數據;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //設置發送的請求方式,GET要大寫,固定寫法
            conn.setRequestMethod("GET");
            //設置請求超時時間為5000ms就是5s
            conn.setConnectTimeout(5000);
            //獲取服務器返回的狀態碼,200代表請求資源成功(206代表請求部分資源成功)
            int code = conn.getResponseCode();
            if (code == 200) {
                //調用HttpURLConnection的getInputStream()方法獲取服務器返回的流對象
                InputStream in = conn.getInputStream();
                //StreamTools中的readStream方法,把InputStream轉換成一個String
                String content = StreamTools.readStream(in);
                tv_reuslt.setText(content);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };
}.start();

StreamTools工具類,實現將輸入流轉換成String:

public static String readStream(InputStream in) throws Exception {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int len = -1;
    byte[] buffer = new byte[1024]; // 1kb
    while ((len = in.read(buffer)) != -1) {
        baos.write(buffer, 0, len);
    }
    in.close();
    String content = new String(baos.toByteArray());
    return content;
}

運行效果:
這裡寫圖片描述

2. ScrollView控件


ScrollView控件能夠滾動,只能包裹一個子布局。注意:ScrollView控件中只能有一個根布局。
這裡寫圖片描述

3. ANR異常


(1) ANR 即Application Not Response,表示應用無響應,這裡指的是主線程(UI線程)無響應;
(2) 如果在主線程中進行了耗時的操作(比如連接網絡,拷貝大數據,調用Thread.sleep()方法)就會發生ANR異常;
(3) 避免ANR,可以把耗時操作放到子線程。
(4) 在4.0之後谷歌強制要求連接網絡不能在主線程中進行訪問。如果在主線程中連接網絡會發生如下錯誤,錯誤日志如下:

09-26 01:49:03.818: W/System.err(1638):android.os.NetworkOnMainThreadException

(5) 只有在主線程(UI線程)才可以更新UI,在子線程更新UI會發生以下錯誤,錯誤日志如下:

09-26 01:51:50.050: W/System.err(1708):android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

(6) 消息機制可以用來處理這種更新UI的情況。

4. Handler消息機制的使用


(1) 在主線程定義一個Handler

private Handler handler = new Handle();

(2) 重寫handler裡面的handlemessage方法

public void handleMessage(android.os.Message msg) {}

(3) 用我們在主線程創建的handler 去子線程發消息

handler.sendMessage(msg);

(4) 當sendMessage(mgs)方法執行後,handlemessage方法就會執行,在這個方法裡面更新UI
首先在主線程中創建Handler對象實例:

//創建Handler對象
private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        //msg.what是用戶自定義的消息碼
        switch (msg.what) {
        case REQUESTSUCESS: 
            //msg.obj是message攜帶的數據
            String content = (String) msg.obj;
            tv_reuslt.setText(content);
            break;
        case REQUESTNOTFOUND: 
            Toast.makeText(getApplicationContext(), "請求資源不存在",0).show();
            break;
        case REQUESTEXCEPTION: 
            Toast.makeText(getApplicationContext(), "服務器忙 請稍後....",1).show();
            break;
        default:
            break;
        }
    };
};

在子線程中調用sendMessage方法:

new Thread(){
    @Override
    public void run() {
        //創建消息對象
        Message msg = new Message();
        //設置自定義的消息碼
        msg.what = REQUESTSUCESS;
        //將數據添加到消息對象中
        msg.obj = content;
        //發送消息,調用該方法後,會調用Handler的handleMessage方法
        handler.sendMessage(msg);
    }
}; 

5. Handler的原理


這裡寫圖片描述

1.Message

Message是在線程之間傳遞的消息,它可以在內部攜帶少量的信息what字段,用於在不同線程之間交換數據。除此之外還可以使用arg1和arg2字段來攜帶一些整型數據,使用obj字段攜帶一個Object對象。

2.Handler

Handler顧名思義也就是處理者的意思,它主要是用於發送和處理消息的。發送消息一般是使用Handler的sendMessage()方法,而發出的消息經過一系列地輾轉處理後,最終會傳遞到Handler的handleMessage()方法中。

3.MessageQueue<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjwvYmxvY2txdW90ZT4NCjxwPk1lc3NhZ2VRdWV1ZcrHz/vPorbTwdC1xNLiy7yjrMv81vfSqtPD09q05rfFy/nT0M2ouf1IYW5kbGVyt6LLzbXEz/vPoqGj1eKyv7fWz/vPorvh0rvWsbTm1NrT2s/7z6K208HQ1tCjrLXItP2xu7SmwO2ho8O/uPbP37PM1tDWu7vh09DSu7j2TWVzc2FnZVF1ZXVlttTP86GjPC9wPg0KPGJsb2NrcXVvdGU+DQoJPHA+NC5Mb29wZXI8L3A+DQo8L2Jsb2NrcXVvdGU+DQo8cD5Mb29wZXLKx8O/uPbP37PM1tC1xE1lc3NhZ2VRdWV1ZbXEudy80qOstffTw0xvb3BlcrXEbG9vcCgpt723qLrzo6y+zbvhvfjI67W90ru49s7ez97Rrbu3tbHW0KOsyLu688O/tbG3os/WTWVzc2FnZVF1ZXVl1tC05tTa0rvM9c/7z6KjrL7Nu+G9q8v8yKGz9qOssqK0q7Xdtb1IYW5kbGVytcRoYW5kbGVNZXNzYWdlKCm3vbeo1tCho8O/uPbP37PM1tDSsta7u+HT0NK7uPZMb29wZXK21M/zoaM8L3A+DQo8cD48c3Ryb25nPjxlbT655tTyOiCyu7ncxOPKssO0sOaxvrXEyta7+iDWu9Kq1/a6xMqxtcSy2df3KLHIyOfBrL3TzfjC5yCxyMjnv72xtLTztcTK/b7dILXItcgpIL7N19S8ur+q0ru49tfTz9+zzKOsu/HIocr9vt2688/r0qq4/NDCdWkgvs3KudPDSGFuZGxlcr7Nv8nS1MHLoaM8L2VtPjwvc3Ryb25nPjwvcD4NCjxoMSBpZD0="6-圖片查看器">6. 圖片查看器


本案例實現在EditText中輸入圖片地址,點擊獲取,通過網絡請求獲取到圖片數據,將圖片數據轉換成Bitmap對象,最終在下方的ImageView中顯示。
這裡寫圖片描述
圖片查看器開發步驟:
(1)網絡請求獲取服務器資源;
(2)把流信息轉換成bitmap對象;
(2)BitmapFactory.decodeStream(inputStream in)換成Bitmap對象
(3)記得加上網絡訪問權限
(4)對圖片進行緩存
定義Handler處理更新UI:

private Handler hander = new Hander(){
    public void handlerMessage(Message msg){
       Bitmapbitmap = (Bitmap)msg.obj;
       iv_img.setImageBitmap(bitmap);
    }
};

獲取網絡圖片:

public void click(View v) {
    new Thread() {
        public void run() {
            String path = et_path.getText().toString().trim();
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                if (code == 200) {
                    InputStream inputStream = conn.getInputStream(); 
                    //利用BitmapFactory.decodeStream()方法將流轉換成Bitmap對象
                    final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    //Message.obtain();該方法返回一個消息對象,如果有消息就復用消息池裡的消息,如果沒有消息就創建一個新的消息
                       Message msg = Message.obtain();
                       //msg.obg可以攜帶參數
                       msg.obj = bitmap;
                       handler.sendMessage(msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
}

7. cache和files目錄


cache和files目錄會生成在應用程序包名目錄下,如下圖:
這裡寫圖片描述
生成cache目錄:

public void click1(View v) {
    try {
        File file = new File(getCacheDir(), "info.txt");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write("hhehe".getBytes());
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

成一個files目錄:

public void click2(View v) {
    try {
        FileOutputStream fos = openFileOutput("info.txt", 0);
        fos.write("haha".getBytes());
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

應用管理界面,清除緩存cache會被清除,files文件夾不會刪除,如果清除數據,都會被清除。
一般做緩存數據,我們用cache目錄,但是如果是重要的數據可以放到files目錄。

8. runOnUiThread()使用


runOnUiThread(),顧名思義就是運行在UI線程,也就是主線程。

new Thread() {
    public void run() {
        try {
            // Thread.sleep(100);
              tv.setText("哈哈 我更新了ui");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };
}.start();

以上代碼在子線程中直接更新UI,運行時並不會報錯,但是如果將第4行代碼注釋打開繼續運行,程序會運行報錯。原因是,並不是在子線程當中不能更新UI,而是Android系統底層有一個審計機制,當阻塞達到一定時間,就不能更新UI。
(1) 如果僅僅就是更新UI,那麼我們可以用runOnUiThread(),當中的代碼都會在主線程中執行。
(2) 當我們需要傳遞參數的時候,這時候就需要使用handler來實現

runOnUiThread(new Runnable() {
    public void run() {
        iv.setImageBitmap(cacheBitmap);
    }
});

9. Handler常用的api


postDelayed();應用場景:手機應用打開Splash頁面,過3秒跳轉到主頁面。

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        tv.setText("哈哈哈哈哈 ");
    }
}, 5000);

類似於定時器Timer類:

private Timer timer;
private TimerTask task;
//創建timer實例
timer = new Timer();
//創建任務實例,run方法是在子線程運行
task = new TimerTask() {
    @Override
    public void run() {             
        runOnUiThread(new Runnable() {
                public void run() {
                tv.setText("呵呵呵呵 ");
            }
        });
    }
};
//調用timer.schedule()執行任務,參數1是任務對象,參數2是過多長時間開始執行任務,參數3是間隔多長時間執行一次任務
timer.schedule(task, 3000,1000);

取消任務:

timer.cancel();
task.cancel();

10. 新聞客戶端案例


本案例,實現網易新聞客戶端頁面ListView展示復雜子條目。
這裡寫圖片描述

10.1. 服務器准備

在服務器tomcat目錄下有一個news.xml文件,文件內容就是服務器返回來的新聞數據,如下圖:
這裡寫圖片描述
注意點:
(1) 啟動tomcat服務器一閃而過,需要配置JAVA_HOME
(2) 圖片訪問地址必須是電腦的ip地址,不能是localhost,因為手機訪問localhost是訪問手機本機的地址,而不是訪問電腦服務器的地址。

10.2. 創建界面

界面只有一個ListView用來展示新聞條目:



    


子條目布局:




    

    

  

 


10.3. 准備ListView顯示的數據

從服務器獲取,將獲取到的數據封裝到javabean,存入集合中。
首先定義新聞實體類News:

public class News {
    private String title;
    private String description;
    private String image;
    private String type;
    private String comment;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getImage() {
        return image;
    }
    public void setImage(String image) {
        this.image = image;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getComment() {
        return comment;
    }
    public void setComment(String comment) {
        this.comment = comment;
    }
}

從服務器獲取數據:

private void initListData() {
    new Thread() {
        public void run() {
            try {
                String path = "http://192.168.11.86:8080/news.xml";
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                if (code == 200) {
                    InputStream in = conn.getInputStream();
                    //XmlParserUtils.parseXml()方法是用來解析服務器獲取到的數據
                    newsLists = XmlParserUtils.parserXml(in);
                    System.out.println("newsLists:"+newsLists.size());
                    runOnUiThread(new  Runnable() {
                        public void run() {
                            //lv.setAdapter()方法需要在主線程中執行,不然會報錯
                            lv.setAdapter(new MyAdapter());
                        }
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
}

下面解析從服務器獲取的數據:

public static List parserXml(InputStream in) throws Exception {
    List newsLists = null;
    News news = null;
    XmlPullParser parser = Xml.newPullParser();
    parser.setInput(in, "utf-8");
    int type = parser.getEventType();
    while (type != XmlPullParser.END_DOCUMENT) {
        switch (type) {
        case XmlPullParser.START_TAG: 
            if ("channel".equals(parser.getName())) {
                newsLists = new ArrayList();
            } else if ("item".equals(parser.getName())) {
                news = new News();
            } else if ("title".equals(parser.getName())) {
                news.setTitle(parser.nextText());
            } else if ("description".equals(parser.getName())) {
                news.setDescription(parser.nextText());
            } else if ("image".equals(parser.getName())) {
                news.setImage(parser.nextText());
            } else if ("type".equals(parser.getName())) {
                news.setType(parser.nextText());
            } else if ("comment".equals(parser.getName())) {
                news.setComment(parser.nextText());
            }
            break;
        case XmlPullParser.END_TAG:
            if ("item".equals(parser.getName())) {
                newsLists.add(news);
            }
            break;
        }
        type = parser.next();
    }
    return newsLists;
}

10.4. 提供ListView數據適配器

創建ListView數據適配器,該類繼承BaseAdapter。

private class MyAdapter extends BaseAdapter {
    @Override
    public int getCount() {
        return newsLists.size();
    }
    @Override
    public Object getItem(int position) {
        return null;
    }
    @Override
    public long getItemId(int position) {
        return 0;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        if (convertView == null) {
            view = View.inflate(getApplicationContext(), R.layout.item, null);
        } else {
            view = convertView;
        }
        SmartImageView iv_icon = (SmartImageView)
                                              view.findViewById(R.id.iv_icon);
        TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
        TextView tv_desc = (TextView) view.findViewById(R.id.tv_desc);
        TextView tv_type = (TextView) view.findViewById(R.id.tv_type);
        String imageUrl = newsLists.get(position).getImage();
        iv_icon.setImageUrl(imageUrl, R.drawable.bg);
        tv_title.setText(newsLists.get(position).getTitle());
        tv_desc.setText(newsLists.get(position).getDescription());
        String typee = newsLists.get(position).getType();
        String comment = newsLists.get(position).getComment();
        int type = Integer.parseInt(typee);
        switch (type) {
        case 1:
            tv_type.setText(comment + "國內");
            break;
        case 2:
            tv_type.setText("跟帖");
            break;
        case 3:
            tv_type.setText("國外");
            break;
        default:
            break;
        }
        return view;
    }
}

運行效果:
這裡寫圖片描述

11. SmartImageView的使用


(1) 把com包,源碼包拷貝到當前工程;
這裡寫圖片描述
(2) 在布局裡面定義,使用類的完整包名+類名。


12. SmartImageView的原理

我們自己寫一個類似SmartImageView的控件來實現快速加載圖片,該類需要繼承ImageView,原理就是在子線程當中獲取圖片數據轉換成bitmap,利用handler顯示數據。

public class MySmartImageView extends ImageView {
private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:  
                Bitmap bitmap = (Bitmap) msg.obj;
                MySmartImageView.this.setImageBitmap(bitmap);
                break;
            case 2: 
                int resource =  (Integer) msg.obj;
                MySmartImageView.this.setBackgroundResource(resource);
                break;
            case 3: 
                int resource1 =  (Integer) msg.obj;
                MySmartImageView.this.setBackgroundResource(resource1);
                break;
            default:
                break;
            }
        };
    };
    public MySmartImageView(Context context, AttributeSet attrs, 
     int defStyle) {
        super(context, attrs, defStyle);
    }
    public MySmartImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MySmartImageView(Context context) {
        super(context);
    }
    public void setImageUrl(final String path) {
        new Thread() {
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        Message msg = Message.obtain();
                        msg.obj = bitmap;
                        handler.sendMessage(msg);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
    public void setImageUrl(final String path, final int resource) {
        new Thread() {
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        Message msg = Message.obtain();
                        msg.what = 1;
                        msg.obj = bitmap;
                        handler.sendMessage(msg); 
                    } else {
                        Message msg = Message.obtain();
                        msg.what = 2;
                        msg.obj = resource;
                        handler.sendMessage(msg); 
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Message msg = Message.obtain();
                    msg.what = 3;
                    msg.obj = resource;
                    handler.sendMessage(msg); 
                }
            };
        }.start();
    }
}

(a)上面的handler,用來更新界面。由於獲取網絡數據是在子線程當中執行的,所以我們需要通過handler來實現在主線程當中更新UI。通過message攜帶參數,將bitmap對象傳遞給ImageView進行顯示
(b)setImageUrl(final String path)方法,開啟一個子線程網絡獲取圖片信息,然後將流信息轉換成bitmap對象,最後通過handler傳遞給ImageView顯示
(c)setImageUrl(final String path, final int resource)方法,該方法多了一個參數resource,用來當請求失敗或者發生異常時顯示的圖片。

13. Get和Post登錄方式


這裡寫圖片描述
Get請求和Post請求的區別:
(1)請求路徑不同;
(2)Post請求比get請求多了content-type和content-length兩個請求頭;
(3)Post請求是以流的形式把數據寫給服務器;
(4)Get請求數據大小有限制,在IE中是1k,在其他浏覽器中是4k。
Get請求登錄代碼:

public void click1(View v) {
        final String pwd = et_password.getText().toString().trim();
        final String name = et_username.getText().toString().trim(); 
        new Thread() {
            public void run() {
                try {
                    /將用戶名和密碼拼接成Url訪問路徑/
                    String path = "http://192.168.19.89:8080/web/LoginServlet?username=" + name + "&password=" + pwd ;
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET"); 
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) { 
                        InputStream inputStream = conn.getInputStream();
                        String content = StreamUtils.readStream(inputStream);
                        showToast(content);

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
public void showToast(final String content) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), content, 1).show();
        }
    });
}

Post請求登錄代碼:

public void click2(View v) {
    final String pwd = et_password.getText().toString().trim();
    final String name = et_username.getText().toString().trim();        
    new Thread() {
        public void run() {
            try {
                //拼接用戶名和密碼,作為參數體傳遞給服務器
                String path = "http://192.168.19.89:8080/web/LoginServlet";
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    //設置請求方式為POST
                    conn.setRequestMethod("POST");
                    conn.setConnectTimeout(5000);
                    String data = "username=" + name + "&password=" + pwd;
                    //設置Content-type頭信息
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    //Content-Length頭信息,第二個參數是數據的字節數
                    conn.setRequestProperty("Content-Length", data.length() + "");
                    //setDoOutput(true)該方法設置一個標記,允許向服務器輸出數據
                    conn.setDoOutput(true); 
                    //調用getOutputStream()方法獲取輸出流,通過輸出流向服務器寫入數據
                    conn.getOutputStream().write(data.getBytes());
                    int code = conn.getResponseCode();
                    if (code == 200) { 
                        InputStream inputStream = conn.getInputStream();
                        String content = StreamUtils.readStream(inputStream);
                        showToast(content);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved