乐趣区

关于harmonyos:线程间通信使用EventHandler下载网络图片

作者:韩茹

公司:程序咖(北京)科技有限公司

程序咖:IT 职业技能评测平台

网址:https://www.chengxuka.com

后面咱们曾经搞明确了鸿蒙利用开发过程中的线程之间是如何通信的。就是利用 EventHandlerInnerEventEventRunner 等。

咱们想实现的内容是,点击按钮进行下载网络上的图片,图片显示前先显示进度条。当图片下载结束后,进度条隐没,显示图片。

先来看一下成果:

<img src=”https://img.chengxuka.com/downloadimageyunxing1.gif” alt=”downloadimageyunxing1″ style=”zoom:50%;” />

因为是在 EventHandler 这个 Demo 上顺手写的,我没有从新创立工程。

一、XML 布局

在 layout 目录下新建 xml 布局文件,demo4_eventhandler_image.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:padding="20vp"

    ohos:orientation="vertical">

    <Button
        ohos:id="$+id:btn1"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text="下载图片"
        ohos:background_element="#eeeeee"
        ohos:padding="20vp"
        ohos:text_size="20fp"
        />

    <RoundProgressBar
        ohos:id="$+id:round_progress_bar1"
        ohos:height="200vp"
        ohos:width="200vp"
        ohos:progress_width="10vp"
        ohos:top_margin="150vp"
        ohos:progress_hint_text="美女加载中"
        ohos:progress_hint_text_color="#C71585"
        ohos:layout_alignment="center"
        ohos:progress_color="#C71585"/>

    <Image
        ohos:id="$+id:image1"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:top_margin="30vp"
        ohos:layout_alignment="center"
        />

</DirectionalLayout>

这里咱们先放好一个进度条 RoundProgressBar,然而不让它显示。

二、AbilitySlice 中的 Java 代码

咱们在 slice 目录下新建一个 AbilitySlice 文件,FourAbilitySlice.java:

1、首先设置一下该 AbilitySlice 所对应的 xml 布局文件:

    @Override
    protected void onStart(Intent intent) {super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_demo4_eventhandler_image);
        }

2、首先应该先获取 xml 布局中的 UI 控件。定义一个初始化 UI 控件的办法,并在 onStart()中进行调用。

public class FourAbilitySlice extends AbilitySlice{
        private Image image;
    private RoundProgressBar roundProgressBar;
    private Button btn;
  
      protected void onStart(Intent intent) {
                ...
        initComponent();}
  
  // 获取 UI 组件
    private void initComponent(){btn= (Button) findComponentById(ResourceTable.Id_btn1);
        image = (Image) findComponentById(ResourceTable.Id_image1);
        roundProgressBar = (RoundProgressBar)findComponentById(ResourceTable.Id_round_progress_bar1);

    }
}

3、定义一个外部类 DownLoadImageEventHandler 用于下载图片。


    // step1:创立自定义的 EventHandler 子类
    //1. 创立自定义的 EventHandler 子类
    private class DownLoadImageEventHandler extends EventHandler{

        //2. 增加构造方法
        public DownLoadImageEventHandler(EventRunner runner)  {super(runner);
        }

        //3. 重写 processEvent()办法
        @Override
        protected void processEvent(InnerEvent event) {super.processEvent(event);


            }
        }
    }

4、创立一个办法,用于初始化 EventRunner 和 EventHandler 对象,并在 onStart()中进行调用:

 //step2:示例化 EventHandler 和 EventRunner 对象,并在 onStart()办法中调用该初始化办法。private void initHandler() {eventRunner = EventRunner.create("TestRunner");
        handler = new DownLoadImageEventHandler(eventRunner);
    }

onStart()办法:

protected void onStart(Intent intent) {
                ...
        initComponent();
        initHandler();}

5、给按钮增加点击事件,发送 InnerEvent 到子线程:

// 先定义全局变量
private static final int EVENT_MESSAGE_NORMAL = 1;

onStart()中为按钮增加点击事件:

                // 点击按钮,下载图片
        btn.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                // step3:发送事件到子线程
                long param = 0;
                InnerEvent innerEvent = InnerEvent.get(EVENT_MESSAGE_NORMAL, param, EventRunner.current());
                handler.sendEvent(innerEvent, EventHandler.Priority.IMMEDIATE);
                System.out.println("UI 线程 -->InnerEvent 已发送。。");
                roundProgressBar.setVisibility(Component.VISIBLE);

            }
        });

6、接下来咱们就该去进行网络操作了。重写 DownLoadImageEventHandler 中的 processEvent()办法。

咱们先捋顺一下思路:1,应该先设置一个图片地址
2,创立 URL 对象
3,通过 HttpURLConnection 链接下载图片
4,因为咱们须要边下载边显示进度条,所以应该先获取图片的总长度
5,下载图片的同时,计算曾经下载的数据量,除以总长度,就是进度条的百分比,咱们须要将这个数据回传给 UI 线程,进行设置进度条。当然这一步也须要应用 EventHandler 对象。6,当图片下载结束后,咱们应该将下载到的字节数组的数据,编码成一个图片对象 PixelMap。7,将图片对象回传给 UI 线程,显示图片,并且让进度条隐没。大略,就这么个思路

这里还要强调一下,因为目前的网络再怎么烂,下载一张图片也是嗖嗖的,所以为了可能让进度条显示一会儿,我边下载边睡眠。😂

示例代码:

 //3. 重写 processEvent()办法
        @Override
        protected void processEvent(InnerEvent event) {super.processEvent(event);
            if (event == null) {return;}
            switch (event.eventId){
                // step4:子线程中解决网络下载
                case EVENT_MESSAGE_NORMAL:
                    // 下载网络图片
                    try {System.out.println("---- 开始网络下载 ---");
                        URL url = new URL(IMAGE_PATH);
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        if(connection.getResponseCode()==HttpURLConnection.HTTP_OK){InputStream inputStream = connection.getInputStream();
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            // 获取总长度
                            long total = connection.getContentLengthLong();
                            System.out.println("total---->"+total);
                            byte[] bs = new byte[1024];
                            int len =0;
                            long sum = 0;

                            // 将图片回传到 UI 线程,设置在 Image 上
                            EventRunner runner = (EventRunner) event.object;
                            while((len=inputStream.read(bs))!=-1){
                                try {Thread.sleep(50);
                                } catch (InterruptedException e) {e.printStackTrace();
                                }
                                baos.write(bs, 0, len);
                                sum += len;
                                long percent = sum*100 / total;
                                System.out.println("percent-->"+percent);
                                // 回传 UI 线程
                                EventHandler eventHandler = new EventHandler(runner) {
                                    @Override
                                    protected void processEvent(InnerEvent event) {int data = (int)event.param;
                                        roundProgressBar.setProgressValue(data);

                                    }
                                };
                                int testEventId = 1;
                                long testParam = percent;
                                Object testObject = null;
                                InnerEvent innerEvent = InnerEvent.get(testEventId, testParam, testObject);
                                eventHandler.sendEvent(innerEvent);



                            }

                            byte[] data = baos.toByteArray();
                            System.out.println("图片数据。。"+data.length);

                            // 解决图片数据
                            ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
                            srcOpts.formatHint = "image/png";
                            ImageSource imageSource = ImageSource.create(data,srcOpts);
                            ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
                            PixelMap pixelMap = imageSource.createPixelmap(decodingOpts);

                            EventHandler eventHandler = new EventHandler(runner) {
                                @Override
                                protected void processEvent(InnerEvent event) {
                                    // 循环下载数据完结后:暗藏掉进度条
                                    roundProgressBar.setVisibility(Component.HIDE);
                                    PixelMap pixelMap = (PixelMap) event.object;
                                    System.out.println("--->>>>>>>test:"+data);
                                    image.setPixelMap(pixelMap);

                                }
                            };
                            int testEventId = 1;
                            long testParam = 0;
                            Object testObject = pixelMap;
                            InnerEvent innerEvent = InnerEvent.get(testEventId, testParam, testObject);
                            eventHandler.sendEvent(innerEvent);
                            System.out.println("---------");

                        }
                    } catch (MalformedURLException e) {e.printStackTrace();
                    } catch (IOException e) {e.printStackTrace();
                    }
                    break;
            }
        }

三、设置网络拜访权限

因为咱们有拜访网络,所以要在配置文件中增加拜访网络的权限。关上 config.json。

{
  "app": {
    "bundleName": "com.example.hanrueventhandler",
    "vendor": "example",
    "version": {
      "code": 1000000,
      "name": "1.0.0"
    },
    "apiVersion": {
      "compatible": 4,
      "target": 5,
      "releaseType": "Release"
    }
  },
  "deviceConfig": {},
  "module": {
    "package": "com.example.hanrueventhandler",
    "name": ".MyApplication",
    "deviceType": ["phone"],
    "distro": {
      "deliveryWithInstall": true,
      "moduleName": "entry",
      "moduleType": "entry"
    },
    "abilities": [
      {
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ],
        "orientation": "unspecified",
        "name": "com.example.hanrueventhandler.MainAbility",
        "icon": "$media:icon",
        "description": "$string:mainability_description",
        "label": "$string:app_name",
        "type": "page",
        "launchType": "standard"
      }
    ],
    "reqPermissions": [
      {"name": "ohos.permission.GET_NETWORK_INFO"},
      {"name": "ohos.permission.INTERNET"},
      {"name": "ohos.permission.SET_NETWORK_INFO"}

    ]
  }
}

最初几行就是网络的权限了。

最初批改一下程序的入口:

public class MainAbility extends Ability {
    @Override
    public void onStart(Intent intent) {super.onStart(intent);
        super.setMainRoute(FourAbilitySlice.class.getName());// 要显示的哪个 AbilitySlice
    }
}
退出移动版