Service takes too much memory - java

I have a service that extracting html code from an URL, converting it to text only (with Jsoup) and then checks something on the string, and if some conditions are true it launches a notification and writes something to a file.
As far as I know, this kind of service shouldn't take much memory, and in Watchdog, it takes ~65 MB, and it is way too much. It takes more than any other process (even more than tw launcher and Android System).
I would like you to tell me what have I done wrong.
Heres my service class:
public class NotifyService extends Service
{
private int number=0;
private Timer timer=new Timer();
private long INTERVAL=1*1000*60*60;//1 hour
public static String Oldhtml;
public static String Newhtml;
public static String currHtml;
// hooks main activity here
/*
* not using ipc...but if we use in future
*/
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate()
{
super.onCreate();
_startService();
Log.w("myApp", "START");
}
#Override
public void onDestroy()
{
super.onDestroy();
_shutdownService();
Log.w("myApp", "STOPPED");
}
/*
* starting the service
*/
private void _startService()
{
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
try {
doServiceWork();
} catch (ClientProtocolException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},0,INTERVAL);
;
}
/*
* start the processing, the actual work, getting config params, get data from network etc
*/
private void doServiceWork() throws ClientProtocolException, IOException
{
String FILENAME="blichData";
String info=null;
String classLetter = null,classNum1=null;
int classNum = 0;
try{
FileInputStream fis=openFileInput(FILENAME);
byte[] dataArray = new byte[fis.available()];
while(fis.read(dataArray)!=-1)
{
info = new String(dataArray);
}
classLetter = info.substring(0, info.lastIndexOf(" "));
classNum1 =info.substring(info.lastIndexOf(" ")+1);
classNum=Integer.parseInt(classNum1);
fis.close();
}catch (Exception e){
}
if (classLetter!=null && classNum1!=null) {
Oldhtml=readHTMLfromFile();
if (GetHTML.isHavingChanges(classLetter,classNum))
{
myNotify();
writeHTMLtoFile(currHtml);
/*
try {
String data= "false";
FileOutputStream fos = openFileOutput("blichService", Context.MODE_PRIVATE);
fos = openFileOutput("blichService",Context.MODE_PRIVATE);
fos.write(data.getBytes());
fos.close();
}
catch (Exception e) {}
*/
}
}
;
}
/*
* shutting down the service
*/
private void _shutdownService()
{
if (timer != null) timer.cancel();
Log.i(getClass().getSimpleName(), "Timer stopped...");
}
public void writeHTMLtoFile(String html) {
try {
String FILENAME = "blichNotifyData";
String data= html;
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos = openFileOutput(FILENAME,Context.MODE_PRIVATE);
fos.write(data.getBytes());
fos.close();
}
catch (Exception e){}
}
public String readHTMLfromFile() {
String FILENAME = "blichNotifyData";
String info="";
try{
FileInputStream fis=openFileInput(FILENAME);
if (fis.available()>0)
{
byte[] dataArray = new byte[fis.available()];
while(fis.read(dataArray)!=-1)
{
info = new String(dataArray);
}
fis.close();
}
else {
Oldhtml="null";
}
}
catch (Exception e) {}
return info;
}
public void myNotify()
{
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent= new Intent (this,SchoolBlichActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
String body = " בליך";
String title = "ישנם שינויים חדשים!";
Notification n =new Notification(R.drawable.table, body, System.currentTimeMillis());
n.flags |=Notification.FLAG_AUTO_CANCEL;
n.setLatestEventInfo(getApplicationContext(), title, body, pi);
n.defaults = Notification.DEFAULT_ALL;
number++;
n.number=number;
nm.notify(0,n);
}
}
And if it is needed, the HTML extracting class:
public class GetHTML {
public static boolean isHavingChanges(String classLetter,int classNum) throws ClientProtocolException, IOException {
int classLetterCode = 0;
int timeTableCode=1;
if (classLetter.equals("ט"))
classLetterCode=0;
else if (classLetter.equals("י"))
classLetterCode=1;
else if (classLetter.equals("יא"))
classLetterCode=2;
else if (classLetter.equals("יב"))
classLetterCode=3;
switch(classLetterCode)
{
case 0:
if (classNum>=1 && classNum<=7)
timeTableCode=1;
else if (classNum>7 && classNum<=14)
timeTableCode=2;
break;
case 1:
if (classNum>=1 && classNum<=7)
timeTableCode=3;
else if (classNum>7 && classNum<=14)
timeTableCode=4;
break;
case 2:
if (classNum>=1 && classNum<=7)
timeTableCode=5;
else if (classNum>7 && classNum<=14)
timeTableCode=6;
break;
case 3:
if (classNum>=1 && classNum<=7)
timeTableCode=7;
else if (classNum>7 && classNum<=14)
timeTableCode=8;
break;
}
String url = "http://blich.iscool.co.il/DesktopModules/IS.TimeTable/MainScreen.aspx?pid=17&mid=6264&page="+timeTableCode+"&msgof=0&static=1";
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
String html = "";
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder str = new StringBuilder();
String line = null;
while((line = reader.readLine()) != null)
{
str.append(line);
}
in.close();
html = str.toString();
html = Jsoup.parse(html).text();
if (NotifyService.Oldhtml.equalsIgnoreCase(html)) {
return false;
}
if (timeTableCode%2!=0){
for (int i=0;i<8;i++) {
if (!html.contains(i+" "+i)) {
NotifyService.currHtml=html;
return true;
}
}
}
if (timeTableCode%2==0) {
for (int i=8;i<15;i++) {
if (!html.contains(i+" "+i)) {
NotifyService.currHtml=html;
return true;
}
}
}
return false;
}
}
Ignore the foreign language. xD
I just want to understand what have I done wrong?
Thanks

While I cannot tell out-of-the-box what portion of your code is problematic, you may try to analyze the memory usage through a heap dump taken with DDMS using Eclipse MAT. You will need to use the hprofconv tool to convert your Android heap dump into a format that MAT understands.
To get the HPROF Heap Dump, open the Dalvik Debug Monitor (DDMS), connect it you your emulator, select the process of your application and hit the "Dump HPROF file" icon.

Related

Bluetooth Printing from Android not working as expected

I have been using the following code to send data to a bluetooth printer:
try {
BluetoothAdapter oBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice oDispositivo = oBluetoothAdapter.getRemoteDevice(cMAC);
Method oMethod = oDispositivo.getClass().getMethod("createRfcommSocket",new Class[] { int.class });
oSocket = (BluetoothSocket) oMethod.invoke(oDispositivo, Integer.valueOf(1));
oSocket.connect();
btoutputstream = new BufferedWriter(new OutputStreamWriter(oSocket.getOutputStream(),"ISO_8859_1"));
// Enviamos el mensaje
int off = 0;
while(off < nLength){
btoutputstream.write(msg,off,nBloque);
btoutputstream.flush();
Thread.sleep(nSleep);
off += nBloque;
if((off + nBloque) > nLength) nBloque = nLength - off;
}
btoutputstream.flush();
}catch(Exception e){
return cFail + " || Exception: " + e.toString();
}
finally{
try{
if(btoutputstream != null) btoutputstream.close();
if(oSocket != null) oSocket.close();
}catch(Exception e2){
return e2.toString();
}
}
The problem is that this code is not working on the same printer with a new Bluetooth device. It prints the first block of code and it doesn't print anymore.
So I've been looking for a way to make it work, and I ended up using this:
public static String BluetoothPrint()
{
try{
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mmDevice = mBluetoothAdapter.getRemoteDevice(cMac);
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
beginListenForData();
mmOutputStream.write(cText.getBytes());
} catch(Exception e) {
return "error: " + e.toString();
} finally {
// try{
// stopWorker = true;
// mmOutputStream.close();
// mmInputStream.close();
// mmSocket.close();
// } catch(Exception e) {
// return "error: " + e.toString()
// }
}
return "ok";
}
public static void beginListenForData(){
try {
final Handler handler = new Handler();
// This is the ASCII code for a newline character
final byte delimiter = 10;
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted()
&& !stopWorker) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0,
encodedBytes, 0,
encodedBytes.length);
final String data = new String(
encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
//myLabel.setText(data);
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (Exception ex) {
stopWorker = true;
}
}
}
});
workerThread.start();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
This code is working correct, but my main problem is that the printing is very slow (up to 1:30 min, while before it was printing in 15-20 seconds). I realized that it is going slow because of the empty lines. The printer is slow while printing them, but there's no problem with the lines which have text.
So I'm looking for a way to speed up that code, but I'm stuck. I tried to make the read buffer bigger, but doesn't seem to do nothing.
I've finally managed to get it working properly.
In the text that I was sending to print, the empty lines just had a linebreak (CRLF). As the other ones were printing OK, I tried to add a character on the empty lines (a space, so it prints the same) and it doesn't print slow anymore. I don't know what's the reason of this, but it is working faster, just like before the bluetooth changed.

Parsing data with Sax Parser via button click all buttons return the same data instead of the exclusive data

Hi everyone this question is similar to a question I answered a few days ago for another user found here. I am displaying the parsed data in a list view the first button I click will successfully display its information however all buttons after that will simply display the same data the first button returned in that question i solved this by setting result to equal nothing in the onclick method but that does not appear to be solving it here. I have provided code and an image of the application below.
[![public void onClick(View aview) {
if (aview == incidentButton) {
startIncidentProgress();
}
if (aview == roadButton) {
startRoadworksProgress();
}
if (aview == plannedButton) {
startPlannedProgress();
}
}
public void startIncidentProgress() {
// Run network access on a separate thread;
new Thread(new Task(url1)).start();
}
public void startRoadworksProgress() {
// Run network access on a separate thread;
new Thread(new Task(url2)).start();
}
public void startPlannedProgress() {
// Run network access on a separate thread;
new Thread(new Task(url3)).start();
} //
class Task implements Runnable {
private String url;
public Task(String aurl) {
url = aurl;
}
#Override
public void run() {
URL aurl;
URLConnection yc;
BufferedReader in = null;
String inputLine = "";
Log.e("MyTag", "in run");
try {
Log.e("MyTag", "in try");
aurl = new URL(url);
yc = aurl.openConnection();
in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
//
// Throw away the first 2 header lines before parsing
//
//
//
while ((inputLine = in.readLine()) != null) {
result = result + inputLine;
Log.e("MyTag", inputLine);
}
in.close();
} catch (IOException ae) {
Log.e("MyTag", "ioexception");
}
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
try{
ListView lv = (ListView) findViewById(R.id.listView1);
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser parser = parserFactory.newSAXParser();
DefaultHandler handler = new DefaultHandler(){
String currentValue = "";
boolean currentElement = false;
public void startElement(String uri, String localName,String qName, Attributes attributes) throws SAXException {
currentElement = true;
currentValue = "";
if(localName.equals("item")){
rss = new HashMap<>();
}
}
public void endElement(String uri, String localName, String qName) throws SAXException {
currentElement = false;
if (localName.equalsIgnoreCase("title"))
rss.put("title", currentValue);
else if (localName.equalsIgnoreCase("description"))
rss.put("description", currentValue);
else if (localName.equalsIgnoreCase("link"))
rss.put("link", currentValue);
else if (localName.equalsIgnoreCase("georss:point"))
rss.put("georss:point", currentValue);
else if (localName.equalsIgnoreCase("author"))
rss.put("author", currentValue);
else if (localName.equalsIgnoreCase("comments"))
rss.put("comments", currentValue);
else if (localName.equalsIgnoreCase("pubDate"))
rss.put("pubDate", currentValue);
else if (localName.equalsIgnoreCase("item"))
RSSList.add(rss);
}
#Override
public void characters(char\[\] ch, int start, int length) throws SAXException {
if (currentElement) {
currentValue = currentValue + new String(ch, start, length);
}
}
};
parser.parse(new InputSource(new StringReader(result)), handler);
ListAdapter adapter = new SimpleAdapter(MainActivity.this, RSSList, R.layout.list_row,new String\[\]{"title","description","link","georss:point","author","comments","pubDate"},
new int\[\]{R.id.title, R.id.description, R.id.link, R.id.georss, R.id.author, R.id.comments, R.id.pubdate});
lv.setAdapter(adapter);
}
catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
});
}
}
}
any help you could offer would be appreciated.
Fixed this by clearing the contents of the list when the user clicks the buttons to execute the XML calls. In doing so the list view repopulates with the correct information instead of the same information. Code below.
public void onClick(View aview) {
if (aview == incidentButton) {
result = "";
RSSList.clear();
startIncidentProgress();
}
if (aview == roadButton) {
result = "";
RSSList.clear();
startRoadworksProgress();
}
if (aview == plannedButton) {
result = "";
RSSList.clear();
startPlannedProgress();
}
}

How to download a file after clicking a button (Android Studio)

I recently created an activity in my app. Now I wanted the user to download a .pdf file when he/she wants to view the guidelines. I wanted to implement this on a button. Any idea how to do this properly?
Heres my code below:
public class Exhibitor_Registration_Activity extends AppCompatActivity {
Button buttonDownload;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exhibitor_registration_);
this.setTitle("Buyer Registration");
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
myToolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp);
final Button buttonDownload = (Button) findViewById(R.id.buttonDownload);
buttonDownload.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
try {
//this is the file you want to download from the remote server
String path ="http://www.manilafame.com/website-assets/downloads/exhibitor-application-kit/local/201704/1-Summary-of-Participation-Details-April-2017_MN_002.pdfp";
//this is the name of the local file you will create
String targetFileName = null;
boolean eof = false;
URL u = new URL(path);
HttpURLConnection c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
FileOutputStream f = new FileOutputStream(new File("c:\\junk\\"+targetFileName));
InputStream in = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ( (len1 = in.read(buffer)) > 0 ) {
f.write(buffer,0, len1);
}
f.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
I also got the source code from here and here.
if you want resumable, speed of download ...
follow this steps
create a class DownloadManager.java
public class DownloadManager extends AsyncTask<String,String,String>{
String downloadlink,fileDestination;
public static final int ON_INIT=100,ON_ERROR=102,ON_PROGRASS=103,ON_COMPLETED=104,STATUS_DOWNLOADED=1500,STATUS_NOT_YET=1501;
private onUpdateListener onUpdateListener;
private String downloadedPath="";
private long downloaded=0;
private File file;
private String returnData=null;
private File cacheDownloadFile;
public DownloadManager(String downloadlink,String fileDestinationPath){
this.downloadlink=downloadlink;
this.fileDestination=fileDestinationPath;
file=new File(fileDestination, Tools.getFileName(downloadlink));
cacheDownloadFile=new File(AppCostants.CHACHE_PATH+Tools.getFileName(downloadlink));
try {
if(cacheDownloadFile.isFile())
downloaded=Tools.getFileSize(cacheDownloadFile);
else
downloaded=0;
Log.d("FILE_DOWNLOAD_TAG_p",downloaded+" <- "+cacheDownloadFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
fireOnUpdate(ON_INIT,"init ...");
}
#Override
protected String doInBackground(String... params) {
try {
File dir=new File(fileDestination);
File chacheDir=new File(AppCostants.CHACHE_PATH);
if(!chacheDir.isDirectory())
chacheDir.mkdirs();
if(!dir.isDirectory()){
dir.mkdirs();
}
if(file.exists()) {
Log.d("FILE_DOWNLOAD_TAG","File exist return complete");
return "COMPLETED";//file exist
}
if(!cacheDownloadFile.exists()){
cacheDownloadFile.createNewFile();
}
Log.d("FILE_DOWNLOAD_TAG","LINK "+downloadlink);
URL url=new URL(downloadlink);
HttpURLConnection urlConnection= (HttpURLConnection) url.openConnection();
if(downloaded>0)
urlConnection.setRequestProperty("Range","byte="+downloaded);
urlConnection.connect();
int status = urlConnection.getResponseCode();
InputStream inputStream=urlConnection.getInputStream();
int totalSize=urlConnection.getContentLength();
if(totalSize<=downloaded){
returnData= "COMPLETED";
publishProgress("File checked "+Tools.getFileName(file.getAbsolutePath()));
return returnData;
}
this.downloadedPath=cacheDownloadFile.getAbsolutePath();
byte[] buffer=new byte[1024];
int bufferLength=0;
FileOutputStream fileOutput=new FileOutputStream(cacheDownloadFile);
long d=0;
long starttime=System.currentTimeMillis();
while ((bufferLength=inputStream.read(buffer))>0){
fileOutput.write(buffer,0,bufferLength);
downloaded+=bufferLength;
d+=bufferLength;
//String l=" "+Tools.getFileName(file.getAbsolutePath())+" ( "+Tools.convertMemory(downloaded)+" / "+Tools.convertMemory(totalSize)+" )";
String l=" "+Tools.convertMemory(downloaded)+" / "+Tools.convertMemory(totalSize)+" ( "+getDownloadSpeed(starttime,d)+" )";
publishProgress(l);
if(downloaded>=totalSize){
break;
}
}
Log.d("FILE_DOWNLOAD_TAG","DWONLOADED TO "+downloadedPath+" ("+cacheDownloadFile.length()+")");
fileOutput.close();
if(Tools.fileCopy(file,cacheDownloadFile)){
Log.d("FILE_DOWNLOAD_TAG","file Copied, delete cache");
cacheDownloadFile.delete();
}
returnData="COMPLETED";
} catch (MalformedURLException e) {
returnData=null;
e.printStackTrace();
publishProgress(e.toString());
Log.d("###################",e+"");
} catch (IOException e) {
returnData=null;
e.printStackTrace();
publishProgress(e.toString());
}
return returnData;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
fireOnUpdate(ON_PROGRASS,values[0]);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(s!=null){
fireOnUpdate(ON_COMPLETED,downloadedPath);
}else{
fireOnUpdate(ON_ERROR,"Download failed");
}
}
public interface onUpdateListener{
void onUpdate(int code,String message);
}
public void setOnUpdateListener(onUpdateListener onUpdateListener){
this.onUpdateListener=onUpdateListener;
}
private void fireOnUpdate(int code,String message){
if(onUpdateListener!=null)
onUpdateListener.onUpdate(code,message);
}
private String getDownloadSpeed(long starttime,float totalDownloaded) {
long elapsedTime = System.currentTimeMillis() - starttime;
//byte :
float speed=1000f * totalDownloaded / elapsedTime;
return convert(speed);
}
private String convert(float value){
long kb=1024
,mb=kb*1024
,gb=mb*1024;
if(value<kb){
String speed=(value+"");
speed=speed.substring(0,speed.indexOf('.')+2);
return speed+" B/s";
}else if(value<mb){
value=value/kb;
String speed=(value+"");
speed=speed.substring(0,speed.indexOf('.'));
return (speed)+" KB/s";
}else if(value<gb){
value=(value/mb);
String speed=(value+"");
speed=speed.substring(0,speed.indexOf('.'));
return speed+" MB/s";
}
return "";
}
}
use this code in onClick()
DownloadManager downloadManager = new DownloadManager(url,filepath);
set event
downloadManager.setOnUpdateListener(new DownloadManager.onUpdateListener() {
#Override
public void onUpdate(int code, String message) {
if (code == DownloadManager.ON_COMPLETED) {
}
if(DownloadManager.ON_PROGRASS==code){}
}
});
start download by
downloadManager.execute();
lib setup
compile "commons-io:commons-io:+"
Tools.java
public static long getFileSize(File file) throws IOException {
FileOutputStream fileOutputStream=new FileOutputStream(file);
fileOutputStream.close();
return file.length();
}
public static boolean fileCopy(File dest,File source){
try {
FileUtils.copyFile(source,dest);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
It is really bad idea to download file in main thread.
Use separate Thread for this
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
//your downloading here
}
});
thread.start();
it`s better, but still not so good. There are some problems with it:
1) User know nothing about downloading
So better to show additional layout which overlays screen with progress bar, probably indeterminate if you want to write less code. Then after downloading is finished you just hide your layout.
You can use runOnUiThread inside run method in thread for it.
runOnUiThread(new Runnable() {
#Override
public void run() {
//just hide some popup
//or do what you want after downloading is finished
popupLayout.serVisibility(View.GONE);
}
});
2) If user will do action which re-creates activity/fragment (like changing screen orientaion) with running thread you will get memory leak and probably activity will not know about end of download.
There are few ways to solve this problem:
You can block screen orientation at this screen, at least while downloading. Probably easiest way in your case.
You can use downloading in foreground service. Its really good
practice, but you will have to learn about services.
You can try to interrupt downloading by calling thread.interrupt()
method in onDestroy of your Activity/Fragment
You can use something like rxJava/rxAndroid (so you don not use threads at all, but you need some time for learn rxJava)
UPD
About threads
Not so bad tutorial about threads in android
You can use AsyncTask instead of Thread, but I highly recommend to use threads especially for long operations.

mediacodec doesn't work smoothly

using this url I wrote below code to encode onpreviewframe data to mp4 video and I used a thread to do this job well, but it seems that it doesn't work properly.
private void initCodec() {
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root + "/Vocalist");
if(!myDir.exists()) {
myDir.mkdirs();
}
try {
File file = new File (myDir, "myVideo.mp4");
if(file.exists()){
file.delete();
}
fos = new FileOutputStream(file, false);
} catch (FileNotFoundException e) {
e.printStackTrace();
}try {
mMediaCodec = MediaCodec.createEncoderByType("video/avc");
}
catch (Exception e){
}
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc",
320,
240);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
mMediaCodec.configure(mediaFormat,
null,
null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
inputBuffers = mMediaCodec.getInputBuffers();
outputBuffers = mMediaCodec.getOutputBuffers();
}
private synchronized void encode(byte[] dataInput)
{
byte[] data = dataInput;
inputBuffers = mMediaCodec.getInputBuffers();// here changes
outputBuffers = mMediaCodec.getOutputBuffers();
int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
} else {
return;
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
Log.i("tag", "outputBufferIndex-->" + outputBufferIndex);
do {
if (outputBufferIndex >= 0) {
ByteBuffer outBuffer = outputBuffers[outputBufferIndex];
System.out.println("buffer info-->" + bufferInfo.offset + "--"
+ bufferInfo.size + "--" + bufferInfo.flags + "--"
+ bufferInfo.presentationTimeUs);
byte[] outData = new byte[bufferInfo.size];
outBuffer.get(outData);
try {
if (bufferInfo.offset != 0) {
fos.write(outData, bufferInfo.offset, outData.length
- bufferInfo.offset);
} else {
fos.write(outData, 0, outData.length);
}
fos.flush();
Log.i("camera", "out data -- > " + outData.length);
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo,
0);
} catch (IOException e) {
e.printStackTrace();
}
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = mMediaCodec.getOutputBuffers();
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat format = mMediaCodec.getOutputFormat();
}
} while (outputBufferIndex >= 0);
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null) {
return;
}
try {
initCodec();
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
#Override
public void onPreviewFrame(final byte[] bytes, Camera camera) {
if (recording == true) {
if(mThread.isAlive())
encode(bytes);
}
}
});
} catch (Exception e) {
Log.d("TAG", "Error starting camera preview: " + e.getMessage());
}
}
}
public void newOpenCamera() {
if (mThread == null) {
mThread = new CameraHandlerThread();
}
synchronized (mThread) {
mThread.openCamera();
}
}
private static void oldOpenCamera() {
try {
c = Camera.open(1);
Camera.Parameters parameters = c.getParameters();
parameters.set("orientation", "portrait");
parameters.setJpegQuality(100);
parameters.setPreviewFormat(ImageFormat.NV21);
parameters.setPreviewSize(320, 240);
c.setParameters(parameters);
}
catch (RuntimeException e) {
Log.e("camera", "failed to open front camera");
}
}
public CameraHandlerThread mThread = null;
public static class CameraHandlerThread extends HandlerThread {
Handler mHandler = null;
CameraHandlerThread() {
super("CameraHandlerThread");
start();
mHandler = new Handler(getLooper());
}
synchronized void notifyCameraOpened() {
notify();
}
public void openCamera() {
mHandler.post(new Runnable() {
#Override
public void run() {
oldOpenCamera();
notifyCameraOpened();
}
});
}
}
I converted onpreviewframe data to a video but after first second video doesn't play smoothly. what should I do ?
First, you're not forwarding the timing information with the frames:
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0)
So your BufferInfo.presentationTimeUs will always be zero when you dequeue the buffer.
Second, you don't appear to be using MediaMuxer, which means you're just writing raw the raw H.264 stream to a file. This is not ".mp4"; it doesn't include the timing information at all. Many video players don't even know what to do with plain H.264.
Wrapping the file as .mp4, with the frame timing from the camera, should yield better results.
Your code structure appears to be assuming that it can feed one frame of input and get one frame of output, which isn't always the case. You want to keep the input full, and drain the output as it becomes available.
You can find more information and some sample code on bigflake and in Grafika.

Download a large pdf with jsoup

I would like to download a large pdf file with jsoup. I have try to change timeout and maxBodySize but the largest file I could download was about 11MB. I think if there is any way to do something like buffering. Below is my code.
public class Download extends Activity {
static public String nextPage;
static public Response file;
static public Connection.Response res;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Bundle b = new Bundle();
b = getIntent().getExtras();
nextPage = b.getString("key");
new Login().execute();
finish();
}
private class Login extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
try {
res = Jsoup.connect("http://www.eclass.teikal.gr/eclass2/")
.ignoreContentType(true).userAgent("Mozilla/5.0")
.execute();
SharedPreferences pref = getSharedPreferences(
MainActivity.PREFS_NAME, MODE_PRIVATE);
String username1 = pref.getString(MainActivity.PREF_USERNAME,
null);
String password1 = pref.getString(MainActivity.PREF_PASSWORD,
null);
file = (Response) Jsoup
.connect("http://www.eclass.teikal.gr/eclass2/")
.ignoreContentType(true).userAgent("Mozilla/5.0")
.maxBodySize(1024*1024*10*2)
.timeout(70000*10)
.cookies(res.cookies()).data("uname", username1)
.data("pass", password1).data("next", nextPage)
.data("submit", "").method(Method.POST).execute();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
String PATH = Environment.getExternalStorageDirectory()
+ "/download/";
String name = "eclassTest.pdf";
FileOutputStream out;
try {
int len = file.bodyAsBytes().length;
out = new FileOutputStream(new File(PATH + name));
out.write(file.bodyAsBytes(),0,len);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I hope somebody could help me!
I think, it's better to download any binary file via HTTPConnection:
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL("http://example.com/file.pdf");
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_name.extension");
byte data[] = new byte[4096];
int count;
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
Jsoup is for parsing and loading HTML pages, not binary files.

Categories

Resources