安卓基础第四天(Android网络编程与消息处理机制,SmartImageView)

Android网络编程

网络编程预备知识

  • 访问网络的Android 应用都必须加上访问互联网的权限 :
    android.permission.INTERNET
  • 开启子线程执行网络或者耗时的操作
    1.凡是对UI 的更新,“耗时”操作等都需要在子线程中进行
    android4.0以上强制要求,访问网络的操作不允许在主线程中执行只能在子线程中进行
    主线程请求网络会报如下错误:
    android.os.NetworkOnMainThreadException
    2.ANR异常:
    Application Not Response,应用程序无响应。在主线程中做一些耗时的操作,阻塞了主线程,当用户点击其时,主线程无法响应,这是就会出ANR 异常
  • 子线程不能修改UI
    主线程也叫UI 线程,别的线程修改UI会报如下错误:
    CalledFromWrongThreadException: Only the original thread that created a viewhierarchy can touch its views.
    解决方式:
    1.使用使用Handler 实现子线程与主线程之间的通信
    消息处理机制原理:
    所有使用UI 界面的操作系统,后台都运行着一个死循环(Looper),在不停的监听和接收用户发出的指令,一旦接收指令就立即执行。
    2.使用activity的runOnUiThread方法更新ui,无论当前线程是否是主线程,都将在主线程执行

  • 模拟器访问本地tomcat
    localhost/127.0.0.1在模拟器上都是访问模拟器自己,
    再模拟器上访问本地电脑需使用android内置IP:0.0.0.2,10.0.0.2

案例 html源码查看器

public class MainActivity extends Activity {
    // 定义两个常量用于代表消息的类型
    private static final int ERROR = 0;
    private static final int OK = 1;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 获取界面的TextView 对象实例
            TextView tv = (TextView) findViewById(R.id.tv);
            // 如果接收到的信息为OK,则将结果显示出来
            if (msg.what == OK) {
                tv.setText((String) msg.obj);
                // 如果ERROR 则提示错误信息
            } else if (msg.what == ERROR) {
                tv.setText("对不起,页面加载失败!");
            }
        }
    };

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

    // 界面Button 绑定的事件
    public void load(View view) {
        EditText editText = (EditText) findViewById(R.id.et);
        if (TextUtils.isEmpty(editText.getText().toString())) {
            Toast.makeText(this, "请输入网页的URL", Toast.LENGTH_SHORT).show();

            return;
        }
        // 调用本类自定义方法,实现网页抓取
        getContent(editText.getText().toString());
    }

    private void getContent(final String path) {
        final StringBuilder sb = new StringBuilder();
        new Thread(new Runnable() {
            @Override
            public void run() {
                URL url;
                try {
                    // 根据url 地址创建一个URL 对象
                    url = new URL(path);
                    // 创建一个HttpURLConnection 对象
                    HttpURLConnection connection = (HttpURLConnection) url
                            .openConnection();
                    // 设置连接超时为5 秒
                    connection.setConnectTimeout(5000);
                    // 设置读取数据流超时为5 秒
                    connection.setReadTimeout(5000);
                    // 设置请求方式为GET
                    connection.setRequestMethod("GET");
                    // 开始连接
                    connection.connect();
                    // 获取返回状态码
                    int responseCode = connection.getResponseCode();
                    if (responseCode == 200) {// 如果成功
                    // 获取字节流
                        InputStream inputStream = connection.getInputStream();
                        // 将字节流转化为BufferedReader

                        BufferedReader bufferedReader = new BufferedReader(
                                new InputStreamReader(inputStream));
                        String tmp = null;
                        while ((tmp = bufferedReader.readLine()) != null) {
                            sb.append(tmp);
                        }
                        // 新创建个消息对象
                        Message msg = new Message();
                        // 设置消息的类型为OK(自定义,用于自己区分不同的消息)
                        msg.what = OK;
                        // 给消息绑定数据
                        msg.obj = sb.toString();
                        // 将消息发给主线程
                        handler.sendMessage(msg);
                    } else {// 如果请求失败
                    // 发送一个空消息
                        handler.sendEmptyMessage(ERROR);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    handler.sendEmptyMessage(ERROR);
                }
            }
        }).start();
    }
}

案例 网络图片查看器

public class MainActivity extends Activity {
    private ImageView imageView;
    // 创建一个Handler 对象,用户接收子线程发送的消息,然后更新UI
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 将数据强转转化为Bitmap,然后显示在ImageView 控件中
            imageView.setImageBitmap((Bitmap) msg.obj);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.iv);
    }

    public void getImage(View view) {
        // 开启一个子线程处理网络请求
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 从页面获取URL 地址
                    EditText editText = (EditText) findViewById(R.id.et);
                    String path = editText.getText().toString();
                    // 调用Android API 中的TextUtils 工具类判断路径是否为空
                    if (TextUtils.isEmpty(path)) {
                        Looper.prepare();

                        Toast.makeText(MainActivity.this, "请输入URL", 0).show();
                        Looper.loop();
                        return;
                    }
                    URL url = new URL(path);
                    // 获取HttpURLConnection 链接对象
                    HttpURLConnection connection = (HttpURLConnection) url
                            .openConnection();
                    // 设置请求方法为GET 方式
                    connection.setRequestMethod("GET");
                    // 设置链接超时时间
                    connection.setConnectTimeout(50000);
                    // 设置输入流读取超时时间
                    connection.setReadTimeout(50000);
                    // 打开链接,发送请求
                    connection.connect();
                    // 判断返回的状态码
                    if (connection.getResponseCode() == 200) {
                        // 获取输入流对象
                        InputStream inputStream = connection.getInputStream();
                        // 调用Android API 提供的BitmapFactory 工具类将字节流转化为位图
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        // 创建一个新的消息
                        Message message = new Message();
                        // 将数据绑定消息
                        message.obj = bitmap;

                        // 调用handler 发送消息给主线程
                        handler.sendMessage(message);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

案例 新闻页

  • 开源第三方图片控件SmartImageView
    开源框架SmartImageView 控件用来显示图片。在布局文件声明此控件时需写全包路径,否则将无法找到。
<com.loopj.android.image.SmartImageView
android:id="@+id/iv"
android:layout_height="match_parent"
android:layout_width="60dp"
android:contentDescription="news"
/>

代码

public class MainActivity extends Activity {
    // 设置新闻访问地址
    private String path = "http://172.16.0.67:8080/news/news.xml";
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final List<News> list = (List<News>) msg.obj;
            // 实例化ListView 对象
            ListView listView = (ListView) findViewById(R.id.lv);
            // 给ListView 设置适配器
            listView.setAdapter(new BaseAdapter() {
                @Override
                public View getView(int position, View convertView,
                        ViewGroup parent) {
                    if (convertView == null) {
                        convertView = (RelativeLayout) View.inflate(
                                MainActivity.this, R.layout.item, null);
                    } else {
                        return convertView;
                    }
                    News news = list.get(position);
                    // 实例化SmartImageView 对象
                    SmartImageView imageView = (SmartImageView) convertView
                            .findViewById(R.id.iv);
                    // 给SmartImageView 设置一个图像的URL 则SmartImageView 会自动加载图片
                    imageView.setImageUrl(news.getImage());

                    TextView connenTextView = (TextView) convertView
                            .findViewById(R.id.tv_commen);
                    connenTextView.setText("评论:" + news.getComment());
                    TextView contentTextView = (TextView) convertView
                            .findViewById(R.id.tv_content);
                    contentTextView.setText(news.getDetail());
                    TextView titleTextView = (TextView) convertView
                            .findViewById(R.id.tv_title);
                    titleTextView.setText(news.getTitle());
                    return convertView;
                }

                @Override
                public long getItemId(int position) {
                    return position;
                }

                @Override
                public Object getItem(int position) {
                    return null;
                }

                @Override
                public int getCount() {
                    return list.size();
                }
            });
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 调用本类私有方法完成网络访问
        getString();
    }

    // 开启子线程下载数据
    private String getString() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                XmlPullParserFactory parserFactory;
                // 创建一个集合,用于存储新闻
                List<News> list = new ArrayList<News>();
                try {
                    // 获取XML 解析器工厂
                    parserFactory = XmlPullParserFactory.newInstance();
                    // 获取解析器
                    XmlPullParser parser = parserFactory.newPullParser();
                    URL url = new URL(path);
                    // 打开一个链接
                    HttpURLConnection connection = (HttpURLConnection) url
                            .openConnection();
                    // 设置连接超时时间
                    connection.setConnectTimeout(5000);
                    connection.setReadTimeout(5000);
                    connection.setRequestMethod("GET");
                    // 开始链接
                    connection.connect();
                    // 获取数据流
                    InputStream inputStream = connection.getInputStream();
                    // 给解析器设置流对象
                    parser.setInput(new InputStreamReader(inputStream));
                    News news = new News();
                    // 解析XML 数据
                    while (parser.next() != XmlPullParser.END_DOCUMENT) {
                        int eventType = parser.getEventType();
                        switch (eventType) {
                        case XmlPullParser.START_TAG:
                            String tagName = parser.getName();
                            if (tagName.equals("newslist")) {
                                continue;

                            } else if (tagName.equals("news")) {
                                news = new News();
                                System.out.println("创建新的新闻对象");
                            } else if (tagName.equals("title")) {
                                String title = parser.nextText();
                                news.setTitle(title);
                            } else if (tagName.equals("detail")) {
                                String detail = parser.nextText();
                                news.setDetail(detail);
                            } else if (tagName.equals("comment")) {
                                String comment = parser.nextText();
                                news.setComment(comment);
                            } else if (tagName.equals("image")) {
                                String image = parser.nextText();
                                news.setImage(image);
                            } else {
                                continue;
                            }
                            break;
                        case XmlPullParser.END_TAG:
                            String endTag = parser.getName();
                            if (endTag.equals("news")) {
                                // 将解析出来的数据添加到集合中
                                list.add(news);
                            }
                            break;
                        default:
                            continue;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Looper.prepare();
                    Toast.makeText(MainActivity.this, "请求网络失败" + e, 0).show();
                    Looper.loop();
                }
                // 如果当前消息池中有则返回一个,没有则创建

                Message msg = Message.obtain();
                // 给当前消息绑定数据
                msg.obj = list;
                handler.sendMessage(msg);
            }
        }).start();
        return null;
    }
}

Android消息处理机制

Looper、Message、Handler 的关系

当我们的Android 应用程序的进程一创建的时候,系统就给这个进程提供了一个Looper,Looper 是一个死循环,它内部维护这个一个消息队列。Looper 不停地从消息队列中取消息(Message),取到消息就发送给了Handler,最后Handler 根据接收到的消息去修改UI。Handler 的sendMessage 方法就是将消息添加到消息队列中
这里写图片描述

runOnUiThread

Activity 中提供了一个runOnUiThread 方法,用于进行消息处理。此方法是通过线程合并——join 来实现消息处理的
线程合并:主线程将子线程的任务拿到自己这里来执行并终止子线程

        /**
         * 在UI 线程中运行我们的任务,如果当前线程是UI 线 程,则立即执行,如果不是则该任务发送到UI 线程的事件队列。
         */
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 自定义我们的业务代码
            }
        });
        return null;

postDelayed

该方法是Handler 对象提供的,Handler 给消息队列发送一个消息,发送成功则返回true,否则返回false,如果返回false 一般是由于looper 进程不存在导致的。
该方法主要用于定时任务。如果返回true 也不一定代表着我们的定时任务就执行了,因为很可能在定时任务的时间未到之前我们的looper 进程退出了,那么消息也就丢失了。
执行该任务的线程用的就是Handler 对象所在的线程。

        /**
         * 该方法将一个Runnable 对象r 添加到消息队列,在指定的时间后会被执行。 这个Runnable 对象会运行在当前handler
         * 所在的线程中。 第一个参数:Runnable 要执行的任务 第二个参数:delayMillis(单位:毫秒) runnable
         * 任务被执行前的延迟时间 返回值:boolean ,如果该Runnable 被成功添加到消息队列则返回true,否 则返回false
         * 不过,通常返回false 是因为looper 进程处理消息队列退出。 注意:返回true 不代表着Runnable
         * 被执行,如果looper 在延时任务还没被执 行前退出了,那么消息就丢失掉了。
         */
        boolean flag = handler.postDelayed(new Runnable() {
            @Override
            public void run() {
            }
        }, 2000);

postAtTime

该方法也属于Handler 对象,
唯一不同的是该方法设置的定时任务是一个绝对时间,
指的是Android 系统的开机时间,
如果想设置从当前时间算起2 秒后执行该任务则可以将时间这样写:SystemClock.uptimeMillis()+2000,其SystemClock.uptimeMillis()是系统运行时间。

        boolean postAtTime = handler.postAtTime(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
            }
        }, SystemClock.uptimeMillis() + 2000);

猜你喜欢

转载自blog.csdn.net/opopopwqwqwq/article/details/79337627