Android代码优化----PullToRefresh+universal-image-loader实现从网络获取数据并刷新

简介:

 效果图:(gif图太大了,有点卡,建议将图片保存到本地查看或者直接本文末尾的源码查看gif图)

加载网络图片我们用universal-image-loader,然后实现ListView的上拉下拉刷新我们用PullToRefresh。下面开始写代码。

整个代码的工程文件结构如下:

  • libs文件夹下是需要用到的一些库和开源框架(这里有个picasso框架,是用来加载网络图片的,暂时不用哈,留着以后备用,现在这个项目用的是universal-image-loader)。
  • adapter文件夹下是listView的自定义适配器(本文的重点)
  • entities是从网络获取到json数据,解析之后,用来存放这些数据的实体
  • utils文件夹下是url的常量
  • MainActivity.java是主程序的入口
  • MyApplication才是真正的app的第一个入口,这个不多解释,都懂得。

一、开始写代码:

(1)activity_main.xml(MainActvity的布局)

复制代码
 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <com.handmark.pulltorefresh.library.PullToRefreshListView
 7         android:id="@+id/lv"
 8         android:layout_width="match_parent"
 9         android:layout_height="match_parent">
10 
11 
12     </com.handmark.pulltorefresh.library.PullToRefreshListView>
13 </RelativeLayout>
复制代码

 

这里就放了个PullToRefreshListView,其实本质上还是个ListView

(2)item_listview.xml(ListView中单个item的布局)

复制代码
 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2               xmlns:tools="http://schemas.android.com/tools"
 3               android:layout_width="match_parent"
 4               android:layout_height="match_parent"
 5               android:orientation="vertical"
 6     >
 7 
 8     <LinearLayout
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_marginBottom="15dp"
12         android:gravity="center_vertical"
13         android:orientation="horizontal"
14         android:paddingTop="28dp">
15 
16         <!--作者头像-->
17         <ImageButton
18             android:id="@+id/mVideoAvatarBtn"
19             android:layout_width="56dp"
20             android:layout_height="56dp"
21             android:background="@mipmap/defaultimg"/>
22 
23         <!--昵称-->
24         <TextView
25             android:id="@+id/mVideoNicknameTv"
26             android:layout_width="match_parent"
27             android:layout_height="wrap_content"
28             android:layout_alignParentTop="true"
29             android:layout_marginLeft="13dp"
30             android:layout_toRightOf="@+id/mVideoAvatarIv"
31             android:singleLine="true"
32             android:text="张三"
33             android:textSize="16sp"/>
34 
35     </LinearLayout>
36 
37 
38     <!--视频缩略图-->
39     <ImageButton
40         android:id="@+id/mVideoImgBtn"
41         android:layout_width="wrap_content"
42         android:layout_height="wrap_content"
43 
44         />
45 
46 
47 </LinearLayout>
复制代码

 

单个item中,一个是头像,一个是文本,一个是图片(包裹内容),其布局效果如下:

(3)MyApplication.java:

复制代码
 1 package com.smyhvae.pulltorefreshdemo;
 2 
 3 import android.app.Application;
 4 
 5 import com.nostra13.universalimageloader.core.ImageLoader;
 6 import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
 7 
 8 /**
 9  * Created by smyhvae on 2015/5/8.
10  */
11 public class MyApplication extends Application {
12 
13     @Override
14     public void onCreate() {
15         super.onCreate();
16 
17 
18         //创建默认的ImageLoader配置参数
19         ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
20                 .writeDebugLogs() //打印log信息
21                 .build();
22 
23 
24         //Initialize ImageLoader with configuration.
25         ImageLoader.getInstance().init(configuration);
26     }
27 
28 }
复制代码

 

主程序一进来,我们就在onCreate()中创建ImageLoader的配置参数,并初始化到ImageLoader中。

 

(4)JavaBean的实体:

这个其实就是下面的这些实体:

这个不需要赘述,代码略,在本文最后有源码下载链接。

(5)ViewHolder.java:(ListView的万能模板,嘿嘿)

复制代码
 1 package com.smyhvae.pulltorefreshdemo.adapter;
 2 
 3 import android.content.Context;
 4 import android.util.SparseArray;
 5 import android.view.LayoutInflater;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 
 9 /**
10  * Created by smyhvae on 2015/5/4.
11  * 通用的viewHolder类
12  */
13 public class ViewHolder {
14 
15     private SparseArray<View> mViews;
16     private int mPosition;
17     private View mConvertView;
18 
19     public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
20         this.mPosition = position;
21         this.mViews = new SparseArray<View>();
22 
23         mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
24 
25         mConvertView.setTag(this);
26 
27     }
28 
29     public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
30         if (convertView == null) {
31             return new ViewHolder(context, parent, layoutId, position);
32         } else {
33             ViewHolder holder = (ViewHolder) convertView.getTag();
34             holder.mPosition = position; //即时ViewHolder是复用的,但是position记得更新一下
35             return holder;
36         }
37     }
38 
39     /*
40     通过viewId获取控件
41      */
42     //使用的是泛型T,返回的是View的子类
43     public <T extends View> T getView(int viewId) {
44         View view = mViews.get(viewId);
45 
46         if (view == null) {
47             view = mConvertView.findViewById(viewId);
48             mViews.put(viewId, view);
49         }
50 
51         return (T) view;
52     }
53 
54     public View getConvertView() {
55         return mConvertView;
56     }
57 
58 }
复制代码

 

(6)ListViewAdapter.java:(同样是ListView的万能模板)

复制代码
 1 package com.smyhvae.pulltorefreshdemo.adapter;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.BaseAdapter;
 8 
 9 import java.util.List;
10 
11 /**
12  * Created by smyhvae on 2015/5/4.
13  * 通用的ListView的BaseAdapter,所有的ListView的自定义adapter都可以继承这个类哦
14  */
15 public abstract class ListViewAdapter<T> extends BaseAdapter {
16 
17     //为了让子类访问,于是将属性设置为protected
18     protected Context mContext;
19     protected List<T> mDatas;
20     protected LayoutInflater mInflater;
21     private int layoutId; //不同的ListView的item布局肯能不同,所以要把布局单独提取出来
22 
23     public ListViewAdapter(Context context, List<T> datas, int layoutId) {
24         this.mContext = context;
25         mInflater = LayoutInflater.from(context);
26         this.mDatas = datas;
27         this.layoutId = layoutId;
28     }
29 
30     @Override
31     public int getCount() {
32         return mDatas.size();
33     }
34 
35     @Override
36     public T getItem(int position) {
37         return mDatas.get(position);
38     }
39 
40     @Override
41     public long getItemId(int position) {
42         return position;
43     }
44 
45     @Override
46     public View getView(int position, View convertView, ViewGroup parent) {
47         //初始化ViewHolder,使用通用的ViewHolder,一样代码就搞定ViewHolder的初始化咯
48         ViewHolder holder = ViewHolder.get(mContext, convertView, parent, layoutId, position);//layoutId就是单个item的布局
49 
50         convert(holder, getItem(position));
51         return holder.getConvertView(); //这一行的代码要注意了
52     }
53 
54     //将convert方法公布出去
55     public abstract void convert(ViewHolder holder, T t);
56 
57 }
复制代码

 

(7)【非常非常重要】VideoListViewAdapter.java:

这个才是我们的这个ListView的自定义适配器哦:

复制代码
  1 package com.smyhvae.pulltorefreshdemo.adapter;
  2 
  3 
  4 import android.content.Context;
  5 import android.view.ViewGroup;
  6 import android.widget.ImageButton;
  7 import android.widget.LinearLayout;
  8 import android.widget.TextView;
  9 
 10 import com.nostra13.universalimageloader.core.DisplayImageOptions;
 11 import com.nostra13.universalimageloader.core.ImageLoader;
 12 import com.nostra13.universalimageloader.core.assist.ImageScaleType;
 13 import com.smyhvae.pulltorefreshdemo.R;
 14 import com.smyhvae.pulltorefreshdemo.entities.Video;
 15 import com.smyhvae.pulltorefreshdemo.utils.Constants;
 16 
 17 import java.util.List;
 18 
 19 /**
 20  * Created by smyhvae on 2015/5/5.
 21  */
 22 
 23 
 24 public class VideoListViewAdapter extends ListViewAdapter<Video> {
 25 
 26 
 27     DisplayImageOptions options;        // DisplayImageOptions是用于设置图片显示的类
 28 
 29 
 30     //MyAdapter需要一个Context,通过Context获得Layout.inflater,然后通过inflater加载item的布局
 31     public VideoListViewAdapter(Context context, List<Video> datas) {
 32         super(context, datas, R.layout.item_listview);
 33     }
 34 
 35 
 36 
 37   /*  @Override
 38     public void convert(ViewHolder holder, Bean bean) {
 39 
 40         ((TextView) holder.getView(R.id.titleTv)).setText(bean.getTitle());
 41         ((TextView) holder.getView(R.id.descTv)).setText(bean.getDesc());
 42         ((TextView) holder.getView(R.id.timeTv)).setText(bean.getTime());
 43         ((TextView) holder.getView(R.id.phoneTv)).setText(bean.getPhone());
 44 
 45 *//*
 46         TextView tv = holder.getView(R.id.titleTv);
 47         tv.setText(...);
 48 
 49        ImageView view = getView(viewId);
 50        Imageloader.getInstance().loadImag(view.url);
 51 /*//*
 52     }*/
 53 
 54     @Override
 55     public void convert(ViewHolder holder, final Video video) {
 56 
 57         //1、作者的头像
 58         ImageButton mVideoAvatarBtn = holder.getView(R.id.mVideoAvatarBtn);
 59         //如果用的是开源框架Picasso获取网络图片,那就按照下面注释掉这一行代码来做。
 60         // load方法里的参数是请求的图片的url。placeholder方法中的参数是说,加载图片成功之前默认显示的图片
 61         //Picasso.with(mContext).load(Constants.CONTENT_HOST + video.getUserAvatarUrl()).placeholder(R.mipmap.ic_launcher).into(mVideoAvatarBtn);
 62         String imageUrl = Constants.CONTENT_HOST + video.getUserAvatarUrl();
 63 
 64         /*
 65         下面的加载网络图片中,用到了Android-Universal-Image-Loader框架
 66          */
 67         //显示图片的配置
 68         // 使用DisplayImageOptions.Builder()创建DisplayImageOptions
 69         options = new DisplayImageOptions.Builder()
 70                 .showStubImage(R.mipmap.phone)            // 设置图片下载期间显示的图片
 71                 .showImageForEmptyUri(R.mipmap.ic_launcher)    // 设置图片Uri为空或是错误的时候显示的图片
 72                 .showImageOnFail(R.drawable.default_ptr_flip)        // 设置图片加载或解码过程中发生错误显示的图片
 73                 .cacheInMemory(true)                        // 设置下载的图片是否缓存在内存中
 74                 .cacheOnDisc(true)                            // 设置下载的图片是否缓存在SD卡中
 75                 .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)   //图片会缩放到目标大小完全。非常重要,也就是说,这个view有多大,图片就会缩放到多大
 76                 .build();
 77 
 78         ImageLoader.getInstance().displayImage(imageUrl, mVideoAvatarBtn, options);
 79 
 80         //2、作者的昵称
 81         TextView mVideoNicknameTv = holder.getView(R.id.mVideoNicknameTv);
 82         mVideoNicknameTv.setText(video.getUserNickName());
 83 
 84 
 85         //3、视频的缩略图
 86         ImageButton mVideoImgBtn = holder.getView(R.id.mVideoImgBtn);
 87 
 88        //让缩略图的宽度为手机屏幕的宽度,高度为手机屏幕宽度的一半。说白了,就是让 图片的尺寸为2:1
 89         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
 90                 ViewGroup.LayoutParams.MATCH_PARENT,
 91                 (int) (Constants.displayWidth * 0.5f + 0.5f));
 92         mVideoImgBtn.setLayoutParams(params);
 93 
 94         String PicUrl = Constants.CONTENT_HOST + video.getPicUrl();
 95 
 96 
 97         //Picasso.with(mContext).load(Constants.CONTENT_HOST + video.getPicUrl()).placeholder(R.mipmap.defaultimg).into(mVideoImgBtn);
 98         ImageLoader.getInstance().displayImage(PicUrl, mVideoImgBtn, options);
 99 
100 
101         System.out.println("---->" + video.getPicUrl());
102 
103 
104 
105         //跳转到具体是哪一个视频(即item的详情页)
106 /*        mVideoImgBtn.setOnClickListener(new View.OnClickListener() {
107             @Override
108             public void onClick(View v) {
109                 int id = video.getVideoUrl();
110                 mContext.startActivity(new Intent());
111             }
112         });*/
113     }
114 }
复制代码

 

尤其要注意第75行的属性哦,这样可以让图片缩放到当前控件的大小。(如果没有这一行,图片大小就是包裹内容;如果加了这一行,图片大小就是匹配当前控件的大小,因为我在第88行设置了这个ImageButton的宽度是手机屏幕的宽度,高度是手机屏幕宽度的一半,这样的话,不管网络上的 图片是多大,都能够保证显示出来的图片比例是2:1)

(8)MainActivity.java:

复制代码
  1 package com.smyhvae.pulltorefreshdemo;
  2 
  3 import android.app.Activity;
  4 import android.content.Context;
  5 import android.os.Bundle;
  6 import android.os.Handler;
  7 import android.os.Message;
  8 import android.util.DisplayMetrics;
  9 import android.util.Log;
 10 import android.widget.ListView;
 11 import android.widget.Toast;
 12 
 13 import com.google.gson.Gson;
 14 import com.google.gson.GsonBuilder;
 15 import com.google.gson.reflect.TypeToken;
 16 import com.handmark.pulltorefresh.library.PullToRefreshBase;
 17 import com.handmark.pulltorefresh.library.PullToRefreshListView;
 18 import com.lidroid.xutils.HttpUtils;
 19 import com.lidroid.xutils.exception.HttpException;
 20 import com.lidroid.xutils.http.RequestParams;
 21 import com.lidroid.xutils.http.ResponseInfo;
 22 import com.lidroid.xutils.http.callback.RequestCallBack;
 23 import com.lidroid.xutils.http.client.HttpRequest;
 24 import com.smyhvae.pulltorefreshdemo.adapter.VideoListViewAdapter;
 25 import com.smyhvae.pulltorefreshdemo.entities.ResponseObject;
 26 import com.smyhvae.pulltorefreshdemo.entities.Video;
 27 import com.smyhvae.pulltorefreshdemo.entities.VideoResponse;
 28 import com.smyhvae.pulltorefreshdemo.utils.Constants;
 29 
 30 import java.util.List;
 31 
 32 
 33 public class MainActivity extends Activity {
 34 
 35     private PullToRefreshListView lv;
 36     private Context mContext;
 37 
 38     private List<Video> videoList; //用来存放视频列表的集合
 39 
 40     private int page = 0;  //当前页码
 41     private int size = 10; //每页显示10个
 42     private int count = 0; //当前页面有多少个视频
 43 
 44     private VideoListViewAdapter videoListViewAdapter;
 45 
 46 
 47 
 48 
 49     @Override
 50     protected void onCreate(Bundle savedInstanceState) {
 51         super.onCreate(savedInstanceState);
 52         setContentView(R.layout.activity_main);
 53         //第一个Activity加载进来时,我们就获取屏幕的宽度和高度
 54         DisplayMetrics displayMetrics = new DisplayMetrics();
 55         getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
 56         Constants.displayWidth = displayMetrics.widthPixels;
 57         Constants.displayHeight = displayMetrics.heightPixels;
 58 
 59 
 60 
 61         initView();
 62 
 63     }
 64 
 65     private void initView() {
 66         lv = (PullToRefreshListView) findViewById(R.id.lv);
 67 
 68         /*
 69         设置刷新的模式:
 70         可选值为:disabled(禁用下拉刷新),
 71         pullFromStart(仅支持下拉刷新),
 72         pullFromEnd(仅支持上拉刷新),
 73         both(二者都支持),
 74         manualOnly(只允许手动触发)
 75          */
 76         lv.setMode(PullToRefreshBase.Mode.BOTH);  //让这个Listview支持上拉加载更多,下拉刷新
 77         lv.setScrollingWhileRefreshingEnabled(true);//滚动的时候不允许刷新,要不然么会很乱
 78         //很重要,刷新时做回调
 79         lv.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener<ListView>() {
 80             @Override
 81             public void onRefresh(PullToRefreshBase<ListView> refreshView) {
 82                 //在这里做数据加载的操作
 83                 loadData(refreshView.getScrollY() < 0);
 84             }
 85         });
 86 
 87         //首次打开页面时,延时200ms后自动加载数据
 88        new Handler (new Handler.Callback(){
 89            @Override
 90             public boolean handleMessage(Message msg) {
 91                lv.setRefreshing();
 92                return true;
 93            }
 94        }).sendEmptyMessageDelayed(0,200);
 95     }
 96 
 97 
 98     //如果是true表示下拉刷新,false表示上拉加载更多(如果y值小于0,说明是下拉操作)
 99     private void loadData(final boolean direction) {
100         //http://172.24.1.49:8081/video/getVideos?apikey=  &typeid=1&page=1
101         RequestParams params = new RequestParams();
102 
103         if (direction) {  //如果是上拉,那应该将page变为第一页
104             page = 1;
105 
106         } else {
107             page++; //如果是下拉,就让page加1
108 
109         }
110 
111         params.addQueryStringParameter("page", String.valueOf(page)); //默认显示第一页
112         // params.addQueryStringParameter("size", "10"); //每页显示10个
113 
114         new HttpUtils().send(HttpRequest.HttpMethod.GET, Constants.VIDEO_LIST + "typeid=1&", params, new RequestCallBack<String>() {
115             @Override
116             public void onSuccess(ResponseInfo<String> responseInfo) {
117                 lv.onRefreshComplete();
118                 Log.d("json", "---video的json数据>" + responseInfo.result);
119 
120                 //解析服务器端的json数据
121                 Gson gson = new GsonBuilder().create();
122                 ResponseObject<VideoResponse> object = gson.fromJson(responseInfo.result, new TypeToken<ResponseObject<VideoResponse>>() {
123                 }.getType());
124 /*                ResponseObject<VideoResponse> object = new GsonBuilder().create().fromJson(responseInfo.result, new TypeToken<VideoResponse>() {
125                 }.getType());*/
126                 page = Integer.parseInt(object.getResult().getPage()); //获取服务器端返回来的当前页码
127                 count = object.getResult().getCnt(); //获取当前页面有多少个视频
128                 Log.d("json","---当前页面的item的个数>"+count);
129                 if (direction) { //下拉刷新
130                     videoList = object.getResult().getVideos();  //获取视频信息的集合,并存放
131 
132                     videoListViewAdapter = new VideoListViewAdapter(MainActivity.this,videoList);
133                     lv.setAdapter(videoListViewAdapter); //为这个listView绑定适配器
134 
135                 } else {//尾部加载更多
136                     videoList.addAll(object.getResult().getVideos());
137 
138                 }
139 
140                 if (count == 0) { //如果当前页面已经没有视频了,那就告诉客户端,不要再拉了,因为后面没有数据了。
141                     lv.setMode(PullToRefreshBase.Mode.PULL_FROM_START);
142                 }
143 
144             }
145 
146             @Override
147             public void onFailure(HttpException e, String s) {
148                 lv.onRefreshComplete();//不管是请求成功还是请求失败,我们都停止加载数据
149                 Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
150 
151             }
152         });
153 
154     }
155 
156 } 
复制代码

这个MainActivity中讲到了xUtils怎样获取到网络上的json数据,并用Gson解析,然后用pull to refresh处理上拉下拉刷新的逻辑,好吧,确实是快速开发,用到的框架还挺多的好伐~

 

相关文章
|
2月前
|
监控 安全 网络安全
云计算与网络安全:保护数据的关键策略
【9月更文挑战第34天】在数字化时代,云计算已成为企业和个人存储、处理数据的优选方式。然而,随着云服务的普及,网络安全问题也日益凸显。本文将探讨云计算环境中的网络安全挑战,并提供一系列策略来加强信息安全。从基础的数据加密到复杂的访问控制机制,我们将一探究竟如何在享受云服务便利的同时,确保数据的安全性和隐私性不被侵犯。
65 10
|
3月前
|
存储 安全 网络安全
云计算与网络安全:守护数据,构筑未来
在当今的信息化时代,云计算已成为推动技术革新的重要力量。然而,随之而来的网络安全问题也日益凸显。本文从云服务、网络安全和信息安全等技术领域展开,探讨了云计算在为生活带来便捷的同时,如何通过技术创新和策略实施来确保网络环境的安全性和数据的保密性。
|
27天前
|
存储 安全 网络安全
云计算与网络安全:保护数据的新策略
【10月更文挑战第28天】随着云计算的广泛应用,网络安全问题日益突出。本文将深入探讨云计算环境下的网络安全挑战,并提出有效的安全策略和措施。我们将分析云服务中的安全风险,探讨如何通过技术和管理措施来提升信息安全水平,包括加密技术、访问控制、安全审计等。此外,文章还将分享一些实用的代码示例,帮助读者更好地理解和应用这些安全策略。
|
1月前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:从漏洞到加密,保护数据的关键步骤
【10月更文挑战第24天】在数字化时代,网络安全和信息安全是维护个人隐私和企业资产的前线防线。本文将探讨网络安全中的常见漏洞、加密技术的重要性以及如何通过提高安全意识来防范潜在的网络威胁。我们将深入理解网络安全的基本概念,学习如何识别和应对安全威胁,并掌握保护信息不被非法访问的策略。无论你是IT专业人士还是日常互联网用户,这篇文章都将为你提供宝贵的知识和技能,帮助你在网络世界中更安全地航行。
|
2月前
|
网络协议 Shell 网络安全
解决两个 Android 模拟器之间无法网络通信的问题
让同一个 PC 上运行的两个 Android 模拟器之间能相互通信,出(qiong)差(ren)的智慧。
24 3
|
2月前
|
存储 安全 网络安全
云计算与网络安全:如何保护您的数据
【10月更文挑战第21天】在这篇文章中,我们将探讨云计算和网络安全的关系。随着云计算的普及,网络安全问题日益突出。我们将介绍云服务的基本概念,以及如何通过网络安全措施来保护您的数据。最后,我们将提供一些代码示例,帮助您更好地理解这些概念。
|
3月前
|
数据采集 存储 监控
网络爬虫的最佳实践:结合 set_time_limit() 与 setTrafficLimit() 抓取云盘数据
本文探讨了如何利用 PHP 的 `set_time_limit()` 与爬虫工具的 `setTrafficLimit()` 方法,结合多线程和代理 IP 技术,高效稳定地抓取百度云盘的公开资源。通过设置脚本执行时间和流量限制,使用多线程提高抓取效率,并通过代理 IP 防止 IP 封禁,确保长时间稳定运行。文章还提供了示例代码,展示了如何具体实现这一过程,并加入了数据分类统计功能以监控抓取效果。
70 16
网络爬虫的最佳实践:结合 set_time_limit() 与 setTrafficLimit() 抓取云盘数据
|
2月前
|
SQL 安全 测试技术
网络安全与信息安全:保护数据的艺术
【9月更文挑战第36天】在数字化时代,网络安全和信息安全已成为维护个人隐私和企业资产的基石。本文深入探讨了网络安全漏洞、加密技术以及安全意识的重要性,旨在为读者提供一份知识宝典,帮助他们在网络世界中航行而不触礁。我们将从网络安全的基本概念出发,逐步深入到复杂的加密算法,最后强调培养安全意识的必要性。无论你是IT专业人士还是日常互联网用户,这篇文章都将为你打开一扇了解和实践网络安全的大门。
42 2
|
3月前
|
数据采集 存储 JavaScript
构建您的第一个Python网络爬虫:抓取、解析与存储数据
【9月更文挑战第24天】在数字时代,数据是新的金矿。本文将引导您使用Python编写一个简单的网络爬虫,从互联网上自动抓取信息。我们将介绍如何使用requests库获取网页内容,BeautifulSoup进行HTML解析,以及如何将数据存储到文件或数据库中。无论您是数据分析师、研究人员还是对编程感兴趣的新手,这篇文章都将为您提供一个实用的入门指南。拿起键盘,让我们开始挖掘互联网的宝藏吧!
|
3月前
|
Android开发 开发者 索引
Android实战经验之如何使用DiffUtil提升RecyclerView的刷新性能
本文介绍如何使用 `DiffUtil` 实现 `RecyclerView` 数据集的高效更新,避免不必要的全局刷新,尤其适用于处理大量数据场景。通过定义 `DiffUtil.Callback`、计算差异并应用到适配器,可以显著提升性能。同时,文章还列举了常见错误及原因,帮助开发者避免陷阱。
211 9