I received a crash report, which is about java.lang.StringIndexOutOfBoundsException in ZhuangDictActivity$SearchDicAsyncTask.doInBackground
Here is the ZhuangDictActivity$SearchDicAsyncTask.doInBackground:
private class SearchDicAsyncTask extends AsyncTask<String, Integer, String> {
private byte searchStatus;
#Override
protected String doInBackground(String... params) {
if (params[0].length() > 0) {
word = params[0].trim();
long[] index = null;
FileAccessor in = null;
DictZipInputStream din = null;
try {
char key = GB2Alpha.Char2Alpha(word.charAt(0));
tableName = DatabaseHelper.transTableName(key);
index = databaseHelper.queryTable(tableName, word);
if (index != null) {
in = new FileAccessor(new File(dictFileName), "r");
byte[] bytes = new byte[(int) index[1]];
if (isDZDict) {
din = new DictZipInputStream(in);
DictZipHeader h = din.readHeader();
int idx = (int) index[0] / h.getChunkLength();
int off = (int) index[0] % h.getChunkLength();
long pos = h.getOffsets()[idx];
in.seek(pos);
byte[] b = new byte[off + (int) index[1]];
din.readFully(b);
System.arraycopy(b, off, bytes, 0, (int) index[1]);
} else {
in.seek(index[0]);
in.read(bytes);
}
wordDefinition = new String(bytes, "UTF-8");
} else {
searchStatus = 0;
return null;
}
} catch (FileNotFoundException ffe) {
searchStatus = 1;
return null;
} catch (IOException ex) {
ex.printStackTrace();
searchStatus = 2;
return null;
} finally {
try {
if (din != null)
din.close();
if (in != null)
in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
return wordDefinition;
}
}
The complete code is available here.
I have limited knowledge in Java and Android development. How should I solve this? I intended to post the complete stack traces but stackoverflow do not allow me to do so because it stated my question has too many code. Anyway, the line which is causing the problem is char key = GB2Alpha.Char2Alpha(word.charAt(0));.
It is possible that your string contains only white spaces. meaning it passed the condition:
if (params[0].length() > 0)
But when you call trim(), these are removed, resulting in an empty stream and an "IndexOutOfBoundsException" exception being thrown when you execute:
word.charAt(0)
EDIT
This is not the reason. After a test, when trim is called on a String with only whitespaces, the String remains unchanged.
Related
I am using a service to download files and extract them if they are archived.
The extraction method is wrapped as a asynctask to improve performance of the extraction process.
My problem is that when I run the app on the virtual device, all is fine and the extraction process is really fast but as soon as I test it on a real device (Nexus 9 tablet, Android 6x) the extraction process is really slow and takes minutes to complete.
Is there anything I can do, to speed up the extraction process?
I execute the asynctask with: new UnRarTask(targetAppName).execute();
Below the piece of code which is relevant:
public class DownloadTask implements Runnable {
private DownloadService service;
private DownloadManager downloadManager;
protected void init(DownloadService service, Intent intent) {
this.service = service;
downloadManager = (DownloadManager) MyApp_.getInstance().
getSystemService(Activity.DOWNLOAD_SERVICE);
DownloadRequest downloadRequest = intent.getParcelableExtra(DownloadService
.DOWNLOAD_REQUEST);
}
private class UnRarTask extends AsyncTask<Void, Integer, String> {
String rarPath = null;
int countRar = 0;
long copiedbytes = 0, totalbytes = 0;
Archive archive = null;
FileHeader fileHeader = null;
File archiveFile;
List<FileHeader> headers;
UnRarTask(String one) {
this.archiveFile = new File(one);
}
#Override
protected String doInBackground(Void... params) {
try {
archive = new Archive(new FileVolumeManager(archiveFile));
} catch (RarException | IOException e) {
e.printStackTrace();
}
String fileName = archiveFile.getName();
String absolutePath = archiveFile.getAbsolutePath();
String archiveDirectoryFileName = absolutePath.substring(0, absolutePath.indexOf(fileName));
if (archive != null) {
fileHeader = archive.nextFileHeader();
headers = archive.getFileHeaders();
for (FileHeader fh : headers) {
totalbytes = totalbytes + fh.getFullUnpackSize();
}
}
while (fileHeader != null) {
BufferedInputStream inputStream;
try {
inputStream = new BufferedInputStream(archive.getInputStream(fileHeader));
String extractedFileName = fileHeader.getFileNameString().trim();
String fullExtractedFileName = archiveDirectoryFileName + extractedFileName;
File extractedFile = new File(fullExtractedFileName);
FileOutputStream fileOutputStream = new FileOutputStream(extractedFile);
BufferedOutputStream flout = new BufferedOutputStream(fileOutputStream, BUFFER_SIZE);
if (extractedFile.getName().toLowerCase().endsWith(".mp3")
|| extractedFile.getName().toLowerCase().endsWith(".epub")
|| extractedFile.getName().toLowerCase().endsWith(".pdf")
|| extractedFile.getName().toLowerCase().endsWith(".mobi")
|| extractedFile.getName().toLowerCase().endsWith(".azw3")
|| extractedFile.getName().toLowerCase().endsWith(".m4b")
|| extractedFile.getName().toLowerCase().endsWith(".apk")) {
rarPath = extractedFile.getPath();
countRar++;
}
int len;
byte buf[] = new byte[BUFFER_SIZE];
while ((len = inputStream.read(buf)) > 0) {
//fileOutputStream.write(buf, 0, len);
copiedbytes = copiedbytes + len;
int progress = (int) ((copiedbytes / (float) totalbytes) * 100);
if (progress > lastProgress) {
lastProgress = progress;
service.showUpdateProgressNotification(downloadId, appName, progress,
"Extracting rar archive: " + lastProgress + " % completed", downloadStart);
}
}
archive.extractFile(fileHeader, flout);
flout.flush();
flout.close();
fileOutputStream.flush();
fileOutputStream.close();
inputStream.close();
fileHeader = archive.nextFileHeader();
} catch (RarException | IOException e) {
e.printStackTrace();
}
}
if (countRar == 0) {
filePath = "Error";
broadcastFailed();
}
if (copiedbytes == totalbytes) {
if (archive != null)
archive.close();
}
return null;
}
}
}
I have a program in which I'm trying to download the content of an image file from a server. I'm using java socket to download it. After downloading, I use BitmapFactory.decodeByteArray() to create a bitmap.
At the server side, the file is a .jpg file and it's only about 180 KBytes, so I don't need to try scaling it. I can see through logs that the exact number of bytes in the file is received by my image download code. I store all the bytes in a byte[] array and then convert it into a bitmap.
The imageView is initially hidden and then supposed to be made visible after populating the image. But using BitmapFactory.decodeByteArray() is returning null always. I did see some other posts about null bitmap, but nothing seems to have an answer for this problem.
I don't want to use any external library just for this, so please do not give me suggestions to try out some other libraries. Can someone spot any problem with the code? The server side program is also mine and I know that part is correct because using that, browsers are able to download the same image file. I have copy-pasted it below.
public class ImageDownloader {
private Socket sockToSrvr;
private PrintWriter strmToSrvr;
private BufferedInputStream strmFromSrvr;
private String srvrAddr;
private int port;
private String remoteFile;
private Context ctxt;
private Bitmap imgBmap;
private View parkSpotImgVwHldr;
private View mngAndFndVwHldr;
private View parkSpotImgVw;
public ImageDownloader(Context c) {
srvrAddr = KloudSrvr.srvrIp();
port = KloudSrvr.port();
sockToSrvr = null;
strmFromSrvr = null;
strmToSrvr = null;
remoteFile = null;
ctxt = c;
imgBmap = null;
parkSpotImgVwHldr = null;
mngAndFndVwHldr = null;
parkSpotImgVw = null;
}
public void downloadFile(String remf, View parkSpotImgVwHldrVal,
View mngAndFndVwHldrVal, View parkSpotImgVwVal) {
remoteFile = remf;
parkSpotImgVwHldr = parkSpotImgVwHldrVal;
mngAndFndVwHldr = mngAndFndVwHldrVal;
parkSpotImgVw = parkSpotImgVwVal;
Thread dwnThrd = new Thread() {
#Override
public void run() {
imgBmap = null;
openServerConnection(); sendReq(); doDownload(); closeServerConnection();
((Activity)ctxt).runOnUiThread(new Runnable() {
public void run() {
((Activity)ctxt).runOnUiThread(new Runnable() {
public void run() {
mngAndFndVwHldr.setVisibility(View.GONE);
parkSpotImgVwHldr.setVisibility(View.VISIBLE);
Toast.makeText(ctxt, "completed", Toast.LENGTH_LONG).show();
}
});
}
});
}
};
dwnThrd.start();
}
private void sendReq() {
if(strmToSrvr == null) return;
String req = "GET /downloadFile " + remoteFile + " HTTP/1.1\r\n\r\n";
Log.d("IMG-DWNL-LOG: ", "writing req msg to socket " + req);
strmToSrvr.write(req); strmToSrvr.flush();
}
private void doDownload() {
boolean gotContLen = false;
int contLen = 0;
while(true) {
String inLine = getLine(strmFromSrvr); if(inLine == null) break;
if((gotContLen == true) &&
(inLine.replace("\r", "").replace("\n", "").isEmpty() == true)) break;
if(inLine.trim().startsWith("Content-Length:") == true) {
// an empty line after this signifies start of content
String s = inLine.replace("Content-Length:", "").trim();
try {contLen = Integer.valueOf(s); gotContLen = true; continue;}
catch(NumberFormatException nfe) {contLen = 0;}
}
}
if((gotContLen == false) || (contLen <= 0)) return;
byte[] imgByts = new byte[contLen];
int totRdByts = 0, rdByts, chnk = 1024, avlByts;
while(true) {
try {
avlByts = strmFromSrvr.available(); if(avlByts < 0) break;
if(avlByts == 0) {try {Thread.sleep(1000);} catch(InterruptedException ie) {} continue;}
rdByts = (avlByts < chnk) ? avlByts : chnk;
rdByts = strmFromSrvr.read(imgByts, totRdByts, rdByts); if(rdByts < 0) break;
if(rdByts == 0) {try {Thread.sleep(1000);} catch(InterruptedException ie) {} continue;}
totRdByts += rdByts;
if(totRdByts >= contLen) break;
} catch(IOException ioe) {return;}
}
if(totRdByts < contLen) {
Log.d("IMG-DWNL-LOG: ", "error - bytes read " + totRdByts
+ " less than content length " + contLen);
return;
}
if(totRdByts <= 0) return;
Log.d("IMG-DWNL-LOG: ", "read all image bytes successfully, setting image into view");
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeByteArray(imgByts, 0, contLen, options);
if(bitmap == null) {Log.d("IMG-DWNL-LOG: ", "got a null bitmap");}
((ImageView)parkSpotImgVw).setImageBitmap(bitmap);
}
private void closeServerConnection() {
if(sockToSrvr == null) return;
if(strmFromSrvr != null) {
try {strmFromSrvr.close();}
catch(IOException e) {Log.d("IMG-DWNL-LOG: ", "Inp strm close exception");}
}
if(strmToSrvr != null) strmToSrvr.close();
try {sockToSrvr.close();}
catch(IOException e) {Log.d("IMG-DWNL-LOG: ", "Conn close exception");}
strmFromSrvr = null; strmToSrvr = null; sockToSrvr = null;
}
private void openServerConnection() {
try {sockToSrvr = new Socket(InetAddress.getByName(srvrAddr), port);}
catch(UnknownHostException e) {
Log.d("IMG-DWNL-LOG: ", "Unknown host exception"); sockToSrvr = null; return;
} catch(IOException e) {
Log.d("IMG-DWNL-LOG: ", "Server connect exception"); sockToSrvr = null; return;
}
Log.d("IMG-DWNL-LOG: ", "Connected to server");
try {
strmFromSrvr = new BufferedInputStream(sockToSrvr.getInputStream());
strmToSrvr = new PrintWriter(new BufferedWriter(new OutputStreamWriter
(sockToSrvr.getOutputStream())), true);
} catch(IOException e) {
closeServerConnection();
Log.d("IMG-DWNL-LOG: ", "Failed to open reader / writer. Closed the connection."); return;
}
}
private String getLine(BufferedInputStream dis) {
String outLine = "";
while(true) {
try {
int c = dis.read(); if((c == -1) && (outLine.length() <= 0)) return(null);
outLine += Character.toString((char)c);
if(c == '\n') return(outLine);
} catch(IOException e) {if(outLine.length() <= 0) return(null); return(outLine);}
}
}
}
I was making a mistake, assuming that a .jpg file's bytes can be directly decode with the android bitmap decoder. Apparently this is not the case. So, I wrote the received bytes into a temporary file in the phone storage and then called BitmapFactory.decodeFile() which is able to return a good bitmap and ends up showing the image.
So, have a working solution now.
Still - if anyone has a better suggestion how to decode directly from the received bytes (which are from a .jpg file), I would be very interested to try it out since that would be more efficient. Thanks.
I'm creating an app to read string values over Bluetooth serial port. My data receiving but in two parts. If I send $F00,A,B,0,M# via bluetooth it only reads $ in first part and F00,A,B,0,M# in next part. I provided my code here. Please do correct me if I'm wrong.
InputStream inputStream=null;
int avilableBytes=0;
public ConnectedThread(BluetoothSocket socket){
InputStream temp=null;
try{
temp=socket.getInputStream();
}catch (IOException e){
e.printStackTrace();
}
inputStream=temp;
}
public void run() {
try{
int bytes;
while (true){
try{
avilableBytes=inputStream.available();
if (avilableBytes>0){
byte[] buffer=new byte[avilableBytes];
bytes=inputStream.read(buffer);
final String readMessage=new String(buffer,0,bytes);
bt_handler.obtainMessage(handlerState,bytes,-1,readMessage).sendToTarget();
Log.d("PRAVEEN",readMessage);
}
}catch (IOException e){
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
Data are like stream bytes and can not be processed immediately when it comes with a few bytes. Data will not come all at once as a single packet. You have to use the other byte[] buffer (MainBuffer) in which you will gradually save incoming byte and move the index in that buffer. Then, from time to time (e.g. in the timer once per second) take data from the main buffer and processed it. By default you must implement some data frame with a separator (eg. Data * data * data * - Many ways to do it good or bad). I dealt with this in .net via Xamarin, but just as an example it may be helpfull :
update example, format
In ConnectedThread :
public override void Run()
{
while (true)
{
try
{
int readBytes = 0;
lock (InternaldataReadLock)
{
readBytes = clientSocketInStream.Read(InternaldataRead, 0, InternaldataRead.Length);
Array.Copy(InternaldataRead, TempdataRead, readBytes);
}
if (readBytes > 0)
{
lock (dataReadLock)
{
dataRead = new byte[readBytes];
for (int i = 0; i < readBytes; i++)
{
dataRead[i] = TempdataRead[i];
}
}
Bundle dataBundle = new Bundle();
dataBundle.PutByteArray("Data", dataRead);
Message message = btlManager.sourceHandler.ObtainMessage();
message.What = 1;
message.Data = dataBundle;
btlManager.sourceHandler.SendMessage(message);
}
}
catch (System.Exception e)
{
btlManager.btlState = BTLService.BTLState.Nothing;
}
}
}
In BTLHandler :
public override void HandleMessage(Message msg)
{
switch (msg.What)
{
case 1:
{
byte[] data = msg.Data != null ? msg.Data.GetByteArray("Data") : new byte[0];
btlService.BTLReceiveData(data);
}
break;
}
}
public void BTLReceiveData(byte[] data)
{
lock (dataReadLock)
{
for (int i = 0; i < data.Length; i++)
{
dataRead[dataReadWriteCursor] = data[i];
dataReadWriteCursor++;
}
}
}
In Timer :
int tmpWriteCursor = dataReadWriteCursor;
int tmpReadCursor = dataReadReadCursor;
lock (dataReadLock)
{
int newBytes = dataReadWriteCursor - dataReadReadCursor;
for (int i = 0; i < newBytes; i++)
{
dataReadMain[dataReadReadCursor] = dataRead[dataReadReadCursor++];
}
}
bool odradkovani = false;
string tmpRadek = "";
int lastLineIndex = 0;
List<string> list = new List<string>();
for (int i = LastWriteLineIndex; i < tmpWriteCursor; i++)
{
if (dataReadMain[i] >= 32 && dataReadMain[i] <= 255)
{
tmpRadek += (char)dataReadMain[i];
}
else if (dataReadMain[i] == 13) odradkovani = true;
else if (dataReadMain[i] == 10)
{
if (odradkovani)
{
odradkovani = false;
list.Add(Utils.GetFormatedDateTime(DateTime.Now) + " " + tmpRadek);
tmpRadek = "";
lastLineIndex = i + 1;
}
}
else
{
tmpRadek += "?" + dataReadMain[i].ToString() + "?";
}
}
WriteDataToLog(list);
LastWriteLineIndex = lastLineIndex;
How can I ping some web server in Android to test if I've Internet connection?
So I need the method which pings the given site and returns false if I've no Internet and true if I have.
See this method, it's the best way to check for connectivity to a given server:
http://developer.android.com/reference/java/net/InetAddress.html#isReachable(int)
Use these methods to ping the server
public static void inSomeWhere()
{
String pingResult = getPingResult("168.126.63.1");
boolean isNetOk = true;
if (pingResult == null) {
// not reachable!!!!!
isNetOk = false;
}
}
public static String getPingResult(String a) {
String str = "";
String result = "";
BufferedReader reader = null;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
try {
Runtime r = Runtime.getRuntime();
Process process = r.exec("/system/bin/ping -c 3 " + a);
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
int i;
while ((i = reader.read(buffer)) > 0)
output.append(buffer, 0, i);
str = output.toString();
final String[] b = str.split("---");
final String[] c = b[2].split("rtt");
if (b.length == 0 || c.length == 0)
return null;
if(b.length == 1 || c.length == 1)
return null;
result = b[1].substring(1, b[1].length()) + c[0] + c[1].substring(1, c[1].length());
} catch (IOException e) {
return null;
} catch (Exception e) {
return null;
}
finally
{
if(reader != null)
{
try{reader.close();}catch(IOException ie){}
}
}
return result;
}
I want to know the exact functionality of Intent.ACTION_PRE_BOOT_COMPLETED. Currently, my requirement is to complete the task before the completion of booting of the device, i.e. before the call of Intent.ACTION_BOOT_COMPLETED.
Can anyone guide me on how to proceed to fulfill the requirement? Any help in this regard will be well appreciated.
ACTION_PRE_BOOT_COMPLETED is sended in ActivityManagerService.java::systemReady.
But to received it, the uid of your application must be system(1000).
for (int i=ris.size()-1; i>=0; i--) {
if ((ris.get(i).activityInfo.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) == 0) {
ris.remove(i);
}
}
Further more, this broadcast could only be received once in each upgrade( not very sure here, maybe should be each wipe data).
Note code below, if the target is in lastDoneReceivers, it will be removed.
ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
for (int i=0; i<ris.size(); i++) {
ActivityInfo ai = ris.get(i).activityInfo;
ComponentName comp = new ComponentName(ai.packageName, ai.name);
if (lastDoneReceivers.contains(comp)) {
ris.remove(i);
i--;
}
}
lastDoneReceivers is read from file /data/system/called_pre_boots.dat.
private static File getCalledPreBootReceiversFile() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
File fname = new File(systemDir, "called_pre_boots.dat");
return fname;
}
static final int LAST_DONE_VERSION = 10000;
private static ArrayList<ComponentName> readLastDonePreBootReceivers() {
ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>();
File file = getCalledPreBootReceiversFile();
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048));
int fvers = dis.readInt();
if (fvers == LAST_DONE_VERSION) {
String vers = dis.readUTF();
String codename = dis.readUTF();
String build = dis.readUTF();
if (android.os.Build.VERSION.RELEASE.equals(vers)
&& android.os.Build.VERSION.CODENAME.equals(codename)
&& android.os.Build.VERSION.INCREMENTAL.equals(build)) {
int num = dis.readInt();
while (num > 0) {
num--;
String pkg = dis.readUTF();
String cls = dis.readUTF();
lastDoneReceivers.add(new ComponentName(pkg, cls));
}
}
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
Slog.w(TAG, "Failure reading last done pre-boot receivers", e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
}
}
}
return lastDoneReceivers;
}
There is no such action as ACTION_PRE_BOOT_COMPLETED. I think that you normally can't fill your requirement. May be there is some mechanism for system signed apps to do that.