編輯:關於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() {
@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();
}
});
}
這部分就是用到XUtils的HttpUtils的部分啦,首先設置一些請求參數,接著調用HttpUtils的send方法進行請求,為了能夠對上傳結果更加直觀,我們添加了進度條,onStart方法是上傳執行開始調用的方法,我們在此顯示出進度條,onLoading是上傳過程中執行的方法,大約每一秒鐘會執行一次,我們在此通過Handler的消息機制將進度封裝成Message對象將其發送給Handler處理,具體的更新進度條的代碼是在我們上面的initView方法出現的,因為他只能在主線程中更新,最後在電泳失敗或者成功之後都要調用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分頁讀取實現方法
一般來說,Android自身就包含了常用於嵌入式系統的SQLite,這樣就免去了開發者自己移植安裝的功夫。SQLite 支持多數SQL92標准,很多常用的SQL命令都能在
Android——ViewPager+Fragment+ListView之間
package com.example.jreduch05;import android.os.Bundle;import android.support.v
android產品研發(六)--)Apk混淆
前面一篇文章中我們講解了android裡面的多渠道打包,對於大型的app來說,幾百個上千個渠道包都是很正常的事,所以效率定制化是一件很重要的事。主要講解了三種多渠道打包方
android錯誤之MediaPlayer用法的Media Player called in state *
用到Media Player,遇到幾個問題,記一下 用法就不說了,使用的時候最好參考一下mediaPlayer的這張圖 第一個錯誤是Medi