pda/zhuike/.svn/pristine/3c/3cf7750eb19ff5f59f5ab58cdc6...

2130 lines
84 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.novelbook.android.utils;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.novelbook.android.BuildConfig;
import com.novelbook.android.MyApp;
import com.novelbook.android.bean.Cache;
import com.novelbook.android.bean.NovelSites;
import com.novelbook.android.bean.Site;
import com.novelbook.android.db.SiteRule;
import com.novelbook.android.db.Chapter;
import com.novelbook.android.db.Novel;
import com.novelbook.android.netsubscribe.BookSubscribe;
import com.novelbook.android.netutils.HttpMethods;
import com.novelbook.android.netutils.NetUtil;
import com.novelbook.android.netutils.OnSuccessAndFaultListener;
import com.novelbook.android.netutils.OnSuccessAndFaultSub;
import org.json.JSONException;
import org.json.JSONObject;
import org.litepal.LitePal;
import org.litepal.util.Const;
import org.w3c.dom.Text;
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.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class BookUtil {
public static final String TAG ="BookUtil";
public static final String storagePath = FileUtils.getDiskCacheDir(MyApp.applicationContext);//Environment.getExternalStorageDirectory() + "/zhuike";
public static final String cachedPath = storagePath + "/cache/";
public static final String chapterPath = storagePath + "/chapter/";
private static final String charachterType = "utf-8";//"UTF-16LE";
private Context mContext;
private ProgressDialog progressDialog;
MuluStatus mMuluStatus; //目录是否下载完成
private Gson gson = new Gson();
public void setContext(Context context) {
this.mContext = context;
}
public boolean isReadingCatalogs() {
return mMuluStatus == MuluStatus.isDownloading;
}
//存储的字符数
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<>();
//目录
private List<Chapter> mChapters = new ArrayList<>();
//当前章节
// private Chapter mCurrentChapter;
public List<Chapter> getChapters() {
return mChapters;
}
public void setChapters(List<Chapter> chapters) {
this.mChapters = chapters;
}
private final int MSG_FILLCONTENTDONE=1;
private final int MSG_READCHAPTER_FAIL=2;
private final int MSG_READCHAPTER_SUCCESS=3;
private static final int MSG_READBOOK_FAIL = 4;
private static final int MSG_READBOOK_EMPTY_CONTENT = 5;
private String m_strCharsetName;
private String bookName;
private String bookPath;
public void setChapterLen(long chapterLen) {
this.chapterLen = chapterLen;
}
private long bookLen;
private long chapterLen;
// private long position;
private Map<Integer,Long> charPosition = new HashMap<Integer,Long>();
private Novel mNovel;
private void clearBook() {
/* try{
throw new Exception("chapters cleared");
}catch (Exception e){
Log.e(TAG, "prepare book: clearbook called", e);
}*/
charPosition.clear();
this.muluRetryCount=0;
this.downloadStatus = DownloadStatus.notStart;
chaptDownStatus.clear();
chaptCache.clear();
fileRetryCnt.clear();
siteRuleRetryCnt=0;
isChangeSource=false;
mChapters.clear();
myArray.clear();
}
public void setNovel(Novel novel) {
clearBook();
this.mNovel = novel;
}
public Novel getNovel( ) {
return mNovel ;
}
//当前目录网站列表
private NovelSites mNovelSites;
//当前目录网站
private Site mSite;
private SiteRule mSiteRule;
private Map<Integer,Cache> chaptCache = new HashMap<Integer,Cache>();
protected Map<Integer,DownloadStatus> chaptDownStatus = new HashMap<Integer, DownloadStatus>();
DownloadStatus downloadStatus = DownloadStatus.notStart;
public Chapter getChapter(int chapId){
chapId = chapId >0 ?chapId : 1;
Log.d(TAG, String.format("prepare book getChapter: chaptId %s,mChapters.size() %s ",chapId,mChapters.size()));
if(chapId > mChapters.size() || mChapters.size() ==0){
return Chapter.getChapter(mNovel.getId(), mNovel.getDomain()==null?"":mNovel.getDomain(),chapId);
}else{
return mChapters.get(chapId-1);
}
}
public NovelSites getmNovelSites() {
return mNovelSites;
}
public synchronized void openBook(Novel novel, long chapter) throws IOException, InterruptedException {
this.mNovel = novel;
//如果当前缓存不是要打开的书本就缓存书本同时删除缓存
//TODO 构建新的缓存策略几个选项1每本书一个缓存 2控制缓存总大小超过限制删除旧缓存 3网络小说的缓存
boolean isLocalImport = novel.isLocalBook();
boolean isOnShelf = isLocalImport || novel.isOnShelf();
boolean isLoadChaptsFromRemote = !isLocalImport ;// && !novel.isFinished() ; //是否从目标网站下载目录
// showProgressDialog();
if(isLocalImport) {
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. 首次打开 本地导入的书
if (bookPath == null || !bookPath.equals(mNovel.getNovelPath())) {
cleanCacheFile();
this.bookPath = mNovel.getNovelPath();
bookName = FileUtils.getFileName(bookPath);
cacheBook();
}
}
}else{ //读取目录列表
Log.d(TAG, String.format("prepare book %s open chapter %s in background.... mMuluStatus %smSiteRule %s,thread %s",mNovel.getName(),chapter,mMuluStatus,mSiteRule,Thread.currentThread().getName()) );
File file =new File(fileChapterName((int)chapter));
if( file.exists()){
Log.d(TAG, String.format("prepare book open chapter file %s, exist,not waiting more...to open file...",fileChapterName((int)chapter) ));
return;
}
MuluStatus m = mMuluStatus;
// Log.d(TAG,String.format("mulu on Site %s download status %s",mSite.getDomain(),mMuluStatus));
Log.d(TAG, String.format("prepare book %s open book in background.... mMuluStatus %smSiteRule %s,thread %s",mNovel.getName(),mMuluStatus,mSiteRule,Thread.currentThread().getName()) );
int sleptTime =0;
while( mSiteRule ==null || mMuluStatus==null || mMuluStatus == MuluStatus.isDownloading){
sleptTime++;
if(sleptTime >400 || sleptTime >30 && !NetUtil.isNetworkConnected()){
break;
}
Thread.sleep(50);
if(mMuluStatus == MuluStatus.failed){
Log.d(TAG,String.format("prepare book %s failed ,mMuluStatus %s,msiteRule %s,slept %s" ,mNovel.getName(),mMuluStatus,mSiteRule,sleptTime*50));
// throw new RuntimeException("读取资源失败,请检查网络");
}
}
Log.d(TAG,String.format("prepare book %s waiting for mulu downloading ,mMuluStatus %s,msiteRule %s,slept %s" ,mNovel.getName(),mMuluStatus,mSiteRule,sleptTime*50));
}
// dismissProgressDialog();
}
public void setNovelSites(NovelSites nvs) {
this.mNovelSites = nvs;
Log.d(TAG, String.format("prepare book %s get novel sites count %s .",mNovel.getName(), nvs.getSites().length) );
if(nvs.getSites().length ==0){
throw new RuntimeException("书本错误 no site, code 001"); //无目标网站
// return;
}
mSite =null;
if(nvs.getSites().length > 0){
for (Site site:nvs.getSites() ) {
if(TextUtils.isEmpty(site.getMuluUrl())){
continue;
}
if(!TextUtils.isEmpty(mNovel.getDomain()) && site.getDomain().equals(mNovel.getDomain())){
mSite = site;
break;
}
}
if(mSite ==null) {
for (Site site : nvs.getSites()) {
if(TextUtils.isEmpty(site.getMuluUrl())){
continue;
}
if (site.getSelectedByDefault()) {
mSite = site;
break;
}
}
}
if(mSite ==null) {
for (Site site : nvs.getSites()) {
if(TextUtils.isEmpty(site.getMuluUrl())){
continue;
}
mSite = site;
break;
}
}
}
if(mSite!=null) {
getSiteRule();
}
}
private void setSiteInfo() {
File file = new File(getChapterPath() +mSite.getDomain());
if(!file.exists()){
file.mkdir();
}
mNovel.setDomain(mSite.getDomain());
mNovel.setDomainName(mSite.getName());
mNovel.setMuluUrl(mSite.getMuluUrl());
mNovel.setToDefault("isUpdated");
mNovel.update(mNovel.getId());
}
public void getTargetSites(){
Log.d(TAG, "prepare book: getTargetSites" );
BookSubscribe.getNovelSites(mNovel.getNovelId(),new OnSuccessAndFaultSub(new OnSuccessAndFaultListener() {
@Override
public void onSuccess(String result) {
//成功
try {
Log.d(TAG, String.format("prepare book %s get target sites done.thread %s",mNovel.getName(),Thread.currentThread().getName()) );
NovelSites nvs = (NovelSites) gson.fromJson(result,NovelSites.class);
//pageFactory.prepareBook(mNovel,nvs, BookActivity.this);
setNovelSites(nvs);
} catch ( Exception e) {
Log.d(TAG, String.format("prepare book %s get target sites fail.thread %s ,msg %s",mNovel.getName(),Thread.currentThread().getName(),e.getMessage()) );
Log.e(TAG, "prepare book fail", e);
e.printStackTrace();
}
// Toast.makeText(mContext,"getMuluInfo 请求成功 " ,Toast.LENGTH_SHORT).show();
}
@Override
public void onFault(String errorMsg) {
Log.d(TAG, String.format("prepare book %s get target sites fail.thread %s ,msg %s",mNovel.getName(),Thread.currentThread().getName(),errorMsg) );
//失败
// Toast.makeText(mContext,"getMuluInfo 请求失败"+errorMsg,Toast.LENGTH_SHORT).show();
}
},null));
}
int siteRuleRetryCnt =0;
public void getSiteRule() {
getSiteRule(false);
}
public void getSiteRule(boolean isForceRefresh) {
mSiteRule = null;
if(mSite==null){
return;
}
BookSubscribe.getSiteRule(mSite.getDomain(),new OnSuccessAndFaultSub(new OnSuccessAndFaultListener() {
@Override
public void onSuccess(String result) {
siteRuleRetryCnt =0;
Log.d(TAG, "prepare book siteRule:" +result);
//成功
SiteRule sr= new SiteRule();
try {
sr = (SiteRule) gson.fromJson(result, SiteRule.class);
}catch (Exception e){
Log.e(TAG, "prepare book error on gson: ", e);
}
List<SiteRule> srs = LitePal.where("domain=?",sr.getDomain()).limit(1).find(SiteRule.class);
long id = srs.size()==1 ?srs.get(0).getId() :0;
if(id>0 ){
sr.update(id);
// mSiteRule =LitePal.find(SiteRule.class,id);
}else
{
sr.save();
}
mSiteRule =sr;
if(TextUtils.isEmpty(mSite.getName())){
mSite.setName(sr.getName());
};
setSiteInfo();
Log.d(TAG, String.format("prepare book %s 目录正则表达式下载完成,开始读章节信息. muluRegex size %s, thread %s ",mNovel.getName(),mSiteRule.getChapterUrlRegexOnMulu().length, Thread.currentThread().getName()) );
Log.d(TAG, String.format("目录正则表达式下载完成,开始读取章节信息") );
int maxAge = isForceRefresh? 0:mNovel.getMaxAge() ;
if(mSiteRule.getChapterUrlRegexOnMulu().length>0) {
mMuluStatus = MuluStatus.isDownloading;
long startTime= new Date().getTime();
Log.d(TAG,String.format("prepare book loadChapts----start download %s,maxAge %s, 目录 from %s", mNovel.getName() ,maxAge ,mSite.getMuluUrl() ));
new Thread(){
@Override
public void run() {
Log.d(TAG, "to get chaps............................>");
Log.d(TAG, "to get chaps siteRule:" +result);
Log.d(TAG, "to get chaps mulu:" + mSite.getMuluUrl() );
String[] chaps = new String[0];
try {
JSONObject siteJson = new JSONObject(result);
mChapters = NovelParseUtil.getChapters(mSite.getMuluUrl(), siteJson,mSite.getDomain(),maxAge,mSiteRule);
if (mChapters != null){
/* int i=0;
for (Chapter chapter:mChapters) {
i++;
// Log.i(TAG, String.format("prepare book to get chaps readChaptersAsync %s-->%s",chapter.getChapterUrl(), chapter.getChapterName()));
if(chapter.getChapterName().equals(mNovel.getChapterName())){
if(i<mChapters.size()-1){
mNovel.setChapterName(mChapters.get(mChapters.size()-1).getChapterName());
break;
}
}
}*/
checkLastUpdatedChapter();
}
} catch (JSONException e) {
Log.e(TAG, "prepare book error on parese :", e);
}
if (mChapters == null ||mChapters.size()== 0) {
readChaptersAsync();
}else
{
mMuluStatus = MuluStatus.isDone;
handler.sendEmptyMessage(MSG_READCHAPTER_SUCCESS);
Log.d(TAG,String.format("prepare book loadChapts----end download %s 目录, 目录数量 %s, cost %s", mNovel.getName() , mChapters.size(), new Date().getTime() -startTime ));
Log.d(TAG, String.format("prepare book %s 章节信息完成.",mNovel.getName()) );
}
}
}.start();
}else{
readChaptersAsync();
}
}
@Override
public void onFault(String errorMsg) {
//失败
Log.e(TAG,"error on get sitRule: "+errorMsg);
siteRuleRetryCnt++;
if(siteRuleRetryCnt <Constants.retryCnt){
getSiteRule();
}
}
},mContext));
/*
if(mSiteRule==null && mSite!=null) {
List<SiteRule> srs = LitePal.where("domain=?", mSite.getDomain()).find(SiteRule.class);
if (srs.size() > 0) {
mSiteRule = srs.get(0);
}
}*/
}
void reportMsg(String reportType,String url,String content,String headers,String httpStatus){
String msg = String.format("{\"type\":%s, \"id\":%s ,\"name\":\"%s\",\"site\":\"%s\",\"url\":\"%s\",\"content\":\"%s\",\"requestHeader\":\"%s\",\"httpStatus\":\"%s\"}," ,
reportType ,mNovel.getId(),mNovel.getName(),mSite.getDomain(),url ,content,headers,httpStatus);
msg = String.format("{\"type\":\"%s\", \"novelId\":%s ,\"site\":\"%s\",\"url\":\"%s\",\"httpStatus\":\"%s\",\"content\":\"%s\"}" ,
reportType ,mNovel.getNovelId(),mSite.getDomain(),url ,httpStatus,content);
BookSubscribe.reportMsg(reportType ,msg);
}
private void checkLastUpdatedChapter() {
/* if(BuildConfig.LOG_DEBUG) {
if(mChapters.size()>1) {
reportMsg(Constants.REPORT_NEW_UPDATE,"", mNovel.getChapterName()+" 测试用例", "", "");
}
}*/
for (int i=mChapters.size()-1;i>0;i--) {
if(mChapters.get(i).getChapterName().equals(mNovel.getChapterName())){
if(i<mChapters.size()-1){
mNovel.setChapterName(mChapters.get(mChapters.size()-1).getChapterName());
Log.d(TAG, "prepare book: set novel lastchaptname " + mNovel.getLastReadChapt());
reportMsg(Constants.REPORT_NEW_UPDATE,"",mNovel.getChapterName(),"","");
break;
}
}
}
if( mChapters.size()> mNovel.getChaptCnt()){
mNovel.setChaptCnt(mChapters.size());
mNovel.update(mNovel.getId());
}
}
public void setChapterNo(int chapterNo) {
this.chapterNo = chapterNo;
/*
if(chapterNo <= mChapters.size()) {
this.chapterNo = chapterNo;
}else{
Log.d(TAG, String.format("setChapterNo: wrong chapno for book %s,site %s,total chapts %s,chaptNo %s" ,mNovel.getName(),mNovel.getDomain(),getChapters().size(),chapterNo));
}
*/
}
public int getChapterNo() {
if(chapterNo > mChapters.size()){
// Log.d(TAG, String.format(" prepare book getChapterNo ,chapterNo %s, getChapters().size() %s ,mChangeChapId %s" ,chapterNo , mChapters.size(),mChangeChapId) );
if(mChangeChapId>0){
return mChangeChapId;
}
// chapterNo=1;
}
chapterNo = chapterNo<=0 ?1 :chapterNo;
// Log.d(TAG, String.format(" prepare book getChapterNo ,chapterNo %s, getChapters().size() %s " ,chapterNo , mChapters.size()) );
return chapterNo;
}
private int chapterNo;//当前章节
public String getLineBreakChar(){
return "\n";
}
public BookUtil(){
checkAndCreateDir(storagePath);
checkAndCreateDir(chapterPath);
checkAndCreateDir(cachedPath);
}
public boolean isBusy() {
return false;
}
private long tmpChaptLen=0;
public void setTmpChaptLen(long tmpChaptLen) {
this.tmpChaptLen = tmpChaptLen;
}
private boolean isChangeSource =false;
private int mChangeChapId;
private String mChangeTitle;
public void changeSite(String domain){
for (Site site:mNovelSites.getSites() ) {
if(site.getDomain().equals(domain)){
mSite = site;
break;
}
}
setSiteInfo();
isChangeSource = true;
mChapters.clear();
Log.e(TAG, "prepare book: clearbook called ,changeSite()" );
getSiteRule();
}
public void changeSource(String domain,int chapId,String chapTitle) {
Log.d(TAG, String.format("changing Source: target domain %s chaptId %s, chapt title %s ",domain,chapId,chapTitle) );
clearBook();
// isDownloadChapt =false;
mChangeChapId = chapId;
mChangeTitle =chapTitle;
changeSite(domain);
BookTask btsk = new BookTask();
btsk.execute( domain, chapId+"", chapTitle);
}
public Site getSite() {
return mSite !=null? mSite :new Site();
}
public boolean chaptCached(int num) {
File f = new File(fileChapterName(num));
return f.exists();
}
public boolean retryDownLoadContent(int chaptId) {
if(chaptDownStatus.containsKey(chaptId)){
Log.d(TAG, "prepare book retryDownLoadContent:chaptDownStatus.get(chaptId) " +chaptDownStatus.get(chaptId) );
if( chaptDownStatus.get(chaptId) == DownloadStatus.failure){
if(fileRetryCnt.containsKey(chaptId)){
if(fileRetryCnt.get(chaptId) < Constants.retryCnt){
fileRetryCnt.put(chaptId,fileRetryCnt.get(chaptId)+1 );
}else{
return false;
}
}else {
fileRetryCnt.put(chaptId, 1 );
}
}else{
return false;
}
}else{
Log.d(TAG, "prepare book retryDownLoadContent:to start new download " );
chaptDownStatus.put(chaptId,DownloadStatus.downloading);
fileRetryCnt.put(chaptId,1 );
}
if(fileRetryCnt.get(chaptId) < Constants.retryCnt) {
try {
loadChaptContent(chaptId);
chaptDownStatus.put(chaptId,DownloadStatus.downloading);
} catch (JSONException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
return false;
}
/**
* delete cache chapter file
* and reload the chapter
*/
public void refreshChapter() {
File file = new File(fileChapterName(chapterNo));
if(file.exists()){
file.delete();
}
fileRetryCnt.clear();
chaptDownStatus.clear();
if(chaptCache.containsKey(chapterNo)){
chaptCache.remove(chapterNo);
}
if(pagefactory!=null) {
pagefactory.changeChapter(chapterNo);
}
}
private class BookTask extends AsyncTask<String,Void,Boolean> {
private String domain;
private int chapId;
private String chapTitle;
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
Log.d("onPostExecute",isCancelled() + "");
if (isCancelled()){
return;
}
if (result) {
Log.d(TAG, String.format("changing Source: target domain %s chaptId %s, chapt title %s,mChangeChapId %s "
,domain,chapId,chapTitle,mChangeChapId) );
int chId=chapId;//chapterNo;
String title ="";
mChangeChapId = mChangeChapId >=1 ?mChangeChapId :1;
if( mChapters.size() >= mChangeChapId && mChapters.get(mChangeChapId-1)!=null ){
title= mChapters.get(mChangeChapId-1).getChapterName();
Log.d(TAG, "changing Source:chapter name in new site " + title );
}
if(title.equals(mChangeTitle) || title.contains(mChangeTitle) || mChangeTitle.contains(title)) {
Log.d(TAG, "changing Source:successed find chapter by original chaptId " + mChangeChapId + ":" + mChangeTitle);
chId = mChangeChapId;
}
else {
int i = 1;
for (Chapter chapter : mChapters) {
// Log.d(TAG, "changing Source: finding chapter " + i + ":" + chapter.getChapterName());
if (chapter.getChapterName().equals(mChangeTitle)) {
Log.d(TAG, "changing Source:successed find chapter by original title " + i + ":" + mChangeTitle);
chId = i;
title = mChangeTitle;
break;
}
i++;
}
if (!title.equals(mChangeTitle)) {
i = 1;
for (Chapter chapter : mChapters) {
// Log.d(TAG, "changing Source: finding chapter " + i + ":" + chapter.getChapterName());
if ( mChangeTitle.contains(chapter.getChapterName())
||chapter.getChapterName().contains(mChangeTitle)
){
Log.d(TAG, "changing Source:successed find chapter by original title " + i + ":" + mChangeTitle);
chId = i;
title = chapter.getChapterName();
break;
}
i++;
}
i = 1;
for (Chapter chapter : mChapters) {
// Log.d(TAG, "changing Source: finding chapter " + i + ":" + chapter.getChapterName());
if ( chapter.getChapterName().startsWith(mChangeTitle)
||mChangeTitle.startsWith(chapter.getChapterName() )
){
Log.d(TAG, "changing Source:successed find chapter by original title " + i + ":" + mChangeTitle);
chId = i;
title = chapter.getChapterName();
break;
}
i++;
}
}
/* if (!title.contains(mChangeTitle)) {
i = 1;
for (Chapter chapter : mChapters) {
// Log.d(TAG, "changing Source: finding chapter " + i + ":" + chapter.getChapterName());
if (mChangeTitle.contains(chapter.getChapterName())) {
Log.d(TAG, "changing Source:successed find chapter by original title " + i + ":" + mChangeTitle);
chId = i;
title = chapter.getChapterName();
break;
}
i++;
}
}*/
}
chId = chId <= mChapters.size() ? chId: mChapters.size();
Log.d(TAG, "changing Source: to open chapter with new site source " + chId + " : "+ mChangeTitle );
if(pagefactory!=null)
pagefactory.changeChapter(chId);
// mChangeChapId=0;
// Toast.makeText(mContext,"换源成功",Toast.LENGTH_LONG).show();
}else{
Log.d(TAG, "changing Source: failed " );
}
}
@Override
protected Boolean doInBackground(String... params) {
domain = params[0];
chapId = Integer.parseInt( params[1]);
chapTitle = params[2];
int splet =0;
while(isChangeSource && splet <Constants.MAX_SLEEP_4_CHAPT_DOWNLOAD){
try {
Thread.sleep(50);
splet+=50;
Log.d(TAG, String.format("prepare book changing Source slept %s,isChangeSource %s ", splet, isChangeSource ));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
}
/**
* 新线程换源 handler 有问题
*/
/* public void changeSourceNewThread(String domain,int chapId,String chapTitle) {
Log.d(TAG, String.format("changing Source: target domain %s chaptId %s, chapt title %s ",domain,chapId,chapTitle) );
if(mSite.getDomain().equals(domain)){ //当前源
Log.d(TAG, "changing Source: same site with original " + domain);
// return;
}
mChangeChapId = chapId;
mChangeTitle =chapTitle;
for (Site site:mNovelSites.getSites() ) {
if(site.getDomain().equals(domain)){
mSite = site;
break;
}
}
setSiteInfo();
// showProgressDialog();
isChangeSource = true;
new Thread(){
@Override
public void run() {
Log.d(TAG, "changing Source: to get site rule" );
getSiteRule();
}
}.start();
*//*
while(isChangeSource){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
new Thread(){
@Override
public void run() {
Log.d(TAG, "changing Source: to get site rule" );
getSiteRule();
}
}.start();*//*
}
*/
enum MuluStatus{
isDownloading,
isDone,
failed
}
/*private void showProgressDialog(String title,boolean canBreak) {
if ( null == progressDialog) {
progressDialog =new ProgressDialog(mContext);
}
progressDialog.setMessage(title);
progressDialog.setCancelable(canBreak);
progressDialog.show();
// progressDialog.show(mContext,"网络不给力","正努力加载",false,true);
}
private void dismissProgressDialog() {
if ( null != progressDialog) {
progressDialog.dismiss();
}
}
*/
private void checkAndCreateDir(String path){
File file = new File(path);
if (!file.exists()){
file.mkdir();
}
}
int muluRetryCount =0;
void readChaptersAsync(){
readChaptersAsync(false);
}
void readChaptersAsync(boolean isForceRefresh ) {
if(mSite==null ||mSiteRule ==null){
Log.d(TAG,String.format("prepare book loadChapts failed---- %s ,mSite is null? %s ,mSiteRule ==null ? %s", mNovel.getName() ,mSite==null,mSiteRule ==null ));
return;
}
String url = mSite.getMuluUrl();
int maxAge= isForceRefresh ?0 : mNovel.getMaxAge();
if(!isHttpOrHttps(url)){
return ;
}
Request request = getTagRequest(url,REUtil.getDomain(url),maxAge);
mMuluStatus = MuluStatus.isDownloading;
long startTime= new Date().getTime();
Log.d(TAG,String.format("prepare book loadChapts----start download %s,maxAge %s, 目录 from %s", mNovel.getName() ,mNovel.getMaxAge() ,url ));
/* if(muluRetryCount<3){
muluRetryCount++;
return;
}
if(muluRetryCount>=3) {
return;
}*/
HttpMethods.getOkClient().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// Log.d(TAG, "onFailure: " + e.getMessage());
Log.e(TAG, "prepare book loadChapts---- failed: ", e);
Log.d(TAG, String.format("prepare book loadChapts---- failed %s 目录 from %s", mNovel.getName(), url));
// handler.sendEmptyMessage(3);
//TODO 如果是取消了访问,则返回
if (e.toString()!=null && e.toString().contains("closed") || e.getMessage()!=null && e.getMessage().contains("Canceled")) {
Log.d(TAG, String.format("prepare book loadChapts---- canceled %s 目录 from %s", mNovel.getName(), url));
return;
}
mMuluStatus = MuluStatus.failed;
if (muluRetryCount < Constants.muluRetryCnt) {
try {
long sleeptime =100;
if(mSiteRule!=null) {
sleeptime = mSiteRule.getMiniInterval4AccessChapter();
}
Thread.sleep(sleeptime);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
muluRetryCount++;
Log.d(TAG,String.format("prepare book loadChapts----failed, retrying count %s",muluRetryCount ));
readChaptersAsync();
return;
}
Log.d(TAG,String.format("prepare book loadChapts----failed, site count %s",mNovelSites.getSites().length ));
if (mNovelSites.getSites().length == 1) { //仅有一个rule,且失败了
// mMuluStatus = MuluStatus.failed;
reportMsg( Constants.REPORT_REX_MULU_ERROR,url,"",getRequestHeader(request), "");
return;
}
//try next site
Message msg =Message.obtain();
msg.what =MSG_READCHAPTER_FAIL;
msg.arg1 =chapterNo;
Bundle bundleData = new Bundle();
for (Site st : mNovelSites.getSites()) {
if (!st.getDomain().equals(mSite.getDomain())) {
//mSite = st;
mNovel.setDomain(st.getDomain());
mNovel.setDomainName(mSite.getName());
bundleData.putString("siteName", st.getName());
msg.setData(bundleData);
break;
}
}
// mNovel.setDomain(mSite.getDomain());
muluRetryCount=0;
fileRetryCnt.clear();
siteRuleRetryCnt=0;
chaptCache.clear();
handler.sendMessage(msg);
// handler.sendEmptyMessage(MSG_READCHAPTER_FAIL);
// readChaptersAsync();
}
@Override
public void onResponse(Call call, Response response){
ResponseBody body = response.body();
/* if(BuildConfig.LOG_DEBUG) {
reportMsg( Constants.REPORT_REX_MULU_ERROR,url,"",getRequestHeader(request),response.code()+"");
}*/
if(response.code()!=200){
Log.d(TAG,String.format("prepare book loadChapts----failed, %s 目录 from %s,return code %s", mNovel.getName() ,url,response.code() ));
// handler.sendEmptyMessage(3);
mMuluStatus = MuluStatus.failed;
if(muluRetryCount <Constants.retryCnt){
Log.d(TAG,String.format("prepare book loadChapts----failed, response code %s retrying count %s",response.code(), muluRetryCount ));
muluRetryCount++;
readChaptersAsync();
}else{
reportMsg( Constants.REPORT_REX_MULU_ERROR,url,"",getRequestHeader(request),response.code()+"");
}
return;
}
// mChangeChapId =0;
muluRetryCount =0;
if (body != null) {
Log.d(TAG, String.format("prepare book %s 章节信息读取成功.thread %s",mNovel.getName(),Thread.currentThread().getName()) );
try {
// String bodyStr = body.string();
String bodyStr =NovelParseUtil.enconding(body ,mSiteRule.getEncoding());
// Log.d(TAG, "onResponse: " +bodyStr);
// Log.d(TAG,String.format("loadChaptContent----end download %s 目录, 目录数量 %s, cost %s", mNovel.getName() , mChapters.size(), new Date().getTime() -startTime ));
// long startTime2= new Date().getTime();
buildChapters(bodyStr,url);
Log.d(TAG,String.format("prepare book loadChapts----end download %s 目录, 目录数量 %s, cost %s", mNovel.getName() , mChapters.size(), new Date().getTime() -startTime ));
mMuluStatus = MuluStatus.isDone;
Log.d(TAG, String.format("prepare book %s 章节信息完成.",mNovel.getName()) );
handler.sendEmptyMessage(MSG_READCHAPTER_SUCCESS); //通知换源过程已读到目录
} catch (IOException e) {
e.printStackTrace();
}finally {
body.close();
}
}
}
});
}
void buildChapters( String content ,String url){
// Log.d(TAG, "buildChapters: " +content);
if(mSite==null ||mSiteRule ==null){
Log.d(TAG,String.format("prepare book buildChapters failed---- %s ,mSite is null? %s ,mSiteRule ==null ? %s", mNovel.getName() ,mSite==null,mSiteRule ==null ));
return;
}
try {
JSONObject siteJson = new JSONObject();
siteJson.put("chapterUrlPattern", mSiteRule.getChapterUrlPattern());
/* if(mSiteRule.getDomain().equals("qu.la")){
siteJson.put("chapterUrlRegexOnMulu", "<dd> <a[^>]*href=\"(/book/[\\d]+/[\\d]+\\.html)\">([^<]+)</a></dd>");
}else{
siteJson.put("chapterUrlRegexOnMulu", mSiteRule.getChapterUrlRegexOnMulu());
}*/
siteJson.put("chapterUrlRegexOnMulu", "");
//
mChapters = NovelParseUtil.getChapters(mSite.getDomain(),url, content, siteJson);
Log.d(TAG,String.format("mulu on Site %s download status %s",mSite.getDomain(),mMuluStatus));
checkLastUpdatedChapter();
} catch (JSONException e) {
// } catch (JSONException | IOException e) {
Log.e(TAG,String.format("prepare book, mulu on Site %s download status %s",mSite.getDomain(),mMuluStatus),e);
e.printStackTrace();
} finally {
// result.close();
// if (result2 != null) result2.close();
}
}
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,int chaptId){
// Log.d(TAG, String.format(" loadchapt next() back %s, chaptId %s, position %s, tmpChaptLen %s",back,chaptId,charPosition.get(chaptId),tmpChaptLen ));
charPosition.put(chaptId,charPosition.get(chaptId)+1) ;
if (charPosition.get(chaptId) > tmpChaptLen){
charPosition.put(chaptId,tmpChaptLen) ;
return -1;
}
char result = chaptCurrent(chaptId); //current();
if (back) {
charPosition.put(chaptId,charPosition.get(chaptId)-1) ;
}
return result;
}
/*public char[] nextLine(){
if (position >= tmpChaptLen){
return null;
}
String line = "";
while (position < tmpChaptLen){
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(int chaptId){
// chapterNo = mChapters.size() < chapterNo ? 1 : chapterNo;
// Log.d(TAG, String.format(" loadchapt chaptCurrent() ,chapterNo %s, getChapters().size() %s " ,chaptId , mChapters.size()) );
char[] charArray = chaptChars(chaptId);
int i = (int) (charPosition.get(chaptId) -1);//(int)position-1;
i =i>0?i:0;
i = i< charArray.length? i:charArray.length-1;
// Log.d(TAG, String.format(" loadchapt chaptCurrent() char position %s - %s, char '%s' " ,i,charPosition.get(chaptId) -1,charArray[i]) );
return charArray[i];
}
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 >= charPosition.get(chaptId) ){
cachePos = i;
pos = (int) (charPosition.get(chaptId) - 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(int chaptId){
return charPosition.get(chaptId);
}
public void setPostition(int chaptId,long position){
charPosition.put(chaptId,position) ;
}
//缓存书本
private void cacheBook() throws IOException {
if (TextUtils.isEmpty(mNovel.getCharset())) {
m_strCharsetName = FileUtils.getCharset(bookPath);
if (m_strCharsetName == null) {
m_strCharsetName = "utf-8";
}
ContentValues values = new ContentValues();
values.put("charset",m_strCharsetName);
LitePal.update(Novel.class,values,mNovel.getId());
}else{
m_strCharsetName = mNovel.getCharset();
}
File file = new File(bookPath);
InputStreamReader reader = new InputStreamReader(new FileInputStream(file),m_strCharsetName);
int index = 0;
bookLen = 0;
mChapters.clear();
Log.e(TAG, "prepare book: clearbook called ,cacheBook()" );
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;
Chapter bookChapter = null;
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());
mChapters.add(bookChapter);
}
bookChapter = new Chapter();
bookChapter.setNovelId(mNovel.getId());
bookChapter.setNovelChapterStartPos(start);
bookChapter.setChapterName(str.replaceAll("###",""));
bookChapter.setNovelPath(bookPath);
bookChapter.setIndex(chaptId+1);
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) {
bookChapter = new Chapter();
bookChapter.setNovelId(mNovel.getId());
bookChapter.setNovelChapterStartPos(start);
bookChapter.setChapterName(str.replaceAll("###",""));
bookChapter.setNovelPath(bookPath);
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();
}
/*
Chapter bookChapter = new Chapter();
bookChapter.setBookId(mNovel.getId());
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);
mChapters.add(bookChapter);
*/
}
}
if(writer!=null) {
writer.close();
}
if(bookChapter!=null) {
bookChapter.setLength((int)(size - start));
bookChapter.setChapterPath(fileChapterName(chaptId) );
bookChapter.update(bookChapter.getId());
mChapters.add(bookChapter);
}
}catch (Exception e){
e.printStackTrace();
}
}
void createChapContent(){
}
public List<Chapter> getmChapters(){
return mChapters;
}
public long getChapterLen(){
return chapterLen;
}
protected String fileName(int index) {
return cachedPath + mNovel.getName() + index ;
}
public String fileChapterName(int chaptId ) {
checkAndCreateDir(chapterPath);
if(mNovel!=null && !TextUtils.isEmpty(mNovel.getDomain())){
return getChapterPath() +mNovel.getDomain()+"/"+ chaptId ;
}
return getChapterPath() + chaptId ;
}
String getChapterPath(){
File file = new File(chapterPath +mNovel.getId());
if(!file.exists()){
file.mkdir();
}
return chapterPath +mNovel.getId()+"/";
}
//获取书本缓存
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;
}
/*boolean isDownloadChapt =false;
synchronized boolean getDownloadStatus(){
return isDownloadChapt;
}
synchronized void setDownloadFlag(boolean flag){
isDownloadChapt = flag;
Log.d(TAG,String.format("set download flat",isDownloadChapt) );
}*/
public ChangeSource pagefactory;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int wt = msg.what;
Log.d(TAG, String.format("prepare book get message what %s ,obj %s",msg.what,msg.obj));
handlerMsg(msg);
}
};
void handlerMsg(Message msg){
if (msg.what == MSG_FILLCONTENTDONE) {
// isDownloadChapt =true;
Log.d(TAG,String.format("handler msg, download %s",true) );
}else if(msg.what==MSG_READCHAPTER_FAIL){
try {
Toast.makeText(mContext, "网络拥堵,已帮您切换其它源", Toast.LENGTH_LONG).show();
}catch (Exception e){
Log.e(TAG, "handlerMsg:toast error ", e);
}
// getSiteRule();
// pagefactory .changeChapter(getChapterNo());
String targetSiteName = msg.getData().getString("siteName");
Log.d(TAG, String.format("prepare book changing Source:target %s -- %s to open chapter %s"
,mNovel.getDomain(),targetSiteName,msg.arg1));
if(pagefactory!=null)
pagefactory.changeSource(targetSiteName, mNovel.getDomain(),msg.arg1,getChapter(msg.arg1).getChapterName());
// isDownloadChapt =true;
// Toast.makeText(mContext,"网络错误",Toast.LENGTH_LONG).show();
}else if(msg.what==MSG_READCHAPTER_SUCCESS){ //change source
isChangeSource =false;
Log.d(TAG, "prepare book changing Source:successed get chapters for " + mSite.getDomain() );
/*if(isChangeSource){
Log.d(TAG, "changing Source:successed get chapters for " + mSite.getDomain() );
isChangeSource =false;
int chapId=chapterNo;
if( mChapters.size() >= mChangeChapId && mChapters.get(mChangeChapId-1)!=null ){
String title = mChapters.get(mChangeChapId-1).getChapterName();
Log.d(TAG, "changing Source:chapter name in new site " + title );
if(title.equals(mChangeTitle)) {
Log.d(TAG, "changing Source:successed find chapter by original chaptId " + mChangeChapId + ":" + mChangeTitle);
chapId = mChangeChapId;
}
}else{
int i =1;
for (Chapter chapter : mChapters) {
if (chapter.getChapterName().equals(mChangeTitle)) {
Log.d(TAG, "changing Source:successed find chapter by original title " +i + ":"+ mChangeTitle );
chapId = i;
break;
}
i++;
}
}
chapId = chapId <= mChapters.size() ? chapId: mChapters.size();
Log.d(TAG, "changing Source: to open chapter with new site source " + chapId + " : "+ mChangeTitle );
pagefactory.changeChapter(chapId);
}
*/
}else if(msg.what==MSG_READBOOK_FAIL ) { //change source
if(pagefactory!=null)
pagefactory.drawStatus(PageFactory.Status.FAIL);
}
else if(msg.what==MSG_READBOOK_EMPTY_CONTENT ) { //change source
if(pagefactory!=null)
pagefactory.drawStatus(PageFactory.Status.CONTENTPARESERROR);
}
}
private enum DownloadStatus{
notStart,
downloading,
failure,
success,
emptyContent
}
public boolean isChapterContentExist(int index) {
char[] block = null;
if (chaptCache.containsKey(Integer.valueOf(index))) {
block = chaptCache.get(index).getData().get();
}
if (block == null) {
// cleanCacheFile(); //to remove
File file = new File(fileChapterName(index));
if (!file.exists()) {
new Thread(){
@Override
public void run() {
try {
loadChaptContent(index);
} catch (JSONException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
return false;
}
}
return true;
}
//获取chapter 缓存
public char[] chaptChars(final int index) {
// Log.d(TAG, String.format("prepare book begin to load content for chapter %s ------------------------------------------------------------------------->", index));
char[] block=null;
if(chaptCache.containsKey(Integer.valueOf(index))) {
block = chaptCache .get(index).getData().get();
// Log.d(TAG, String.format("chaptChars get block in cache, chapter: %s", index));
}
// Log.d(TAG, String.format("prepare book begin to load content for chapter %s", index));
if (block == null) {
// cleanCacheFile(); //to remove
try {
File file = new File(fileChapterName(index));
Log.d(TAG, String.format("prepare book begin to load content for chapter %s,file exists?%s", index,file.exists()));
if ( !file.exists()) {
if(getNovel().isLocalBook()){
return "".toCharArray();
}
Log.d(TAG, String.format("prepare book loadChapts---- %s, 目录数量 %s, MuluStatus %s , mChapters.size() %s, thread %s", mNovel.getName(), mChapters.size(), mMuluStatus
, mChapters.size()
, Thread.currentThread().getName()));
if (mMuluStatus == null) {
Log.e(TAG, String.format("prepare book loadChapts---- 还未有目录信息,出错了 %s 目录, 目录数量 %s, MuluStatus %s ,thread %s", mNovel.getName(), mChapters.size(), mMuluStatus, Thread.currentThread().getName()));
getTargetSites();
}
if( mChapters.size() ==0) {
if (mMuluStatus != MuluStatus.isDownloading){
getSiteRule();
}
}
int maxSleep = Constants.MAX_SLEEP_4_CHAPT_DOWNLOAD;
int slepttime = 0;
while (NetUtil.isNetworkConnected() && slepttime < maxSleep &&(mMuluStatus ==null || mMuluStatus == MuluStatus.isDownloading)) {
try {
Thread.sleep(50);
slepttime+=50;
Log.d(TAG, String.format("prepare book loadChapts----等待中 %s 目录, 目录数量 %s, slept %s, MuluStatus %s", mNovel.getName(), mChapters.size(), slepttime, mMuluStatus));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!NetUtil.isNetworkConnected() || muluRetryCount >= Constants.retryCnt && (mChapters == null || mChapters.size() == 0)) {
Log.d(TAG, String.format("prepare book loadChapts----超时。。。或出错了 %s 目录, 目录数量 %s, slept %s, MuluStatus %s,thread %s", mNovel.getName(), mChapters.size(), slepttime, mMuluStatus, Thread.currentThread().getName()));
String error = "网络不给力";
return error.toCharArray();
}
Log.d(TAG, String.format("prepare book loadChaptContent----start %s", new Date().toString()));
Log.d(TAG, String.format("prepare book chaptDownStatus.containsKey %s ? %s", Integer.valueOf(index),chaptDownStatus.containsKey(Integer.valueOf(index))));
if (!chaptDownStatus.containsKey(Integer.valueOf(index)) || !file.exists() &&chaptDownStatus.get(Integer.valueOf(index))== DownloadStatus.success ) {
chaptDownStatus.put(index, DownloadStatus.downloading);
Log.d(TAG, String.format("prepare book put chaptDownStatus index %s,start to load chapcontent", index));
loadChaptContent(index);
}else{
Log.d(TAG, String.format("prepare book chaptDownStatus for chapt %s status %s", Integer.valueOf(index),chaptDownStatus.get(Integer.valueOf(index))));
}
Log.d(TAG, String.format(" prepare book loadChaptContent %s for downloading, chaptDownStatus %s, thread %s ",
index, chaptDownStatus.get(Integer.valueOf(index)), Thread.currentThread().getName()));
slepttime = 0;
// while(!file.exists() && !getDownloadStatus()){//&& slepttime <maxSleep){
// while( !getDownloadStatus() && slepttime <maxSleep){
/* while (NetUtil.isNetworkConnected() && !getDownloadStatus() && chaptDownStatus.get(Integer.valueOf(index)) == DownloadStatus.downloading && slepttime < maxSleep) {
Thread.sleep(50);
slepttime += 50;
Log.d(TAG, String.format(" prepare book loadChaptContent slept %s for downloading,isDownload %s thread %s ", slepttime, getDownloadStatus(), Thread.currentThread().getName()));
}*/
while (!file.exists() && NetUtil.isNetworkConnected() && chaptDownStatus.get(Integer.valueOf(index)) == DownloadStatus.downloading && slepttime < maxSleep ) {
Thread.sleep(200);
slepttime += 200;
Log.d(TAG, String.format(" prepare book loadChaptContent %s , slept %s for downloading, thread %s ",index, slepttime, Thread.currentThread().getName()));
}
Log.d(TAG, String.format("prepare book loadChaptContent slept %s for downloading ", slepttime));
if (chaptDownStatus.get(Integer.valueOf(index)) == DownloadStatus.failure) {
if (fileRetryCnt.containsKey(index)) {
fileRetryCnt.put(index, fileRetryCnt.get(index) + 1);
} else {
fileRetryCnt.put(index, 1);
}
Log.d(TAG, String.format("prepare book loadChaptContent %s, rertying count %s ",index, fileRetryCnt.get(index)));
if (fileRetryCnt.get(index) < Constants.retryCnt) {
Log.d(TAG, String.format("prepare book loadChaptContent ,rertying to download chapt %s ", index));
loadChaptContent(index);
chaptDownStatus.put(index, DownloadStatus.downloading);
if(mSiteRule!=null) {
Thread.sleep(mSiteRule.getMiniInterval4AccessChapter());
}
}
}
}
Log.d(TAG, String.format(" prepare book %s, file.exists()? %s", file.getPath(), file.exists()));
if (!file.exists()) {
String error = "";
if( !NetUtil.isNetworkConnected()){
error = "";
}
// loadChaptContent(index);
chaptDownStatus.put( index , DownloadStatus.failure);
Log.d(TAG, String.format("prepare book loadChaptContent retrying " ));
// return chaptChars( index);
handler.sendEmptyMessage(MSG_READBOOK_FAIL);
return error.toCharArray();
}
if( Constants.PRE_LOAD_CHAPT) {
if (mChapters.size() > index && NetUtil.isNetworkConnected()) {
if (!chaptDownStatus.containsKey(index + 1) || chaptDownStatus.get(index + 1).equals(DownloadStatus.failure)) {
File file2 = new File(fileChapterName(index + 1));
if (!file2.exists()) {
Log.d(TAG, String.format(" prepare book to load next chapt %s,down status %s ", index + 1, chaptDownStatus.get(index + 1)));
chaptDownStatus.put(index + 1, DownloadStatus.downloading);
loadChaptContent(index + 1);
}
}
}
if (index > 1 && index - 1 < mChapters.size() && NetUtil.isNetworkConnected()) {
if (!chaptDownStatus.containsKey(index - 1) || chaptDownStatus.get(index - 1).equals(DownloadStatus.failure)) {
File file2 = new File(fileChapterName(index - 1));
if (!file2.exists()) {
Log.d(TAG, String.format(" prepare book to load pre chapt %s,down status %s ", index - 1, chaptDownStatus.get(index - 1)));
chaptDownStatus.put(index - 1, DownloadStatus.downloading);
loadChaptContent(index - 1);
}
}
}
}
// mChangeChapId =0;
int size = (int) file.length();
if (size < 0) {
Log.e(TAG, "prepare book chaptChars: Error during reading"+ fileChapterName(index) );
// throw new RuntimeException("Error during reading " + fileChapterName(index));
}
block = new char[size / 2];
InputStreamReader reader = null;
try {
reader =
new InputStreamReader(
new FileInputStream(file),
charachterType
// mSiteRule.getEncoding()
);
long l = reader.read(block);
// Log.d(TAG, String.format("loadchapt: load from file chaptId %s,--->%s",index, new String(block )));
/* for (char c :block
) {
Log.d(TAG, String.valueOf(c));
}*/
if (reader.read(block) != block.length) {
// throw new RuntimeException("Error during reading " + fileChapterName(index));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
reader.close();
}
}
} catch (JSONException | IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
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));
Log.d(TAG, String.format("prepare book content reading finish, chapter %s", index));
}
return block;
}
boolean isHttpOrHttps(String url){
if(TextUtils.isEmpty(url) || !url.toLowerCase().startsWith("http") && !url.toLowerCase().startsWith("https")){
Log.d(TAG, String.format("prepare book isHttpOrHttps: url % is bad for request ",url));
return false ;
}
return true;
}
Map<Integer,Integer> fileRetryCnt = new HashMap<Integer,Integer>();
private void loadChaptContent(final int chapterIndex) throws JSONException, InterruptedException {
/* 章节内容没有缓存在本地
1. 根据本地的章节网络地址信息,读取章节内容到本地,若读取失败则
2. 查询主服务器若有地址更新则更新本地信息并重复1若没有更新地址则地址无效返回章节内容正待手打
*/
//
if(!NetUtil.isNetworkConnected() || mChapters.size()==0||mSite ==null){
handler.sendEmptyMessage(1);
return ;
}
final int index = mChapters.size() < chapterIndex ? 1 : chapterIndex;
if(mChapters.size() <chapterIndex){
Log.d(TAG,String.format("loadChaptContent----wrong index, chapter size %s,load index %s,bookname %s", mChapters.size(),index,mNovel.getName() ));
}
Chapter chapter = mChapters.get(index -1);
String refUrl=mSite.getMuluUrl();
if(index>1){
refUrl = mChapters.get(index -2).getChapterUrl();
}
String url = chapter.getChapterUrl();
if( TextUtils.isEmpty( url)){
handler.sendEmptyMessage(1);
return ;
}
long startTime= new Date().getTime();
Log.d(TAG,String.format("prepare book loadChaptContent----start download %s from %s", chapter.getChapterName() ,url ));
// setDownloadFlag(false);
JSONObject siteJson = new JSONObject();
siteJson.put("chapterContentRegex", mSiteRule.getChapterContentRegex());
siteJson.put("chapterContentDumpRegex", mSiteRule.getChapterContentDumpRegex());
if(!isHttpOrHttps(url)){
return ;
}
Request request = getTagRequest(url, refUrl,-1);
/*try {
Response response = HttpMethods.getOkClient().newCall(request).execute();
Log.d( TAG,String.format("prepare book loadChaptContent get response, thread %s", Thread.currentThread().getName()));
ResponseBody body = response.body();
if (body != null ) {
if(response.code()!=200){
Log.d(TAG, "prepare book loadChaptContent----network failure returnCode " + response.code());
// setDownloadFlag(true);
chaptDownStatus.put(index,DownloadStatus.failure);
Log.d( TAG,String.format("prepare book loadChaptContent error %s ,isDownloadChapt: %s", response.code(),false));
handler.sendEmptyMessage(1);
return;
}
try {
if(mSiteRule==null){
return;
}
*//* Charset charset = body.contentType().charset();
if(charset!=null){
String name = charset.displayName();
}*//*
// String bodyStr = body.string();
// bodyStr =NovelParseUtil.enconding(bodyStr,mSiteRule.getEncoding());
// Log.d( TAG,String.format("prepare book content %s", body ));
String bodyStr =NovelParseUtil.enconding(body,mSiteRule.getEncoding());
if(TextUtils.isEmpty(bodyStr)){
Log.e( TAG,String.format("prepare book loadChaptContent %s isEmpty,retry....", index, Thread.currentThread().getName()));
chaptDownStatus.put(index,DownloadStatus.failure);
return;
}
// Log.d( TAG,String.format("prepare book encoded content %s", bodyStr ));
bodyStr = NovelParseUtil.getChapterContent(bodyStr, siteJson);
*//* if(BuildConfig.LOG_DEBUG) {
reportMsg(Constants.REPORT_REX_CHAPTER_CONTENT_ERROR, url,"",getRequestHeader(request),200+"");//TODO: remove
}*//*
if(TextUtils.isEmpty(bodyStr) || bodyStr.trim().length()<10 ){
Log.e( TAG,String.format("prepare book loadChaptContent %s isEmpty,reporting empty ....", index ));
reportMsg(Constants.REPORT_REX_CHAPTER_CONTENT_ERROR, url,bodyStr,getRequestHeader(request),200+"");
chaptDownStatus.put(index,DownloadStatus.failure);
// bodyStr += Constants.BAD_CHAR +Constants.BAD_CHAR +"内容正在搜索中,请稍后刷新内容";
return;
}
String title = chapter.getChapterName();
String chapterContent = title+ "\n" + bodyStr;
char[] buf = chapterContent.toCharArray();
File file = new File(fileChapterName(index));
file.createNewFile();
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(index)), charachterType);//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小
// final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(index)), mSiteRule.getEncoding() );//charachterType);//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小
writer.write(buf);
writer.close();
Log.d( TAG,String.format("prepare book loadChaptContent file created: %s, thread %s", file.getPath(), Thread.currentThread().getName()));
handler.sendEmptyMessage(MSG_FILLCONTENTDONE);
// setDownloadFlag(true);
} catch (IOException | JSONException e) {
e.printStackTrace();
Log.e(TAG, "onResponse: prepare book error ",e );
chaptDownStatus.put(index,DownloadStatus.failure);
return;
// throw new RuntimeException("Error during writing " + fileChapterName( index));
}
finally {
body.close();
handler.sendEmptyMessage(MSG_FILLCONTENTDONE);
// setDownloadFlag(true);
}
chapter.setNovelId(mNovel.getId());
chapter.setChapterPath(fileChapterName(index));
chapter.setDomain(mSite.getDomain());
if(chapter.getId()>0) {
chapter.update(chapter.getId());
}else{
chapter.save();
}
//setDownloadFlag(true);
chaptDownStatus.put(index,DownloadStatus.success);
Log.d(TAG,String.format(" prepare book loadChaptContent---- finished download %s, cost time %s ,content path %s ,thread %s", chapter.getChapterName(), new Date().getTime() -startTime ,chapter.getChapterPath() , Thread.currentThread().getName() ));
}
} catch (IOException e) {
Log.e(TAG, "onResponse: prepare book error ",e );
}*/
HttpMethods.getOkClient().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
if(e!=null &&
(e.toString().contains("closed")
||e.getMessage().contains("Canceled")))
{
Log.d(TAG, "onFailure: prepare book cancled request ,return, " + request.url());
return;
}
// handler.sendEmptyMessage(MSG_FILLCONTENTDONE);
// handler.sendEmptyMessage(1);
chaptDownStatus.put(index,DownloadStatus.failure);
// setDownloadFlag(true);
Log.e( TAG,String.format("prepare book loadChaptContent %s fail, isDownloadChapt: %s",index,false),e);
// e.printStackTrace();
// throw new RuntimeException("Error during writing " + fileChapterName( index));
}
@Override
public void onResponse(Call call, Response response){
Log.d( TAG,String.format("prepare book loadChaptContent get response, thread %s", Thread.currentThread().getName()));
ResponseBody body = response.body();
if (body != null ) {
if(response.code()!=200){
Log.d(TAG, "prepare book loadChaptContent----network failure returnCode " + response.code());
// setDownloadFlag(true);
chaptDownStatus.put(index,DownloadStatus.failure);
Log.d( TAG,String.format("prepare book loadChaptContent error %s ,isDownloadChapt: %s", response.code(),false));
handler.sendEmptyMessage(1);
return;
}
try {
if(mSiteRule==null){
return;
}
Charset charset = body.contentType().charset();
if(charset!=null){
String name = charset.displayName();
}
// String bodyStr = body.string();
// bodyStr =NovelParseUtil.enconding(bodyStr,mSiteRule.getEncoding());
// Log.d( TAG,String.format("prepare book content %s", body ));
String bodyStr =NovelParseUtil.enconding(body,mSiteRule.getEncoding());
if(TextUtils.isEmpty(bodyStr)){
Log.e( TAG,String.format("prepare book loadChaptContent %s isEmpty,retry....", index, Thread.currentThread().getName()));
chaptDownStatus.put(index,DownloadStatus.failure);
return;
}
// Log.d( TAG,String.format("prepare book encoded content %s", bodyStr ));
bodyStr = NovelParseUtil.getChapterContent(bodyStr, siteJson);
/* if(BuildConfig.LOG_DEBUG) {
reportMsg(Constants.REPORT_REX_CHAPTER_CONTENT_ERROR, url,"",getRequestHeader(request),200+"");//TODO: remove
}*/
if(TextUtils.isEmpty(bodyStr) || bodyStr.trim().length()<10 ){
Log.e( TAG,String.format("prepare book loadChaptContent %s isEmpty,reporting empty ....", index ));
reportMsg(Constants.REPORT_REX_CHAPTER_CONTENT_ERROR, url,bodyStr,getRequestHeader(request),200+"");
chaptDownStatus.put(index,DownloadStatus.failure);
// bodyStr += Constants.BAD_CHAR +Constants.BAD_CHAR +"内容正在搜索中,请稍后刷新内容";
return;
}
String title = chapter.getChapterName();
String chapterContent = title+ "\n" + bodyStr;
char[] buf = chapterContent.toCharArray();
File file = new File(fileChapterName(index));
Log.d( TAG,"prepare book to create file " +fileChapterName(index));
file.createNewFile();
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(index)), charachterType);//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小
// final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(index)), mSiteRule.getEncoding() );//charachterType);//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小
writer.write(buf);
writer.close();
Log.d( TAG,String.format("prepare book loadChaptContent file created: %s, thread %s", file.getPath(), Thread.currentThread().getName()));
handler.sendEmptyMessage(MSG_FILLCONTENTDONE);
// setDownloadFlag(true);
} catch (IOException | JSONException e) {
e.printStackTrace();
Log.e(TAG, "onResponse: prepare book error ",e );
chaptDownStatus.put(index,DownloadStatus.failure);
return;
// throw new RuntimeException("Error during writing " + fileChapterName( index));
}
finally {
body.close();
handler.sendEmptyMessage(MSG_FILLCONTENTDONE);
// setDownloadFlag(true);
}
chapter.setNovelId(mNovel.getId());
chapter.setChapterPath(fileChapterName(index));
chapter.setDomain(mSite.getDomain());
if(chapter.getId()>0) {
chapter.update(chapter.getId());
}else{
chapter.save();
}
//setDownloadFlag(true);
chaptDownStatus.put(index,DownloadStatus.success);
Log.d(TAG,String.format(" prepare book loadChaptContent---- finished download %s, cost time %s ,content path %s ,thread %s", chapter.getChapterName(), new Date().getTime() -startTime ,chapter.getChapterPath() , Thread.currentThread().getName() ));
}
}
});
}
/***
*
* @param url
* @param maxAge
* @return
*/
private Request getTagRequest(String url, String refUrl ,int maxAge) {
try {
Request.Builder builder = new Request.Builder()
.tag(mNovel.getNovelId()) //标记 请求的tag,切换小说或离开小说界面(BookActivity) 时 取消未执行完毕的 此tag的所有请求
.url(url)
.removeHeader("Pragma");
if (!TextUtils.isEmpty(refUrl)) {
builder.header("Referer", refUrl);
}
for (int i = 0; i < mSiteRule.getHeaders().length; i += 2) {
builder.header(mSiteRule.getHeaders()[i], mSiteRule.getHeaders()[i + 1]);
}
if (mSiteRule.getUserAgents() != null && mSiteRule.getUserAgents().length > 0) {
String siteAgent = mSiteRule.getUserAgents()[new Random().nextInt(mSiteRule.getUserAgents().length - 1)];
Log.d(TAG, "prepare book on getTagRequest:add site user agent " + siteAgent);
builder.removeHeader("User-Agent").addHeader("User-Agent", siteAgent); //加 随机agent
} else {
builder.removeHeader("User-Agent").addHeader("User-Agent", HttpMethods.USERAGENT);
}
if (maxAge > 0) {
builder.header("Cache-Control", "public, max-age=" + maxAge);
}
return builder.build();
}catch (Exception er){
}
return null;
}
private String getRequestHeader(Request request) {
String head = String.format("\"Referer\":\"%s\",\"User-Agent\":\"%s\",\"Cache-Control\":\"%s\"",
request.header("Referer"),request.header("User-Agent"), request.header("Cache-Control")
);
for (int i = 0; i < mSiteRule.getHeaders().length; i += 2) {
head += String.format(",\"%s\":\"%s\"",mSiteRule.getHeaders()[i], mSiteRule.getHeaders()[i + 1]) ;
}
return "{" + head +"}";
}
public boolean isChapterTitle(String line){
return (line.length() <= 30 && (line.matches(".*第.{1,8}章.*") || line.matches(".*第.{1,8}节.*"))) ;
}
void clear(){
clearBook();
mContext=null;
this.pagefactory =null;
}
}