go 安全通道(缓存器)缓存池 io.Reader error类

本文探讨了在并发编程中如何使用缓冲器来避免运行时恐慌,重点介绍了非阻塞Put和Get方法的设计,以及Close方法确保缓冲器状态一致性的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  •  向一个已经关闭的通道发送值和关闭一个已经关闭的通道,

都会引发运行时候的恐慌,缓冲器就是解决这个问题诞生的。
Put 方法先检查缓冲器实例是否关闭,并且保证只有在检查结果是否的时候
进行存入。,在Close 方法中仅在当前缓冲器实例未关闭的情况下
进行关闭操作

package buffer

import (
	"sync"
	"sync/atomic"
)

import (
	"fmt"
	"gopcp.v2/chapter6/webcrawler/toolkit/buffer"
	"sync"
)

/***
向一个已经关闭的通道发送值和关闭一个已经关闭的通道,
都会引发运行时候的恐慌,缓冲器就是解决这个问题诞生的。
Put 方法先检查缓冲器实例是否关闭,并且保证只有在检查结果是否的时候
进行存入。,在Close 方法中仅在当前缓冲器实例未关闭的情况下
进行关闭操作
 */
// Buffer 代表FIFO的缓冲器的接口类型。
type Buffer interface {
	// Cap 用于获取本缓冲器的容量。
	Cap() uint32
	// Len 用于获取本缓冲器中的数据数量。
	Len() uint32
	// Put 用于向缓冲器放入数据。
	// 注意!本方法应该是非阻塞的。
	// 若缓冲器已关闭则会直接返回非nil的错误值。
	Put(datum interface{}) (bool, error)
	// Get 用于从缓冲器获取器。
	// 注意!本方法应该是非阻塞的。
	// 若缓冲器已关闭则会直接返回非nil的错误值。
	Get() (interface{}, error)
	// Close 用于关闭缓冲器。
	// 若缓冲器之前已关闭则返回false,否则返回true。
	Close() bool
	// Closed 用于判断缓冲器是否已关闭。
	Closed() bool
}

type myBuffer struct {
	//ch 代表采访数据的通道
	ch chan interface{}
	//closed 代表缓冲器的关闭状态
	closed uint32
	//closingLock 代表为了消除因关闭缓冲器而产生的竟态条件的读写锁
	closingLock sync.RWMutex
}

func NewBuffer(size uint32)(Buffer, error){
	if size==0 {
		errMsg := fmt.Sprintf(" illegal size for buffer:%d",size)
		return nil, error.NewIllegalParameterError(errMsg)
	}

	return &myBuffer{
		ch : make(chan interface{},size),
	},nil
}

func (buf *myBuffer) Cap() uint32 {
	return uint32(cap(buf.ch))
}

func (buf *myBuffer) Len() uint32 {
	return uint32(len(buf.ch))
}

func (buf *myBuffer) Put (datum interface{}) (ok bool,err error){
		buf.closingLock.RLock()
		defer buf.closingLock.Unlock()

		if buf.Closed() {
			return false,ErrClosedBuffer
		}

		select {
		case buf.ch <- datum:
			ok = true
		default:
			ok = false
		}

		return
}

func (buf *myBuffer) Get() (interface{}, error) {
	select {
	case datum, ok := <-buf.ch:
		if !ok {
			return nil, ErrClosedBuffer
		}
		return datum, nil
	default:
		return nil, nil
	}
}

func (buf *myBuffer)Close() bool {
	if atomic.CompareAndSwapUint32(&buf.closed,0,1){
		buf.closingLock.Lock()
		close(buf.ch)
		buf.closingLock.Unlock()
		return true
	}

	return false
}
  • io.Reader 

io.Reader 实现类型:bytes.Reader  bufio.Reader  ,这类读取器只能够读取他们底层的一遍数据,当读完数据后,他们的Read 方法总会把io.EOF 变量的值做为错误值返回。net/http 中的 http.Reaponse 类型的body 就是 io.Readcloser接口类型的,io.Readcloser的类型声明嵌入了io.Reader,前者只比后者多一个Close的方法。   

  • 错误类
package errors

import (
	"bytes"
	"fmt"
	"strings"
)

type ErrorType string

const (
	// ERROR_TYPE_DOWNLOADER 代表下载器错误。
	ERROR_TYPE_DOWNLOADER ErrorType = "downloader error"
	// ERROR_TYPE_ANALYZER 代表分析器错误。
	ERROR_TYPE_ANALYZER ErrorType = "analyzer error"
	// ERROR_TYPE_PIPELINE 代表条目处理管道错误。
	ERROR_TYPE_PIPELINE ErrorType = "pipeline error"
	// ERROR_TYPE_SCHEDULER 代表调度器错误。
	ERROR_TYPE_SCHEDULER ErrorType = "scheduler error"
)

type CrawlerError interface {
	Type() ErrorType
	error() string
}

type myCrawlerError struct{
	errType ErrorType
	errMsg  string
	fullErrMsg string
}

// NewCrawlerError 用于创建一个新的爬虫错误值。
func NewCrawlerError(errType ErrorType, errMsg string) CrawlerError {
	return &myCrawlerError{
		errType: errType,
		errMsg:  strings.TrimSpace(errMsg),
	}
}

// NewCrawlerErrorBy 用于根据给定的错误值创建一个新的爬虫错误值。
func NewCrawlerErrorBy(errType ErrorType, err error) CrawlerError {
	return NewCrawlerError(errType, err.Error())
}

func (ce *myCrawlerError) Type() ErrorType {
	return ce.errType
}

func (ce *myCrawlerError) Error() string {
	if ce.fullErrMsg == "" {
		ce.genFullErrMsg()
	}
	return ce.fullErrMsg
}

func (ce *myCrawlerError) getFullErrMsg() {
	var buffer bytes.Buffer

	buffer.WriteString("crawler error:")
	if ce.errType!="" {
		buffer.WriteString(string(ce.errType))
		buffer.WriteString(":")
	}

	buffer.WriteString(ce.errMsg)
	ce.fullErrMsg =fmt.Sprintf("%s",buffer.String())
	return
}

// IllegalParameterError 代表非法的参数的错误类型。
type IllegalParameterError struct {
	msg string
}

// NewIllegalParameterError 会创建一个IllegalParameterError类型的实例。
func NewIllegalParameterError(errMsg string) IllegalParameterError {
	return IllegalParameterError{
		msg: fmt.Sprintf("illegal parameter: %s",
			strings.TrimSpace(errMsg)),
	}
}

func (ipe IllegalParameterError) Error() string {
	return ipe.msg
}

 

 

<think>好的,我现在需要帮用户解决在Android中加载内部存储JSON文件时出现FileNotFoundException的问题。用户还提到了Toast和screen_config,可能是在处理配置文件时遇到了问题。首先,我需要理解这个异常通常是什么原因导致的。 FileNotFoundException在尝试访问文件时发生,可能原因包括文件不存在、路径错误或者权限问题。内部存储的文件路径通常是应用的私有目录,比如/data/data/<package_name>/files/,所以用户可能没有正确构建文件路径,或者文件尚未创建。 接下来,用户提到使用Toast,可能是在捕获异常时显示错误信息。需要确认他们是否正确使用了Toast,特别是在Fragment中,可能需要使用getActivity()来获取Context,否则可能会出错。例如,在Fragment中应该用requireContext()而不是Activity的context吗?不过根据用户提供的引用[2],使用getActivity()在Fragment中是可行的,但要注意Fragment是否已经附加到Activity。 然后,用户提到screen_config,这可能是一个配置文件的名称,比如screen_config.json。需要确认用户是否正确拼写了文件名,以及是否在正确的位置保存了文件。比如,是否使用内部存储的files目录,或者有没有可能文件被保存到了其他位置,比如缓存目录。 根据引用[1]到[4],用户提供的引用主要涉及Toast的使用和内部机制,比如显示时长和Handler的处理。这可能与问题本身关系不大,但用户可能在异常处理中使用了Toast来显示错误信息,所以需要确保Toast的调用正确,尤其是在Fragment中。例如,引用[2]提到在Fragment中使用getActivity()来显示Toast,这可能需要注意Fragment的生命周期,避免在Fragment未附加时调用导致空指针。 接下来,我需要给出解决方案的步骤。首先,检查文件路径是否正确。用户应该使用Context.getFilesDir()来获取内部存储的files目录,然后构建文件的绝对路径。例如,File file = new File(context.getFilesDir(), "screen_config.json");。如果用户直接使用文件名而没有路径,或者错误地使用了外部存储路径,就会导致文件找不到。 其次,确认文件是否存在。在尝试读取之前,应该先检查file.exists(),如果不存在,可能需要先创建文件或从资源中复制。例如,如果用户首次运行应用,文件可能还未生成,这时候需要处理这种情况,比如提供一个默认配置或提示用户文件不存在。 第三,检查文件权限。内部存储不需要运行时权限,但需要确保应用有写入权限,如果用户尝试在写入文件之后读取,可能权限没问题。但如果用户误用了外部存储,可能需要检查权限,但根据问题描述,用户使用的是内部存储,所以可能权限不是问题所在。 第四,正确使用输入流。读取内部存储的文件应该使用FileInputStream,并且要正确处理异常。例如,使用try-with-resources来自动关闭流,避免资源泄漏。 第五,在异常处理中使用Toast显示错误信息。根据引用[2],在Fragment中应该使用getActivity()作为Context,但要确保Fragment处于活动状态。例如,在onCreateView或onViewCreated中调用,避免在onDetach之后调用导致崩溃。此外,引用[4]提到Toast的显示时长,确保用户设置正确,以便错误信息能足够显示。 可能的代码示例中,用户需要正确构建文件路径,检查文件是否存在,捕获异常并显示Toast。例如: try { File file = new File(requireContext().getFilesDir(), "screen_config.json"); if (!file.exists()) { throw new FileNotFoundException("File not found"); } // 读取文件 } catch (FileNotFoundException e) { Toast.makeText(getActivity(), "文件未找到", Toast.LENGTH_SHORT).show(); } catch (IOException e) { Toast.makeText(getActivity(), "读取错误", Toast.LENGTH_SHORT).show(); } 同时,用户可能在保存文件时没有正确写入,导致文件不存在。需要确认保存文件的代码是否正确,比如使用FileOutputStream,并且是否调用了flush()和close()方法,确保数据写入。 总结步骤:检查路径、确认文件存在、正确读取、异常处理中显示Toast。此外,确保在Fragment中正确获取Context,避免空指针。</think>针对加载内部存储JSON文件时出现$FileNotFoundException$的问题,以下是分步解决方案: --- ### 1. **验证文件路径正确性** - **内部存储路径**应通过$context.getFilesDir()$获取,完整路径示例: $$ \text{/data/data/<package_name>/files/screen_config.json} $$ - **代码示例**: ```java File file = new File(requireContext().getFilesDir(), "screen_config.json"); ``` --- ### 2. **检查文件是否存在** - 在读取前需确认文件已创建: ```java if (!file.exists()) { // 处理文件未创建的情况,如初始化默认配置或提示用户 Toast.makeText(getActivity(), "配置文件不存在", Toast.LENGTH_SHORT).show(); return; } ``` --- ### 3. **正确读取文件流** - 使用$FileInputStream$并妥善关闭资源: ```java try (FileInputStream fis = new FileInputStream(file)) { BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); StringBuilder jsonString = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { jsonString.append(line); } // 解析JSON数据 } catch (IOException e) { e.printStackTrace(); } ``` --- ### 4. **异常处理与Toast提示** - **Fragment中正确使用Context**: 通过$getActivity()$获取宿主Activity的上下文[^2],但需检查Fragment是否已附加: ```java if (isAdded() && getActivity() != null) { Toast.makeText(getActivity(), "加载文件失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } ``` - **Toast显示时长**: $Toast.LENGTH\_SHORT$对应2秒,$LENGTH\_LONG$对应3.5秒[^4]。 --- ### 5. **检查文件写入逻辑** - 若文件需动态生成,确保写入代码正确: ```java try (FileOutputStream fos = requireContext().openFileOutput("screen_config.json", Context.MODE_PRIVATE)) { fos.write(jsonData.getBytes()); } catch (IOException e) { Toast.makeText(getActivity(), "保存文件失败", Toast.LENGTH_SHORT).show(); } ``` --- ### 6. **验证权限** - **内部存储无需运行时权限**,但需确认应用有写入权限($Manifest.permission.WRITE\_EXTERNAL\_STORAGE$仅在操作外部存储时需动态申请)。 --- **关键点总结**:路径正确性 > 文件存在性检查 > 流操作规范性 > 异常处理与用户反馈。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值