从头学android_网络图片查看器

需求:

一个文本框,一个按钮,在文本框中输入一个图片的url,点击按钮,在按钮下方显示图像内容。

layout:

manifest:

申请使用网络的权限

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

activity

        String path = editText.getText().toString();
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setReadTimeout(8000);
            conn.setConnectTimeout(8000);
            if (conn.getResponseCode() == 200){
                InputStream is = conn.getInputStream();
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                imageView.setImageBitmap(bitmap);
            }else{
                Toast.makeText(MainActivity.this, "url错误", Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

结果

API14+

但是在API 14 后的android系统中,主线程不允许访问网络,以预防网络耗时所造成的阻塞报错是

android.os.NetworkOnMainThreadException。

那么就用子线程来访问网络吧

        editText = (EditText) findViewById(R.id.et_url);
        imageView = (ImageView) findViewById(R.id.imageView);
        final String path = editText.getText().toString();
        Thread thread = new Thread(){
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setReadTimeout(8000);
                    conn.setConnectTimeout(8000);
                    if (conn.getResponseCode() == 200){
                        InputStream is = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(is);
                        imageView.setImageBitmap(bitmap);
                    }else{
                        Toast.makeText(MainActivity.this, "url错误", Toast.LENGTH_SHORT).show();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();

结果还是不行,报错Only the original thread that created a view hierarchy can touch its views

大致意思应该就是说这个view不是你这个线程创建的,你不能更改它,那么刷新UI就只能在主线程中执行了,但是网络又不能在主线程,如果两个线程直接并行的话,无法保证给ImageView设置Bitmap时,已经拿到了服务器发送的Bitmap,这怎么办呢?

android提供了messageQueue,looper 机制来解决线程间的通信问题,消息队列中存放着消息,一旦有新的消息,轮巡器就会发现,而默认情况下,轮巡器只是发现有新消息,并不做任何事情的,如果我们希望子线程已经得到了Bitmap,发送个主线程一个消息,并附带Bitmap对象,那么就要在主线程中添加一个Handler成员,并且重写它的HandleMessage方法,在这个方法中判断消息是否是子线程发过来的得到Bitmap的消息。当子线程拿到了Bitmap时,用Handler对象发送一个消息,打上标志,附加上Bitmap对象,发送出去,那么消息队列中就会有一个新消息,轮巡器就会发现它,并调用handler的handleMessage方法执行之。真是完美,不得不膜拜谷歌大神的智慧啊!

activity(API14+)

    private final static int GOTBITMAPSUCCESS = 0;
    private final static int WRONGURL = 1;
    private EditText editText;
    private ImageView imageView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case GOTBITMAPSUCCESS:
                    Bitmap bm = (Bitmap) msg.obj;
                    imageView.setImageBitmap(bm);
                    break;
                case WRONGURL:
                    Toast.makeText(MainActivity.this, "url错误", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = (EditText) findViewById(R.id.et_url);
        imageView = (ImageView) findViewById(R.id.imageView);
    }

    public void showImage(View v){
        final String path = editText.getText().toString();
        Thread thread = new Thread(){
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setReadTimeout(8000);
                    conn.setConnectTimeout(8000);
                    if (conn.getResponseCode() == 200){
                        InputStream is = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(is);

                        Message msg = new Message();
                        msg.what = GOTBITMAPSUCCESS;
                        msg.obj = bitmap;
                        handler.sendMessage(msg);
                    }else{
                        handler.sendEmptyMessage(WRONGURL);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
    }

总结:

1.耗时操作不能放在主线程中,网络是谷歌强制要求剥离主线程的,其他的耗时操作,如读取数据等,应该自觉遵守规范,写在其他线程中去。

2.子线程不能刷新UI,刷新UI必须要用主线程。

3.线程间的通讯可以使用MessageQueue-looper-Handler机制来实现。其中message可以携带对象传送数据。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值