网络库和图片库的简单架构模型

这里我们就简单分析一下demo中网络课和图片库的架构模型

网络库基本模型

架构设计过程分析:

对于一个框架的设计, 使用面向接口编程是必不可少的技能,

http/https是基于请求-响应模型, 我们需要抽象出一个请求和响应接口类,

抽象一个请求接口, 其实现可以是基于HttpURLConnection、Socket等, 抽象接口是为了约束使用者构造请求时提供请求所需要的参数, 如下接口满足一个网络请求的条件: url、请求参数、请求类型、执行请求 。

public interface IHttpRequest {

  void setUrl(String url);

  void setMethod(@HttpMethodType String method);

  void setRequestData(byte[] requestData);

  void execute();

  // 设置两个接口之间的关系, 由execute()方法可知,实现类会执行真正的网络请求, 请求的响应接口只能由自己维护
  void setHttpCallback(IHttpCallback httpCallback);
}
抽象响应接口,考虑到框架的扩展性, 返回到应用层的数据可能是Json、String或者范型对象, 这里传递了网络请求最原始的流数据, 
public interface IHttpCallback {

  // 接受上一个接口的结果
  void onSuccess(InputStream inputStream);

  void onFailure(String error);
}

请求管理类的实现, 具体代码看注释

public class RequestManager {

  private LinkedBlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();

  private ThreadPoolExecutor threadPoolExecutor;

  // 3.将请求任务添加到请求队列
  public void execute(Runnable runnable) {
    // TODO queue.put(runnable);
  }

  // 1.构建消费者任务
  private Runnable runnable = new Runnable() {
    @Override
    public void run() {
      while(true) {
        Runnable runnable = null;
        // TODO 从阻塞队列中取出请求任务
        // runnable = queue.take();
        // 2.请求任务放入到线程池执行
        // threadPoolExecutor.execute(runnable);
      }
    }
  };

  private ThreadPoolManage() {
    threadPoolExecutor = new ThreadPoolExecutor(4,
        20, 15, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), rejectedExecutionHandler);
    // 让消费者线程放入到线程池执行
    threadPoolExecutor.execute(runnable);
  }

  ......
}

现在该生产者出场了, 我们这里仿照Volley的实现, 通过中间层Volley类来进行面向接口的Json数据格式的请求,

public class Volley {

  public static <T, M>void sendJSONRequest(@HttpMethodType String method, T requestInfo, String url, Class<M> responseType, IDataCallback<M> dataCallback) {
    IHttpRequest httpRequest = new JsonRequest();
    IHttpCallback httpCallback = new JsonHttpCallback(responseType, dataCallback);

    HttpTask<T> httpTask = new HttpTask<T>(method, requestInfo, url, httpRequest, httpCallback);
    RequestManager.getInstance().execute(httpTask);
  }
}

对Volley类的分析:

1. 这里设计IDataCallback<M>的原因是, IHttpRequest的实现类完成网络请求后将原始流数据回调给IHttpCallback的实现类JsonHttpCallback, 在JsonHttpCallback类中将流数据转变成范型的对象后, 网络请求结果数据传递中断, 故需要调用处提供IDataCallback实现来处理后续的网络请求数据, 当时思考如果网络请求回调使用一个callback那么出现在调用处的将是数据流对象, 对调用者不够友好且增加了解析的工作量;

2. 这里构造了HttpTask对象, 并将其添加到RequestManager的请求队列中, 那么很明显HttpTask需要继承Runnable接口, 当HttpTask任务被线程池中的线程执行时就会调用其run()方法, 我们就在方法体内执行httpRequest.execute()来进行真正的网络请求执行, 那么HttpTask类中就需要持有httpRequest的引用。

public interface IDataCallback<M> {

  void onSuccess(M m);

  void onFailure(String error);
}
public class JsonHttpCallback<M> implements IHttpCallback {

  Class<M> responseClass;
  IDataCallback<M> dataCallback;
  // 用于切换线程
  Handler handler = new Handler(Looper.getMainLooper());

  @Override
  public void onSuccess(InputStream inputStream) {
    String content = getContent(inputStream);
    final M response = new Gson().fromJson(content, responseClass);
    handler.post(new Runnable() {
      @Override
      public void run() {
        if (dataCallback != null) {
          dataCallback.onSuccess(response);
        }
      }
    });
  }

  private String getContent(InputStream inputStream) {
    String content = null;
    try {
      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
      StringBuilder sb = new StringBuilder();
      String line;
      try {
        while((line = reader.readLine()) != null) {
          sb.append(line + "\n");
        }
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        // TODO 关闭流
      }
      return sb.toString();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return content;
  }
}
public class JsonHttpRequest implements IHttpRequest{

  String url;
  private byte[] requestData;
  IHttpCallback httpCallback;
  String method;

  @Override
  public void setRequestData(byte[] requestData) {
    this.requestData = requestData;
  }

  @Override
  public void execute() {
    if (TextUtils.equals(method, HttpMethodType.GET)) {
      connectionGet();
    } else if (TextUtils.equals(method, HttpMethodType.POST)){
      connectionPost();
    }
  }

  private void connectionPost() {
    HttpURLConnection connection = null;
    URL url;
    try {
      url = new URL(this.url);
      connection = (HttpURLConnection) url.openConnection();
      connection.setConnectTimeout(6000);
      connection.setUseCaches(false);
      connection.setInstanceFollowRedirects(true); // 仅作用于当前函数
      connection.setReadTimeout(3000);
      connection.setDoInput(true); // 设置连接可写入数据
      connection.setDoOutput(true); // 设置连接可以输出数据
      connection.setRequestMethod("POST");
      connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
      connection.connect();

      // ----------使用字节流发送数据------------
      OutputStream out = connection.getOutputStream();
      BufferedOutputStream bos = new BufferedOutputStream(out);
      if (requestData != null) {
        bos.write(requestData);
      }

      // 把字节数组的数据写入缓冲区
      bos.flush();  // 刷新缓冲区,发送数据
      out.close();
      bos.close();

      // --------字节流写入数据---------
      if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
        InputStream in = connection.getInputStream();
        httpCallback.onSuccess(in);
      } else {
        httpCallback.onFailure(String.valueOf(connection.getResponseCode()) + ":" + connection.getResponseMessage());
      }
    } catch (Exception e) {
      e.printStackTrace();
      httpCallback.onFailure(e.getMessage());
    } finally {
      connection.disconnect(); // 使用完关闭TCP连接,释放资源
    }
  }

  private void connectionGet() {
    // TODO 
  }

}

网络请求库的架构设计

这里的架构设计和网络请求库的架构设计大同小异, 也是一种典型的生产者-消费者模式, 类比较简单就直接贴代码了, 具体分析会在代码中

因为要构造链式调用, 所以请求类不在使用接口编程, 

public class ImageRequest {

  private String url;
  private Context context;
  private SoftReference<ImageView> imageViewRef;
  private int placeHoldId;
  private ImageCallback requestListener;
  private String urlMd5;

  public static ImageRequest with(Context context) {
    ImageRequest request = new ImageRequest();
    request.context = context;
    return request;
  }

  public ImageRequest load(String url) {
    this.url = url;
    // TODO md5加密
    this.urlMd5 = url;
    return this;
  }

  public void into(ImageView imageView) {
    imageView.setTag(urlMd5);
    this.imageViewRef = new SoftReference<>(imageView);
    ImageManager.getInstance().addBitmapRequest(this);
  }
}

在每次调用into的时候就会将构造的请求添加到ImageManager类中,

class ImageManager {


  // TODO 疑问: 1. 添加的请求存在队列里(队列里的请求一般是Runnable),如何调度, 管理者内部维护着一个阻塞式的死循环轮询队列;
  // TODO 疑问: 2. 如何关联ImageDispatcher;
  // TODO 疑问: 3. ImageDispatcher的复用. 如果用线程池, 那么线程池怎么调度ImageDispatcher

  // TODO 思考: 1. 线程池是复用内部的线程来execute从请求队列中取出的Runnable任务
  // TODO 思考: 2. 目前把图片请求的实现放在了Thread的子类执行,不满足threadPoolExecutor.execute()的入参

  private static volatile ImageManager instance;

  // 同步问题
  private LinkedBlockingDeque<ImageRequest> mDeque = new LinkedBlockingDeque<>();

  private ImageDispatcher[] bitmapDispatchers;

  public static ImageManager getInstance() {
    // TODO
  }

  private void start() {
    stop();
    startAllDispatchers();
  }

  private void stop() {
    if (bitmapDispatchers != null && bitmapDispatchers.length > 0) {
      for (ImageDispatcher bitmapDispatcher : bitmapDispatchers) {
        if (!bitmapDispatcher.isInterrupted()) {
          bitmapDispatcher.interrupt();
        }
      }
    }
  }

  private void startAllDispatchers() {

    int threadCount = Runtime.getRuntime().availableProcessors();
    bitmapDispatchers = new ImageDispatcher[threadCount];
    for (int i = 0; i < threadCount; i++) {
      ImageDispatcher bitmapDispatcher = new ImageDispatcher(mDeque);
      bitmapDispatcher.start();
      bitmapDispatchers[i] = bitmapDispatcher;
    }
  }

  public void addBitmapRequest(ImageRequest bitmapRequest) {
    if (bitmapRequest == null) {
      return;
    }
    if (!mDeque.contains(bitmapRequest)) {
      mDeque.add(bitmapRequest);
    }
  }
}

最后是图片请求加载

public class ImageDispatcher extends Thread {

  // 由银行创建
  private LinkedBlockingDeque<ImageRequest> mDeque;

  private Handler mHandler = new Handler(Looper.getMainLooper());

  // 比如银行办理业务的人和窗口
  // TODO 为啥是外部传递  线程安全、 先进先出
  public ImageDispatcher(LinkedBlockingDeque deque) {
    mDeque = deque;
  }

  @Override
  public void run() {
    super.run();

    // 办业务

    // 从队列里获取请求
    // 设置占位图
    // 从服务器加载具体图片
    // 把图片显示到ImageView
    while (!isInterrupted()) {
      ImageRequest request = null;
      try {
        request = mDeque.take();

        showLoadingImg(request);

        // 内存找, 找不到,去硬盘找
        // LruCache<key, Bitmap>
        Bitmap bitmap = findBitmap(request);

        // 硬盘找不到, 去网络找
        // DiskLruCache
        showImageView(request, bitmap);

        if (request.requestListener() != null) {
          request.requestListener().onSuccess(bitmap);
        }
      } catch (Exception e) {
        e.printStackTrace();
        if (request != null && request.requestListener() != null) {
          request.requestListener().onFailure();
        }
      }
    }
  }

  private Bitmap findBitmap(ImageRequest request) {
    return downloadImage(request.url());
  }

  private Bitmap downloadImage(@Nullable String url) {
    InputStream is = null;
    Bitmap bitmap = null;
    try {
      URL uri = new URL(url);
      HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
      is = conn.getInputStream();
      bitmap = BitmapFactory.decodeStream(is);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // TODO
    }
    return bitmap;
  }

  private void showImageView(final ImageRequest request, final Bitmap bitmap) {
    if (TextUtils.equals(request.url(), request.urlMd5()) && request.imageView() != null) {
      // TODO
    }
  }

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值