I've just wrote my first Android app with Android Studio. It's a vocabulary trainer and it reads in a text file in my assets folder when starting, which contains all the words (until now, I have only ~1000) like this: english$japanese$category. So, I thought this shouldn't be much work, even though I have an old Samsung S2. But It takes like 10 secs to start and sometimes it crashes.
Here's the critical code:
static String word;
static String[] listOfWords;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
readWords();
generateRandomWord();
}
public void readWords() {
try {
InputStream is = getAssets().open("words.txt");
String ww = "";
int data = is.read();
while(data != -1){
ww += (char) data;
data = is.read();
}
listOfWords = ww.split("\n");
} catch (IOException e) {
e.printStackTrace();
}
}
public void generateRandomWord() {
TextView textView = new TextView(this);
textView.setTextSize(40);
textView = (TextView) findViewById(R.id.text_id);
Random random = new Random();
int randomKey = random.nextInt(listOfWords.length-1);
String line = listOfWords[randomKey];
String[] parts = line.split("/");
Log.d("Tango-renshuu", "line: "+line+" "+parts.length+" "+parts[1]);
textView.setText(parts[1]);
word = parts[2];
}
The same thing happens when I try to go back to that Activity from another one, even though I'm using Intent.FLAG_ACTIVITY_CLEAR_TOP Like this:
public void back(View view) {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
Any idea or do you think its just my device?
Thank
You are reading the asset on the Main-Thread, you need to start a Task to load it, while the Activity is rendered the asset loading happens in background.
Your readWords method is fairly inefficient: you are creating a new string at every loop iteration, and you're reading the file character by character. Consider using a BufferedReader to read the strings line by line directly:
InputStream stream = getAssets().open("words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
ArrayList<String> lines = new ArrayList<String>();
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
listOfWords = lines.toArray(new String[lines.size()]);
reader.close();
If your code is still too slow after this optimization, then you should move this code to an AsyncTask so at least it doesn't freeze the UI, and you can display a loading spinner in the meantime.
Related
How can we read logs(verbose,debug etc) programmatically from android class and then search for a string or matching with a provided string in android.
Sometimes we need to handle some system or kernel layer related event. But as we have limited access of those code we can't handle them. We can see the logcat via adb and also see some log comes from kernel/framework layer.
Now the question is, How we can override or handling some event in our app based on those logs?
Here is a solution to this from any android app:
We need to make a class with some code like below:
public class LogsUtil {
private static final String processId = Integer.toString(android.os.Process
.myPid());
public static StringBuilder readLogs() {
StringBuilder logBuilder = new StringBuilder();
try {
String[] command = new String[] { "logcat", "-d", "threadtime" };
Process process = Runtime.getRuntime().exec(command);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.contains(processId)) {
logBuilder.append(line);
//Code here
}
}
} catch (IOException e) {
}
return logBuilder;
}
}
Then need to write below code in our activity from where we need to check the logs string:
//read the logs
StringBuilder logs = LogsUtil.readLogs();
if(logs.toString().contains("your_text"))
//your code
else //your code
I'm using this code to read text from assets:
private void Read(String file){
try{
String Name = file;
Name = Name.replaceAll("'", "");
file = getAssets().open(Name + ".txt");
reader = new BufferedReader(new InputStreamReader(file));
line = reader.readLine();
Text ="";
while(line != null){
line = reader.readLine();
if (line !=null){
Text += line+"\n";
LineNumber++;
if(LineNumber==50){btnv.setVisibility(View.VISIBLE);break; }
}
}
if (LineNumber<50){btnv.setVisibility(View.GONE);}
txtv.setText(Text);
}
catch(IOException ioe){
ioe.printStackTrace();
}
}
So I have to read first 50 lines of text, because the text is more then 300 lines, and all I know is to read file line by line, so if I read 300 lines line by line the app freezes for to long, so I read 50 first lines then 50 other lines and so on...
So after I read the first 50 lines with that code I call this other code to read next ones:
private void ContinueReading(){
if (LineNumber >= 50){
try{
while(line != null){
line = reader.readLine();
if (line !=null){
Text += line+"\n";
LineNumber++;
if (LineNumber==100){break;}
if (LineNumber==150){break;}
if (LineNumber==200){break;}
if (LineNumber==250){break;}
if (LineNumber==300){break;}
if (LineNumber==350){break;}
if (LineNumber==400){break;}
if (LineNumber==450){break;}
}
else{
btnv.setVisibility(View.GONE);
}
}
txtv.setText(Text);
}
catch(IOException ioe){ioe.printStackTrace();}
}
}
But as you see I leave open :
file = getAssets().open(emri + ".txt");
reader = new BufferedReader(new InputStreamReader(file));
And this is no good, is anyway to close them and open them again and start reading from last line, or any idea how to start reading from ex. line 50, then from line 100, etc.. ?
This looks like a good place for an AsyncTask. You can even update the TextView with the text as it's being read from the file.
txtv.setText("");
new MyFileReader().execute(filename);
.
.
.
// inner class
public class MyFileReader extends AsyncTask<String, String, Void> {
#Override
protected Void doInBackground(String... params) {
try{
InputStream file = getAssets().open(params[0].replaceAll("'", "") + ".txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(file));
String line;
while ((line = reader.readLine()) != null) {
publishProgress(line + "\n");
}
reader.close();
} catch(IOException ioe){
Log.e(TAG, ioe);
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
txtv.append(values[0]);
}
}
You should use another Thread to read the complete file at once,
Please read this Asynctask in Android
But be care full you can not perform any UI related operations(Like change text of a TextView) on a different Thread instead Of Main Thread....! For that purpose, please also concern below link,
Android “Only the original thread that created a view hierarchy can touch its views.”
I am trying to break a String into multiple pages. The string contains a story(hence a bit long one) and I need to load this into an ArrayList of Strings which will be displayed as various pages.
Below is the method in my code which takes in the long string and breaks it into pages and loads the ArrayList of Strings. My problem is that this works pretty fast in Android 3.2. But in my another phone which is 4.3, it loads very slow(say it takes about 10 seconds for what works in 2 seconds in 3.2 one). Below is my code. Can anyone of you please suggest any improvements here in the code which can make it work faster in the new version. Not sure why should new version process slower than the old one.
TextView contentTextView = (TextView)findViewById(R.id.textViewStory1);
String contentString;
TextPaint tp = contentTextView.getPaint();
String[] pages;
int totalPages=0;
ArrayList<String> pagesArray = new ArrayList<String>();
public void loadStory(String storyName){
//initialize variables
numCharsLine=0;
contentString = getStringFromTxtFile(storyName);
int linesInOnePage = getLinesInPage();//get how many lines will be displayed in one page
//load story into arraylist pagesArray
while (contentString != null && contentString.length() != 0)
{
totalPages ++;
int numChars = 0;
int lineCount = 0;
while ((lineCount < linesInOnePage) && (numChars < contentString.length())) {
numCharsLine = tp.breakText(contentString.substring(numChars), true, pageWidth, null);
numChars = numChars + numCharsLine;
lineCount ++;
}
// retrieve the String to be displayed in pagesArray
String toBeDisplayed = contentString.substring(0, numChars);
contentString = contentString.substring(numChars);
pagesArray.add(toBeDisplayed);
numChars = 0;
lineCount = 0;
}
//get the pagecount and reset pageNumber to current page
totalPages=pagesArray.size();
pages = new String[pagesArray.size()];
pages = pagesArray.toArray(pages);
}
Also below is the method for loading the contents of text file into the string
public String getStringFromTxtFile(String fileName) {
try {
InputStream is = getAssets().open(fileName+".txt");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
return new String(buffer);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
For now you are reading whole file to string, then split this string into lines and process it.
I would suggest you to union reading text from file and processing it line by line.
for example, using class LineNumberReader or BufferedReader:
BufferedReader bis = new BufferedReader(new FileReader("my/file/path"));
String line;
while ((line = bis.readLine()) != null) {
processLine(line); //process current line
}
therefore you don't have to do so many concat and substring actions.
I want to write a simple android application that listens for a command such as long press menu button or press home button 3 times and then changes value in /sys/class/mdnie/mdnie/negative to 1 or 0. I have no idea where to begin, I know modifying this value requires root access and I can successfully do this by echo > 1 /sys/class/mdnie/mdnie/negative
Any guidance is appreciated, I need this for a friend who is visually impaired. This application will toggle negative colors for himon some samsung devices and he would like to press the home key 3 times to toggle the negative colors on and off from anywhere on the device.
It shouldn't be difficult. It would be an activity with no associated display that toggles the value and finishes immediately afterwards. The code would look like this (not it is untested code - if you face issues, post them as new StackOverflow questions different from this one):
public class ToggleNegativeColorsActivity extends Activity {
private static final String FILEPATH = "/sys/class/mdnie/mdnie/negative";
#Override
public void onCreate(Bundle savedInstanceState) {
super(savedInstanceState);
try {
String value = readFileAsString(FILEPATH);
if ("1".equals(value.trim())) {
writeStringToFile(FILEPATH, "0");
} else {
writeStringToFile(FILEPATH, "1");
}
catch (IOException e) {
e.printStackTrace();
}
finish();
}
// Grabbed from http://stackoverflow.com/questions/1656797/how-to-read-a-file-into-string-in-java
private String readFileAsString(String filePath) throws IOException {
StringBuffer fileData = new StringBuffer();
BufferedReader reader = new BufferedReader(
new FileReader(filePath));
char[] buf = new char[1024];
int numRead;
while((numRead=reader.read(buf)) != -1){
String readData = String.valueOf(buf, 0, numRead);
fileData.append(readData);
}
reader.close();
return fileData.toString();
}
// Grabbed from http://stackoverflow.com/questions/1053467/how-do-i-save-a-string-to-a-text-file-using-java
private void writeStringToFile(String filePath, String value) throws IOException {
PrintWriter out = new PrintWriter(filePath);
out.print(value);
out.close();
}
}
I have a file with 72000 lines and 2,6M size. In this File are all String items for a AutoCompleteTextView.
Currently i read them like this:
List<IcdObject> codes = new ArrayList<IcdObject>();
try {
InputStream input = getAssets().open("icd.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(input));
String line;
while ((line = br.readLine()) != null) {
String[] icd = line.split("|");
codes.add(new IcdObject(icd[0], icd[1]));
}
input.close();
} catch (IOException e) {
// TODO
e.printStackTrace();
}
ArrayAdapter<IcdObject> adapter = new ArrayAdapter<IcdObject>(this,
android.R.layout.select_dialog_item, codes);
AutoCompleteTextView at = (AutoCompleteTextView) findViewById(R.id.AutoComplete);
at.setThreshold(3);
at.setAdapter(adapter);
The Garbage Collector goes crazy when i start the activity. But in the end all items get loaded, after waiting around 1min.
Is there a method to load the file on demand or make it faster?
You can use Loaders API to perform these large data set reading.
The LoaderManager.LoaderCallbacks class is used to get callback from LoaderManager