Android基础第六篇(上)

转载请标明出处:
http://blog.csdn.net/gj782128729/article/details/52374012
本文出自:【高境的博客】

1. 网页源码查看器


网页源码查看器案例实现在EditText中输入网址,点击按钮获取,获取到网页源码,显示在TextView上。
在IE浏览器中,快捷键Shift+F12可以调出httpwatch。用来查看发送请求的一些信息。
案例效果图:
这里写图片描述
使用api:HttpURLConnection ,用于发送或接收数据。
由于是网络请求,所以需要加入联网权限:

<uses-permission android:name="android.permission.INTERNET"/>

源码查看器实现逻辑:

new Thread() {
    public void run() {
        try {
            //获取EditText中输入的网址路径
            String path = et_path.getText().toString().trim();
            //创建URL对象,参数传入我们需要访问的网址路径
            URL url = new URL(path);
            //通过URL的openConnection()方法获取一个HttpURLConnection对象,用来发送和接收网络数据;
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //设置发送的请求方式,GET要大写,固定写法
            conn.setRequestMethod("GET");
            //设置请求超时时间为5000ms就是5s
            conn.setConnectTimeout(5000);
            //获取服务器返回的状态码,200代表请求资源成功(206代表请求部分资源成功)
            int code = conn.getResponseCode();
            if (code == 200) {
                //调用HttpURLConnection的getInputStream()方法获取服务器返回的流对象
                InputStream in = conn.getInputStream();
                //StreamTools中的readStream方法,把InputStream转换成一个String
                String content = StreamTools.readStream(in);
                tv_reuslt.setText(content);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };
}.start();

StreamTools工具类,实现将输入流转换成String:

public static String readStream(InputStream in) throws Exception {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int len = -1;
    byte[] buffer = new byte[1024]; // 1kb
    while ((len = in.read(buffer)) != -1) {
        baos.write(buffer, 0, len);
    }
    in.close();
    String content = new String(baos.toByteArray());
    return content;
}

运行效果:
这里写图片描述

2. ScrollView控件


ScrollView控件能够滚动,只能包裹一个子布局。注意:ScrollView控件中只能有一个根布局。
这里写图片描述

3. ANR异常


(1) ANR 即Application Not Response,表示应用无响应,这里指的是主线程(UI线程)无响应;
(2) 如果在主线程中进行了耗时的操作(比如连接网络,拷贝大数据,调用Thread.sleep()方法)就会发生ANR异常;
(3) 避免ANR,可以把耗时操作放到子线程。
(4) 在4.0之后谷歌强制要求连接网络不能在主线程中进行访问。如果在主线程中连接网络会发生如下错误,错误日志如下:

09-26 01:49:03.818: W/System.err(1638):android.os.NetworkOnMainThreadException

(5) 只有在主线程(UI线程)才可以更新UI,在子线程更新UI会发生以下错误,错误日志如下:

09-26 01:51:50.050: W/System.err(1708):android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

(6) 消息机制可以用来处理这种更新UI的情况。

4. Handler消息机制的使用


(1) 在主线程定义一个Handler

private Handler handler = new Handle();

(2) 重写handler里面的handlemessage方法

public void handleMessage(android.os.Message msg) {}

(3) 用我们在主线程创建的handler 去子线程发消息

handler.sendMessage(msg);

(4) 当sendMessage(mgs)方法执行后,handlemessage方法就会执行,在这个方法里面更新UI
首先在主线程中创建Handler对象实例:

//创建Handler对象
private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        //msg.what是用户自定义的消息码
        switch (msg.what) {
        case REQUESTSUCESS: 
            //msg.obj是message携带的数据
            String content = (String) msg.obj;
            tv_reuslt.setText(content);
            break;
        case REQUESTNOTFOUND: 
            Toast.makeText(getApplicationContext(), "请求资源不存在",0).show();
            break;
        case REQUESTEXCEPTION: 
            Toast.makeText(getApplicationContext(), "服务器忙 请稍后....",1).show();
            break;
        default:
            break;
        }
    };
};

在子线程中调用sendMessage方法:

new Thread(){
    @Override
    public void run() {
        //创建消息对象
        Message msg = new Message();
        //设置自定义的消息码
        msg.what = REQUESTSUCESS;
        //将数据添加到消息对象中
        msg.obj = content;
        //发送消息,调用该方法后,会调用Handler的handleMessage方法
        handler.sendMessage(msg);
    }
}; 

5. Handler的原理


这里写图片描述

1.Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息what字段,用于在不同线程之间交换数据。除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。

2.Handler

Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。

3.MessageQueue

MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

4.Looper

Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

规则: 不管你什么版本的手机 只要做耗时的操作(比如连接网络 比如拷贝大的数据 等等) 就自己开一个子线程,获取数据后想要更新ui 就使用Handler就可以了。

6. 图片查看器


本案例实现在EditText中输入图片地址,点击获取,通过网络请求获取到图片数据,将图片数据转换成Bitmap对象,最终在下方的ImageView中显示。
这里写图片描述
图片查看器开发步骤:
(1)网络请求获取服务器资源;
(2)把流信息转换成bitmap对象;
(2)BitmapFactory.decodeStream(inputStream in)换成Bitmap对象
(3)记得加上网络访问权限
(4)对图片进行缓存
定义Handler处理更新UI:

private Handler hander = new Hander(){
    public void handlerMessage(Message msg){
       Bitmapbitmap = (Bitmap)msg.obj;
       iv_img.setImageBitmap(bitmap);
    }
};

获取网络图片:

public void click(View v) {
    new Thread() {
        public void run() {
            String path = et_path.getText().toString().trim();
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                if (code == 200) {
                    InputStream inputStream = conn.getInputStream(); 
                    //利用BitmapFactory.decodeStream()方法将流转换成Bitmap对象
                    final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    //Message.obtain();该方法返回一个消息对象,如果有消息就复用消息池里的消息,如果没有消息就创建一个新的消息
                       Message msg = Message.obtain();
                       //msg.obg可以携带参数
                       msg.obj = bitmap;
                       handler.sendMessage(msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
}

7. cache和files目录


cache和files目录会生成在应用程序包名目录下,如下图:
这里写图片描述
生成cache目录:

public void click1(View v) {
    try {
        File file = new File(getCacheDir(), "info.txt");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write("hhehe".getBytes());
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

成一个files目录:

public void click2(View v) {
    try {
        FileOutputStream fos = openFileOutput("info.txt", 0);
        fos.write("haha".getBytes());
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

应用管理界面,清除缓存cache会被清除,files文件夹不会删除,如果清除数据,都会被清除。
一般做缓存数据,我们用cache目录,但是如果是重要的数据可以放到files目录。

8. runOnUiThread()使用


runOnUiThread(),顾名思义就是运行在UI线程,也就是主线程。

new Thread() {
    public void run() {
        try {
            // Thread.sleep(100);
              tv.setText("哈哈 我更新了ui");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };
}.start();

以上代码在子线程中直接更新UI,运行时并不会报错,但是如果将第4行代码注释打开继续运行,程序会运行报错。原因是,并不是在子线程当中不能更新UI,而是Android系统底层有一个审计机制,当阻塞达到一定时间,就不能更新UI。
(1) 如果仅仅就是更新UI,那么我们可以用runOnUiThread(),当中的代码都会在主线程中执行。
(2) 当我们需要传递参数的时候,这时候就需要使用handler来实现

runOnUiThread(new Runnable() {
    public void run() {
        iv.setImageBitmap(cacheBitmap);
    }
});

9. Handler常用的api


postDelayed();应用场景:手机应用打开Splash页面,过3秒跳转到主页面。

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        tv.setText("哈哈哈哈哈 ");
    }
}, 5000);

类似于定时器Timer类:

private Timer timer;
private TimerTask task;
//创建timer实例
timer = new Timer();
//创建任务实例,run方法是在子线程运行
task = new TimerTask() {
    @Override
    public void run() {             
        runOnUiThread(new Runnable() {
                public void run() {
                tv.setText("呵呵呵呵 ");
            }
        });
    }
};
//调用timer.schedule()执行任务,参数1是任务对象,参数2是过多长时间开始执行任务,参数3是间隔多长时间执行一次任务
timer.schedule(task, 3000,1000);

取消任务:

timer.cancel();
task.cancel();

10. 新闻客户端案例


本案例,实现网易新闻客户端页面ListView展示复杂子条目。
这里写图片描述

10.1. 服务器准备

在服务器tomcat目录下有一个news.xml文件,文件内容就是服务器返回来的新闻数据,如下图:
这里写图片描述
注意点:
(1) 启动tomcat服务器一闪而过,需要配置JAVA_HOME
(2) 图片访问地址必须是电脑的ip地址,不能是localhost,因为手机访问localhost是访问手机本机的地址,而不是访问电脑服务器的地址。

10.2. 创建界面

界面只有一个ListView用来展示新闻条目:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

子条目布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp" >

    <com.loopj.android.image.SmartImageView
        android:id="@+id/iv_icon"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="3dp"
        android:layout_toRightOf="@id/iv_icon"
        android:ellipsize="end"
        android:singleLine="true"
        android:text="新闻标题"
        android:textColor="#000000"
        android:textSize="22sp" />

  <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_title"
        android:layout_marginTop="3dp"
        android:layout_toRightOf="@id/iv_icon"
        android:ellipsize="end"
        android:maxLines="2"
        android:text="新闻的描述"
        android:textColor="#9e9e9e"
        android:textSize="18sp" />

 <TextView
        android:id="@+id/tv_type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/iv_icon"
        android:layout_alignParentRight="true"
        android:layout_marginRight="3dp"
        android:text="跟帖"
        android:textColor="#ff0000"
        android:textSize="16sp" />

</RelativeLayout>

10.3. 准备ListView显示的数据

从服务器获取,将获取到的数据封装到javabean,存入集合中。
首先定义新闻实体类News:

public class News {
    private String title;
    private String description;
    private String image;
    private String type;
    private String comment;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getImage() {
        return image;
    }
    public void setImage(String image) {
        this.image = image;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getComment() {
        return comment;
    }
    public void setComment(String comment) {
        this.comment = comment;
    }
}

从服务器获取数据:

private void initListData() {
    new Thread() {
        public void run() {
            try {
                String path = "http://192.168.11.86:8080/news.xml";
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                if (code == 200) {
                    InputStream in = conn.getInputStream();
                    //XmlParserUtils.parseXml()方法是用来解析服务器获取到的数据
                    newsLists = XmlParserUtils.parserXml(in);
                    System.out.println("newsLists:"+newsLists.size());
                    runOnUiThread(new  Runnable() {
                        public void run() {
                            //lv.setAdapter()方法需要在主线程中执行,不然会报错
                            lv.setAdapter(new MyAdapter());
                        }
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
}

下面解析从服务器获取的数据:

public static List<News> parserXml(InputStream in) throws Exception {
    List<News> newsLists = null;
    News news = null;
    XmlPullParser parser = Xml.newPullParser();
    parser.setInput(in, "utf-8");
    int type = parser.getEventType();
    while (type != XmlPullParser.END_DOCUMENT) {
        switch (type) {
        case XmlPullParser.START_TAG: 
            if ("channel".equals(parser.getName())) {
                newsLists = new ArrayList<News>();
            } else if ("item".equals(parser.getName())) {
                news = new News();
            } else if ("title".equals(parser.getName())) {
                news.setTitle(parser.nextText());
            } else if ("description".equals(parser.getName())) {
                news.setDescription(parser.nextText());
            } else if ("image".equals(parser.getName())) {
                news.setImage(parser.nextText());
            } else if ("type".equals(parser.getName())) {
                news.setType(parser.nextText());
            } else if ("comment".equals(parser.getName())) {
                news.setComment(parser.nextText());
            }
            break;
        case XmlPullParser.END_TAG:
            if ("item".equals(parser.getName())) {
                newsLists.add(news);
            }
            break;
        }
        type = parser.next();
    }
    return newsLists;
}

10.4. 提供ListView数据适配器

创建ListView数据适配器,该类继承BaseAdapter。

private class MyAdapter extends BaseAdapter {
    @Override
    public int getCount() {
        return newsLists.size();
    }
    @Override
    public Object getItem(int position) {
        return null;
    }
    @Override
    public long getItemId(int position) {
        return 0;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        if (convertView == null) {
            view = View.inflate(getApplicationContext(), R.layout.item, null);
        } else {
            view = convertView;
        }
        SmartImageView iv_icon = (SmartImageView)
                                              view.findViewById(R.id.iv_icon);
        TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
        TextView tv_desc = (TextView) view.findViewById(R.id.tv_desc);
        TextView tv_type = (TextView) view.findViewById(R.id.tv_type);
        String imageUrl = newsLists.get(position).getImage();
        iv_icon.setImageUrl(imageUrl, R.drawable.bg);
        tv_title.setText(newsLists.get(position).getTitle());
        tv_desc.setText(newsLists.get(position).getDescription());
        String typee = newsLists.get(position).getType();
        String comment = newsLists.get(position).getComment();
        int type = Integer.parseInt(typee);
        switch (type) {
        case 1:
            tv_type.setText(comment + "国内");
            break;
        case 2:
            tv_type.setText("跟帖");
            break;
        case 3:
            tv_type.setText("国外");
            break;
        default:
            break;
        }
        return view;
    }
}

运行效果:
这里写图片描述

11. SmartImageView的使用


(1) 把com包,源码包拷贝到当前工程;
这里写图片描述
(2) 在布局里面定义,使用类的完整包名+类名。

<com.loopj.android.image.SmartImageView
     android:id="@+id/iv_icon"
     android:layout_width="70dp"
     android:layout_height="70dp"
     android:src="@drawable/ic_launcher" />

12. SmartImageView的原理

我们自己写一个类似SmartImageView的控件来实现快速加载图片,该类需要继承ImageView,原理就是在子线程当中获取图片数据转换成bitmap,利用handler显示数据。

public class MySmartImageView extends ImageView {
private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:  
                Bitmap bitmap = (Bitmap) msg.obj;
                MySmartImageView.this.setImageBitmap(bitmap);
                break;
            case 2: 
                int resource =  (Integer) msg.obj;
                MySmartImageView.this.setBackgroundResource(resource);
                break;
            case 3: 
                int resource1 =  (Integer) msg.obj;
                MySmartImageView.this.setBackgroundResource(resource1);
                break;
            default:
                break;
            }
        };
    };
    public MySmartImageView(Context context, AttributeSet attrs, 
     int defStyle) {
        super(context, attrs, defStyle);
    }
    public MySmartImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MySmartImageView(Context context) {
        super(context);
    }
    public void setImageUrl(final String path) {
        new Thread() {
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        Message msg = Message.obtain();
                        msg.obj = bitmap;
                        handler.sendMessage(msg);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
    public void setImageUrl(final String path, final int resource) {
        new Thread() {
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        Message msg = Message.obtain();
                        msg.what = 1;
                        msg.obj = bitmap;
                        handler.sendMessage(msg); 
                    } else {
                        Message msg = Message.obtain();
                        msg.what = 2;
                        msg.obj = resource;
                        handler.sendMessage(msg); 
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Message msg = Message.obtain();
                    msg.what = 3;
                    msg.obj = resource;
                    handler.sendMessage(msg); 
                }
            };
        }.start();
    }
}

(a)上面的handler,用来更新界面。由于获取网络数据是在子线程当中执行的,所以我们需要通过handler来实现在主线程当中更新UI。通过message携带参数,将bitmap对象传递给ImageView进行显示
(b)setImageUrl(final String path)方法,开启一个子线程网络获取图片信息,然后将流信息转换成bitmap对象,最后通过handler传递给ImageView显示
(c)setImageUrl(final String path, final int resource)方法,该方法多了一个参数resource,用来当请求失败或者发生异常时显示的图片。

13. Get和Post登录方式


这里写图片描述
Get请求和Post请求的区别:
(1)请求路径不同;
(2)Post请求比get请求多了content-type和content-length两个请求头;
(3)Post请求是以流的形式把数据写给服务器;
(4)Get请求数据大小有限制,在IE中是1k,在其他浏览器中是4k。
Get请求登录代码:

public void click1(View v) {
        final String pwd = et_password.getText().toString().trim();
        final String name = et_username.getText().toString().trim(); 
        new Thread() {
            public void run() {
                try {
                    /将用户名和密码拼接成Url访问路径/
                    String path = "http://192.168.19.89:8080/web/LoginServlet?username=" + name + "&password=" + pwd ;
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET"); 
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) { 
                        InputStream inputStream = conn.getInputStream();
                        String content = StreamUtils.readStream(inputStream);
                        showToast(content);

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
public void showToast(final String content) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), content, 1).show();
        }
    });
}

Post请求登录代码:

public void click2(View v) {
    final String pwd = et_password.getText().toString().trim();
    final String name = et_username.getText().toString().trim();        
    new Thread() {
        public void run() {
            try {
                //拼接用户名和密码,作为参数体传递给服务器
                String path = "http://192.168.19.89:8080/web/LoginServlet";
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    //设置请求方式为POST
                    conn.setRequestMethod("POST");
                    conn.setConnectTimeout(5000);
                    String data = "username=" + name + "&password=" + pwd;
                    //设置Content-type头信息
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    //Content-Length头信息,第二个参数是数据的字节数
                    conn.setRequestProperty("Content-Length", data.length() + "");
                    //setDoOutput(true)该方法设置一个标记,允许向服务器输出数据
                    conn.setDoOutput(true); 
                    //调用getOutputStream()方法获取输出流,通过输出流向服务器写入数据
                    conn.getOutputStream().write(data.getBytes());
                    int code = conn.getResponseCode();
                    if (code == 200) { 
                        InputStream inputStream = conn.getInputStream();
                        String content = StreamUtils.readStream(inputStream);
                        showToast(content);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值