优化本地书籍阅读

pull/5/head
fengyuecanzhu 4 years ago
parent fdea628154
commit 88b345cef4
  1. BIN
      .idea/caches/build_file_checksums.ser
  2. 6
      app/build.gradle
  3. BIN
      app/libs/ZHConverter.jar
  4. BIN
      app/libs/antlr-2.7.4.jar
  5. BIN
      app/libs/chardet-1.0.jar
  6. BIN
      app/libs/cpdetector_1.0.10.jar
  7. BIN
      app/libs/jargs-1.0.jar
  8. 5
      app/src/main/assets/updatelog.fy
  9. 3
      app/src/main/java/xyz/fycz/myreader/entity/Setting.java
  10. 2
      app/src/main/java/xyz/fycz/myreader/greendao/entity/Book.java
  11. 1
      app/src/main/java/xyz/fycz/myreader/greendao/entity/Chapter.java
  12. 3
      app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
  13. 4
      app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
  14. 9
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  15. 11
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookMarkAdapter.java
  16. 8
      app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java
  17. 9
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/CatalogHolder.java
  18. 1
      app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
  19. 76
      app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java
  20. 6
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java
  21. 294
      app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java
  22. 5
      app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java
  23. 4
      app/version_code.properties

@ -161,12 +161,16 @@ dependencies {
//
implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.3.7'
//Material Dialogs
//
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
/*//email
implementation 'com.sun.mail:android-mail:1.6.2'
implementation 'com.sun.mail:android-activation:1.6.2'*/
//Material Dialogs
/*def dialog_version = '3.3.0'
implementation "com.afollestad.material-dialogs:core:$dialog_version"
implementation "com.afollestad.material-dialogs:input:$dialog_version"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,9 +1,14 @@
1、极大地优化本地书籍章节拆分速度
2、优化本地书籍文件编码识别
3、修复目录未加载时搜索闪退的bug
2020.12.06
风月读书v1.20.120612
1、新增书架排序设置
2、新增书籍分享(以图片+二维码的形式分享)及扫一扫(书架菜单)
3、新增书源:老幺小说、星星小说、时光小说、峡谷文学、红尘小说
4、修复欢迎页图片概率性未完全加载的bug
5、修复部分版本更新到最新版本闪退的bug
2020.11.28
风月读书v1.20.112822

@ -89,8 +89,7 @@ public class Setting implements Serializable {
public ReadStyle getCurReadStyle(){
//Log.d("curReadStyleIndex", String.valueOf(curReadStyleIndex));
ReadStyle readStyle = readStyles.get(curReadStyleIndex);
if (readStyle == null) {
if (readStyles == null || readStyles.size() == 0) {
initReadStyle();
}
if (!dayStyle){

@ -30,7 +30,7 @@ public class Book implements Serializable {
private String name;//书名
private String chapterUrl;//书目Url(本地书籍为:本地书籍地址)
private String infoUrl;//书目详情Url
private String infoUrl;//书目详情Url(本地书籍为:文件编码)
private String imgUrl;//封面图片url
private String desc;//简介
private String author;//作者

@ -79,6 +79,7 @@ public class Chapter {
this.url = url;
}
public String getContent() {
if (end > 0) return end + "";
String filePath = APPCONST.BOOK_CACHE_PATH + bookId
+ File.separator + title + FileUtils.SUFFIX_FY;
File file = new File(filePath);

@ -42,6 +42,8 @@ public class ChapterService extends BaseService {
chapter.setTitle(cursor.getString(3));
chapter.setUrl(cursor.getString(4));
chapter.setContent(cursor.getString(5));
chapter.setStart(cursor.getInt(6));
chapter.setEnd(cursor.getInt(7));
chapters.add(chapter);
}
} catch (Exception e) {
@ -284,6 +286,7 @@ public class ChapterService extends BaseService {
return file.exists();
}
private void deleteAllChapterCacheFile(String bookId) {
FileUtils.deleteFile(APPCONST.BOOK_CACHE_PATH + bookId);
}

@ -182,10 +182,10 @@ public class MainActivity extends BaseActivity {
}
try {
int sourceVersion = SysManager.getSetting().getSourceVersion();
if (sourceVersion < APPCONST.SOURCE_VERSION) {
//if (sourceVersion < APPCONST.SOURCE_VERSION) {
SysManager.resetSource();
Log.d(TAG, "resetSource");
}
//}
} catch (Exception e) {
ToastUtils.showError(e.getLocalizedMessage());
e.printStackTrace();

@ -911,14 +911,18 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
*/
private void getData() {
mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(mBook.getId());
if (!isCollected || mChapters.size() == 0 || ("本地书籍".equals(mBook.getType()) &&
!ChapterService.isChapterCached(mBook.getId(), mChapters.get(0).getTitle()))) {
if (!isCollected || mChapters.size() == 0 || ("本地书籍".equals(mBook.getType()) && !ChapterService.isChapterCached(mBook.getId(), mChapters.get(0).getTitle())
)) {
if ("本地书籍".equals(mBook.getType())) {
if (!new File(mBook.getChapterUrl()).exists()) {
ToastUtils.showWarring("书籍缓存为空且源文件不存在,书籍加载失败!");
finish();
return;
}
if (mChapters.size() != 0 && mChapters.get(0).getEnd() > 0){
initChapters();
return;
}
((LocalPageLoader) mPageLoader).loadChapters(new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
@ -935,6 +939,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
@Override
public void onError(Exception e) {
e.printStackTrace();
mChapters.clear();
initChapters();
mHandler.sendMessage(mHandler.obtainMessage(1));

@ -9,6 +9,8 @@ import android.widget.Filter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.entity.Setting;
@ -58,13 +60,14 @@ public class BookMarkAdapter extends ArrayAdapter<BookMark> {
private void initView(int postion,final ViewHolder viewHolder){
final BookMark bookMark = getItem(postion);
viewHolder.tvTitle.setText(bookMark.getTitle() + "[" + (bookMark.getBookMarkReadPosition() + 1) + "]");
assert bookMark != null;
viewHolder.tvTitle.setText(String.format("%s[%s]", bookMark.getTitle(), bookMark.getBookMarkReadPosition() + 1));
if (ChapterService.isChapterCached(bookMark.getBookId(), bookMark.getTitle())){
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(getContext().getResources().getDrawable(R.drawable.selector_category_load),null,null,null);
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(), R.drawable.selector_category_load),null,null,null);
} else {
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(getContext().getResources().getDrawable(R.drawable.selector_category_unload),null,null,null);
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_unload),null,null,null);
}
viewHolder.tvTitle.setTextColor(getContext().getColor(R.color.textSecondary));
viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.textSecondary));
/*if (!setting.isDayStyle()) {
viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.sys_night_word));
viewHolder.vLine.setBackground(getContext().getDrawable(R.color.sys_dialog_setting_line));

@ -9,6 +9,8 @@ import android.widget.Filter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.entity.Setting;
@ -66,10 +68,10 @@ public class ChapterTitleAdapter extends ArrayAdapter<Chapter> {
final Chapter chapter = getItem(postion);
// viewHolder.tvTitle.setText("【" + chapter.getTitle() + "】");
viewHolder.tvTitle.setText(chapter.getTitle());
if (ChapterService.isChapterCached(chapter.getBookId(), chapter.getTitle())) {
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(getContext().getResources().getDrawable(R.drawable.selector_category_load), null, null, null);
if (ChapterService.isChapterCached(chapter.getBookId(), chapter.getTitle()) || chapter.getEnd() > 0 ) {
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load), null, null, null);
} else {
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(getContext().getResources().getDrawable(R.drawable.selector_category_unload), null, null, null);
viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_unload), null, null, null);
}
viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.textSecondary));
/*if (!setting.isDayStyle()) {

@ -1,6 +1,9 @@
package xyz.fycz.myreader.ui.adapter.holder;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
import xyz.fycz.myreader.greendao.entity.Chapter;
@ -24,10 +27,10 @@ public class CatalogHolder extends ViewHolderImpl<Chapter> {
@Override
public void onBind(Chapter data, int pos) {
if (ChapterService.isChapterCached(data.getBookId(), data.getTitle())) {
tvTitle.setCompoundDrawablesWithIntrinsicBounds(getContext().getResources().getDrawable(R.drawable.selector_category_load), null, null, null);
if (ChapterService.isChapterCached(data.getBookId(), data.getTitle()) || data.getEnd() > 0) {
tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load), null, null, null);
} else {
tvTitle.setCompoundDrawablesWithIntrinsicBounds(getContext().getResources().getDrawable(R.drawable.selector_category_unload), null, null, null);
tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_unload), null, null, null);
}
tvTitle.setText(data.getTitle());
}

@ -146,6 +146,7 @@ public class CatalogPresenter implements BasePresenter {
* @param query
*/
public void startSearch(String query) {
if (mChapters.size() == 0) return;
mChapterTitleAdapter.getFilter().filter(query);
mCatalogFragment.getLvChapterList().setSelection(0);
}

@ -1,6 +1,5 @@
package xyz.fycz.myreader.util.utils;
import android.app.Application;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
@ -11,7 +10,25 @@ import android.os.StatFs;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
import info.monitorenter.cpdetector.io.*;
import org.mozilla.universalchardet.UniversalDetector;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
@ -20,11 +37,6 @@ import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.StringHelper;
import java.io.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public class FileUtils {
//采用自己的格式去设置文件,防止文件被系统文件查询到
@ -215,32 +227,42 @@ public class FileUtils {
}
/**
* 文件编码
* @param filePath
* 文件编码
* @param file
* @return
* @throws Exception
*/
public static String getFileEncode(String filePath) {
String charsetName = null;
public static String getFileCharset(File file){
FileInputStream fis = null;
FileInputStream temFis = null;
FileOutputStream fos = null;
File temFile = null;
try {
File file = new File(filePath);
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
detector.add(new ParsingDetector(false));
detector.add(JChardetFacade.getInstance());
detector.add(ASCIIDetector.getInstance());
detector.add(UnicodeDetector.getInstance());
java.nio.charset.Charset charset = null;
charset = detector.detectCodepage(file.toURI().toURL());
if (charset != null) {
charsetName = charset.name();
temFile = getFile(APPCONST.TEM_FILE_DIR + "tem.fy");
fis = new FileInputStream(file);
fos = new FileOutputStream(temFile);
byte[] bytes = new byte[1024 * 10];
int len;
if ((len = fis.read(bytes)) != -1){
fos.write(bytes, 0, len);
}
fos.flush();
temFis = new FileInputStream(temFile);
String encoding = UniversalDetector.detectCharset(temFis);
if (encoding != null) {
Log.d("encoding", encoding);
return encoding;
} else {
charsetName = "UTF-8";
return "UTF-8";
}
} catch (IOException e) {
e.printStackTrace();
return "UTF-8";
}finally {
IOUtils.close(fis, temFis, fos);
if (temFile != null) {
temFile.delete();
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
return charsetName;
}
public static byte[] getBytes(File file) {

@ -89,7 +89,11 @@ public class ReadCrawlerUtil {
public synchronized static void addReadCrawler(BookSource... bookSources){
SharedPreUtils spu = SharedPreUtils.getInstance();
String searchSource = spu.getString(MyApplication.getmContext().getString(R.string.searchSource), null);
String searchSource = spu.getString(MyApplication.getmContext().getString(R.string.searchSource));
if ("".equals(searchSource)){
resetReadCrawlers();
return;
}
StringBuilder sb = new StringBuilder(searchSource);
for (BookSource bookSource : bookSources){
sb.append(",");

@ -80,11 +80,11 @@ public class LocalPageLoader extends PageLoader {
* 1. 序章的添加
* 2. 章节存在的书本的虚拟分章效果
*/
public List<BookChapterBean> loadChapters() throws IOException {
/*public List<Chapter> loadChapters() throws IOException {
mBookFile = new File(mCollBook.getChapterUrl());
//获取文件编码
mCharset = FileUtils.getFileEncode(mBookFile.getAbsolutePath());
List<BookChapterBean> chapters = new ArrayList<>();
List<Chapter> chapters = new ArrayList<>();
RandomAccessFile bookStream = null;
boolean hasChapter = false;
//获取文件流
@ -127,38 +127,38 @@ public class LocalPageLoader extends PageLoader {
//如果当前对整个文件的偏移位置为0的话,那么就是序章
if (curOffset == 0) {
//创建序章
BookChapterBean preChapter = new BookChapterBean();
preChapter.title = "序章";
preChapter.start = 0;
preChapter.end = chapterContent.getBytes(mCharset).length; //获取String的byte值,作为最终值
Chapter preChapter = new Chapter();
preChapter.setTitle("序章");
preChapter.setStart(0);
preChapter.setEnd(chapterContent.getBytes(mCharset).length); //获取String的byte值,作为最终值
//如果序章大小大于30才添加进去
if (preChapter.end - preChapter.start > 30) {
if (preChapter.getEnd() - preChapter.getStart() > 30) {
chapters.add(preChapter);
}
//创建当前章节
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = preChapter.end;
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(preChapter.getEnd());
chapters.add(curChapter);
}
//否则就block分割之后,上一个章节的剩余内容
else {
//获取上一章节
BookChapterBean lastChapter = chapters.get(chapters.size() - 1);
Chapter lastChapter = chapters.get(chapters.size() - 1);
//将当前段落添加上一章去
lastChapter.end += chapterContent.getBytes(mCharset).length;
lastChapter.setEnd(lastChapter.getEnd() + chapterContent.getBytes(mCharset).length);
//如果章节内容太小,则移除
if (lastChapter.end - lastChapter.start < 30) {
if (lastChapter.getEnd() - lastChapter.getStart() < 30) {
chapters.remove(lastChapter);
}
//创建当前章节
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = lastChapter.end;
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(lastChapter.getEnd());
chapters.add(curChapter);
}
} else {
@ -169,25 +169,25 @@ public class LocalPageLoader extends PageLoader {
seekPos += chapterContent.length();
//获取上一章节
BookChapterBean lastChapter = chapters.get(chapters.size() - 1);
lastChapter.end = lastChapter.start + chapterContent.getBytes(mCharset).length;
Chapter lastChapter = chapters.get(chapters.size() - 1);
lastChapter.setEnd(lastChapter.getStart() + chapterContent.getBytes(mCharset).length);
//如果章节内容太小,则移除
if (lastChapter.end - lastChapter.start < 30) {
if (lastChapter.getEnd() - lastChapter.getStart() < 30) {
chapters.remove(lastChapter);
}
//创建当前章节
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = lastChapter.end;
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(lastChapter.getEnd());
chapters.add(curChapter);
}
//如果章节不存在则创建章节
else {
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = 0;
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(0);
chapters.add(curChapter);
}
}
@ -215,20 +215,20 @@ public class LocalPageLoader extends PageLoader {
break;
}
}
BookChapterBean chapter = new BookChapterBean();
chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")";
chapter.start = curOffset + chapterOffset + 1;
chapter.end = curOffset + end;
Chapter chapter = new Chapter();
chapter.setTitle("第" + blockPos + "章" + "(" + chapterPos + ")");
chapter.setStart(curOffset + chapterOffset + 1);
chapter.setEnd(curOffset + end);
chapters.add(chapter);
//减去已经被分配的长度
strLength = strLength - (end - chapterOffset);
//设置偏移的位置
chapterOffset = end;
} else {
BookChapterBean chapter = new BookChapterBean();
chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")";
chapter.start = curOffset + chapterOffset + 1;
chapter.end = curOffset + length;
Chapter chapter = new Chapter();
chapter.setTitle("第" + blockPos + "章" + "(" + chapterPos + ")");
chapter.setStart(curOffset + chapterOffset + 1);
chapter.setEnd(curOffset + length);
chapters.add(chapter);
strLength = 0;
}
@ -240,8 +240,8 @@ public class LocalPageLoader extends PageLoader {
if (hasChapter) {
//设置上一章的结尾
BookChapterBean lastChapter = chapters.get(chapters.size() - 1);
lastChapter.end = curOffset;
Chapter lastChapter = chapters.get(chapters.size() - 1);
lastChapter.setEnd(curOffset);
}
//当添加的block太多的时候,执行GC
@ -255,39 +255,204 @@ public class LocalPageLoader extends PageLoader {
System.gc();
System.runFinalization();
return chapters;
}*/
/**
* 未完成的部分:
* 1. 序章的添加
* 2. 章节存在的书本的虚拟分章效果
*/
private List<Chapter> loadChapters() throws IOException {
mBookFile = new File(mCollBook.getChapterUrl());
//获取文件编码
mCharset = FileUtils.getFileCharset(mBookFile);
Log.d("mCharset", mCharset);
List<Chapter> mChapterList = new ArrayList<>();
//获取文件流
RandomAccessFile bookStream = new RandomAccessFile(mBookFile, "r");
//寻找匹配文章标题的正则表达式,判断是否存在章节名
boolean hasChapter = checkChapterType(bookStream);
//加载章节
byte[] buffer = new byte[BUFFER_SIZE];
//获取到的块起始点,在文件中的位置
long curOffset = 0;
//block的个数
int blockPos = 0;
//读取的长度
int length;
int allLength = 0;
//获取文件中的数据到buffer,直到没有数据为止
while ((length = bookStream.read(buffer, 0, buffer.length)) > 0) {
++blockPos;
//如果存在Chapter
if (hasChapter) {
//将数据转换成String
String blockContent = new String(buffer, 0, length, mCharset);
int lastN = blockContent.lastIndexOf("\n");
if (lastN != 0) {
blockContent = blockContent.substring(0, lastN);
length = blockContent.getBytes(mCharset).length;
allLength = allLength + length;
bookStream.seek(allLength);
}
//当前Block下使过的String的指针
int seekPos = 0;
//进行正则匹配
Matcher matcher = mChapterPattern.matcher(blockContent);
//如果存在相应章节
while (matcher.find()) {
//获取匹配到的字符在字符串中的起始位置
int chapterStart = matcher.start();
//如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容
//第一种情况一定是序章 第二种情况可能是上一个章节的内容
if (seekPos == 0 && chapterStart != 0) {
//获取当前章节的内容
String chapterContent = blockContent.substring(seekPos, chapterStart);
//设置指针偏移
seekPos += chapterContent.length();
if (mChapterList.size() == 0) { //如果当前没有章节,那么就是序章
//加入简介
mCollBook.setDesc(chapterContent);
//创建当前章节
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(chapterContent.getBytes(mCharset).length);
mChapterList.add(curChapter);
} else { //否则就block分割之后,上一个章节的剩余内容
//获取上一章节
Chapter lastChapter = mChapterList.get(mChapterList.size() - 1);
//将当前段落添加上一章去
lastChapter.setEnd(lastChapter.getEnd() + chapterContent.getBytes(mCharset).length);
//创建当前章节
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(lastChapter.getEnd());
mChapterList.add(curChapter);
}
} else {
//是否存在章节
if (mChapterList.size() != 0) {
//获取章节内容
String chapterContent = blockContent.substring(seekPos, matcher.start());
seekPos += chapterContent.length();
//获取上一章节
Chapter lastChapter = mChapterList.get(mChapterList.size() - 1);
lastChapter.setEnd(lastChapter.getStart() + chapterContent.getBytes(mCharset).length);
//创建当前章节
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(lastChapter.getEnd());
mChapterList.add(curChapter);
} else { //如果章节不存在则创建章节
Chapter curChapter = new Chapter();
curChapter.setTitle(matcher.group());
curChapter.setStart(0L);
curChapter.setEnd(0L);
mChapterList.add(curChapter);
}
}
}
} else { //进行本地虚拟分章
//章节在buffer的偏移量
int chapterOffset = 0;
//当前剩余可分配的长度
int strLength = length;
//分章的位置
int chapterPos = 0;
while (strLength > 0) {
++chapterPos;
//是否长度超过一章
if (strLength > MAX_LENGTH_WITH_NO_CHAPTER) {
//在buffer中一章的终止点
int end = length;
//寻找换行符作为终止点
for (int i = chapterOffset + MAX_LENGTH_WITH_NO_CHAPTER; i < length; ++i) {
if (buffer[i] == Charset.BLANK) {
end = i;
break;
}
}
Chapter chapter = new Chapter();
chapter.setTitle("第" + blockPos + "章" + "(" + chapterPos + ")");
chapter.setStart(curOffset + chapterOffset + 1);
if (chapter.getStart() == 1) chapter.setStart(0);
chapter.setEnd(curOffset + end);
mChapterList.add(chapter);
//减去已经被分配的长度
strLength = strLength - (end - chapterOffset);
//设置偏移的位置
chapterOffset = end;
} else {
Chapter chapter = new Chapter();
chapter.setTitle("第" + blockPos + "章" + "(" + chapterPos + ")");
chapter.setStart(curOffset + chapterOffset + 1);
if (chapter.getStart() == 1) chapter.setStart(0);
chapter.setEnd(curOffset + length);
mChapterList.add(chapter);
strLength = 0;
}
}
}
//block的偏移点
curOffset += length;
if (hasChapter) {
if (mChapterList.size() != 0) {
//设置上一章的结尾
Chapter lastChapter = mChapterList.get(mChapterList.size() - 1);
lastChapter.setEnd(curOffset);
}
}
//当添加的block太多的时候,执行GC
if (blockPos % 15 == 0) {
System.gc();
System.runFinalization();
}
}
int i = 0;
for (Chapter chapter : mChapterList) {
chapter.setTitle(chapter.getTitle().trim());
chapter.setBookId(mCollBook.getId());
chapter.setId(StringHelper.getStringRandom(25));
chapter.setNumber(i++);
}
IOUtils.close(bookStream);
System.gc();
System.runFinalization();
return mChapterList;
}
public void loadChapters(final ResultCallback resultCallback) {
// 通过RxJava异步处理分章事件
Single.create((SingleOnSubscribe<List<BookChapterBean>>) e -> {
Single.create((SingleOnSubscribe<List<Chapter>>) e -> {
e.onSuccess(loadChapters());
}).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver<List<BookChapterBean>>() {
}).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver<List<Chapter>>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDisp = d;
}
@Override
public void onSuccess(List<BookChapterBean> chapters) {
public void onSuccess(List<Chapter> chapters) {
mChapterDisp = null;
isChapterListPrepare = true;
List<Chapter> mChapters = new ArrayList<>();
int i = 0;
for (BookChapterBean bookChapterBean : chapters) {
Chapter chapter = new Chapter();
chapter.setBookId(mCollBook.getId());
chapter.setTitle(bookChapterBean.getTitle().replace("\n", "").replaceAll("^\\s+|\\s+$", ""));
chapter.setId(StringHelper.getStringRandom(25));
String content = getChapterContent(bookChapterBean);
if (StringHelper.isEmpty(content)) {
continue;
}
chapter.setNumber(i++);
mChapterService.saveOrUpdateChapter(chapter, content);
mChapters.add(chapter);
}
mCollBook.setInfoUrl(mCharset);
if (resultCallback != null) {
resultCallback.onFinish(mChapters, 1);
resultCallback.onFinish(chapters, 1);
}
}
@ -307,15 +472,15 @@ public class LocalPageLoader extends PageLoader {
* @param chapter
* @return
*/
private String getChapterContent(BookChapterBean chapter) {
private byte[] getContent(Chapter chapter) {
RandomAccessFile bookStream = null;
try {
bookStream = new RandomAccessFile(mBookFile, "r");
bookStream.seek(chapter.start);
int extent = (int) (chapter.end - chapter.start);
bookStream.seek(chapter.getStart());
int extent = (int) (chapter.getEnd() - chapter.getStart());
byte[] content = new byte[extent];
bookStream.read(content, 0, extent);
return new String(content, mCharset);
return content;
} catch (IOException e) {
e.printStackTrace();
} finally {
@ -367,6 +532,8 @@ public class LocalPageLoader extends PageLoader {
// 对于文件是否存在,或者为空的判断,不作处理。 ==> 在文件打开前处理过了。
mBookFile = new File(mCollBook.getChapterUrl());
mCharset = mCollBook.getInfoUrl();
// 判断文件是否已经加载过,并具有缓存
if (mCollBook.getChapterTotalNum() != 0) {
@ -387,16 +554,25 @@ public class LocalPageLoader extends PageLoader {
@Override
protected BufferedReader getChapterReader(Chapter chapter) throws Exception {
if (chapter.getEnd() > 0) {
Log.d("getChapterReader", chapter.getTitle());
//从文件中获取数据
byte[] content = getContent(chapter);
ByteArrayInputStream bais = new ByteArrayInputStream(content);
BufferedReader br = new BufferedReader(new InputStreamReader(bais, mCharset));
return br;
}
File file = new File(APPCONST.BOOK_CACHE_PATH + mCollBook.getId()
+ File.separator + chapter.getTitle() + FileUtils.SUFFIX_FY);
if (!file.exists()) return null;
Log.d("getChapterReader", file.getPath());
return new BufferedReader(new FileReader(file));
}
@Override
public boolean hasChapterData(Chapter chapter) {
return ChapterService.isChapterCached(mCollBook.getId(), chapter.getTitle());
return chapter.getEnd() > 0 || ChapterService.isChapterCached(mCollBook.getId(), chapter.getTitle());
}

@ -1502,10 +1502,15 @@ public abstract class PageLoader {
float rHeight = mVisibleHeight;
int titleLinesCount = 0;
boolean showTitle = true; // 是否展示标题
boolean firstLine = true;
String paragraph = chapter.getTitle();//默认展示标题
paragraph = paragraph.trim() + "\n";
try {
while (showTitle || (paragraph = br.readLine()) != null) {
if (firstLine && !showTitle){
paragraph = paragraph.replace(chapter.getTitle(), "");
firstLine = false;
}
if (mSettingManager.getLanguage() == Language.traditional) {
paragraph = ChineseUtils.toTraditional(paragraph);
} else if (mSettingManager.getLanguage() == Language.simplified) {

@ -1,2 +1,2 @@
#Sun Dec 06 12:06:58 CST 2020
VERSION_CODE=175
#Sun Dec 06 19:19:42 CST 2020
VERSION_CODE=176

Loading…
Cancel
Save