pda/zhuike/src/main/java/com/novelbook/android/utils/BookUtil.java

599 lines
20 KiB
Java
Raw Normal View History

2019-04-02 17:41:53 +08:00
package com.novelbook.android.utils;
import android.content.ContentValues;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import com.novelbook.android.bean.Cache;
2019-04-03 16:21:00 +08:00
import com.novelbook.android.db.Chapter;
import com.novelbook.android.db.Novel;
2019-04-03 18:09:00 +08:00
import com.novelbook.android.netutils.HttpMethods;
2019-04-02 17:41:53 +08:00
2019-04-03 18:09:00 +08:00
import org.json.JSONException;
import org.json.JSONObject;
2019-04-02 17:41:53 +08:00
import org.litepal.LitePal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
2019-04-03 18:09:00 +08:00
import okhttp3.Request;
import okhttp3.Response;
2019-04-02 17:41:53 +08:00
public class BookUtil {
public static final String TAG ="BookUtil";
private static final String storagePath = Environment.getExternalStorageDirectory() + "/zhuike";
private static final String cachedPath = storagePath + "/cache/";
private static final String chapterPath = storagePath + "/chapter/";
private static final String charachterType = "utf-8";//"UTF-16LE";
//存储的字符数
public static final int cachedSize = 30000;
// protected final ArrayList<WeakReference<char[]>> myArray = new ArrayList<>();
public static final String lineBreakChar ="\n";
protected final ArrayList<Cache> myArray = new ArrayList<>();
//目录
2019-04-03 18:09:00 +08:00
private List<Chapter> mChapters = new ArrayList<>();
public List<Chapter> getChapters() {
return mChapters;
}
public void setChapters(List<Chapter> chapters) {
this.mChapters = chapters;
}
2019-04-02 17:41:53 +08:00
private String m_strCharsetName;
private String bookName;
private String bookPath;
public void setBookLen(long bookLen) {
this.bookLen = bookLen;
}
private long bookLen;
private long position;
2019-04-03 18:09:00 +08:00
private Novel mNovel;
private Chapter mChapter;
public Chapter getChapter() {
return mChapter;
}
public void setChapter(Chapter chapter) {
this.mChapter = chapter;
}
2019-04-02 17:41:53 +08:00
public void setChapterNo(int chapterNo) {
this.chapterNo = chapterNo;
}
public int getChapterNo() {
return chapterNo;
}
private int chapterNo;//当前章节
public String getLineBreakChar(){
return "\n";
}
public BookUtil(){
checkAndCreateDir(storagePath);
checkAndCreateDir(chapterPath);
checkAndCreateDir(cachedPath);
}
private void checkAndCreateDir(String path){
File file = new File(path);
if (!file.exists()){
file.mkdir();
}
}
2019-04-03 18:09:00 +08:00
public synchronized void openBook(Novel novel) throws IOException {
this.mNovel = novel;
2019-04-02 17:41:53 +08:00
//如果当前缓存不是要打开的书本就缓存书本同时删除缓存
//TODO 构建新的缓存策略几个选项1每本书一个缓存 2控制缓存总大小超过限制删除旧缓存 3网络小说的缓存
2019-04-03 18:09:00 +08:00
boolean isLocalImport = TextUtils.isEmpty( novel.getNovelId());
boolean isOnShelf = isLocalImport || novel.isOnShelf();
boolean isLoadChaptsFromRemote = !isLocalImport ;// && !novel.isFinished() ; //是否从目标网站下载目录
2019-04-02 17:41:53 +08:00
2019-04-03 18:09:00 +08:00
if(isLocalImport) {
2019-04-02 17:41:53 +08:00
2019-04-03 18:09:00 +08:00
mChapters = LitePal.where("novelId=?", mNovel.getId() + "").find(Chapter.class);
for (Chapter c : mChapters) {
Log.d(TAG, String.format("bookchapter :%s,fileName :%s, chapter Size %s", c.getChapterName(), c.getChapterPath(), c.getLength()));
}
chaptCache = new HashMap<Integer, Cache>();
if (mChapters.isEmpty()) { //1. 首次打开 本地导入的书 2. 首次打开 未缓存的在线小说
if (bookPath == null || !bookPath.equals(mNovel.getNovelPath())) {
cleanCacheFile();
this.bookPath = mNovel.getNovelPath();
bookName = FileUtils.getFileName(bookPath);
cacheBook();
}
2019-04-02 17:41:53 +08:00
}
}
2019-04-03 18:09:00 +08:00
2019-04-02 17:41:53 +08:00
}
private void cleanCacheFile(){
File file = new File(cachedPath );
if (!file.exists()){
file.mkdir();
}else{
File[] files = file.listFiles();
for (int i = 0; i < files.length;i++){
files[i].delete();
}
}
file = new File(getChapterPath());
if (!file.exists()){
file.mkdir();
}else{
File[] files = file.listFiles();
for (int i = 0; i < files.length;i++){
files[i].delete();
}
}
}
public int next(boolean back){
position += 1;
if (position > bookLen){
position = bookLen;
return -1;
}
char result = chaptCurrent(); //current();
if (back) {
position -= 1;
}
return result;
}
public char[] nextLine(){
if (position >= bookLen){
return null;
}
String line = "";
while (position < bookLen){
int word = next(false);
if (word == -1){
break;
}
char wordChar = (char) word;
if ((wordChar + "").equals("\n") ){// if ((wordChar + "").equals("\r") && (((char)next(true)) + "").equals("\n")){
// next(false);
break;
}
line += wordChar;
}
return line.toCharArray();
}
public char[] preLine(){
if (position <= 0){
return null;
}
String line = "";
while (position >= 0){
int word = pre(false);
if (word == -1){
break;
}
char wordChar = (char) word;
if ((wordChar + "").equals("\n") ){ // if ((wordChar + "").equals("\n") && (((char)pre(true)) + "").equals("\r")){
// pre(false); // /r/n ->/n 不需要再往前读一个字符了
// line = "\r\n" + line;
break;
}
line = wordChar + line;
}
return line.toCharArray();
}
public char chaptCurrent(){
char[] charArray = chaptChars(chapterNo);
return charArray[(int)position-1];
}
public char current(){
// int pos = (int) (position % cachedSize);
// int cachePos = (int) (position / cachedSize);
int cachePos = 0;
int pos = 0;
int len = 0;
for (int i = 0;i < myArray.size();i++){
long size = myArray.get(i).getSize();
if (size + len - 1 >= position){
cachePos = i;
pos = (int) (position - len);
break;
}
len += size;
}
char[] charArray = block(cachePos);
return charArray[pos];
}
public int pre(boolean back){
position -= 1;
if (position < 0){
position = 0;
return -1;
}
char result = current();
if (back) {
position += 1;
}
return result;
}
public long getPosition(){
return position;
}
public void setPostition(long position){
this.position = position;
}
//缓存书本
private void cacheBook() throws IOException {
2019-04-03 18:09:00 +08:00
if (TextUtils.isEmpty(mNovel.getCharset())) {
2019-04-02 17:41:53 +08:00
m_strCharsetName = FileUtils.getCharset(bookPath);
if (m_strCharsetName == null) {
m_strCharsetName = "utf-8";
}
ContentValues values = new ContentValues();
values.put("charset",m_strCharsetName);
2019-04-03 18:09:00 +08:00
LitePal.update(Novel.class,values,mNovel.getId());
2019-04-02 17:41:53 +08:00
}else{
2019-04-03 18:09:00 +08:00
m_strCharsetName = mNovel.getCharset();
2019-04-02 17:41:53 +08:00
}
File file = new File(bookPath);
InputStreamReader reader = new InputStreamReader(new FileInputStream(file),m_strCharsetName);
int index = 0;
bookLen = 0;
2019-04-03 18:09:00 +08:00
mChapters.clear();
2019-04-02 17:41:53 +08:00
myArray.clear();
while (true){
char[] buf = new char[cachedSize];
int result = reader.read(buf);
if (result == -1){
reader.close();
break;
}
String bufStr = new String(buf);
// Log.e(TAG,String.format("缓存的内容是\n %s",bufStr));
bufStr = bufStr.replaceAll("\r\n","\n");
// bufStr = bufStr.replaceAll("\u3000\u3000+[ ]*","\u3000\u3000");
bufStr = bufStr.replaceAll("\n+\\s*","\n\u3000\u3000");// bufStr = bufStr.replaceAll("\r\n+\\s*","\r\n\u3000\u3000");
// bufStr = bufStr.replaceAll("\r\n[ {0,}]","\r\n\u3000\u3000");
// bufStr = bufStr.replaceAll(" ","");
bufStr = bufStr.replaceAll("\u0000","");
buf = bufStr.toCharArray();
bookLen += buf.length;
// Log.e(TAG,String.format("缓存的内容脱空格处理后\n %s",bufStr));
Cache cache = new Cache();
cache.setSize(buf.length);
cache.setData(new WeakReference<char[]>(buf));
// bookLen += result;
myArray.add(cache);
// myArray.add(new WeakReference<char[]>(buf));
// myArray.set(index,);
Log.e(TAG,String.format("缓存的内容写入文件\n %s",fileName(index)));
Log.e(TAG,"---------------------------------------------------------------------------------------------------------");
try {
File cacheBook = new File(fileName(index));
if (!cacheBook.exists()){
cacheBook.createNewFile();
}
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileName(index)), "UTF-16LE"); // UTF-16LE 比 utf-8 文件小
writer.write(buf);
writer.close();
} catch (IOException e) {
throw new RuntimeException("Error during writing " + fileName(index));
}
index ++;
}
chaptId =0; //初始化导入的chapid
int endchp = myArray.size()>3 ?3:myArray.size();
getChapter(1,3); //先导入2个部分 立即进行阅读
new Thread(){
@Override
public void run() {
getChapter(4,myArray.size()); //剩余部分后台导入
}
}.start();
}
int chaptId =0;
//获取章节
public synchronized void getChapter(int startblk,int endblk){
if(endblk <startblk) return;
try {
long size = 0;
String title ="";
long start =0;
int chaptFileId=0;
2019-04-03 16:21:00 +08:00
Chapter bookChapter = null;
2019-04-02 17:41:53 +08:00
OutputStreamWriter writer = null;
for (int i = startblk-1; i < endblk; i++) {
char[] buf = block(i);
String bufStr = new String(buf);
String[] paragraphs = bufStr.split(lineBreakChar); // String[] paragraphs = bufStr.split("\r\n");
for (String str : paragraphs) {
// if (str.length() <= 30 && (str.matches(".*第.{1,8}章.*") || str.matches(".*第.{1,8}节.*"))) {
if(isChapterTitle(str)) {
if(title.length()==0) {
title = str;
start =0;
}else {
start = size;
title = str;
}
if(bookChapter!=null) {
bookChapter.setLength((int)(size - start));
bookChapter.setChapterPath(fileChapterName(chaptId) );
bookChapter.update(bookChapter.getId());
2019-04-03 18:09:00 +08:00
mChapters.add(bookChapter);
2019-04-02 17:41:53 +08:00
}
2019-04-03 16:21:00 +08:00
bookChapter = new Chapter();
2019-04-03 18:09:00 +08:00
bookChapter.setNovelId(mNovel.getId());
2019-04-03 16:21:00 +08:00
bookChapter.setNovelChapterStartPos(start);
2019-04-02 17:41:53 +08:00
bookChapter.setChapterName(str.replaceAll("###",""));
2019-04-03 16:21:00 +08:00
bookChapter.setNovelPath(bookPath);
2019-04-02 17:41:53 +08:00
bookChapter.save();
int id= bookChapter.getId();
Log.d(TAG,str + " chaptId is " + id);
File chapter = new File(fileChapterName(++chaptId));
if (!chapter.exists()){
chapter.createNewFile();
}
if(writer!=null) {
writer.close();
}
writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(chaptId)), charachterType);
}
if(writer==null) {
2019-04-03 16:21:00 +08:00
bookChapter = new Chapter();
2019-04-03 18:09:00 +08:00
bookChapter.setNovelId(mNovel.getId());
2019-04-03 16:21:00 +08:00
bookChapter.setNovelChapterStartPos(start);
2019-04-02 17:41:53 +08:00
bookChapter.setChapterName(str.replaceAll("###",""));
2019-04-03 16:21:00 +08:00
bookChapter.setNovelPath(bookPath);
2019-04-02 17:41:53 +08:00
bookChapter.save();
writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(++chaptId)), charachterType); //序
}
str+=lineBreakChar;
writer.write(str);
// Log.e(TAG,String.format("当前行\n %s",str));
if (str.contains("\u3000\u3000")) {
size += str.length() + 2;
}else if (str.contains("\u3000")){
size += str.length() + 1;
}else {
size += str.length();
}
/*
2019-04-03 16:21:00 +08:00
Chapter bookChapter = new Chapter();
2019-04-03 18:09:00 +08:00
bookChapter.setBookId(mNovel.getId());
2019-04-02 17:41:53 +08:00
bookChapter.setBookChapterStartPos(start);
bookChapter.setChapterName(title.replaceAll("###",""));
bookChapter.setBookpath(bookPath);
bookChapter.setLength((int)(size - start));
bookChapter.save();
int id= bookChapter.getId();
Log.d(TAG,str + " chaptId is " + id);
2019-04-03 18:09:00 +08:00
mChapters.add(bookChapter);
2019-04-02 17:41:53 +08:00
*/
}
}
if(writer!=null) {
writer.close();
}
if(bookChapter!=null) {
bookChapter.setLength((int)(size - start));
bookChapter.setChapterPath(fileChapterName(chaptId) );
bookChapter.update(bookChapter.getId());
2019-04-03 18:09:00 +08:00
mChapters.add(bookChapter);
2019-04-02 17:41:53 +08:00
}
}catch (Exception e){
e.printStackTrace();
}
}
void createChapContent(){
}
2019-04-03 16:21:00 +08:00
public List<Chapter> getDirectoryList(){
2019-04-03 18:09:00 +08:00
return mChapters;
2019-04-02 17:41:53 +08:00
}
public long getBookLen(){
return bookLen;
}
protected String fileName(int index) {
return cachedPath + bookName + index ;
}
protected String fileChapterName(int chaptId ) {
return getChapterPath() + chaptId ;
}
String getChapterPath(){
2019-04-03 18:09:00 +08:00
return chapterPath +mNovel.getId()+"/";
2019-04-02 17:41:53 +08:00
}
//获取书本缓存
public char[] block(int index) {
if (myArray.size() == 0){
return new char[1];
}
char[] block = myArray.get(index).getData().get();
if (block == null) {
try {
File file = new File(fileName(index));
int size = (int)file.length();
if (size < 0) {
throw new RuntimeException("Error during reading " + fileName(index));
}
block = new char[size / 2];
InputStreamReader reader =
new InputStreamReader(
new FileInputStream(file),
"UTF-16LE"
);
if (reader.read(block) != block.length) {
throw new RuntimeException("Error during reading " + fileName(index));
}
reader.close();
} catch (IOException e) {
throw new RuntimeException("Error during reading " + fileName(index));
}
Cache cache = myArray.get(index);
cache.setData(new WeakReference<char[]>(block));
// myArray.set(index, new WeakReference<char[]>(block));
}
return block;
}
private Map<Integer,Cache> chaptCache = new HashMap<Integer,Cache>();
//获取chapter 缓存
public char[] chaptChars(int index) {
char[] block=null;
if(chaptCache.containsKey(Integer.valueOf(index))) {
block = chaptCache .get(index).getData().get();
}
if (block == null) {
try {
File file = new File(fileChapterName(index));
2019-04-03 18:09:00 +08:00
if(!file.exists()) {
2019-04-02 17:41:53 +08:00
/* 章节内容没有缓存在本地
1. 根据本地的章节网络地址信息读取章节内容到本地若读取失败则
2. 查询主服务器若有地址更新则更新本地信息并重复1若没有更新地址则地址无效返回章节内容正待手打
*/
2019-04-03 18:09:00 +08:00
Chapter chapter = mChapters.get(index);
String url = chapter.getChapterUrl();
JSONObject siteJson = new JSONObject();
siteJson.put("chapterContentRegex", "<div id=\"content\">([\\s\\S]+?)</div>");
siteJson.put("chapterContentDumpRegex", "<script>chaptererror();</script>");
Request request = new Request.Builder()
.url(url)
// .header("User-Agent", "OkHttp Example")
.build();
Response response = HttpMethods.getOkClient().newCall(request).execute();
String content = NovelParseUtil.getChapterContent(response.body().string(), siteJson);
char[] buf = content.toCharArray();
try {
file.createNewFile();
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(index)), "UTF-16LE"); // UTF-16LE 比 utf-8 文件小
writer.write(buf);
writer.close();
} catch (IOException e) {
throw new RuntimeException("Error during writing " + fileName(index));
}
2019-04-02 17:41:53 +08:00
}
int size = (int)file.length();
if (size < 0) {
throw new RuntimeException("Error during reading " + fileChapterName(index));
}
block = new char[size / 2];
InputStreamReader reader =
new InputStreamReader(
new FileInputStream(file),
charachterType
);
long l = reader.read(block);
if (reader.read(block) != block.length) {
// throw new RuntimeException("Error during reading " + fileChapterName(index));
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Error during reading " + fileChapterName(index));
2019-04-03 18:09:00 +08:00
} catch (JSONException e) {
e.printStackTrace();
2019-04-02 17:41:53 +08:00
}
Cache cache = new Cache();
cache.setSize(block.length);
cache.setData(new WeakReference<char[]>(block));
chaptCache.put(index, cache);
// myArray.set(index, new WeakReference<char[]>(block));
}
return block;
}
public boolean isChapterTitle(String line){
return (line.length() <= 30 && (line.matches(".*第.{1,8}章.*") || line.matches(".*第.{1,8}节.*"))) ;
}
}