Android網絡編程(一)

網絡編程

網絡圖片查看

網絡圖片查看器

try {
        // 2.把網址字符串封裝成一個URL
        URL url = new URL(path);
        // 3.獲取鏈接對象
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        // 4.作參數設置,注意大寫
        conn.setRequestMethod("GET");
        // 5.設置鏈接超時時間
        conn.setConnectTimeout(8000);
        // 設置讀取超時時間
        conn.setReadTimeout(8000);
        // 發送請求,創建鏈接
        conn.connect();
        // 6. 獲取返回碼,判斷請求返回狀態
        if (conn.getResponseCode() == 200) {
            // 請求成功
            // 7.拿到服務器返回的流,裏面的數據就是客戶端請求的內容
            InputStream is = conn.getInputStream();
            // 在肯定流中數據是圖片的狀況下可使用Google提供的API直接生成圖片,就不須要本身讀取流了
//====================================================================================================================
            //從流裏讀取數據就是在下載數據,若是網速過慢就會形成主線程阻塞(主線程阻塞在2.3能夠,可是4.0+版本的Android會拋出異常)
            Bitmap bm = BitmapFactory.decodeStream(is);
//====================================================================================================================
            ImageView iv = (ImageView) findViewById(R.id.imageView1);
            iv.setImageBitmap(bm);
        } else {
            Toast.makeText(this, "請求失敗", Toast.LENGTH_SHORT).show();
        }

    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

以上代碼,注意細節~

  • (應用)主線程阻塞html

    • 界面會中止刷新
    • 應用中止響應用戶任何操做
    • 影響用戶體驗
  • 規範android

    • 耗時的操做不要放在主線程((4.0之後的版本強制要求不能夠放在主線程,要不就報錯)放在子線程裏面就能夠了)

ANR異常

  • application not responding(應用無響應)
  • 主線程長時間阻塞,會爆出ANR異常

只有主線程才能夠刷新UI(View)

消息隊列

問題:怎麼用子線程中處理過的數據去刷新UI

MessageQueue 消息隊列git

Looper 輪詢器(會一直不停的檢測消息隊列裏面是否有消息) * 消息隊列沒有數據,就什麼都不幹 * 消息隊列消息,就扔給Handler去處理程序員

Handler * handleMessage()方法用來處理消息 - 重寫該方法來處理咱們的消息github

在主線程建立時會同時建立MessageQueue和Looper對象,可是Handler對象在程序員須要使用是,(程序員)自行建立。 只要消息隊列有消息,handleMessage()方法就會在主線程調用編程

子線程須要刷新UI,只須要往主線程的消息隊列中發送一條數據便可數組


栗子:瀏覽器

public class MainActivity extends Activity {
    // 建立消息處理器
    Handler handler = new Handler() {
        // 重寫handleMessage方法
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 0:
                ImageView iv = (ImageView) findViewById(R.id.imageView1);
                iv.setImageBitmap((Bitmap) msg.obj);
                break;  
            case 1:
                Toast.makeText(MainActivity.this, "請求失敗", Toast.LENGTH_SHORT)
                        .show();
                break;
            }   
        };
    };  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 
    }

    public void click(View V) {
        Thread t = new Thread() {
            @Override
            public void run() {
                // 網址
                String path = "http://192.168.15.27:8080/dd.jpg";

                try {
                    // 2.把網址字符串封裝成一個URL
                    URL url = new URL(path);
                    // 3.獲取鏈接對象
                    HttpURLConnection conn = (HttpURLConnection) url
                            .openConnection();

                    // 4.作參數設置,注意大寫
                    conn.setRequestMethod("GET");
                    // 5.設置鏈接超時時間
                    conn.setConnectTimeout(8000);
                    // 設置讀取超時時間
                    conn.setReadTimeout(8000);
                    // 發送請求,創建鏈接
                    conn.connect();
                    // 6. 獲取返回碼,判斷請求返回狀態
                    if (conn.getResponseCode() == 200) {
                        // 請求成功
                        // 7.拿到服務器返回的流,裏面的數據就是客戶端請求的內容
                        InputStream is = conn.getInputStream();
                        // 在肯定流中數據是圖片的狀況下可使用Google提供的API直接生成圖片,就不須要本身讀取流了
                        Bitmap bm = BitmapFactory.decodeStream(is);

                        // ImageView iv = (ImageView)
                        // findViewById(R.id.imageView1);
                        // iv.setImageBitmap(bm);

                        // 發送消息至主線程消息隊列
                        Message msg = new Message();
                        // 利用消息對象攜帶數據
                        msg.obj = bm;
                        // 設置狀態標識(這裏設置的是成功獲取返回0)
                        msg.what = 0;

                        handler.sendMessage(msg);

                    } else {

                        // Message msg = new Message();
                        // msg.what = 0;
                        // handler.sendMessage(msg);
                        // 不須要攜帶數據時也能夠發送空消息,參數what
                        handler.sendEmptyMessage(1);
                    }

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        t.start();

    }
}

數據緩存到本地

看代碼緩存

public class MainActivity extends Activity {

// 建立消息處理器
Handler handler = new Handler() {
    // 重寫handleMessage方法
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {
        case 0:
            ImageView iv = (ImageView) findViewById(R.id.imageView1);
            iv.setImageBitmap((Bitmap) msg.obj);
            break;
        case 1:
            Toast.makeText(MainActivity.this, "請求失敗", Toast.LENGTH_SHORT)
                    .show();
            break;
        }
    };
};

    …………

public void click(View V) {
    // 網址
    final String path = "http://192.168.15.27:8080/dd.jpg";
    final File file = new File(getCacheDir(), getFileName(path));
    // 判斷文件是否已將緩存到本地
    if (!file.exists()) {
        // 是 直接讀取顯示
        Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
        ImageView iv = (ImageView) findViewById(R.id.imageView1);
        iv.setImageBitmap(bm);

    } else {
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    // 2.把網址字符串封裝成一個URL
                    // 3.獲取鏈接對象
                    // 4.作參數設置,注意大寫
                    // 5.設置鏈接超時時間
                    // 設置讀取超時時間
                    // 發送請求,創建鏈接
                    // 6. 獲取返回碼,判斷請求返回狀態
                    if (conn.getResponseCode() == 200) {
                        // 請求成功
                        // 7.拿到服務器返回的流,裏面的數據就是客戶端請求的內容
                        InputStream is = conn.getInputStream();

                        FileOutputStream fos = new FileOutputStream(file);

                        byte[] b = new byte[1024];
                        int len;
                        while ((len = is.read(b)) != -1) {
                            fos.write(b, 0, len);
                        }
                        fos.close();
                        //生成圖片
                        Bitmap bm = BitmapFactory.decodeFile(file
                                .getAbsolutePath());

                        // 在肯定流中數據是圖片的狀況下可使用Google提供的API直接生成圖片,就不須要本身讀取流了
                        // Bitmap bm = BitmapFactory.decodeStream(is);
                        // ImageView iv = (ImageView)
                        // 發送消息至主線程消息隊列
                        // 利用消息對象攜帶數據
                        // 設置狀態標識(這裏設置的是成功獲取返回0)

                        ………………

                    } else {
                        handler.sendEmptyMessage(1);
                    }

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        };
        t.start();

    }
}

/**
 * 獲取文件名
 * 
 * @param path
 * @return
 */
public String getFileName(String path) {
    int index = path.lastIndexOf("/");
    return path.substring(index + 1);
}


獲取開源代碼的網站

  • code.google.com
  • github.com
  • 在github搜索smart-image-view
  • 下載開源項目smart-image-view
  • 使用自定義組件時,標籤名字要寫包名安全

    <com.loopj.android.image.SmartImageView/>
  • SmartImageView的使用

    SmartImageView siv = (SmartImageView) findViewById(R.id.siv);
    siv.setImageUrl("http://192.168.1.102:8080/dd.jpg");

使用第三方ImageView(本身試)

測試用第三方資源:android-smart-image-view-master.zip


Html源文件查看器

  • 發送GET請求

    URL url = new URL(path);
    //獲取鏈接對象
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    //設置鏈接屬性
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(5000);
    //創建鏈接,獲取響應嗎
    if(conn.getResponseCode() == 200){
    
    }
  • 獲取服務器返回的流,從流中把html源碼讀取出來

    byte[] b = new byte[1024];
    int len = 0;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    while((len = is.read(b)) != -1){
        //把讀到的字節先寫入字節數組輸出流中存起來
        bos.write(b, 0, len);
    }
    //把字節數組輸出流中的內容轉換成字符串
    //默認使用utf-8
    text = new String(bos.toByteArray());

新聞客戶端實現

注意:線程異步問題

由於主線程不能執行網絡下載等其餘耗時的操做,而必須使用子線程去實現網絡下載等操做,可是要注意這裏面的線程異步問題。 如:主線程在刷新UI是調用了子線程沒有處理完畢內容致使的空指針等異常

ViewHolder * 避免了重複生成大量的view對象 * 先把佈局文件中的全部的組件封裝到ViewHolder對象中 * ViewHolder的對象會與View一塊兒被緩存起來 * 須要的時候直接xxx


亂碼問題的解決

解決亂碼問題很是簡單: 統一兩遍的編碼集。 android下默認編碼 utf-8

客戶端: 中文和特殊字符 URLEncoder.encode(); 服務器端: 默認的編碼 iso-8859-1 和 本地編碼(gbk)

發現 數據

�� gbk->utf-8 ??? utf-8 -> iso-8859-1 編碼不存在 錕腳達拷錕斤拷 無藥可救 刪除了重寫。

提交數據(亂碼問題)

GET方式提交數據

  • get方式提交的數據是直接拼接在url的末尾

    final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + name + "&pass=" + pass;
  • 發送get請求,代碼和以前同樣

    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setReadTimeout(5000);
    conn.setConnectTimeout(5000);
    if(conn.getResponseCode() == 200){
    
    }
  • 瀏覽器在發送請求攜帶數據時會對數據進行URL編碼,咱們寫代碼時也須要爲中文進行URL編碼

    String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;

GET方式提交數據的缺點: * 組拼http的URL的方式不安全。 * GET方式提交數據,對數據的長度是有要求的。http規範最大長度4K。

POST方式提交數據

  • post提交數據是用流寫給服務器的
  • 協議頭中多了兩個屬性

    • Content-Type: application/x-www-form-urlencoded,描述提交的數據的mimetype
    • Content-Length: 32,描述提交的數據的長度

      //給請求頭添加post多出來的兩個屬性
      String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass;
      conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
      conn.setRequestProperty("Content-Length", data.length() + "");
  • 設置容許打開post請求的流

    conn.setDoOutput(true);
  • 獲取鏈接對象的輸出流,往流裏寫要提交給服務器的數據

    OutputStream os = conn.getOutputStream();
    os.write(data.getBytes());

POST方式提交數據的優缺點: * 代碼寫起來麻煩 注意4個細節。

優勢: * 安全。 * 提交數據的長度沒有限制。

相關文章
相關標籤/搜索