Android 网络技术练习

1.WebView控件使用

1.1布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <WebView
        android:id="@+id/wv_web"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></WebView>
</LinearLayout>
1.2代码文件
public class MainActivity extends AppCompatActivity {

    private WebView webView;
    private String url;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        去除标题栏
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
//        初始化WebView控件实例
        initUI();
//        允许WebView运行JS
        webView.getSettings().setJavaScriptEnabled(true);
//        限制浏览器跳转二级界面时在控件中打开新的页面,而不是跳转系统浏览器
        webView.setWebViewClient(new WebViewClient());
//        打开网页
        webView.loadUrl("http://www.baidu.com");
    }

    private void initUI() {
        webView=(WebView) findViewById(R.id.wv_web);
    }
}

1.3配置权限

在AndroidManifest.xml中配置网络权限。
<uses-permission android:name="android.permission.INTERNET"></uses-permission>


2.练习HttpURLconnection

2.1发起网络请求

核心类

HttpURLConnection{

//核心方法

setRequestMethod();

setConnectTimeout();

etReadTimeout();

}
发起网络请求
 
 private void getRequest(URL url) {
	new Thread(new Runnable(){
		HttpURlConnection connection=(HttpURLConnection)url.openConnection();
        	BufferedReader reader=null;
        	StringBuilder builder=null;
        		try {
            	connection.setRequestMethod("GET");
            	connection.setConnectTimeout(8000);
            	connection.setReadTimeout(8000);
            	InputStream in=connection.getInputStream();
            	reader=new BufferedReader(new InputStreamReader(in));
            	builder=new StringBuilder();
            	String line;
            	while ((line=reader.readLine())!=null){
                	builder.append(line);
            		}
       	 	} catch (Exception e) {
            	e.printStackTrace();
        		}finally {
            	if (reader!=null){
                	try {
                    	reader.close();
               		} catch (IOException e) {
                    		e.printStackTrace();
                	}
            	}
        	}
	}).start();	
    }
2.2提交信息
//    提交信息给服务器
    private void postRequest(HttpURLConnection connection) {
        try {
            connection.setRequestMethod("POST");
            DataOutputStream out=new DataOutputStream(connection.getOutputStream());
//信息以键值对的形式,数据与数据之间用&连接
            out.writeBytes("username=Administrator&password=123456");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3使用Okhttp

学习资料  https://blog.csdn.net/fightingXia/article/details/70947701

第一步,添加依赖库'com.squareup.okhttp3:okhttp:3.10.0'
自动下载两个库,OkHttp和Okio,后者是前者的通信基础
第二步,具体用法

创建OkHttpClient实例

实现一个默认的客户端,没有连接时间限制

OkHttpClient client=new OkHttpClient();

实现一个对连接时间读取时间有限制的客户端,超过时间限制,强制失败

OkHttpClient client=new OkHttpClient.Builder().connectTimeout(8000, TimeUnit.MILLISECONDS).readTimeout(8000,TimeUnit.MILLISECONDS).build();

发起Http请求(GET)

有同步和异步两种

同步需要自己开一个线程,执行网络连接

创建Request请求,并在build()方法前连缀属性;

调用client的newCall()方法创建Call对象;

调用Call对象的excute()方法获取服务器返回的数据response;

返回结果数据格式依赖服务器实现
new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client=new OkHttpClient.Builder().connectTimeout(8000, TimeUnit.MILLISECONDS).readTimeout(8000,TimeUnit.MILLISECONDS).build();
                Request request= new Request.Builder().url(address).build();
                try {
                    Response response=client.newCall(request).execute();
                    if (response.body() != null) {
                        String strResponse = response.body().string();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } 
            }
        }).start();

异步get请求,因为onResponse和onFailure()默认开启子线程,需要更新UI时,需要跳转主UI线程

        OkHttpClient client=new OkHttpClient.Builder().connectTimeout(8000, TimeUnit.MILLISECONDS).readTimeout(8000,TimeUnit.MILLISECONDS).build();
        Request request= new Request.Builder().url(address).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
//                do something
                Log.d("http connect","失败");
                Log.d("http connect","e"+e.toString());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                if (response.isSuccessful()){
//                    do something
                    Log.d("http connect","获取数据");
                    Log.d("http connect","response.code()=="+response.code());
                    Log.d("http connect","response.body().string()=="+response.body().string());
                }
            }
        });

发起POST请求
创建RequestBody对象来存放待提交数据;
新建request请求,在build()前调用post()方法,并把body对象传入;
后续操作与get并没有区别
OkHttpClient client=new OkHttpClient.Builder().connectTimeout(8000, TimeUnit.MILLISECONDS).readTimeout(8000,TimeUnit.MILLISECONDS).build();
        RequestBody body=new FormBody.Builder().add("account",account).add("password",password).build();
        Request request= new Request.Builder().url(address).post(body).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
//                do something
                Log.d("http connect","失败");
                Log.d("http connect","e"+e.toString());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                if (response.isSuccessful()){
//                    do something
                    Log.d("http connect","获取数据");
                    Log.d("http connect","response.code()=="+response.code());
                    Log.d("http connect","response.body().string()=="+response.body().string());
                }
            }
        });

response.code(),这个是http协议自带的,200表示连接成功

response.body().string()要放在子线程,且只执行一次

做一个小小的封装

public class OkHttpUtils {
    private static OkHttpClient client ;
    private static final String TAG = "OkHttpUtils";
    private static ConcurrentHashMap<String, List<Cookie>> cookieStore = new ConcurrentHashMap<>();


//单例模式返回一个实例
    public static OkHttpClient getInstance(){
        if (client==null)
        synchronized (OkHttpClient.class){

            if (client==null){

//添加cookieJar,自动化管理cookie,获得一致的sessions值
//添加连接超时和读取超时,在网络状况不好的时候可以做出提示.
                client=new OkHttpClient.Builder().cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                        Log.i(TAG, "saveFromResponse: cookies="+cookies);
                        Log.i(TAG, "saveFromResponse: cookieStore="+cookieStore.size());

                        cookieStore.put(url.host(), cookies);
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {

                        List<Cookie> cookies = cookieStore.get(url.host());

                        Log.i(TAG, "loadForRequest: cookies="+cookies);

                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                }).connectTimeout(8000, TimeUnit.MILLISECONDS).readTimeout(8000,TimeUnit.MILLISECONDS).build();
            }
        }
        return client;
    }

//封装了一个静态方法.用来实现登录,url是你要访问的网址或者接口.在使用时可以通过匿名类,根据实际情况实现一个callback,在callback方法中,分别对成功和失败做处理
   public static void sendHttpLoginRequest( String account, String password, Callback callback)  {
        OkHttpClient client=OkHttpUtils.getInstance();
        RequestBody body=new FormBody.Builder().add("account",account).add("password",password).build();
        Request request= new Request.Builder().url("url").post(body).build();
        client.newCall(request).enqueue(callback);
    }

}

下面实际使用下,demo,点击获取按钮,会把获取到的信息设置到TextView中

public class Main2Activity extends AppCompatActivity implements View.OnClickListener{

    private TextView mContent;
    private Button bInsquire;
    private Button bInfor;
    private Button bModifyPhone;
    private static final String TAG = "Main2Activity";

//定义常量
    public static final int GET_CONTACT=1;
    public static final int GET_INFOR=2;
    public static final int MODIFY_PHONE=3;


//定义一个内部Handler,用来获取callback发送的消息,重写内部方法,并处理消息
    private  Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg!=null){
                switch (msg.what){
                    case GET_CONTACT:
                        mContent.setText((String)msg.obj);
                    case GET_INFOR:
                        mContent.setText((String)msg.obj);
                    case MODIFY_PHONE:
                        mContent.setText((String)msg.obj);
                }
            }else{
                Log.i(TAG, "hanlderNews: msg null");
            }
        }
    });


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        mContent=findViewById(R.id.tv_content);
        bInsquire=findViewById(R.id.b_inquire_contacts);
        bInfor=findViewById(R.id.b_Information);
        bModifyPhone=findViewById(R.id.b_modify_phone);

        bInsquire.setOnClickListener(this);
        bInfor.setOnClickListener(this);
        bModifyPhone.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.b_inquire_contacts:
                Log.i(TAG, "onClick: bInsquire");
//匿名类,重写了失败和成功的处理,成功的时候,给Handler发送消息,消息内容为获得的相应体Response的内容
                OkHttpUtils.sendHttpContactsRequest(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        Log.i(TAG, "onFailure: connect failed");
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if (response!=null){
                            String strResponse=response.body().string();
                            Log.d("http connect","response.code()=="+response.code());
                            Log.d("http connect","response.body().toString()=="+strResponse);

                            Message msg=Message.obtain();
                            msg.what=GET_CONTACT;
                            msg.obj=strResponse;
                            mHanlderUtils.sendMessage(msg);
                        }else {
                            Log.i(TAG, "onResponse: response failed");
                        }
                    }
                });
                break;
            case R.id.b_Information:
                
            case R.id.b_modify_phone:
               
        }
    }
}

注意,在新建Handler的时候,AS3.0版本以后会出现告警信息

This Handler class should be static or leaks might occur (anonymous android.os.Handler)

这是由于handler获取到activity的引用,可能由于消息处理的不及时,延时任务会导致activity不能被成功finish(),出现内存泄漏的风险,这里参考  https://blog.csdn.net/banxiali/article/details/51494842

对hanlder进行封装,持有当前活动的弱引用,由于封装的hanlder会处理不同的message,所以在封装Handler的handlerMessage()方法中回调接口方法,在不同的活动里根据情况实现这个接口即可

接口方法

public interface HandlerNewsInterface {
    void hanlderNews(Message msg);
}

封装handler

public class HandlerUtils extends Handler {

    private WeakReference<Activity> mActivityReference;
    private HandlerNewsInterface mHanlderNews;

    HandlerUtils(Activity mActivity,HandlerNewsInterface mHanlderNews) {
        this.mActivityReference = new WeakReference<>(mActivity);
        this.mHanlderNews=mHanlderNews;
    }


    @Override
    public void handleMessage(Message msg) {
        mHanlderNews.hanlderNews(msg);
        super.handleMessage(msg);
    }
}

所以上面的Demo中的Handler就可以这样实现

private  HandlerUtils mHanlderUtils=new HandlerUtils(this, new HandlerNewsInterface() {
        @Override
        public void hanlderNews(Message msg) {
            if (msg!=null){
                switch (msg.what){
                    case GET_CONTACT:
                        mContent.setText((String)msg.obj);
                    case GET_INFOR:
                        mContent.setText((String)msg.obj);
                    case MODIFY_PHONE:
                        mContent.setText((String)msg.obj);
                }
            }else{
                Log.i(TAG, "hanlderNews: msg null");
            }

        }
    });

记得要在活动消失前,移除hanlder中添加的消息

@Override
    protected void onDestroy() {
        mHandlerUtils.removeMessages(GET_CONTACT);
        mHandlerUtils.removeMessages(GET_INFOR);
        mHandlerUtils.removeMessages(MODIFY_PHONE);
        super.onDestroy();
    }

注意,

A.网络请求,需要在配置文件中声明权限

记得在AndroidManifest.xml中配置网络权限。
<uses-permission android:name="android.permission.INTERNET"></uses-permission>


B.对okhttp进行底层封装

对okHttp的更多用法,参考

https://www.jianshu.com/p/ef9282217d07

1.单例模式,获取客户端实例保证

这个唯一的实例,可以在初始化时就设置连接超时,读取超时和写入超时,cookieJar自动管理cookie,可以设置缓存大小和目录

2,cookie的持久化

https://www.jianshu.com/p/ef9282217d07

移动端和服务端连接的过程中,为了保证安全,


3对网络连接超时和读取超时的处理

首先,在设置客户端的时候,我们在build()方法中添加了connectTimOut()和ReadTimeOut()方法,添加了超时属性

接下来,在callBack中返回失败的接口中分情况处理

@Override
    public void onFailure(Call call, IOException e) {

//                do something
        Log.d("http connect","失败");
//连接失败,一般是没有网络或者服务器问题
        if (e instanceof ConnectException){
            Log.d("http connect","ConnectException e="+e.toString());
        }
//超时异常,一般是网络不好,可以设置重连以及重连次数
        if (e instanceof SocketTimeoutException){
            Log.d("http connect","SocketTimeoutException e="+e.toString());
        }
    }


4,psot数据的类型

表单数据(键值对)

RequestBody body=new FormBody.Builder().add("pwd",pwd).add("newPwd1",new1).add("newPwd2",new2).build();

json数组

//设置媒体类型。application/json表示传递的是一个json格式的对象
        MediaType mediaType = MediaType.parse("application/json");
//使用JSONObject封装参数
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("参数名","参数值");
        } catch (JSONException e) {
            e.printStackTrace();
        }
//创建RequestBody对象,将参数按照指定的MediaType封装
        RequestBody requestBody = RequestBody.create(mediaType,jsonObject.toString());

更多的媒体类型(MediaType)信息  https://baike.baidu.com/item/Internet%20Media%20Type?fr=aladdin

图片格式   参考https://blog.csdn.net/zhan10001/article/details/78461143    

暂时没用到,用到了再贴自己的


表单数据提交能完成大部分的网络请求,毕竟一般发送少,下载多

如果需要发送大量的数据,或者需要接受不同格式的数据,就需要包装成json数组

https://blog.csdn.net/muyi_amen/article/details/58586605


更多深入的okhttp

https://www.cnblogs.com/ldq2016/p/8796300.html

猜你喜欢

转载自blog.csdn.net/RungBy/article/details/80015725