編輯:關於Android編程
想必大家都在android中或多或少的使用過XUtils框架了吧,今天我們通過他來實現一個照片上傳的Demo,希望能夠對大家有幫助,下一篇再從源碼角度來分析下XUtils的HttpUtils是怎麼一個執行流程的;
先上執行效果圖:
客戶端實現:
首先來看布局文件:
很簡單吧,就是一個按鈕和一個用於顯示圖片的ImageView;
接下來是MainActivity,直接看onCreate方法:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); httpUtils = new HttpUtils(100000); httpUtils.configCurrentHttpCacheExpiry(5000); }這個方法首先會調用initView來初始化界面,接著創建了一個HttpUtils對象,並且設置他的連接超時時間是100s,設置他的緩存有效時間是5s,來看看initView方法:
/** * 初始化view控件 */ public void initView() { uploadImageBt = (Button) findViewById(R.id.upload_image); imageView = (ImageView)findViewById(R.id.imageView); uploadImageBt.setOnClickListener(this); progressDialog = getProgressDialog();//獲得進度條 dialogListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: tempFile = new File(Environment.getExternalStorageDirectory(),getPhotoFileName()); //調用系統拍照 startCamera(dialog); break; case 1: //打開系統圖庫 startWall(dialog); break; default: break; } } }; mHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.arg1 > 0) progressDialog.setProgress(msg.arg1);//更新進度條 } }; }
首先第9行獲得一個ProgressDialog對象,第10行為選擇對話框綁定點擊監聽事件,用來提示用戶是通過拍照獲得照片還是從圖庫獲得,這個對象的定義如下:
/** * 顯示選擇圖片來源的dialog(來自拍照還是本地圖庫) * @param title * @param items */ public void showDialog(String title,String[] items) { AlertDialog.Builder dialog = new AlertDialog.Builder(this).setTitle(title).setItems(items, dialogListener); //顯示dialog dialog.show(); }如果選擇拍照,則通過startCamera方法來調用系統照相機:
/** * 調用相機來照相 * @param dialog */ public void startCamera(DialogInterface dialog) { dialog.dismiss();//首先隱藏選擇照片來源的dialog //調用系統的拍照功能 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra("camerasensortype", 2);//調用前置攝像頭 intent.putExtra("autofocus", true);//進行自動對焦操作 intent.putExtra("fullScreen", false);//設置全屏 intent.putExtra("showActionIcons", false); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));//指定調用相機之後所拍照存儲到的位置 startActivityForResult(intent, PHOTO_CAMERA); }該方法首先會將選擇對話框隱藏,接著開啟系統攝像頭並且進行相應的設置,並且指定保存照片的位置,最後在返回上一個Activity之前將照片結果封裝在intent中返回,並且設置返回標志是PHOTO_CAMERA;
如果選擇的是相冊的話,則通過調用startWall方法來獲得SD上面照片:
/** * 打開系統圖庫 * @param dialog */ public void startWall(DialogInterface dialog) { dialog.dismiss();//設置隱藏dialog Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent, PHOTO_WALL); }該方法同樣首先將選擇對話框隱藏,接著調用系統服務顯示出SD卡中所有存在的圖片,並且通過intent返回圖片信息,同時設置返回標志為PHOTO_WALL;
接下來我們看看不同的返回標志各自所執行的到底是什麼內容呢?
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case PHOTO_CAMERA: //表示從相機獲得的照片,需要進行裁剪 startPhotoCut(Uri.fromFile(tempFile), 300,true); break; case PHOTO_WALL: if(null != data) startPhotoCut(data.getData(),300,false); break; case PHOTO_STORE: if(null != data) { setPictureToImageView(data,true); } break; case PHOTO_NOT_STORE: if(null != data) { setPictureToImageView(data,false); } break; default: break; } }該方法是在調用startActivityForResult之後由系統調用的,看到了我們剛剛見到的PHOTO_CAMREA以及PHOTO_WALL標志,首選來看看PHOTO_CAMREA,他會調用startPhotoCut對照片進行裁剪,來看看startPhotoCut方法:
/** * 將圖片裁剪到指定大小 * @param uri * @param size * @param flag */ public void startPhotoCut(Uri uri,int size,boolean flag) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); intent.putExtra("crop", true);//設置Intent中的view是可以裁剪的 //設置寬高比 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); //設置裁剪圖片的寬高 intent.putExtra("outputX", size); intent.putExtra("outputY", size); //設置是否返回數據 intent.putExtra("return-data", true); if(flag == true) startActivityForResult(intent, PHOTO_STORE); else { tempIntent = intent; try { startActivityForResult(tempIntent, PHOTO_NOT_STORE); System.out.println("haha"); } catch (Exception e) { System.out.println(e.toString()); } } }這個方法前面的幾行都是對照片進行裁剪的一些設置,接著通過flag標志來判斷是否將照片存儲到SD卡上面,因為如果flag為false的話,表示這張照片是我們通過圖庫獲取的,他來自於SD卡,因此沒什麼必要再存一次了,因而返回標志PHOTO_NOT_STORE,如果flag為true的話,才需要存儲一遍,返回標志PHOTO_STORE,同樣的調用的是startActivityForResult方法,那麼他也會執行onActivityResult方法;
那麼對於PHOTO_WALL標志,如果選擇的圖片不為空的話,則執行startPhotoCut方法,同樣也進行裁剪;
對於PHOTO_NOT_STORE和PHOTO_STORE標志,他們都會執行setPictureToImageView方法,所以我們直接看他的代碼就可以了:
/** * 將圖片顯示到ImageView上面 * @param data * @param flag 表示如果是拍照獲得的照片的話則是true,如果是從系統選擇的照片的話就是false */ public void setPictureToImageView(Intent data,boolean flag) { Bundle bundle = data.getExtras(); if(null != bundle) { Bitmap bitmap = bundle.getParcelable("data"); imageView.setImageBitmap(bitmap);//將圖片顯示到ImageView上面 //上傳圖片到服務器 if(flag == false) { //需要首先修改tempFile的值 String path = getSelectPhotoPath(tempIntent); System.out.println("path: "+path); tempFile = new File(path); //uploadPicture(); //上傳圖片 UploadThread thread = new UploadThread(); thread.start(); }else { //uploadPicture(); //上傳圖片 UploadThread thread = new UploadThread(); thread.start(); } if(flag == true) savePictureToSD(bitmap);//保存圖片到sd卡上面 } }這個方法做的事比較多,首先呢,他會從intent中獲取到獲取到圖片並且顯示到ImageView上面,接著會調用UploadThread線程將圖片上傳到服務器上面,最如果flag為true的話表示需要將圖片存儲到本地,那麼我們需要調用savePictureToSD來存儲圖片,先來看看UploadThread線程:
class UploadThread extends Thread { @Override public void run() { uploadPicture(); } }很簡單,就只有一個uploadPicture方法了,自然我們需要查看uploadPicture的代碼:
/** * 上傳圖片到數據庫 */ public void uploadPicture() { RequestParams params = new RequestParams(); params.addBodyParameter("msg",tempFile.getAbsolutePath()); params.addBodyParameter(tempFile.getPath().replace("/", ""), tempFile); httpUtils.send(HttpMethod.POST, url,params,new RequestCallBack這部分就是用到XUtils的HttpUtils的部分啦,首先設置一些請求參數,接著調用HttpUtils的send方法進行請求,為了能夠對上傳結果更加直觀,我們添加了進度條,onStart方法是上傳執行開始調用的方法,我們在此顯示出進度條,onLoading是上傳過程中執行的方法,大約每一秒鐘會執行一次,我們在此通過Handler的消息機制將進度封裝成Message對象將其發送給Handler處理,具體的更新進度條的代碼是在我們上面的initView方法出現的,因為他只能在主線程中更新,最後在電泳失敗或者成功之後都要調用ProgressDialog的dismiss方法來隱藏進度條;() { @Override public void onStart() { progressDialog.show();//顯示進度條 } @Override public void onFailure(HttpException arg0, String arg1) { System.out.println("上傳失敗"); System.out.println(arg0.toString()); //上傳失敗之後隱藏進度條 progressDialog.dismiss(); } @Override public void onLoading(long total, long current, boolean isUploading) { System.out.println("current/total: "+current+"/"+total); int process = 0; if(total != 0) { process = (int)(current/(total/100)); } Message message = new Message(); message.arg1 = process; mHandler.sendMessage(message); super.onLoading(total, current, isUploading); } @Override public void onSuccess(ResponseInfo arg0) { System.out.println("上傳成功"); //上傳成功之後隱藏進度條 progressDialog.dismiss(); } }); }
最後就只剩下保存圖片到SD卡的操作了,這個比較簡單,就只是簡單的文件存儲操作了,只不過路徑是SD卡而已:
/** * 將圖片保存到SD卡上面 * @param bitmap */ public void savePictureToSD(Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); FileOutputStream fos = null; bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//第2個參數表示壓縮率,100表示不壓縮 try { fos = new FileOutputStream(tempFile); fos.write(baos.toByteArray()); fos.flush(); } catch (Exception e) { e.printStackTrace(); }finally{ try { if(null != baos) { baos.close(); baos = null; } if(null != fos) { fos.close(); fos = null; } } catch (Exception e2) { } } }至此,客戶端代碼講解完畢,接下來是服務器端代碼:
服務器端:
代碼比較少,直接copy出來了:
public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html,charset=utf-8"); SmartUpload smartUpload = new SmartUpload(); String msg= null; try { smartUpload.initialize(this.getServletConfig(), request, response); smartUpload.upload(); msg = smartUpload.getRequest().getParameter("msg"); System.out.println(smartUpload.getFiles().getCount()); com.jspsmart.upload.File smartFile = smartUpload.getFiles().getFile(0); if (!smartFile.isMissing()) { String saveFileName = getServletContext().getRealPath("/")+"images\\" + smartFile.getFileName(); System.out.println(saveFileName); smartFile.saveAs(saveFileName, SmartUpload.SAVE_PHYSICAL); } } catch (Exception e) { e.printStackTrace(); } } }
最主要是doPost方法了,本實例采用的是SmartUpload的方式來實現文件上傳操作的,當然你也可以采用別的方法,至於jar包等會源碼下載鏈接的工程裡面就有啦,第18行首先對SmartUpload進行初始化,接著調用upload方法准備上傳,第22行獲得客戶端上傳文件的第一個文件,從這裡我們也可以看出來SmartUpload是支持多文件上傳的,接著第23行判斷這個文件是否存在,24行生成存放文件的路徑,26行進行文件的存儲操作,注意SmartUpload.SAVE_PHYSICAL的意思指的是絕對路徑,因此你的文件存儲路徑必須是全路徑,這樣服務器端代碼講解結束,是不是很簡單呀,提醒一下web.xml的配置,我的配置如下:
基本上講解結束啦,記得在客戶端裡面別忘記添加網絡訪問和SD卡訪問權限哈:UploadServlet com.hzw.servlet.UploadServlet UploadServlet /upload
一般來說,Android自身就包含了常用於嵌入式系統的SQLite,這樣就免去了開發者自己移植安裝的功夫。SQLite 支持多數SQL92標准,很多常用的SQL命令都能在
package com.example.jreduch05;import android.os.Bundle;import android.support.v
前面一篇文章中我們講解了android裡面的多渠道打包,對於大型的app來說,幾百個上千個渠道包都是很正常的事,所以效率定制化是一件很重要的事。主要講解了三種多渠道打包方
用到Media Player,遇到幾個問題,記一下 用法就不說了,使用的時候最好參考一下mediaPlayer的這張圖 第一個錯誤是Medi