package com.novelbook.android.service; import android.app.IntentService; import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.text.TextUtils; import android.util.Log; import com.novelbook.android.db.Chapter; import com.novelbook.android.db.DownloadTask; import com.novelbook.android.db.SiteRule; import com.novelbook.android.netutils.HttpMethods; import com.novelbook.android.netutils.NetUtil; import com.novelbook.android.utils.BookUtil; import com.novelbook.android.utils.NovelParseUtil; import com.novelbook.android.utils.PageFactory; import org.json.JSONException; import org.json.JSONObject; import org.litepal.LitePal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; public class ServiceDownload extends Service { private static final String TAG ="ServiceDownload" ; private final String storagePath = BookUtil.storagePath; private final String cachedPath = BookUtil.cachedPath; private final String chapterPath = BookUtil.chapterPath; public final String EXTR_TASKID ="taskId"; public final String EXTR_OFFSET ="offset"; //多线程下载,每个线程取章节偏移数量 // public ServiceDownload(String name) { // super(name); // } /* public ServiceDownload() { super("ServiceDownload"); }*/ private Map siteRuleMap = new HashMap() ; // key = novelId private DownloadTask processingTask ; private int taskIndex=0; private int chaptIndex=0; private Map> tasksMap = new ConcurrentHashMap>();// key = taskId private Map> tasksMapDone = new ConcurrentHashMap>();// key = taskId @Override public void onCreate() { super.onCreate(); Log.d(TAG,"test service onCreate..."); /* downloadTasks =LitePal.where("status = ?","0").find(DownloadTask.class); for(DownloadTask dt :downloadTasks){ List chps =Chapter.getUnCachedChapters(dt.getNovelId(),dt.getDomain()); if(chps.size()>0) { SiteRule sr = SiteRule.getSiteRuleByDomain(dt.getDomain()); siteRuleMap.put(dt.getNovelId(), sr); tasksMap.put(dt.getId(), chps); } } for(DownloadTask tmp: downloadTasks){ Log.d(TAG, String.format("test service task list : %s",tmp.getId())); } */ } @Override public int onStartCommand(Intent intent, int flags, int startId){ // // QLog.getLogger().d( TAG,TAG+"... service NaviSvc onStartCommand.....," ); onHandleIntent(intent); // We want this service to continue running until it is explicitly // stopped, so return sticky. // return START_NOT_STICKY; //TODO: checking why can not stop the service. return START_STICKY; } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.d(TAG,"test service onStart"); } @Override public void onDestroy() { Log.d(TAG,"test service onDestroy"); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } private List downloadTasks = new ArrayList(); List cancelId =new ArrayList(); protected void onHandleIntent( Intent intent) { Log.d(TAG,"ServiceDownload onHandleIntent...begin"); if(null==intent){ return; } String key ="taskId"; int taskId =0; if ( intent.hasExtra(key)){ taskId= intent.getExtras().getInt(key); DownloadTask dt = LitePal.find(DownloadTask.class,taskId); if(intent.hasExtra("start")){ if(!intent.getBooleanExtra("start",true)){ cancelId.add(taskId); for(DownloadTask t : downloadTasks){ if( t.getId() == taskId){ t.setDownSatus(DownloadTask.DownStatus.暂停下载); break; } } stopTask(taskId); }else{ boolean newTask = true; if(cancelId.contains(taskId)){ cancelId.remove(cancelId.indexOf(taskId)); } for(DownloadTask t : downloadTasks){ if(t.getId() == taskId){ newTask =false; break; } } if(newTask) { startNewTask(dt); } else { for (DownloadTask t : downloadTasks) { if (t.getId() == taskId) { t.setDownSatus(DownloadTask.DownStatus.正在下载); break; } } } startTask(taskId); if(taskIndex==0) { // handler.sendEmptyMessage(1); } } } } else{ Log.d(TAG,"ServiceDownload downloadTask size: " +downloadTasks.size()); new Thread() { @Override public void run() { List tmp = LitePal.findAll(DownloadTask.class); if (downloadTasks.size() == 0) { taskIndex = 0; for (DownloadTask dt : tmp) { startNewTask(dt); } }else{ for (DownloadTask dt : tmp) { boolean exist = false; for(DownloadTask t: downloadTasks){ if(t.getId() == dt.getId()){ exist =true; break; } } if(!exist){ startNewTask(dt); } } } if (taskIndex == 0) { startTask(); // handler.sendEmptyMessage(1); } } }.start(); /* if(processingTask==null) { startTask(); }*/ } Log.d(TAG,"ServiceDownload test service onHandleIntent...over"); } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { /* if(msg.what ==1){ startTask(); }else*/ if (msg.what == 2) { if(!NetUtil.isNetworkConnected()) { /* Intent broadcastIntent = new Intent(); broadcastIntent.setAction("ServiceDownload.ChapterContent.finished"); broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); broadcastIntent.putExtra("network", "没有网络了,下载失败"); sendBroadcast(broadcastIntent);*/ return; } int tIndex = msg.getData().getInt("tIndex"); if (tIndex >= downloadTasks.size()) { return; } int taskId = downloadTasks.get(tIndex).getId(); /* if( downloadTasks.get(taskId).getDownSatus() == DownloadTask.DownStatus.等待下载){ return; } if(tasksMap.get(processingTask.getId())==null){ return; } */ if (tasksMap.get(taskId) == null) { return; } Log.d(TAG, String.format("%s start new chapt download---- taskId :%s, chapter count %s,chaptIndex %s", TAG , processingTask.getId(), tasksMap.get(processingTask.getId()).size(), chaptIndex)); // if(taskIndex==tIndex+1){ Intent broadcastIntent = new Intent(); broadcastIntent.setAction("ServiceDownload.ChapterContent.finished"); broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); broadcastIntent.putExtra("progress", processingTask.getFinishedChpats() > processingTask.getTotalChapts() ? processingTask.getTotalChapts() : processingTask.getFinishedChpats()); broadcastIntent.putExtra("novelId", processingTask.getNovelId()); broadcastIntent.putExtra("taskId", processingTask.getId()); sendBroadcast(broadcastIntent); // } // if (downloadTasks.get(tIndex).getDownSatus() != DownloadTask.DownStatus.正在下载) { startNewTask(); return; } // if( Chapter.getUnCachedChapters(taskId).size() >0 ){ //if( tasksMap.get(taskId).size()-1 > chaptIndex){ if (tasksMap.get(taskId).size() - 1 > chaptIndex) { chaptIndex++; // } /* else{ startNewTask(); return; }*/ } else { Log.d(TAG, String.format("%s task done ---- taskId :%s, chapter count %s,chaptIndex %s", TAG, processingTask.getId(), tasksMap.get(processingTask.getId()).size(), chaptIndex)); //章节全部处理完毕了 if (taskId == processingTask.getId() && tasksMapDone.get(taskId) != null && tasksMapDone.get(taskId).size() == tasksMap.get(taskId).size()) { int abc = 1; abc++; processingTask.setFinishedChpats(processingTask.getTotalChapts()); processingTask.setStatus(1); processingTask.setDownSatus(DownloadTask.DownStatus.下载完成); processingTask.update(processingTask.getId()); tasksMap.remove(taskId); broadcastIntent = new Intent(); broadcastIntent.setAction("ServiceDownload.ChapterContent.finished"); broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); broadcastIntent.putExtra("progress", processingTask.getTotalChapts()); broadcastIntent.putExtra("status", processingTask.getStatus()); broadcastIntent.putExtra("taskId", processingTask.getId()); sendBroadcast(broadcastIntent); } startNewTask(); return; } doDownloadContent(tIndex, chaptIndex); } } }; void startNewTask(){ if(!NetUtil.isNetworkConnected()){ Intent broadcastIntent = new Intent(); broadcastIntent.setAction("ServiceDownload.ChapterContent.finished"); broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); broadcastIntent.putExtra("network", "没有网络了,下载失败"); sendBroadcast(broadcastIntent); return; } taskIndex=0; startTask(); //auto start next task } private void stopTask(int taskId) { if(processingTask==null || processingTask.getId()==taskId) { //startTask();//to start next } } /** * 启动指定task * @param taskId */ private void startTask(int taskId) { if ( downloadTasks.size()==0){ return; } if(processingTask!=null) { processingTask.update(processingTask.getId()); } taskIndex=0; for(DownloadTask task : downloadTasks) { if(task.getId()==taskId) { processingTask = task; break; } taskIndex++; } processingTask.setDownSatus(DownloadTask.DownStatus.正在下载); chaptIndex=0; doDownloadContent(taskIndex,chaptIndex); } /** * auto next task */ private void startTask() { if ( downloadTasks.size()==0 || downloadTasks.size() <=taskIndex){ Log.d(TAG,"ServiceDownload No Task to do, exit."); return; } if(processingTask!=null) { processingTask.update(processingTask.getId()); } Log.d(TAG,"ServiceDownload start task to download,index " +taskIndex); /* if(downloadTasks.size() <=taskIndex){ taskIndex=0; processingTask =null; // Log.d(TAG,"No task to download."); // return; }*/ processingTask = downloadTasks.get(taskIndex); if(processingTask.getDownSatus()== DownloadTask.DownStatus.暂停下载 ||processingTask.getDownSatus()== DownloadTask.DownStatus.下载完成 ){ taskIndex++; startTask(); return; } processingTask.setDownSatus(DownloadTask.DownStatus.正在下载); chaptIndex=0; doDownloadContent(taskIndex,chaptIndex); if(taskIndex < downloadTasks.size()-1){ taskIndex++; } } void doDownloadContent(int taskIndex,int chaptIndex){ Message mg = Message.obtain(); mg.what =2; Bundle bundleData = new Bundle(); bundleData.putInt("tIndex", taskIndex); mg.setData(bundleData); Log.d(TAG, String.format("ServiceDownload--- taskId: %s ",processingTask.getId())); List chps = tasksMap.get(processingTask.getId()); if(chps!=null && chps.size()>0 && chps.size() >chaptIndex) { Chapter chapter = chps.get(chaptIndex); /* while(!NetUtil.isNetworkConnected() ){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } }*/ if (NetUtil.isNetworkConnected() && TextUtils.isEmpty(chapter.getChapterPath())) { try { ServiceDownload(taskIndex,chapter); } catch (JSONException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); // } catch (IOException e) { // e.printStackTrace(); } } else { handler.sendMessage(mg); } }else{ handler.sendMessage(mg); } } void startNewTask(DownloadTask dt){ if(dt!=null){ List chps = Chapter.getUnCachedChapters(dt.getId()); if(chps==null || chps.size()==0){ return; } SiteRule siteRule = SiteRule. getSiteRuleByDomain(dt.getDomain()); if(siteRule==null){ //to do get siterule from web Log.d(TAG, String.format("没找到site rule: %s",dt.getDomain())); }else { siteRuleMap.put(dt.getNovelId(),siteRule); downloadTasks.add(dt); /* for(DownloadTask tmp: downloadTasks){ Log.d(TAG, String.format("test service task list : %s",tmp.getId())); }*/ tasksMap.put(dt.getId(), chps); /* if(taskIndex==0) { startTask(); // handler.sendEmptyMessage(1); }*/ } } /* new Thread() { @Override public void run() { } }.start(); */ } private void ServiceDownload(int taskIndex, Chapter chapter ) throws JSONException, InterruptedException { int taskID= downloadTasks.get(taskIndex).getId() ; int msg =2; Message mg = Message.obtain(); mg.what =msg; Bundle bundleData = new Bundle(); bundleData.putInt("tIndex", taskIndex); mg.setData(bundleData); String url = chapter.getChapterUrl(); if( TextUtils.isEmpty( url)){ // handler.sendEmptyMessage(msg); handler.sendMessage(mg); return ; } long startTime= new Date().getTime(); Log.d(TAG,String.format("ServiceDownload----start download %s from %s", chapter.getChapterName() ,url )); // Log.d( "ServiceDownload",String.format("ServiceDownload isDownloadChapt: %s",isDownloadChapt)); JSONObject siteJson = new JSONObject(); siteJson.put("chapterContentRegex", siteRuleMap.get(chapter.getNovelId()).getChapterContentRegex()); siteJson.put("chapterContentDumpRegex", siteRuleMap.get(chapter.getNovelId()).getChapterContentDumpRegex()); Request request = getTagRequest(url); HttpMethods.getOkClient().newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { handler.sendMessage(mg); // handler.sendEmptyMessage(msg); // Log.d( "ServiceDownload",String.format("ServiceDownload fail, isDownloadChapt: %s",isDownloadChapt)); e.printStackTrace(); // throw new RuntimeException("Error during writing " + fileChapterName( index)); } @Override public void onResponse(Call call, Response response){ ResponseBody body = response.body(); if (body != null ) { if(response.code()!=200){ Log.d(TAG, "ServiceDownload----network failure returnCode " + response.code()); // setDownloadFlag(true); // chaptDownStatus.put(index, BookUtil.DownloadStatus.failure); // Log.d( "ServiceDownload",String.format("ServiceDownload error %s ,isDownloadChapt: %s", response.code(),isDownloadChapt)); // handler.sendEmptyMessage(msg); handler.sendMessage(mg); return; } try { // NovelParseUtil.enconding(body, siteRuleMap.get(chapter.getNovelId()).getEncoding()); String bodyStr =NovelParseUtil.enconding(body, siteRuleMap.get(chapter.getNovelId()).getEncoding());; //body.string(); String title = chapter.getChapterName(); String chapterContent = title+ "\n" + NovelParseUtil.getChapterContent(bodyStr, siteJson); char[] buf = chapterContent.toCharArray(); File file = new File(fileChapterName(chapter)); file.createNewFile(); final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(chapter)), "utf-8");//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小 writer.write(buf); writer.close(); Log.d( "ServiceDownload",String.format("ServiceDownload file created: %s", file.getPath())); processingTask.setFinishedChpats(processingTask.getFinishedChpats()+1); processingTask.update(processingTask.getId()); Thread.sleep(siteRuleMap.get(chapter.getNovelId()).getMiniInterval4AccessChapter()); // setDownloadFlag(true); } catch (IOException | JSONException | InterruptedException e) { e.printStackTrace(); // throw new RuntimeException("Error during writing " + fileChapterName( index)); } finally { body.close(); // setDownloadFlag(true); } chapter.setChapterPath(fileChapterName(chapter)); chapter.update(chapter.getId()); if(! tasksMapDone.containsKey(taskID)){ tasksMapDone.put(taskID,new ArrayList()); } tasksMapDone.get(taskID).add(chapter); handler.sendMessage(mg); //setDownloadFlag(true); // chaptDownStatus.put(index, BookUtil.DownloadStatus.success); Log.d(TAG,String.format("ServiceDownload---- finished download %s, cost time %s ,content path %s ", chapter.getChapterName(), new Date().getTime() -startTime ,chapter.getChapterPath() )); } } }); } /** * 直接 * @param chapter * @throws JSONException * @throws InterruptedException private void ServiceDownload2( Chapter chapter ) throws IOException, JSONException, InterruptedException { int msg = 2; String url = chapter.getChapterUrl(); if (TextUtils.isEmpty(url)) { handler.sendEmptyMessage(msg); return; } long startTime = new Date().getTime(); Log.d(TAG, String.format("ServiceDownload----start download %s from %s", chapter.getChapterName(), url)); // Log.d( "ServiceDownload",String.format("ServiceDownload isDownloadChapt: %s",isDownloadChapt)); JSONObject siteJson = new JSONObject(); siteJson.put("chapterContentRegex", siteRuleMap.get(chapter.getNovelId()).getChapterContentRegex()); siteJson.put("chapterContentDumpRegex", siteRuleMap.get(chapter.getNovelId()).getChapterContentDumpRegex()); Request request = getTagRequest(url); Response response = HttpMethods.getOkClient().newCall(request).execute(); ResponseBody body = response.body(); if (body != null) { if (response.code() != 200) { Log.d(TAG, "ServiceDownload----network failure returnCode " + response.code()); // setDownloadFlag(true); // chaptDownStatus.put(index, BookUtil.DownloadStatus.failure); // Log.d( "ServiceDownload",String.format("ServiceDownload error %s ,isDownloadChapt: %s", response.code(),isDownloadChapt)); handler.sendEmptyMessage(msg); return; } try { String bodyStr = body.string(); String title = chapter.getChapterName(); String chapterContent = title + "\n" + NovelParseUtil.getChapterContent(bodyStr, siteJson); char[] buf = chapterContent.toCharArray(); File file = new File(fileChapterName(chapter)); file.createNewFile(); final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(chapter)), "utf-8");//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小 writer.write(buf); writer.close(); Log.d("ServiceDownload", String.format("ServiceDownload file created: %s", file.getPath())); processingTask.setFinishedChpats(processingTask.getFinishedChpats() + 1); processingTask.update(processingTask.getId()); Thread.sleep(siteRuleMap.get(chapter.getNovelId()).getMiniInterval4AccessChapter()); // setDownloadFlag(true); } catch (IOException | JSONException | InterruptedException e) { e.printStackTrace(); // throw new RuntimeException("Error during writing " + fileChapterName( index)); } finally { body.close(); // setDownloadFlag(true); } chapter.setChapterPath(fileChapterName(chapter)); chapter.update(chapter.getId()); handler.sendEmptyMessage(msg); //setDownloadFlag(true); // chaptDownStatus.put(index, BookUtil.DownloadStatus.success); Log.d(TAG, String.format("ServiceDownload---- finished download %s, cost time %s ,content path %s ", chapter.getChapterName(), new Date().getTime() - startTime, chapter.getChapterPath())); } } */ protected String fileChapterName(Chapter chapter ) { return getChapterPath(chapter.getNovelId()) +chapter.getDomain()+"/"+ chapter.getIndex() ; } String getChapterPath(int novelId){ File file = new File(chapterPath +novelId); if(!file.exists()){ file.mkdir(); } return chapterPath +novelId+"/"; } private Request getTagRequest(String url) { return new Request.Builder() .tag(processingTask.getNovelId()) //标记 .url(url) // .header("User-Agent", "OkHttp Example") .build(); } }