1. 程式人生 > >android 頭像利用okhttp上傳到伺服器部分----萌新成長之路

android 頭像利用okhttp上傳到伺服器部分----萌新成長之路

上一篇部落格我們成功完成了從照相機拍攝和相簿裡選擇圖片並在app中顯示出來。我們也完成了初步的裁剪。今天我們來把使用者的資料上傳到伺服器,模擬一個使用者在遊戲內部修改自己頭像,點選確定以後的儲存過程。

步驟1:佈局以及基礎程式碼

這裡我們在上一篇部落格的基礎上再加一個按鈕用來表示使用者確定選擇當前圖片作為頭像。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.choosephoto.MainActivity">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity
="center_horizontal" android:onClick="choosePicture" android:text="選擇頭像" />
<ImageView android:id="@+id/headPicture" android:layout_width="match_parent" android:layout_height="300dp" /> <Button android:layout_width="wrap_content" android:layout_height
="wrap_content" android:layout_gravity="center_horizontal" android:onClick="confirm" android:text="確定" />
</LinearLayout>

這裡寫圖片描述

private String url;//上傳圖片到指定的網址

public void confirm(View view)
{
    //點選以後上傳使用者id和使用者圖片到伺服器
} 

步驟2:編寫一個okhttp的工具類

由於我們在專案中肯定不止一個地方要上傳資料,如果不復用程式碼的話會大大提高程式碼量(雖然okhttp相比於HttpConnection已經簡便了許多)。

我們新建一個java類叫做OkhttpUtils.java,還要新建一個介面用於處理伺服器的返回值。

public interface HttpResponseCallBack {
    void response(String response);//處理伺服器返回成功
    void error(Exception e);//處理異常
}
public static void doPost(final String url, final File file, JSONObject jsonObject, final HttpResponseCallBack httpResponseCallBack)
{
//上傳操作
}

我先講一下我的設計吧。定義一個doPost方法,裡面所做的操作自然就是把我們要穿的資料都傳到伺服器上去。那麼必然需要傳進來的引數是:伺服器網址url,圖片檔案file(沒有就傳null),需要上傳的json資料(id之類的資訊),最後就是我們自定義的介面。
下面先開始寫上傳json資料的部分。
那麼根據我的研究發現okhttp的post大概流程是這個樣子滴(把這個看做是導彈去打目標):

  1. 定義一個okhttpClient。這是無論用什麼方式和伺服器互動都必須的第一步,相當於導彈的控制檯;
  2. 定義一個RequestBody(字面意思是請求體)。大概意思就是我要把一大坨東西扔到伺服器上去,RequestBody就是用來放這一大坨東西的容器,相當於導彈的彈藥。
  3. 定義一個Request。這裡面放的是我要和誰互動(伺服器的url),和我要上傳的資訊(RequestBody),相當於導彈的瞄準系統。
  4. 定義一個Call。訊息的傳送點(暫時這麼理解吧),相當於導彈的基座。同一個導彈可以有不同基座,也可以有不同的控制檯
  5. 執行Call。控制檯說:“導彈發射!”,然後就成功發出了訊息。
    接下來是程式碼部分:

    //定義一個JSON的MediaType(網際網路媒體型別)
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    //1.定義一個OkhttpClient
    private static OkHttpClient client = new OkHttpClient();

    public static void doPost(final String url, final File file, JSONObject jsonObject, final HttpResponseCallBack httpResponseCallBack)
    {
        //建立body,然後設定這個body裡面放的資料型別是什麼。
        RequestBody body = RequestBody.create(JSON,jsonObject.toString());
        //建立請求
        Request request = new Request.Builder().post(body).url(url).build();
        //定義Call
        Call call = client.newCall(request);
        //執行Call
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                httpResponseCallBack.error(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                httpResponseCallBack.response(response.body().string());
            }
        });
    }

大家記一下寫法,當然我這裡寫的不是很詳細。大家可以去看看OkHttp原始碼或者專門詳解OkHttp的部落格~
上面的程式碼完成了Json的上傳,那接下來我們在寫一個方法專門用來上傳圖片。

/引數為要上傳的網址,本地照片在本地的地址,我們自己定義的介面
    public static void doPostPicture(String url, File file,final HttpResponseCallBack httpResponseCallBack) {
        //OkHttpClient作為全域性靜態變數已經定義過了
        //2.建立一個請求體
        RequestBody body;
        //3.建立一個請求體建造器
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);

        builder.addFormDataPart("headPicture", "headPicture.jpg", RequestBody.create(MediaType.parse("image/jpg"), file));

        body = builder.build();

        //3.建立一個請求,利用構建器方式新增url和請求體。
        Request request = new Request.Builder().post(body).url(url).build();

        //4.定義一個call,利用okhttpclient的newcall方法來建立物件。因為Call是一個介面不能利用構造器例項化。
        Call call = client.newCall(request);

        //5.這是非同步排程方法,上傳和接受的工作都在子執行緒裡面運作,如果要使用同步的方法就用call.excute(),此方法返回的就是Response
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                httpResponseCallBack.error(e);//錯誤發生時的處理
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                httpResponseCallBack.response(response.body().string());//把伺服器發回來的資料response解析成string傳入方法
            }
        });
    }

相信大家也能看懂,大致上程式碼還是差不多的就細微的地方有差距(別問我為什麼不分析一下為什麼要這麼寫,因為我也不清楚。。。)
然後我們把這兩個方法有機結合一下下:

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                httpResponseCallBack.response(response.body().string());
                if (file!=null)//如果有檔案需要傳輸的話
                {
                    doPostPicture(url, file, new HttpResponseCallBack() {
                        @Override
                        public void response(String response) {
                            //做操作
                        }

                        @Override
                        public void error(Exception e) {
                            //做操作
                        }
                    });
                }
            }

我們在上面上傳json併成功返回之後做個判斷,如果需要上傳file則再呼叫doPostPicture上傳圖片。
之後就是我們在點選按鈕以後呼叫doPost方法:

    public void confirm(View view)
    {
        //點選以後上傳使用者id和使用者圖片到伺服器
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("id","test");
            jsonObject.put("phoneNum","111111");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        OkhttpUtils.doPost(url, file, jsonObject, new HttpResponseCallBack() {
            @Override
            public void response(String response) {
                Toast.makeText(MainActivity.this,"上傳成功",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void error() {
                Toast.makeText(MainActivity.this,"上傳失敗",Toast.LENGTH_SHORT).show();
            }
        });
    }

注意由於要聯網所以別忘了加許可權

TIPS

1、為什麼要寫介面?考慮一下這種情況:我們在不同的地方都要向伺服器傳輸不同的資料。在登入的時候要上傳手機號和密碼,在修改頭像的時候要上傳使用者的遊戲id和頭像等等。這個的不同我們通過在方法中傳入不同的引數解決,而伺服器對於每個不同的客戶端請求的相應是不同的,我們要解析每個伺服器的相應並在客戶端裡面做出相應的動作(比如登入失敗就要提示使用者,登入成功就要從登入介面跳轉到遊戲介面等等)。okhttp自身提供onResponse方法表示伺服器相應以後的呼叫,而我們在裡面寫的東西卻是不一樣的。最好的辦法就是在我們定義的方法裡面傳入一個介面,也就是java的回撥機制。
2、因為在call.enqueue()方法的回撥裡面是出於子執行緒的,所以我們不能在裡面修改UI佈局,也不能Toast。這個需要注意下。
3、在onResponse回撥裡面,response引數只能解析一次。也就是說如果你這麼寫

                String s = response.body().string();
                String ss = response.body().string();

是要報異常的。大概意思好像是說這個東西解析一次以後就關掉了不讓解析了。
FATAL EXCEPTION: OkHttp Dispatcher
Process: com.example.choosephoto, PID: 9638
java.lang.IllegalStateException: closed……
4、https://github.com/dlovetco/ChoosePhoto github

相關知識: