一、什麼是http協議
超文本傳送協議(HTTP-Hypertext transfer protocol)定義了瀏覽器(既萬維網客戶進程)怎樣向萬維網服務器請求萬維網文檔,以及服務器怎樣把文檔傳送給瀏覽器。從層次的角度看,HTTP是面向(transaction-oriented)應用層協議,它是萬維網上能夠可靠地交換文件(包括文本、聲音、圖像等各種多媒體文件)的重要基礎。
1、工作流程
一次HTTP操作稱作爲一個事務,其工作過程可分爲四步:
1)首先客戶機與服務器需要建立連接,只要單擊某個超級鏈接,HTTP的工作開始。
2)建立連接後,客戶機發送一個請求給服務器,請求方式的格式爲:統一資源標識符(URL)、協議版本號,後面是MIME信息包括請求修飾符、客戶機信息和可能的內容。
3)服務器接收到請秋後,給予相應的響應信息,其格式爲一個狀態行,包括信息的協議版本號、一個成功或錯誤的代碼,後邊是MIME信息包括服務器信息、實體信息和可能的內容。
4)客戶端接收服務器所返回的信息通過瀏覽器顯示在用戶的顯示屏上,然後客戶機與服務器斷開鏈接。
如果在以上過程中的某一步出現錯誤,那麼產生錯誤的信息將返回到客戶端,有顯示屏輸出。對於用戶來說,這些過程是由HTTP自己完成的,用戶只要用鼠標點擊,等待信息顯示就可以了。
HTTP協議永遠都是客戶端發起請求,服務器回送響應。
這樣就限制了使用HTTP協議,無法實現在客戶端沒有發起請求的時候,服務器將消息推送給客戶端。
HTTP協議是一個無狀態的協議,同一個客戶端的這次請求和上次請求是沒有對應關係。
2、URL
統一資源定位符,也被稱爲網頁地址,是因特網網上標準的資源的地址。
URL的格式由下列三部分組成:
第一部分是協議(或稱爲服務方式);
第二部分是存有該資源的主機IP地址(有時也包括端口號);
第三部分是主機資源的具體地址,如目錄和文件名等。
第一部分和第二部分之間用“://“符號隔開,第二部分和第三部分用”/“符號隔開,第一和第二部分是不可缺少的,第三部分有時可以忽略。現在幾乎所有的URI都是URL
3、SYN
是TCP/IP建立連接時使用的握手信號,在客戶機和服務器之間建立正常的TCP網絡連接時,客戶端首先發出一個SYN消息,服務器使用SYN+ACK應答標識接收到了這個消息,最後客戶機再以ACK消息響應。這樣在客戶機和服務器之間才能建立起可靠的TCP連接,數據纔可以在客戶機和服務器之間傳遞。
4、ACK
即確認字符,在數據通信中,接收站發給發送站的一種傳輸類控制字符。標識發來的數據已確認接收無誤。
5、http請求方式
二、HttpURLConnection的介紹
public class HttpThread extends Thread{
private String url;
private WebView webView;
private Handler handler;
public HttpThread(String url,WebView webView,Handler handler){
this.url = url;
this.webView = webView;
this.handler = handler;
}
@Override
public void run() {
try {
URL httpUrl = new URL(url);
try {
HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection();
connection.setReadTimeout(5000);//設置讀取的超時時間
connection.setRequestMethod("GET");//網絡請求方式
//拿到網頁回傳的信息
final StringBuffer sb = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String str;
while ((str = reader.readLine())!=null){
sb.append(str);
}
handler.post(new Runnable() {
@Override
public void run() {
webView.loadData(sb.toString(),"text/html;charset=utf-8",null);
}
});
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
main文件
public WebView webView;
private Handler handler= new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView) findViewById(R.id.webView);
new HttpThread("http://www.baidu.com",webView,handler).start();
}
2、加載網絡圖片信息
(1)拿到圖片二進制流InputStream
(2)判斷sd卡是否存在->存在則判斷文件輸出目錄在哪->通過該流將文件下載到本地的目錄
(3)下載完後,在本地進行解析,將本地文件轉換成bitMap
三、http傳遞參數信息和傳遞方式post、get
1、doPost()發送的數據通過output發送,而非url,這樣只需在outout時,將我們要發送的實體數據轉化成字節
2、發送數據量較少,一般限制幾k:doGet()所有數據通過url暴露,有安全問題
四、HTTP常見交互返回值的解析
1、什麼是json格式:是一種輕量級的數據交換格式{“name”:“nate”}
personData:json數組
2、ListView具有重用convertView的功能,convertView可能是前後緩衝池中的view,在切換View時,可能從緩衝池中選出已經存在的convertView,會導致新圖片加載前,先顯示舊的圖片
五、Android異步加載
1、如何提高用戶體驗:使用緩存
2、Lru算法:1)Lru:Least Recently Used近期最少使用算法
2)Android提供了LruCache類來實現這個緩存算法
(1)創建LruCache並指定鍵值對類型
(2)初始方法中,獲取當前應用可使用的內存大小,並設置一個緩衝的大小值,初始化LruCache,必須要重寫sizeOf方法在每次存入緩存的時間調用,告訴當前存入對象到底有多大。
(3)獲取圖片時,先從緩存中取出圖片,如果緩存中沒有,則必須去下載
(4)下載模式中,還需將下載完畢的圖片保存到LruCache中
3、如何提高效率
1)ListView滑動停止後才加載可見項
2)ListView滑動時,取消所有加載項
NewsAdapter類
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
private List<NewsBean> mList;
//轉化一個Layout佈局,最爲每一個item
private LayoutInflater mInflater;
private ImageLoader mImageLoader;
private int mStart,mEnd;
public static String[] URLS;//保存當前所獲取的所有圖片的URL地址
private boolean mFirstIn;
public NewsAdapter(Context context,List<NewsBean> data,ListView listView){
mList = data;
mInflater = LayoutInflater.from(context);
//這樣就只保留一個ImageLoader
mImageLoader = new ImageLoader(listView);
URLS = new String[data.size()];
for(int i=0;i<data.size();i++){
URLS[i] = data.get(i).newsIconUrl;
}
mFirstIn = true;
//一定要註冊對應的事件
listView.setOnScrollListener(this);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int i) {
return mList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder = null;
if(view == null){
viewHolder = new ViewHolder();
view = mInflater.inflate(R.layout.item_layout,null);
viewHolder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
view.setTag(viewHolder);
}else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);
String url = mList.get(i).newsIconUrl;
viewHolder.ivIcon.setTag(url);
// new ImageLoader().showImageByThread(viewHolder.ivIcon,mList.get(i).newsIconUrl);//使用多線程
// new ImageLoader().showImageByAsynvTask(viewHolder.ivIcon,url);
mImageLoader.showImageByAsynvTask(viewHolder.ivIcon,url);
viewHolder.tvTitle.setText(mList.get(i).newsTitle);
viewHolder.tvContent.setText(mList.get(i).newsContent);
return view;
}
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
//判斷ListView當前滾動的一個狀態,處於滾動過程,要取消所有正在下載的Task,滾動完畢,根據ListView的首項和尾項,加載之間項目
//停止狀態
if(i == SCROLL_STATE_IDLE){
//加載可見項
mImageLoader.loadImages(mStart,mEnd);
}else {
//停止任務
mImageLoader.cancelAllTasks();
}
}
@Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
mStart = i;
mEnd = i + i1;
//當前列表第一次顯示,且列表已經畫出
if(mFirstIn && i2 > 0){
mImageLoader.loadImages(mStart,mEnd);
mFirstIn = false;
}
}
class ViewHolder{
public TextView tvTitle,tvContent;
public ImageView ivIcon;
}
}
ImageLoader 類
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
//創建Cache<對象名字,對象>
private LruCache<String,Bitmap> mCaches;
private ListView mListView;
private Set<NewsAsyncTask> mTask;
public ImageLoader(ListView listView){
mListView = listView;
mTask = new HashSet<>();
//獲取最大可用內存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
//取一部分作爲緩存空間
int cacheSize = maxMemory/4;
mCaches = new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
//在每次存入緩存的時候調用
return value.getByteCount();
}
};
}
//增加到緩存
public void addBitmapToCache(String url,Bitmap bitmap){
if(getBitmapFromCache(url) == null){
mCaches.put(url,bitmap);
}
}
//從緩存中獲取數據
public Bitmap getBitmapFromCache(String url){
return mCaches.get(url);
}
//子線程不能更新UI所以用handler
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap((Bitmap) msg.obj);
}
}
};
public void showImageByThread(ImageView imageView, final String url){
mImageView = imageView;
mUrl = url;
new Thread(){
@Override
public void run() {
super.run();
Bitmap bitmap = getBitmapFromURL(url);
Message message = Message.obtain();//通過該方式創建的msg,可以使用現有的及回收掉的msg,提高msg的使用效率
message.obj = bitmap;
mHandler.sendMessage(message);
}
}.start();
}
public Bitmap getBitmapFromURL(String urlString){
Bitmap bitmap;
InputStream is = null;
try {
URL url = new URL(urlString);
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(connection.getInputStream());
bitmap = BitmapFactory.decodeStream(is);
connection.disconnect();
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}finally{
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
//給ListView的每一個Item都設置一張圖
public void showImageByAsynvTask(ImageView imageView,String url){
//從緩存中取出對應的圖片
Bitmap bitmap = getBitmapFromCache(url);
//如果緩存中沒有,那麼必須去下載
if(bitmap == null){
imageView.setImageResource(R.mipmap.ic_launcher);
}else {
imageView.setImageBitmap(bitmap);
}
}
public void cancelAllTasks(){
if(mTask != null){
for(NewsAsyncTask task:mTask){
task.cancel(false);
}
}
}
//將顯示圖片的控制權,不在在getView時觸發下載,而使用ListView滾動時去觸發下載任務
//用來加載從start到end的所有圖片
public void loadImages(int start,int end){
for(int i = start;i<end;i++){
String url = NewsAdapter.URLS[i];
//從緩存中取出對應的圖片
Bitmap bitmap = getBitmapFromCache(url);
//如果緩存中沒有,那麼必須去下載
if(bitmap == null) {
NewsAsyncTask task = new NewsAsyncTask(url);
task.execute(url);
mTask.add(task);//將新創建的Task存到集合中,統一做管理
}else {
ImageView imageView = (ImageView) mListView.findViewWithTag(url);//url作爲唯一的tag
imageView.setImageBitmap(bitmap);
}
}
}
private class NewsAsyncTask extends AsyncTask<String,Void,Bitmap>{
// private ImageView mImageView;
private String mUrl;
public NewsAsyncTask(String url){
// mImageView = imageView;
mUrl = url;
}
@Override
protected Bitmap doInBackground(String... strings) {
String url = strings[0];
//從網絡獲取圖片
Bitmap bitmap = getBitmapFromURL(url);
if(bitmap!=null){
//將不再緩存的圖片加入緩存
addBitmapToCache(url,bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
if(imageView != null && bitmap != null){
imageView.setImageBitmap(bitmap);
}
mTask.remove(this);
// if(mImageView.getTag().equals(mUrl)){
// mImageView.setImageBitmap(bitmap);
// }
// mImageView.setImageBitmap(bitmap);
}
}
}
MainActivity類
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private static String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.lv_main);
new NewsAsyncTask().execute(URL);//將url傳遞進去
}
/**
*將url對應的json格式數據轉化爲我們所封裝的NewsBean
*/
private List<NewsBean> getJsonData(String url){
List<NewsBean> newsBeansList = new ArrayList<>();
try {
//此句功能與url.openConnection().getInputStream()相同,可根據URL直接聯網獲取網絡數據,簡單粗暴,返回類型爲InputStream
String jsonString = readStream(new URL(url).openStream());
JSONObject jsonObject;
NewsBean newsBean;
try {
jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("data");
for(int i=0;i<jsonArray.length();i++){
jsonObject = jsonArray.getJSONObject(i);
newsBean = new NewsBean();
newsBean.newsIconUrl = jsonObject.getString("picSmall");
newsBean.newsTitle = jsonObject.getString("name");
newsBean.newsContent = jsonObject.getString("description");
newsBeansList.add(newsBean);
}
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return newsBeansList;
}
/**
*通過is解析網頁返回的數組,獲取Json格式字符串(網頁返回的字符串)
*/
private String readStream(InputStream is){
InputStreamReader isr;
String result="";
try {
String line = "";
isr = new InputStreamReader(is,"utf-8");//字節流轉化爲字符流
BufferedReader br = new BufferedReader(isr);
while ((line = br.readLine())!=null){
result += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* <String類型的請求網址,中間過程,返回結果>
* 實現網絡的異步訪問
*/
class NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{
@Override
//在該方法中獲取URL
protected List<NewsBean> doInBackground(String... strings) {
return getJsonData(strings[0]);
}
//將生成的newsBeans設置給listView
@Override
protected void onPostExecute(List<NewsBean> newsBeans) {
super.onPostExecute(newsBeans);
NewsAdapter adapter = new NewsAdapter(MainActivity.this,newsBeans,mListView);
mListView.setAdapter(adapter);
}
}
}
4、總結
1)通過異步加載,避免阻塞UI線程
2)通過LruCache,將已下載圖片放到內存中
3)通過判斷ListView滑動狀態,決定何時加載圖片
4)不僅僅是ListView,任何控件都可以使用異步加載